Chrome 扩展中的持久服务工作者

时间:2021-03-13 20:16:17

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

我需要在 Chrome 扩展程序中将我的 Service Worker 定义为持久性,因为我使用 webRequest API 来拦截在表单中为特定请求传递的一些数据,但我不知道如何做到这一点。我已经尝试了所有方法,但我的 Service Worker 一直在卸载。

如何保持加载并等待请求被拦截?

2 个答案:

答案 0 :(得分:6)

在您的情况下,这可能是 ManifestV3 中的一个错误,https://crbug.com/1024211:工作人员不会因 webRequest 事件而醒来。所以只要使用 ManifestV2 直到它被修复,因为没有持久化服务工作者这样的东西。

防止扩展的 Service Worker 卸载的解决方法:

0。精简版:通过 waitUntil

最多五分钟
  • 示例 1,硬编码超时:

    self.onactivate = e => {
      e.waitUntil(new Promise(resolve => setTimeout(resolve, 5 * 60e3)));
    };
    
  • 示例 2,等待承诺:

    let allDone;
    self.onactivate = e => e.waitUntil(new Promise(r => { allDone = r; })));
    //................
    chrome.some.event.addListener(() => {
      foo().bar().finally(allDone);
    });
    

1.通过运行时端口永远保持活动状态

从任何选项卡的 runtime 或扩展程序的另一个页面(如弹出页面)打开 content script 端口。此端口将持续五分钟(Service Worker 的固有限制),因此您必须使用计时器和端口的 onDisconnect 事件再次与某个随机选项卡重新连接。

缺点:

  • 需要网页标签或扩展标签/弹出窗口。
  • 内容脚本的广泛主机权限(如 <all_urls>*://*/*),将大多数扩展程序放入网上商店的慢速审核队列。

实现示例:

  • manifest.json,相关部分:

      "permissions": ["scripting"],
      "host_permissions": ["<all_urls>"],
      "background": {"service_worker": "bg.js"}
    
    
  • 后台服务工作者 bg.js:

    let lifeline;
    
    keepAlive();
    
    chrome.runtime.onConnect.addListener(port => {
      if (port.name === 'keepAlive') {
        lifeline = port;
        setTimeout(keepAliveForced, 295e3); // 5 minutes minus 5 seconds
        port.onDisconnect.addListener(keepAliveForced);
      }
    });
    
    function keepAliveForced() {
      lifeline?.disconnect();
      lifeline = null;
      keepAlive();
    }
    
    async function keepAlive() {
      if (lifeline) return;
      for (const tab of await chrome.tabs.query({ url: '*://*/*' })) {
        try {
          await chrome.scripting.executeScript({
            target: { tabId: tab.id },
            func: () => chrome.runtime.connect({ name: 'keepAlive' }),
          });
          chrome.tabs.onUpdated.removeListener(retryOnTabUpdate);
          return;
        } catch (e) {}
      }
      chrome.tabs.onUpdated.addListener(retryOnTabUpdate);
    }
    
    async function retryOnTabUpdate(tabId, info, tab) {
      if (info.url && /^(file|https?):/.test(info.url)) {
        keepAlive();
      }
    }
    

2.替代方法:专用标签

打开一个带有扩展页面的新标签页,例如chrome.tabs.create({url: 'bg.html'})

它将具有与 ManifestV2 的持久背景页面相同的功能,但 a) 它是可见的,并且 b) 无法通过 chrome.extension.getBackgroundPage(可以用 chrome.extension.getViews 替换)访问。

缺点:

  • 消耗更多内存,
  • 浪费标签条中的空间,
  • 分散用户的注意力,
  • 当多个扩展程序打开这样一个标签时,不利因素会滚雪球并变成真正的 PITA。

您可以通过向页面添加信息/日志/图表/仪表板来让您的用户更容易忍受,还可以添加 beforeunload 侦听器以防止标签被意外关闭。

ManifestV3 的未来

让我们希望 Chromium 能够提供一个 API 来控制这种行为,而无需诉诸此类肮脏的黑客和可悲的解决方法。同时,在 crbug.com/1152255 中描述您的用例(如果尚未在此处进行描述)以帮助 Chromium 团队了解既定事实,即许多扩展可能需要在任意时间段内使用持久性后台脚本,并且至少有一个这样的扩展程序可能由大多数扩展程序用户安装。

答案 1 :(得分:0)

从我的扩展程序的 service workerWebSocket 侦听器注册中注册的

chrome.runtime 回调不会被调用,这听起来几乎是相同的问题。

我通过添加以下代码来确保我的服务工作者永不结束来解决这个问题:

function keepServiceRunning() {
    setTimeout(keepServiceRunning, 2000);
  }

keepServiceRunning()

在此之后,我的回调现在按预期调用。