【问题标题】:How to properly handle chrome extension updates from content scripts如何正确处理来自内容脚本的 chrome 扩展更新
【发布时间】:2025-12-01 13:15:02
【问题描述】:

在后台页面中,我们能够使用chrome.runtime.onInstalled.addListener 检测扩展更新。

但在扩展更新后,所有内容脚本都无法连接到后台页面。我们得到一个错误:Error connecting to extension ....

可以使用chrome.tabs.executeScript 重新注入内容脚本...但是如果我们有敏感数据应该在更新前保存并在更新后使用怎么办?我们能做什么?

此外,如果我们重新注入所有内容脚本,我们应该正确地删除以前的内容脚本。

在不丢失用户数据的情况下处理来自内容脚本的扩展更新的正确方法是什么?

【问题讨论】:

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


【解决方案1】:

一旦发生 Chrome 扩展更新,“孤立”的内容脚本就会从扩展中完全切断。它仍然可以通信的唯一方法是通过共享 DOM。如果您谈论的是真正敏感数据,那么从页面来看这并不安全。稍后会详细介绍。

首先,您可以延迟更新。在您的后台脚本中,为chrome.runtime.onUpdateAvailable 事件添加一个处理程序。只要听者在,你就有机会进行清理。

// Background script
chrome.runtime.onUpdateAvailable.addListener(function(details) {
  // Do your work, call the callback when done
  syncRemainingData(function() {
    chrome.runtime.reload();
  });
});

其次,假设最坏的情况发生了,你被切断了。您仍然可以使用 DOM 事件进行通信:

// Content script
// Get ready for data
window.addEventListener("SendRemainingData", function(evt) {
  processData(evt.detail);
}, false);

// Request data
var event = new CustomEvent("RequestRemainingData");
window.dispatchEvent(event);

// Be ready to send data if asked later
window.addEventListener("RequestRemainingData", function(evt) {
  var event = new CustomEvent("SendRemainingData", {detail: data});
  window.dispatchEvent(event);
}, false);

但是,此通信渠道可能会被主机页面窃听。而且,如前所述,窃听不是您可以绕过的。

不过,您可以拥有一些带外预共享数据。假设您在第一次安装时生成了一个随机密钥并将其保存在chrome.storage - 无论如何网页都无法访问该密钥。当然,一旦成为孤儿就无法读取它,但在注入的那一刻你可以。

var PSK;
chrome.storage.local.get("preSharedKey", function(data) {
  PSK = data.preSharedKey;

  // ...

  window.addEventListener("SendRemainingData", function(evt) {
    processData(decrypt(evt.detail, PSK));
  }, false);

  // ...

  window.addEventListener("RequestRemainingData", function(evt) {
    var event = new CustomEvent("SendRemainingData", {detail: encrypt(data, PSK)});
    window.dispatchEvent(event);
  }, false);
});

这当然是概念验证代码。我怀疑您需要的不仅仅是onUpdateAvailable 听众。

【讨论】:

    【解决方案2】:

    如果您已通过var port = chrome.runtime.connect(...) 建立了通信(如在 https://developer.chrome.com/extensions/messaging#connect),应该可以收听runtime.Port.onDisconnectevent:

    tport.onDisconnect.addListener(function(msg) {...})
    

    在那里你可以做出反应,例如应用某种记忆,比如说通过localStorage。但总的来说,我会建议将内容脚本保持尽可能小,并在后台执行所有数据操作,让内容仅收集/传递数据并在需要时呈现某种状态。

    【讨论】:

    • 我使用内容脚本添加到 DOM onload 的 div 来代理通信——在我的 onDisconnect 处理程序中,我删除了 DOM 元素。这允许我在网页中使用突变观察器来检测 DOM 元素何时出现和消失,因此我知道正在加载的扩展程序的状态是什么(适用于更新以及完全删除扩展程序)。