Chrome扩展中的持久服务工作线程

rbpvctlc  于 2022-12-06  发布在  Go
关注(0)|答案(6)|浏览(323)

我需要在我的Chrome扩展中将我的Service Worker定义为持久的,因为我正在使用webRequest API来拦截在表单中为特定请求传递的一些数据,但我不知道如何才能做到这一点。我尝试了所有方法,但我的Service Worker总是卸载。
我如何保持它加载并等待直到请求被拦截?

dxpyg8gm

dxpyg8gm1#

根据定义,服务工作者(Service worker,SW)不能是持久的,浏览器必须在一定时间后强制终止所有SW连接,如网络请求或运行时端口,Chrome中的时间为5分钟。当没有此类请求或端口打开时,不活动计时器甚至更短:三十秒。
Chromium团队currently considers这个行为是有意的,也是好的,但是这只适用于观察到不频繁事件的扩展,所以它们一天只运行几次,从而减少运行之间的浏览器内存占用。例如,webRequest/webNavigation事件带有urls过滤器,用于很少访问的站点。
在许多情况下,这样的田园诗般的生活是不可持续的。

尽管您可以尝试订阅chrome.webNavigation之类的API(如其他答案所示),但它仅对工作线程启动后发生的事件有帮助。

无解决方法。请尝试卸载此扩展,然后重新安装。

  • 问题3:SW inactivity timer isn't prolonged for a new chrome API event在已经运行的后台脚本中。这意味着当事件发生在30秒非活动超时的最后几毫秒时,您的代码将无法可靠地运行任何异步操作。这意味着您的扩展将被用户视为不可靠。
  • 问题4:性能比MV 2差,如果扩展保持套接字连接或状态(变量)需要很长时间来重建,或者您经常观察到如下事件:
  • chrome.tabs.onUpdated/onActivated,
  • 如果没有限制到一个罕见的URL,
  • webRequest如果未限定为罕见的url或类型,
  • chrome.runtime.onMessage/onConnect用于所有选项卡中来自内容脚本消息

为新事件启动软件基本上类似于打开新选项卡。创建环境需要约50 ms,运行整个软件脚本可能需要100 ms(或甚至1000毫秒,具体取决于代码量),从存储阅读状态并对其进行重建/水合可能需要1毫秒(或1000 ms,取决于数据的复杂性)。即使是一个几乎为空的脚本,也至少需要50 ms,这对于调用事件侦听器来说是一个相当大的开销,而事件侦听器只需要1 ms。
软件每天可能会重启数百次,因为此类事件是在响应用户操作时生成的,这些用户操作中存在自然间隙,例如,点击选项卡,然后写入内容,在此期间,软件会终止并因新事件而再次重启,从而会磨损CPU、磁盘、电池,通常会导致扩展的React出现频繁的可感知延迟。

连接nativeMessaging主机时的“持久”服务工作进程

在Chrome 105和更新版本中,只要服务worker通过chrome. runtime. connectNative连接到nativeMessaging主机,它就会运行。如果主机进程由于崩溃或用户操作而终止,端口将关闭,软件将照常终止。您可以通过监听端口的onDisconnect事件并再次调用chrome.runtime.connectNative来防范这种情况。

存在可连接选项卡时,“持久”服务工作进程

缺点:

  • 需要一个打开的网页标签
  • 内容脚本的广泛主机权限(如<all_urls>*://*/*)将大多数扩展放入Web商店中的慢速审查队列。

警告!如果您已连接端口,请不要使用此解决方法,请为下面的端口使用另一个解决方法。
警告!如果您使用sendMessage,也请实作sendMessage的解决方法(如下所示)。

  • manifest.json,相关部分:
"permissions": ["scripting"],
  "host_permissions": ["<all_urls>"],
  "background": {"service_worker": "bg.js"}
  • 后台服务工作人员bg.js:
const onUpdate = (tabId, info, tab) => /^https?:/.test(info.url) && findTab([tab]);
findTab();
chrome.runtime.onConnect.addListener(port => {
  if (port.name === 'keepAlive') {
    setTimeout(() => port.disconnect(), 250e3);
    port.onDisconnect.addListener(() => findTab());
  }
});
async function findTab(tabs) {
  if (chrome.runtime.lastError) { /* tab was closed before setTimeout ran */ }
  for (const {id: tabId} of tabs || await chrome.tabs.query({url: '*://*/*'})) {
    try {
      await chrome.scripting.executeScript({target: {tabId}, func: connect});
      chrome.tabs.onUpdated.removeListener(onUpdate);
      return;
    } catch (e) {}
  }
  chrome.tabs.onUpdated.addListener(onUpdate);
}
function connect() {
  chrome.runtime.connect({name: 'keepAlive'})
    .onDisconnect.addListener(connect);
}
  • 所有其他扩展页面,如弹出窗口或选项:
;(function connect() {
  chrome.runtime.connect({name: 'keepAlive'})
    .onDisconnect.addListener(connect);
})();
如果还使用sendMessage

在Chrome 99-101中,你需要在chrome.runtime.onMessage监听器中调用sendResponse(),即使你不需要响应。这是MV 3中的一个bug。另外,确保你在5分钟内完成,否则立即调用sendResponse,并在工作完成后通过chrome.tabs.sendMessage(到标签页)或chrome.runtime.sendMessage(到弹出窗口)发送一条新消息。

如果您已经使用了端口,例如chrome.runtime.connect

警告!如果你还连接了更多的端口到service worker,你需要在5分钟内重新连接每一个端口,例如295秒。这在Chrome 104之前的版本中是至关重要的,它会杀死软件,而不考虑其他连接的端口。在Chrome 104和更新版本中,这个bug已经修复,但你仍然需要重新连接它们,因为它们的5分钟生命周期没有改变。因此最简单的解决方案是在所有版本的Chrome中以相同的方式重新连接:例如每295秒。

  • 后台脚本示例:
chrome.runtime.onConnect.addListener(port => {
  if (port.name !== 'foo') return;
  port.onMessage.addListener(onMessage);
  port.onDisconnect.addListener(deleteTimer);
  port._timer = setTimeout(forceReconnect, 250e3, port);
});
function onMessage(msg, port) {
  console.log('received', msg, 'from', port.sender);
}
function forceReconnect(port) {
  deleteTimer(port);
  port.disconnect();
}
function deleteTimer(port) {
  if (port._timer) {
    clearTimeout(port._timer);
    delete port._timer;
  }
}
  • 客户端脚本示例,例如内容脚本:
let port;
function connect() {
  port = chrome.runtime.connect({name: 'foo'});
  port.onDisconnect.addListener(connect);
  port.onMessage.addListener(msg => {
    console.log('received', msg, 'from bg');
  });
}
connect();
“永久”,通过专用选项卡,当选项卡打开时

打开一个新标签页,其中包含扩展页,例如chrome.tabs.create({url: 'bg.html'})
它将具有与ManifestV 2的持久化背景页面相同的功能,但a)它是可见的,b)不能通过chrome.extension.getBackgroundPage(可以用chrome.extension.getViews替换)访问。
缺点:

  • 消耗更多的存储器,

  • 浪费了标签条中的空间,

  • 分散使用者的注意力,

  • 当多个扩展打开这样一个选项卡时,其缺点会像滚雪球一样,变成一个真实的的PITA。

