重新加载Chrome扩展程序后,chrome.runtime.sendMessage会从内容脚本中抛出异常

c00*_*0fd 5 javascript google-chrome google-chrome-extension content-script

我将注入的内容脚本中的消息发送回Chrome扩展程序中的后台脚本,如下所示:

chrome.runtime.sendMessage({action: "myResult"});
Run Code Online (Sandbox Code Playgroud)

这很好,直到我重新加载我的扩展(通过转到设置 - >扩展 - >"重新加载(Ctrl + R)"为我的扩展.)

反过来,当我的后台脚本启动时,它反复调用chrome.tabs.executeScript所有打开的选项卡以编程方式重新注入我的内容脚本(正如我在这个问题中所示).

但是在我这样做之后,如果我sendMessage从我的内容脚本中调用第一行,它会抛出此异常:

错误:连接到扩展名my_extension_id时出错

知道为什么会这样吗?

Rob*_*b W 7

重新加载扩展运行时,在以下任何一种情况下都会发生

  • 你打过电话chrome.runtime.reload().
  • 您已点击了重新加载扩展程序chrome://extensions/.
  • 扩展程序已更新.

然后内容脚本中的大多数扩展API方法停止工作(包括chrome.runtime.sendMessage导致问题中的错误).有两种方法可以解决此问题.

选项1:回退到仅限内容脚本的功能

如果您的扩展程序可以在没有后台页面的情况下完美运行,那么这可能是一个可接受 例如,除了修改DOM和/或执行跨源请求之外,您的内容脚本不会执行任何其他操作.

我在我的一个扩展程序中使用以下代码段,以便在从我的内容脚本调用任何Chrome扩展程序API之前检测运行时是否仍然有效.

// It turns out that getManifest() returns undefined when the runtime has been
// reload through chrome.runtime.reload() or after an update.
function isValidChromeRuntime() {
    // It turns out that chrome.runtime.getManifest() returns undefined when the
    // runtime has been reloaded.
    // Note: If this detection method ever fails, try to send a message using
    // chrome.runtime.sendMessage. It will throw an error upon failure.
    return chrome.runtime && !!chrome.runtime.getManifest();
}

// E.g.
if (isValidChromeRuntime()) {
    chrome.runtime.sendMessage( ... );
} else {
    // Fall back to contentscript-only behavior
}
Run Code Online (Sandbox Code Playgroud)

选项2:在内容脚本插入时卸载以前的内容脚本

当与后台页面的连接对您的内容脚本很重要时,您必须实现正确的卸载例程,并设置一些事件以在插入内容脚本时卸载以前的内容脚本chrome.tabs.executeScript.

// Content script
function main() {
    // Set up content script
}

function destructor() {
    // Destruction is needed only once
    document.removeEventListener(destructionEvent, destructor);
    // Tear down content script: Unbind events, clear timers, restore DOM, etc.
}

var destructionEvent = 'destructmyextension_' + chrome.runtime.id;
// Unload previous content script if needed
document.dispatchEvent(new CustomEvent(destructionEvent));
document.addEventListener(destructionEvent, destructor);
main();
Run Code Online (Sandbox Code Playgroud)

请注意,任何知道事件名称的页面都可能触发内容脚本的破坏.这是不可避免的,因为在扩展运行时被销毁之后,没有适当的方法来与扩展安全地进行通信.

  • @c00000fd 我正在使用自定义 DOM 事件(请参阅代码片段末尾的 `main();` 之前的最后四行)来管理卸载。首先,我触发自定义事件来指示任何侦听器(可能是以前的内容脚本)自行清理。然后我绑定一个新的事件监听器来监听该事件。最后,我通过调用“main()”函数来设置内容脚本。 (2认同)