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

Kev*_*sto 8 javascript google-chrome xmlhttprequest request google-chrome-extension

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

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

wOx*_*xOm 13

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

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

0. 精简版:最多五分钟通过 waitUntil

  • 示例 1,硬编码超时:

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

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

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

从任何选项卡的内容脚本或从扩展程序的另一个页面(如弹出页面)打开运行时端口。此端口将持续五分钟(服务工作者的固有限制),因此您必须使用计时器和端口的 onDisconnect 事件再次与某个随机选项卡重新连接。

缺点:

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

实现示例:

  • manifest.json,相关部分:

      "permissions": ["scripting"],
      "host_permissions": ["<all_urls>"],
      "background": {"service_worker": "bg.js"}
    
    
    Run Code Online (Sandbox Code Playgroud)
  • 后台服务工作者 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 },
            function: () => chrome.runtime.connect({ name: 'keepAlive' }),
            // `function` will become `func` in Chrome 93+
          });
          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();
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)

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 团队意识到这一既定事实,即许多扩展可能需要在任意时间段内使用持久性后台脚本,并且至少有一个这样的扩展程序可能由大多数扩展程序用户安装。

  • @surajsharma,这个答案中的所有内容都经过多个用户的测试,并且完全按照它所说的进行。 (7认同)
  • 非常感谢!!我花了两天时间认为这是我的代码中的错误,我刚刚切换到 MV2,现在它可以工作了! (2认同)
  • 这种技术似乎对我来说稳定地工作失败 - 并且重要的是对我的用例有效。谢谢@wOxxOm。我一直在谷歌论坛上阅读您的大部分贡献并同意您的观点。您认为扩展团队决定堵塞这个“漏洞”的可能性有多大? (2认同)
  • AFAIK 无法插入。 (2认同)

小智 10

chrome.webRequest API不同, chrome.webNavigation API工作得很好,因为chrome.webNavigation API可以唤醒Service Worker,现在您可以尝试将chrome.webRequest API api 放入chrome.webNavigation中。

chrome.webNavigation.onBeforeNavigate.addListener(function(){

   chrome.webRequest.onResponseStarted.addListener(function(details){

      //.............
      
      //.............

   },{urls: ["*://domain/*"],types: ["main_frame"]});


},{
    url: [{hostContains:"domain"}]
});
Run Code Online (Sandbox Code Playgroud)


Val*_*iou 7

如果我理解正确,您可以通过警报唤醒服务人员(background.js)。看下面的例子:

  1. 清单 v3
"permissions": [
    "alarms"
],
Run Code Online (Sandbox Code Playgroud)
  1. 服务工作者background.js:
chrome.alarms.create({ periodInMinutes: 4.9 })
chrome.alarms.onAlarm.addListener(() => {
  console.log('log for debug')
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,这不是我的问题,也许你也有不同的问题。当我刷新开发扩展或停止并运行产品扩展时,有时服务工作人员会完全死亡。当我关闭并打开浏览器时,工作人员不会运行,工作人员内部的任何侦听器也不会运行它。它尝试手动注册工人。例如:

// override.html
<!DOCTYPE html>
<html lang="en">

  <head>...<head>
  <body>
    ...
    <script defer src="override.js"></script>
  <body>
<html>
Run Code Online (Sandbox Code Playgroud)
// override.js - this code is running in new tab page
navigator.serviceWorker.getRegistrations().then((res) => {
  for (let worker of res) {
    console.log(worker)
    if (worker.active.scriptURL.includes('background.js')) {
      return
    }
  }

  navigator.serviceWorker
    .register(chrome.runtime.getURL('background.js'))
    .then((registration) => {
      console.log('Service worker success:', registration)
    }).catch((error) => {
      console.log('Error service:', error)
    })
})
Run Code Online (Sandbox Code Playgroud)

这个解决方案部分帮助了我,但这并不重要,因为我必须在不同的选项卡上注册工作人员。可能有人知道决定。我会很高兴。