如何在一体式 (Edge/Firefox/Chrome) 浏览器扩展的内容脚本中实现消息传递回调?
How to implement message passing callbacks in an all-in-one (Edge/Firefox/Chrome) browser extension's content script?
开发环境OS: Windows 7 Enterprise LTS
浏览器兼容性最低要求: 应支持所有 Edge、Firefox、Chrome 浏览器,截至 2018 年。
当前存在的问题: 无法 运行 开发工作站上的 VM;无法 运行 Windows 10 个 VM 来调试 Microsoft Edge 扩展。
说明:
- 一个 "all-in-one browser extension" 指的是一个浏览器扩展代码,它使用相同的代码但略有不同,以在各种 WebExtensions / Chrome 扩展支持的浏览器上工作。至少,相同的代码库应该可以工作,运行 在 Edge、Firefox 和 Chrome 上只需很小的改动。
- 针对 Edge/Firefox/Chrome 扩展的 内容脚本 的回调处理方式不同。
- 由于未知原因,我无法在我的工作站计算机上 运行 虚拟机。 VM 运行ning 时,VM client 为黑色。这是我无法解决的本地化问题,因此我不得不寻找其他 solution/alternative.
它们在内容脚本上的处理方式有何不同:
- Edge:
browser.runtime.sendMessage
使用回调,returns undefined
。
- Firefox:
browser.runtime.sendMessage
使用 Promise,returns 使用 Promise。
- Chrome:
chrome.runtime.sendMessage
使用回调,returns undefined
.
根据各种参考:
https://www.smashingmagazine.com/2017/04/browser-extension-edge-chrome-firefox-opera-brave-vivaldi/
在内容脚本中,您可以在顶部声明以下 JavaScript 片段,以创建一个可以在其他任何地方引用的全局变量:
//Global "browser" namespace definition.
window.browser = (function() {
return window.msBrowser || window.browser || window.chrome;
})();
不幸的是,由于我遇到的问题(VM 不是 运行ning),我无法判断 window.msBrowser
是否仍在使用。在使用 namespace.runtime.sendMessage
.
处理 消息回调 时,此解决方案对我没有帮助
综上所述,我的主要问题是:如何编写可以正确处理回调的消息传递函数?
目前,我正在使用以下代码:
function sendGlobalMessage(messageRequest, callback) {
if (chrome && window.openDatabase) {
//This is Chrome browser
chrome.runtime.sendMessage(messageRequest, callback);
}
else if (browser) {
try {
//Edge will error out because of a quirk in Edge IndexedDB implementation.
//See https://gist.github.com/nolanlawson/a841ee23436410f37168
let db = window.indexedDB.open("edge", (Math.pow(2, 30) + 1));
db.onerror = function(e) {
throw new Error("edge is found");
};
db.onsuccess = function(e) {
//This is Firefox browser.
browser.runtime.sendMessage(messageRequest).then(callback);
};
}
catch (e) {
//This is Edge browser
browser.runtime.sendMessage(messageRequest, callback);
}
}
}
我真的觉得这是一个 hacky 解决方案,因为代码是基于浏览器平台独有的怪癖来分离 chrome.runtime.sendMessage
和 browser.runtime.sendMessage
API 调用,从而在各自的平台上处理回调。我真的想改变这个。
所以我想问一下,有什么更好的方法可以检测不同的平台,并且可以同时正确处理消息传递回调?
提前致谢。
我相信我解决了它。
编辑: FINAL 最终版本(更新更稳定,消息传递更少):
//Global "browser" namespace definition, defined as "namespace". Can be renamed to anything else.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
//Using instanceof to check for object type, and use the returned evaluation as a truthy value.
let supportPromises = false;
try {
supportPromises = window.namespace.runtime.getPlatformInfo() instanceof Promise;
}
catch(e) { }
if (supportPromises){
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
else {
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
}
}
(原Post):
最终版本(现已废弃):
//Global "browser" namespace definition.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
let returnValue = window.namespace.runtime.sendMessage({});
if (typeof returnValue === "undefined"){
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
else {
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
}
}
在第二个if
语句中,通过检查window.browser.runtime.sendMessage
的return值是Promise
还是undefined
,我们可以检测平台是 Firefox
还是 Edge
.
我认为这是处理消息传递 callbacks/message 内容脚本响应的唯一解决方案。
我真的想不出比这更好的解决方案了。所以我将从现在开始使用它。
但是,如果其他人知道更好的方法,一种不需要在每次函数调用时为 Firefox 和 Edge 发送 1 条额外虚拟消息的方法,那就太好了!
内容脚本中的任何内容都不是永久性的,这很糟糕,即使您存储了有关代码在哪个平台上运行的信息 运行,您仍然必须在过滤之前从后台脚本中获取信息知道调用哪个 runtime.sendMessage
函数,所以并没有真正节省多少时间。
开发环境OS: Windows 7 Enterprise LTS
浏览器兼容性最低要求: 应支持所有 Edge、Firefox、Chrome 浏览器,截至 2018 年。
当前存在的问题: 无法 运行 开发工作站上的 VM;无法 运行 Windows 10 个 VM 来调试 Microsoft Edge 扩展。
说明:
- 一个 "all-in-one browser extension" 指的是一个浏览器扩展代码,它使用相同的代码但略有不同,以在各种 WebExtensions / Chrome 扩展支持的浏览器上工作。至少,相同的代码库应该可以工作,运行 在 Edge、Firefox 和 Chrome 上只需很小的改动。
- 针对 Edge/Firefox/Chrome 扩展的 内容脚本 的回调处理方式不同。
- 由于未知原因,我无法在我的工作站计算机上 运行 虚拟机。 VM 运行ning 时,VM client 为黑色。这是我无法解决的本地化问题,因此我不得不寻找其他 solution/alternative.
它们在内容脚本上的处理方式有何不同:
- Edge:
browser.runtime.sendMessage
使用回调,returnsundefined
。 - Firefox:
browser.runtime.sendMessage
使用 Promise,returns 使用 Promise。 - Chrome:
chrome.runtime.sendMessage
使用回调,returnsundefined
.
根据各种参考:
https://www.smashingmagazine.com/2017/04/browser-extension-edge-chrome-firefox-opera-brave-vivaldi/
在内容脚本中,您可以在顶部声明以下 JavaScript 片段,以创建一个可以在其他任何地方引用的全局变量:
//Global "browser" namespace definition.
window.browser = (function() {
return window.msBrowser || window.browser || window.chrome;
})();
不幸的是,由于我遇到的问题(VM 不是 运行ning),我无法判断 window.msBrowser
是否仍在使用。在使用 namespace.runtime.sendMessage
.
综上所述,我的主要问题是:如何编写可以正确处理回调的消息传递函数?
目前,我正在使用以下代码:
function sendGlobalMessage(messageRequest, callback) {
if (chrome && window.openDatabase) {
//This is Chrome browser
chrome.runtime.sendMessage(messageRequest, callback);
}
else if (browser) {
try {
//Edge will error out because of a quirk in Edge IndexedDB implementation.
//See https://gist.github.com/nolanlawson/a841ee23436410f37168
let db = window.indexedDB.open("edge", (Math.pow(2, 30) + 1));
db.onerror = function(e) {
throw new Error("edge is found");
};
db.onsuccess = function(e) {
//This is Firefox browser.
browser.runtime.sendMessage(messageRequest).then(callback);
};
}
catch (e) {
//This is Edge browser
browser.runtime.sendMessage(messageRequest, callback);
}
}
}
我真的觉得这是一个 hacky 解决方案,因为代码是基于浏览器平台独有的怪癖来分离 chrome.runtime.sendMessage
和 browser.runtime.sendMessage
API 调用,从而在各自的平台上处理回调。我真的想改变这个。
所以我想问一下,有什么更好的方法可以检测不同的平台,并且可以同时正确处理消息传递回调?
提前致谢。
我相信我解决了它。
编辑: FINAL 最终版本(更新更稳定,消息传递更少):
//Global "browser" namespace definition, defined as "namespace". Can be renamed to anything else.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
//Using instanceof to check for object type, and use the returned evaluation as a truthy value.
let supportPromises = false;
try {
supportPromises = window.namespace.runtime.getPlatformInfo() instanceof Promise;
}
catch(e) { }
if (supportPromises){
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
else {
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
}
}
(原Post):
最终版本(现已废弃):
//Global "browser" namespace definition.
window.namespace = (function() {
return window.browser || window.chrome;
})();
function sendGlobalResponse(message, callback){
if (window.namespace === window.chrome) {
//Chrome
window.namespace.runtime.sendMessage(message, callback);
}
else if (window.namespace === window.browser) {
let returnValue = window.namespace.runtime.sendMessage({});
if (typeof returnValue === "undefined"){
//Edge
window.namespace.runtime.sendMessage(message, callback);
}
else {
//Firefox
window.namespace.runtime.sendMessage(message).then(callback);
}
}
}
在第二个if
语句中,通过检查window.browser.runtime.sendMessage
的return值是Promise
还是undefined
,我们可以检测平台是 Firefox
还是 Edge
.
我认为这是处理消息传递 callbacks/message 内容脚本响应的唯一解决方案。
我真的想不出比这更好的解决方案了。所以我将从现在开始使用它。
但是,如果其他人知道更好的方法,一种不需要在每次函数调用时为 Firefox 和 Edge 发送 1 条额外虚拟消息的方法,那就太好了!
内容脚本中的任何内容都不是永久性的,这很糟糕,即使您存储了有关代码在哪个平台上运行的信息 运行,您仍然必须在过滤之前从后台脚本中获取信息知道调用哪个 runtime.sendMessage
函数,所以并没有真正节省多少时间。