【问题标题】:chrome.runtime.sendMessage throws exception from content script after reloading Chrome Extensionchrome.runtime.sendMessage 在重新加载 Chrome 扩展后从内容脚本中抛出异常
【发布时间】:2014-11-08 13:04:05
【问题描述】:

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

chrome.runtime.sendMessage({action: "myResult"});

这工作正常,直到我重新加载我的扩展程序(通过转到设置 -> 扩展程序 -> 为我的扩展程序“重新加载 (Ctrl+R)”。)

反过来,当我的后台脚本启动时,它会重复调用chrome.tabs.executeScript,让所有打开的标签以编程方式重新注入我的内容脚本 (as I showed in this question。)

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

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

知道为什么会这样吗?

【问题讨论】:

  • 在主题上,确保两件事:1)您没有从内容脚本的先前实例中获取此行,2)您没有在注册 @ 之前注入脚本987654325@听众。
  • @Xan:是的。这不是这个网站的目的吗?显然,我只在遇到死胡同时才努力研究和询问。
  • 所以回到你的主题cmets。我如何确保#1?

标签: javascript google-chrome google-chrome-extension content-script


【解决方案1】:

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

  • 您已致电chrome.runtime.reload()
  • 您点击了chrome://extensions/ 的重新加载扩展程序。
  • 扩展程序已更新。

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

选项 1:回退到 contentscript-only 功能

如果您的扩展程序可以在没有背景页面的情况下完美运行,那么这可能是一个可以接受的解决方案。例如。如果您的内容脚本除了修改 DOM 和/或执行跨域请求之外什么都不做。

我在我的一个扩展程序中使用以下 sn-p 来检测运行时是否仍然有效,然后再从我的内容脚本调用任何 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
}

选项 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();

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

【讨论】:

  • 感谢您花时间解释这一点!关于您的选项 2 的一个问题。我在重新加载后开始重新注入内容脚本的原因是因为我无法再从后台脚本与它们通信。但如果是这样,我怎么让他们知道他们必须卸载?另一方面,内容脚本和后台脚本本身都无法知道扩展程序即将被卸载以执行您解释的操作。这种情况不符合catch 22的条件吗?
  • @c00000fd 我正在使用自定义 DOM 事件(请参阅我的 sn-p 末尾 main(); 之前的最后四行)来管理卸载。首先,我触发自定义事件以指示任何侦听器(可能是以前的内容脚本)自行清理。然后我绑定一个新的事件监听器来监听事件。最后我通过调用main() 函数来设置内容脚本。
  • 是的,我可以看到。所以 DOM 事件会起作用,哈哈。这是个好主意,谢谢!
  • +1 用于发送 DOM 事件,从没想过。
  • @c00000fd DOM 事件起作用,因为内容脚本实际上只是继续执行,只是切断了父背景页面。
猜你喜欢
  • 2016-04-22
  • 2018-07-18
  • 1970-01-01
  • 1970-01-01
  • 2014-11-01
  • 2011-11-25
  • 2014-01-18
  • 1970-01-01
  • 2022-11-16
相关资源
最近更新 更多