P粉0345716232023-08-25 17:51:37
與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"}] });
P粉3863180862023-08-25 00:41:03
問題描述
解決方法:
• 漏洞利用
• 離屏
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
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
#<script src="offscreen.js"></script>
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();
連線
nativeMessaging在 Chrome 105 及更高版本中,只要透過 chrome.runtime.connectNative。如果主機進程因崩潰或使用者操作而終止,則連接埠將關閉,並且軟體將照常終止。您可以透過監聽連接埠的 onDisconnect
事件來防範它再次呼叫 chrome.runtime.connectNative。Chrome 116 及更高版本:每隔 30 秒交換一次 WebSocket 訊息以保持其活動狀態,例如每 25 秒一次。
缺點:
內容腳本的廣泛主機權限(例如
或
*://*/*警告!如果您已連接端口,請不要使用此解決方法,請對下面的端口使用另一種解決方法。
警告!如果您使用 sendMessage,也可以實作 sendMessage 的解決方法(如下)。
"permissions": ["scripting"],
"host_permissions": ["<all_urls>"],
"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)。
缺點:
您可以透過在頁面上新增 info/logs/charts/dashboard 來讓使用者更容易忍受,也可以新增一個 beforeunload
偵聽器以防止標籤意外關閉。 p>
您仍然需要保存/恢復狀態(變數),因為不存在持久服務工作人員之類的東西,並且這些解決方法具有如上所述的限制,因此工作人員可以終止。您可以維護儲存中的狀態,範例。
請注意,您不應該只是為了簡化狀態/變數管理而讓您的工作執行緒持久化。這樣做只是為了恢復因重新啟動工作線程而惡化的效能,以防您的狀態重建成本非常昂貴,或者如果您掛接到本答案開頭列出的頻繁事件。