如何防止多个 injection/execution 内容脚本响应单个事件?

How can I prevent multiple injection/execution of content script(s) in response to a single event?

我正在构建一个 chrome 扩展来响应上下文菜单上的点击事件。

我的后台脚本使用 chrome.contextMenus.create api 方法调用创建上下文菜单并设置点击处理程序,如下面的代码所示:

//event.js
function onItemClick(info, tab){
    // Inject the content script into the current page
    chrome.tabs.executeScript(null, { file: 'content.js' });

    // Perform the callback when a message is received from the content script
    chrome.runtime.onMessage.addListener(function(message){
        var url = "data:text/html;charset=utf8,";

        function append(key, value){
            var input = document.createElement('textarea');
            input.setAttribute('name', key);
            input.textContent = value;
            form.appendChild(input);
        }

        var form = document.createElement('form');
        form.method = 'POST';
        form.action = 'http://localhost/myapp/myapp.php';
        form.style.visibility = "hidden";
        append('url', message.url);
        append('text', message.selectedText);
        url = url + encodeURIComponent(form.outerHTML);
        url = url + encodeURIComponent('<script>document.forms[0].submit();</script>');
        chrome.tabs.create({url: url, active: true});
    });
}

var context = "selection";
var title = "Share in new tab";
var id = chrome.contextMenus.create({"title": title, "contexts": [context], "onclick": onItemClick});

上面的后台脚本以编程方式创建一个自动在新选项卡中提交的表单。这样做时,它会调用下面的 "content script" 以从当前 page/tab.

中获取一些信息
//content.js
chrome.runtime.sendMessage({
    'url': window.location.href,
    'selectedText': window.getSelection().toString()
});

问题是这样的。后台脚本中的点击处理程序多次将 "content script" 注入当前页面(即每次调用点击处理程序时)。作为多次注入的结果,"content script" 的每个注入实例都会被执行,从而导致打开多个新的 tabs/pages。每次单击上下文菜单项时,打开的新选项卡数量都会增加一个,这表明问题确实是内容脚本的多次注入和执行。我怎样才能只注入一次内容脚本,或者至少确保只有一个 "instance" 注入的脚本将消息发送回我的后台脚本?

我曾尝试在清单中自动注入脚本,但此后调用 chrome.tabs.executeScript 会导致无休止地创建选项卡。所以,我真的需要能够按需注入脚本,但要找到一种方法来防止多次注入或至少确保只有一个 "injection" 发回消息。请帮忙!

解决方法很简单:可以在内容脚本中创建一个全局控制变量。在开始时检查它,如果是 undefined 则将其设置为 true 并继续。第一个执行的内容脚本将设置变量并防止其他人做任何事情。

顺便说一句,我看到您在另一个侦听器中向 chrome.runtime.onMessage 添加一个侦听器:这不是好的做法,因为它会为同一事件添加多个侦听器并导致多次执行它们.您应该改为在外部声明侦听器,发送一条不同的消息说 "do something" 或 "do something else".

在内容脚本中:

if (window.messageSent === undefined) {
    window.messageSent = true;

    chrome.runtime.sendMessage({
        action: "submit the form",
        url: window.location.href,
        selectedText: window.getSelection().toString()
    });
}

background.js:

chrome.runtime.onMessage.addListener(function(message){
    if (message.action == "submit the form") {
        // do what you need to submit the form
        var url = "data:text/html;charset=utf8,";
        function append(key, value){
        ...
    }
});