在扩展更新后发送消息时,如何避免“扩展上下文无效”错误?

11t*_*nth 13 google-chrome-extension

发布更新后,我正在尝试为Chrome扩展程序的用户提供流畅的体验。

我在应用程序更新时重新注入了内容脚本,即使用户继续在扩展程序更新后未刷新的页面上继续使用我的扩展程序,我的功能也起作用。刷新页面是理想的选择,但是我不想强迫用户使用。

但是,在扩展程序更新之后,但在代码尝试从内容脚本/页面发送消息到后台页面时,在刷新页面之前,我的内容脚本控制台(将内容脚本插入的页面)中出现以下错误:

未捕获的错误:扩展上下文无效。

有办法“重建”连接吗?我尝试了长期使用的端口和常规消息传递-更新扩展具有相同的效果。

任何想法/方向表示赞赏。

这是我的简单消息传递代码(在内容脚本中),引发错误...但是可以与后台脚本进行通信:

chrome.runtime.sendMessage({ msg: "recordFeedback", obj: commentObj, source: source}, function(response){
  console.log('Sent to DB...response was: ', response, response.recordFeedbackResponse);
});
Run Code Online (Sandbox Code Playgroud)

use*_*314 22

我建议通过访问 chrome.runtime.id 来判断扩展上下文是否无效。

// use null-safe operator since chrome.runtime
// is lazy inited and might return undefined
//
if (chrome.runtime?.id) {
   
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*der 12

我花了几个小时阅读文档、SO、chromium bug 报告,最终修复了与“扩展重新加载后重新连接到运行时”问题相关的所有 bug。

此解决方案在扩展更新/重新加载后重新安装内容脚本,并确保旧的内容脚本不再与无效的运行时通信。

这是完整的相关代码:

清单.json

来自的所有 URLcontent_scripts.matches也必须包含在permissions数组中。这是chrome.tabs.executeScript()工作所必需的。
实际上,您可以删除该content_scripts对象(因为它的唯一原因是自动注入内容脚本)并自己处理 background.js 中的所有内容(请参阅文档:“注入脚本”)。但出于更好的概述和“兼容性”原因,我保留并使用了它。

{
    "permissions": [
        "tabs",
        "http://example.org",
    ],
    "background": {
        "scripts": ["background.js"],
        "persistent": false
    },
    "content_scripts": [
        {
            "matches": ["http://example.org/*"],
            "js": ["contentScript.js"]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

请注意,content_scripts也是一个数组content_scripts[n]['js']

背景.js

const manifest = chrome.runtime.getManifest();

function installContentScript() {
  // iterate over all content_script definitions from manifest
  // and install all their js files to the corresponding hosts.
  let contentScripts = manifest.content_scripts;
  for (let i = 0; i < contentScripts.length; i++) {
    let contScript = contentScripts[i];
    chrome.tabs.query({ url: contScript.matches }, function(foundTabs) {
      for (let j = 0; j < foundTabs.length; j++) {
        let javaScripts = contScript.js;
        for (let k = 0; k < javaScripts.length; k++) {
          chrome.tabs.executeScript(foundTabs[j].id, {
            file: javaScripts[k]
          });          
        }
      }
    });
  }
}

chrome.runtime.onInstalled.addListener(installContentScript);
Run Code Online (Sandbox Code Playgroud)

内容脚本.js

var chromeRuntimePort = chrome.runtime.connect();
chromeRuntimePort.onDisconnect.addListener(() => {
  chromeRuntimePort = undefined;
});

// when using the port, always check if valid/connected
function postToPort(msg) {
  if (chromeRuntimePort) {
    chromeRuntimePort.postMessage(msg);
  }
}

// or
chromeRuntimePort?.postMessage('Hey, finally no errors');

Run Code Online (Sandbox Code Playgroud)


小智 10

if(typeof chrome.app.isInstalled!=='undefined'){
   chrome.runtime.sendMessage()
}
Run Code Online (Sandbox Code Playgroud)

  • `chrome.app` 已被弃用,不应使用,请参阅我的答案以获取更多详细信息。 (2认同)

小智 10

卸载扩展时,现有的内容脚本将失去与扩展的其余部分的连接(即端口关闭,它们将无法使用),runtime.sendMessage()但是内容脚本本身仍然可以继续工作,因为它们已被注入到它们中。页面。

重新加载扩展程序时是相同的:那些现有的内容脚本仍将存在,但无法发送消息;尝试这样做会导致类似您遇到的错误的错误。此外,扩展程序通常在加载时将其内容脚本注入到现有标签中(在Chrome上,Firefox会自动执行),因此最终会在给定的标签中运行多个内容脚本副本:原来的(现在已断开),而当前的已断开。

如果发生以下情况,则可能会出现问题:(1)您的原始内容脚本仍在尝试与扩展的其余部分通信,或者(2)您的原始内容脚本做了诸如修改DOM之类的事情,因为您所做的这些更改可能会比一旦。

这也许就是为什么即使重新注入内容脚本后仍然出现错误的原因:错误来自仍然存在但已断开连接的原始内容脚本。

这解释了我以前研究此问题时发现的一些有用的背景信息。请注意,Firefox会自动卸载内容脚本(稍后会详细介绍)。

解决方案

如果您没有自动重新注入内容脚本,那么可以按照以上wOxxOm的说明,尝试使现有脚本在升级时重新连接

这里是一些其他信息和建议,如果您注入新的内容脚本并希望在注入新的内容脚本时禁用原始的断开连接的脚本,则这些信息和建议特别有用。

无论您是想尝试使用现有内容脚本重新连接,还是要停止对原始内容脚本进行进一步的更改并注入新的脚本,您都需要检测是否已发生卸载。您可以使用端口的disconnect处理程序来捕获扩展程序的卸载时间。如果您的扩展仅使用简单的一次性消息传递,而不使用端口,那么该代码就像在您的内容脚本启动时运行以下代码一样简单(我从lydell的一些代码中首次看到了此技术)。

browser.runtime.connect().onDisconnect.addListener(function() {
    // clean up when content script gets disconnected
})
Run Code Online (Sandbox Code Playgroud)

在Firefox上,disconnect不会触发该端口,因为内容脚本会在接收前被删除。这样做的优点是不需要检测(手动内容脚本注入也不需要,因为内容脚本也会自动注入),但这确实意味着没有机会重置内容脚本对DOM所做的任何更改正在运行。

  • `onDisconnect.addListener` 在 MV3 中不起作用,因为每次卸载后台服务工作线程时它都会触发(2 分钟不活动后) (3认同)
  • 我遇到了“Chrome扩展消息传递:未检查的runtime.lastError:无法建立连接”的问题。接收端不存在`原来你必须先在后台脚本中打开一个监听器。将其放入后台脚本中可以解决此问题: `chrome.runtime.onConnect.addListener(port =&gt; { console.log('connected ', port); if (port.name === 'hi') { port.onMessage. addListener(this.processMessage); } });` (2认同)