首页 > 解决方案 > Chrome 扩展程序:如何在 chrom 扩展程序更新后删除孤立的脚本

问题描述

我有一个带有弹出页面的 chrome 扩展,它通过简单的一次性请求将布尔变量传递给我的内容页面。然后,内容页面将根据从弹出页面传递的布尔变量的状态执行一些操作。直到我不小心删除了扩展程序(仍处于开发人员模式,扩展程序已解压缩)并不得不重新加载它之前,这一直运行良好。

这导致弹出检查控制台中出现扩展上下文无效错误,并且网页控制台似乎验证弹出页面和内容脚本没有通信。激活 chrome 扩展程序的网页显示此错误:Unchecked runtime.lastError: The message port closed before a response was received

根据我已经看到的一些答案,似乎重新加载我的 chrome 扩展程序已经从我的扩展程序的其余部分“孤立”了我的原始工作内容脚本,这导致前面提到的“未检查的 runtime.lastError:消息端口在 a收到了回复。” 网页控制台上的错误。

我相信我不能只是再次重新注入我的内容脚本,因为我的内容脚本有 DOM 事件侦听器。有没有可能删除当前正在运行的孤立脚本的方法?或者是否有任何建议的解决方法来解决这个问题?

这是我的popup.js:

chrome.tabs.query({'active': true, 'currentWindow': true}, function (tabs) {
    chrome.tabs.sendMessage(tabs[0].id, {cTabSettings: (some boolean variable)});
});

这是我的 content.js:

// Listening for message from popup.js
chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    if (request.cTabSettings === true) {
      enabled = true;
    } else if (request.cTabSettings === false) {
      enabled = false;
    }
});

// DOM listener and action
document.addEventListener('mousemove', function (e) {
   // Some action
   chrome.runtime.sendMessage({sender: "content", selText : "blah"}, function () {
      console.log("success");
   });
}, false);

我正在使用 chrome 开发人员模式版本 76。只是换个说法,这个 chrome 扩展在我不小心重新加载之前正在工作(内容脚本与弹出窗口通信)。

标签: javascriptgoogle-chromegoogle-chrome-extension

解决方案


由于孤立的内容脚本仍然可以接收 DOM 消息,例如,从您的新工作内容脚本发送一条到幻影内容脚本window。收到消息后,您将注销所有侦听器(并使所有全局变量无效),这也将使您的旧脚本符合自动垃圾收集的条件。

内容.js:

var orphanMessageId = chrome.runtime.id + 'orphanCheck';
window.dispatchEvent(new Event(orphanMessageId));
window.addEventListener(orphanMessageId, unregisterOrphan);

// register all listeners with named functions to preserve their object reference
chrome.runtime.onMessage.addListener(onMessage);
document.addEventListener('mousemove', onMouseMove);

// the popup script checks it to see if a usable instance of content script is running
window.running = true;

function unregisterOrphan() {
  if (chrome.i18n) {
    // someone tried to kick us out but we're not orphaned! 
    return;
  }
  window.removeEventListener(orphanMessageId, unregisterOrphan);
  document.removeEventListener('mousemove', onMouseMove);
  try {
    // 'try' is needed to avoid an exception being thrown in some cases 
    chrome.runtime.onMessage.removeListener(onMessage);
  } catch (e) {}
  return true;
});

function onMessage(msg, sender, sendResponse) {
  //...........
}

function onMouseMove(event) {
  // DOM events still fire in the orphaned content script after the extension
  // was disabled/removed and before it's re-enabled or re-installed
  if (unregisterOrphan()) { return }
  //...........
}

我假设 popup.html 加载WebExtension browserAPI polyfill ,因为它允许我们使用 async/await 语法而不是令人难以置信的回调地狱,从而使生活变得更加轻松。

<script src="browser-polyfill.min.js"></script>

popup.js 应该确保在发送消息之前注入内容脚本:

async function sendMessage(data) {
  const [tab] = await browser.tabs.query({active: true, currentWindow: true});
  if (await ensureContentScript(tab.id)) {
    return await browser.tabs.sendMessage(tab.id, data);
  }
}

async function ensureContentScript(tabId) {
  try {
    const [running] = await browser.tabs.executeScript(tabId, {
      code: 'window.running === true',
    });
    if (!running) {
      await browser.tabs.executeScript(tabId, {file: 'content.js'});
    }
    return true;
  } catch (e) {}
}

推荐阅读