84669 人學習
152542 人學習
20005 人學習
5487 人學習
7821 人學習
359900 人學習
3350 人學習
180660 人學習
48569 人學習
18603 人學習
40936 人學習
1549 人學習
1183 人學習
32909 人學習
與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"}] });
問題描述
解決方法:
• 漏洞利用 • 離屏 API • nativeMessaging API# • WebSocket API# • chrome 訊息 API • 專用選項卡
離屏
nativeMessaging
WebSocket
chrome
注意
根據定義,Service Worker (SW) 不能持久,瀏覽器必須在一定時間後強制終止其所有活動/請求,在 Chrome 中為 5 分鐘。不活動計時器(即沒有正在進行的此類活動時)甚至更短:30 秒。
Chromium 團隊目前認為這種行為很好(團隊偶爾放寬了某些方面,例如Chrome 114 延長了chrome.runtime每個訊息後的連接埠),但這僅適用於觀察不頻繁事件的擴展,這些事件每天只運行幾次,從而減少運行之間的瀏覽器內存佔用(例如,帶有url 的webRequest/webNavigation 事件> 過濾很少訪問的網站)。可以重新設計這些擴充功能以維持狀態,範例。不幸的是,這樣的田園風光在許多情況下是不可持續的。
問題 1:Chrome 106 及更早版本不會針對 webRequest 事件喚醒軟體。 p>
儘管您可以嘗試訂閱像其他答案中所示的 chrome.webNavigation 這樣的 API,但它僅對工作執行緒啟動後發生的事件有幫助。
chrome.webNavigation
問題 2:工作人員隨機停止因事件而醒來。
解決方法可能是呼叫 chrome.runtime.reload()。
問題 3:Chrome 109 及更早版本無法延長新 chrome 的軟體生命週期 已經執行的後台腳本中的 API 事件。這意味著當事件發生在 30 秒不活動超時的最後幾毫秒內時,您的程式碼將無法可靠地非同步運行任何內容。這意味著用戶會認為您的擴充功能不可靠。
chrome 的軟體生命週期
問題 4:如果擴充功能維持遠端連線或狀態(變數)需要很長時間才能重建,或者您觀察到以下頻繁事件,則效能會比 MV2 差:
為新事件啟動 SW 本質上就像打開一個新選項卡。創建環境大約需要50 毫秒,運行整個SW 腳本可能需要100 毫秒(甚至1000 毫秒,取決於程式碼量),從儲存讀取狀態並重建/水合可能需要1 毫秒(或1000 毫秒,取決於資料的複雜性) 。即使使用幾乎空的腳本,也至少需要 50 毫秒,這對於呼叫事件偵聽器來說是相當巨大的開銷,而事件偵聽器只需要 1 毫秒。
SW 每天可能會重新啟動數百次,因為此類事件是為了響應具有自然間隙的用戶操作而生成的,例如單擊一個選項卡,然後寫入一些內容,在此期間,軟體被終止並且為新事件再次重新啟動,從而消耗CPU、磁碟、電池,通常會引入擴展反應的頻繁可察覺的延遲。
Chrome 110 引入了一個錯誤:呼叫任何非同步 chrome API 都會使工作執行緒多運行 30 秒。該錯誤尚未修復。
//背景.js
const keepAlive = () => setInterval(chrome.runtime.getPlatformInfo, 20e3); chrome.runtime.onStartup.addListener(keepAlive); keepAlive();
由凱文·奧古斯托提供。
在 Chrome 109 及更高版本中,您可以使用offscreen API 建立離屏文件並每 30 秒或更短時間從其中發送一些訊息,以保持 Service Worker 運行。目前該文件的生命週期不受限制(僅音訊播放受到限制,我們不使用),但將來可能會發生變化。
manifest.json
"permissions": ["offscreen"]
offscreen.html
offscreen.js
setInterval(async () => { (await navigator.serviceWorker.ready).active.postMessage('keepAlive'); }, 20e3);
async function createOffscreen() { await chrome.offscreen.createDocument({ url: 'offscreen.html', reasons: ['BLOBS'], justification: 'keep service worker running', }).catch(() => {}); } chrome.runtime.onStartup.addListener(createOffscreen); self.onmessage = e => {}; // keepAlive createOffscreen();
連線
在 Chrome 105 及更高版本中,只要透過 chrome.runtime.connectNative。如果主機進程因崩潰或使用者操作而終止,則連接埠將關閉,並且軟體將照常終止。您可以透過監聽連接埠的 onDisconnect
內容腳本的廣泛主機權限(例如
或
"permissions": ["scripting"], "host_permissions": [""], "background": {"service_worker": "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); })();
警告!如果您還將更多連接埠連接到 Service Worker,則需要在 5 分鐘過去之前重新連接每個端口,例如295 秒內。這在 104 之前的 Chrome 版本中至關重要,無論有多少額外的連接端口,它都會殺死 SW。在Chrome 104 及更高版本中,此錯誤已修復,但您仍然需要重新連接它們,因為它們的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'})
它將具有與ManifestV2 的持久後台頁面相同的功能,但a) 它是可見的,b) 無法透過chrome.extension.getBackgroundPage 存取(可以替換為chrome.extension .getViews)。
chrome.extension.getBackgroundPage
缺點:
您可以透過在頁面上新增 info/logs/charts/dashboard 來讓使用者更容易忍受,也可以新增一個 beforeunload 偵聽器以防止標籤意外關閉。 p>
beforeunload
您仍然需要保存/恢復狀態(變數),因為不存在持久服務工作人員之類的東西,並且這些解決方法具有如上所述的限制,因此工作人員可以終止。您可以維護儲存中的狀態,範例。
請注意,您不應該只是為了簡化狀態/變數管理而讓您的工作執行緒持久化。這樣做只是為了恢復因重新啟動工作線程而惡化的效能,以防您的狀態重建成本非常昂貴,或者如果您掛接到本答案開頭列出的頻繁事件。
與chrome.webRequest API 不同,chrome.webNavigation API 可以完美地工作,因為chrome.webNavigation API 可以喚醒 Service Worker,現在您可以嘗試將chrome.webRequest API API 放入chrome.webNavigation 中。
目錄
問題描述
解決方法:
• 漏洞利用
•
離屏
API•
nativeMessaging
API# •
WebSocket
API# •
chrome
訊息 API• 專用選項卡
注意
根據定義,Service Worker (SW) 不能持久,瀏覽器必須在一定時間後強制終止其所有活動/請求,在 Chrome 中為 5 分鐘。不活動計時器(即沒有正在進行的此類活動時)甚至更短:30 秒。
Chromium 團隊目前認為這種行為很好(團隊偶爾放寬了某些方面,例如Chrome 114 延長了chrome.runtime每個訊息後的連接埠),但這僅適用於觀察不頻繁事件的擴展,這些事件每天只運行幾次,從而減少運行之間的瀏覽器內存佔用(例如,帶有url 的webRequest/webNavigation 事件> 過濾很少訪問的網站)。可以重新設計這些擴充功能以維持狀態,範例。不幸的是,這樣的田園風光在許多情況下是不可持續的。
已知問題
問題 1:Chrome 106 及更早版本不會針對 webRequest 事件喚醒軟體。 p>
儘管您可以嘗試訂閱像其他答案中所示的
chrome.webNavigation
這樣的 API,但它僅對工作執行緒啟動後發生的事件有幫助。問題 2:工作人員隨機停止因事件而醒來。
解決方法可能是呼叫 chrome.runtime.reload()。
問題 3:Chrome 109 及更早版本無法延長新
chrome 的軟體生命週期
已經執行的後台腳本中的 API 事件。這意味著當事件發生在 30 秒不活動超時的最後幾毫秒內時,您的程式碼將無法可靠地非同步運行任何內容。這意味著用戶會認為您的擴充功能不可靠。問題 4:如果擴充功能維持遠端連線或狀態(變數)需要很長時間才能重建,或者您觀察到以下頻繁事件,則效能會比 MV2 差:
為新事件啟動 SW 本質上就像打開一個新選項卡。創建環境大約需要50 毫秒,運行整個SW 腳本可能需要100 毫秒(甚至1000 毫秒,取決於程式碼量),從儲存讀取狀態並重建/水合可能需要1 毫秒(或1000 毫秒,取決於資料的複雜性) 。即使使用幾乎空的腳本,也至少需要 50 毫秒,這對於呼叫事件偵聽器來說是相當巨大的開銷,而事件偵聽器只需要 1 毫秒。
SW 每天可能會重新啟動數百次,因為此類事件是為了響應具有自然間隙的用戶操作而生成的,例如單擊一個選項卡,然後寫入一些內容,在此期間,軟體被終止並且為新事件再次重新啟動,從而消耗CPU、磁碟、電池,通常會引入擴展反應的頻繁可察覺的延遲。
透過錯誤利用「持久」服務工作者
Chrome 110 引入了一個錯誤:呼叫任何非同步
chrome
API 都會使工作執行緒多運行 30 秒。該錯誤尚未修復。//背景.js
具有離螢幕 API 的「持久性」服務工作人員
由凱文·奧古斯托提供。
在 Chrome 109 及更高版本中,您可以使用offscreen API 建立離屏文件並每 30 秒或更短時間從其中發送一些訊息,以保持 Service Worker 運行。目前該文件的生命週期不受限制(僅音訊播放受到限制,我們不使用),但將來可能會發生變化。
manifest.json
#offscreen.html
#offscreen.js
連線
nativeMessaging在 Chrome 105 及更高版本中,只要透過 chrome.runtime.connectNative。如果主機進程因崩潰或使用者操作而終止,則連接埠將關閉,並且軟體將照常終止。您可以透過監聽連接埠的 onDisconnect
事件來防範它再次呼叫 chrome.runtime.connectNative。內容腳本的廣泛主機權限(例如
或
*://*/*如果您已經使用端口,例如chrome.runtime.connect
警告!如果您還將更多連接埠連接到 Service Worker,則需要在 5 分鐘過去之前重新連接每個端口,例如295 秒內。這在 104 之前的 Chrome 版本中至關重要,無論有多少額外的連接端口,它都會殺死 SW。在Chrome 104 及更高版本中,此錯誤已修復,但您仍然需要重新連接它們,因為它們的5 分鐘生命週期沒有改變,因此最簡單的解決方案是在所有版本的Chrome 中以相同的方式重新連接:例如每295 秒一次。
後台腳本範例:
客戶端腳本範例,例如內容腳本:
“永遠”,透過專用選項卡,當選項卡開啟時
不使用軟體,而是打開一個內部包含擴充頁面的新選項卡,因此該頁面將充當“可見背景頁面”,即軟體要做的唯一事情就是開啟此選項卡。您也可以從操作彈出視窗中開啟它。
它將具有與ManifestV2 的持久後台頁面相同的功能,但a) 它是可見的,b) 無法透過
chrome.extension.getBackgroundPage
存取(可以替換為chrome.extension .getViews)。缺點:
您可以透過在頁面上新增 info/logs/charts/dashboard 來讓使用者更容易忍受,也可以新增一個
beforeunload
偵聽器以防止標籤意外關閉。 p>關於持久性的警告
您仍然需要保存/恢復狀態(變數),因為不存在持久服務工作人員之類的東西,並且這些解決方法具有如上所述的限制,因此工作人員可以終止。您可以維護儲存中的狀態,範例。
請注意,您不應該只是為了簡化狀態/變數管理而讓您的工作執行緒持久化。這樣做只是為了恢復因重新啟動工作線程而惡化的效能,以防您的狀態重建成本非常昂貴,或者如果您掛接到本答案開頭列出的頻繁事件。