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

Posted

技术标签:

【中文标题】Chrome 扩展中的持久服务工作者【英文标题】:Persistent Service Worker in Chrome Extension 【发布时间】:2021-06-11 13:08:21 【问题描述】:

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

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

【问题讨论】:

【参考方案1】:

解决方案:

如果事件可能很少发生,例如每天有几个关系,重新设计您的扩展程序以通过chrome.storage.session(临时,最大 1MB)或chrome.storage.local,甚至IndexedDB(对于大/复杂数据更快)保存/加载每个侦听器中的变量/状态。

如果可能,请使用 ManifestV2,因为没有持久服务工作者这样的东西。

使用以下变通方法来防止扩展的服务工作者卸载。

ManifestV3 中的这些错误可能与此有关:

crbug.com/1024211,工作人员不会因 webRequest 事件而唤醒(请参阅解决方法)。 crbug.com/1271154,更新后worker坏掉了(在another answer解决)。

解决方法:

0。五分钟,通过waitUntil

示例 1,硬编码超时:

self.onactivate = e => 
  e.waitUntil(new Promise(resolve => setTimeout(resolve, 5 * 60e3)));
;

例子2,等待一个promise:

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

1。 “永远”,通过运行时端口,同时存在可连接的选项卡

从任何选项卡的content script 或从扩展的另一个页面(如弹出页面)打开一个runtime 端口。此端口将存活五分钟(服务工作者的固有限制),因此您必须使用计时器和端口的 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 ,
        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();
  

2。 “永远”,通过专用标签,在标签打开时

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

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

缺点:

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

您可以通过将 info/logs/charts/dashboard 添加到页面并添加beforeunload 侦听器来防止标签被意外关闭,从而让您的用户更容易忍受。

ManifestV3 的未来

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

【讨论】:

非常感谢!!我花了两天时间认为这是我的代码中的错误,我刚刚切换到 MV2,现在它可以工作了! 因此人们知道在“通过运行时端口永远保持活动状态”选项中,executeScript 调用的 ScriptInjection 需要 function 密钥而不是 func 密钥(版本 90.0.4430.212) .这与ScriptInjection 文档相矛盾,但与scripting documentation 相匹配 这些方法都没有达到答案的目的。 keepAlive 策略只是冗余,只有在他们正确的头脑中谁想要保持标签打开?由于这些原因,这个答案被否决了。 @surajsharma,此答案中的所有内容都经过了多个用户的测试,并且完全按照它所说的进行。【参考方案2】:

从我的扩展的service worker 的chrome.runtime 侦听器注册中注册的WebSocket 回调不会被调用,这听起来几乎是同样的问题。

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

function keepServiceRunning() 
    setTimeout(keepServiceRunning, 2000);
  

keepServiceRunning()

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

【讨论】:

这不适用于服务工作者的规范。您必须为 service worker 打开 devtools,这会使 worker 保持活跃,故意规避规范的超时以简化调试。 实际上,我有点困惑,因为我建议的答案代码确实可以在没有打开 devtools 的情况下无限期地保持服务。 Chromium Beta 版本 93.0.4577.51(官方构建)测试版(64 位)。 这意味着浏览器中存在错误或脚本中的某些内容正在使用延长的 5 分钟超时,例如端口、消息和其他一些东西。 谢谢,我已将我的用例添加到 crbug.com/1152255,因为我没有以任何我知道的方式明确使用扩展超时 没有MCVE 我不知道哪里出了问题。我只验证了它在几个不同版本的 Chrome 中不起作用,包括符合规范的 93。请注意,chrome.runtime 消息传递是启用延长 5 分钟超时的事情之一。【参考方案3】:

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

    清单 v3
"permissions": [
    "alarms"
],
    服务工作者背景.js:
chrome.alarms.create( periodInMinutes: 4.9 )
chrome.alarms.onAlarm.addListener(() => 
  console.log('log for debug')
);

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

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

  <head>...<head>
  <body>
    ...
    <script defer src="override.js"></script>
  <body>
<html>
// 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)
    )
)

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

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。 从技术上讲,这个答案与后台脚本的持久性无关,但它仍然为 ManifestV3 中的一个固有错误提供了一种解决方法,即后台脚本在更新期间完全丢失,crbug.com/1271154。

以上是关于Chrome 扩展中的持久服务工作者的主要内容,如果未能解决你的问题,请参考以下文章

Chrome 扩展中的 CORS HTTPS 到 HTTP 网络服务

chrome 扩展 mv3 - 模块化服务工作者 js 文件

本地主机不工作 - vs 代码中的 chrome 扩展

Chrome 开发者扩展中的 CORS 问题

如何链接到 chrome 扩展中的选项页面?

chrome 扩展中的跨域 XMLHttpRequest