您可以通过在页面中添加info/logs/charts/dashboard来让用户更容易接受它,还可以添加一个beforeunload侦听器来防止选项卡被意外关闭。

有关持久性的注意事项

你仍然需要保存/恢复状态(变量),因为没有持久的服务worker,这些解决方案有如上所述的限制,所以worker可以终止。你可以在存储example中维护状态。
请注意,您不应该仅仅为了简化状态/变量管理而使工作线程持久化。只有在您的状态重建成本非常高,或者您挂钩到本答案开头列出的频繁事件时,才应该这样做,以恢复因重新启动工作线程而恶化的性能。

清单V3的未来

让我们希望Chromium能提供一个API来控制这种行为,而不需要求助于这样肮脏的黑客和可悲的解决方案。同时,如果它不是,请描述您在crbug.com/1152255中的用例。为了帮助Chromium团队意识到以下事实:许多扩展可能需要一个持续任意时间的后台脚本,并且至少有一个这样的扩展可以通过大多数分机用户。

2nbm6dog

2nbm6dog2#

chrome.webRequest API不同,chrome.webNavigation API工作正常,因为 chrome.webNavigation API可以唤醒服务工作进程*,现在您可以尝试将chrome.webRequest APIAPI放在chrome.webNavigation内部。

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

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

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

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

},{
    url: [{hostContains:"domain"}]
});
nkoocmlb

nkoocmlb3#

如果我理解正确,你可以唤醒服务工人(background.js)的警报.看看下面的例子:
1.清单v3

"permissions": [
    "alarms"
],

1.服务工作者背景。js:

chrome.alarms.create({ periodInMinutes: 4.9 })
chrome.alarms.onAlarm.addListener(() => {
  console.log('log for debug')
});

不幸的是,这不是我的问题,也可能是你有不同的问题。当我刷新开发扩展或停止和运行产品扩展的一些时间服务工作者死在所有。当我关闭和打开浏览器工作者不运行,任何侦听器内的工作者也不运行它。它试图手动注册工作者。举个例子:
第一次
这个解决方案部分帮助了我,但这并不重要,因为我必须在不同的标签上注册工人。可能有人知道决定。我会高兴。

wrrgggsh

wrrgggsh4#

由于Clairzil Bawon samdi's answer,**chrome.webNavigation**可能会唤醒MV3中服务工作人员,下面是我的案例中的解决方法:

// manifest.json
...
"background": {
  "service_worker": "background.js"
},
"host_permissions": ["https://example.com/api/*"],
"permissions": ["webRequest", "webNavigation"]
...

在我的例子中,它侦听onHistoryStateUpdated事件以唤醒服务工作者:

// background.js
chrome.webNavigation.onHistoryStateUpdated.addListener((details) => {
  console.log('wake me up');
});

chrome.webRequest.onSendHeaders.addListener(
  (details) => {
    // code here
  },
  {
    urls: ['https://example.com/api/*'],
    types: ['xmlhttprequest'],
  },
  ['requestHeaders']
);
mbzjlibv

mbzjlibv5#

我发现了一个不同的解决方案来保持扩展的活动。它改进了wOxxOm的答案,使用一个辅助扩展来打开到主扩展的连接端口。然后两个扩展在任何一个断开连接的情况下尝试相互通信,从而保持两个扩展都活动。
之所以需要这样做,是因为根据我公司的另一个团队的说法,wOxxOm的答案是不可靠的。据报道,他们的软件最终会以不确定的方式失败。
我的解决方案适用于我的公司,因为我们正在部署企业安全软件,我们将强制安装扩展。让用户安装2个扩展在其他用例中可能仍然不受欢迎。

bvjxkvbb

bvjxkvbb6#

从我的扩展的service workerchrome.runtime侦听器注册中注册的WebSocket回调不会被调用,这听起来几乎是同一个问题。
为了解决这个问题,我向服务工作线程添加了以下代码,以确保它永不结束:

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

keepServiceRunning()

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

相关问题