首页 > 解决方案 > 如何在一体化(Edge/Firefox/Chrome)浏览器扩展的内容脚本中实现消息传递回调?

问题描述

开发环境操作系统: Windows 7 Enterprise LTS
浏览器兼容性最低要求:截至 2018 年应支持所有 Edge、Firefox、Chrome 浏览器。
当前持续存在的问题:无法在开发工作站上运行 VM;无法运行 Windows 10 虚拟机来调试 Microsoft Edge 扩展。

解释:

它们在内容脚本上的处理方式有何不同:

根据各种参考资料:

Firefox / Chrome / MS Edge 扩展使用 chrome.* 或 browser.*

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 未运行),我无法判断是否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.sendMessagebrowser.runtime.sendMessageAPI 调用,以便在各自的平台上处理回调。我真的很想改变这一点。

所以我在问有什么更好的方法,可以用来检测不同的平台,同时正确处理消息传递回调

提前致谢。

标签: firefoxgoogle-chrome-extensioncallbackmicrosoft-edgefirefox-addon-webextensions

解决方案


我相信我解决了它。

编辑:最终版本(更新更稳定,消息传递更少):

//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);
        }
    }
}

(原帖):

最终版本(现已过时):

//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语句中,通过检查 a 的返回值window.browser.runtime.sendMessage是否为Promiseor undefined,我们可以检测平台是否为Firefoxor Edge

我认为这是在内容脚本上处理消息传递回调/消息响应的唯一解决方案。

我真的想不出比这更好的解决方案。所以我将从现在开始使用它。

但是,如果其他人知道更好的方法,即您不需要在每次函数调用时为 Firefox 和 Edge 发送 1 条额外的虚拟消息,那就太好了!

内容脚本中的任何内容都不是持久的,这很糟糕,即使您存储有关代码正在运行的平台的信息,您仍然必须在过滤出runtime.sendMessage要调用的函数之前从后台脚本中获取信息,所以它并没有真正节省太多时间。


推荐阅读