Skip to content

Commit 712cbab

Browse files
feat: chrome. implement offscreen document to keep service worker alive
1 parent 1da7bd5 commit 712cbab

File tree

5 files changed

+53
-23
lines changed

5 files changed

+53
-23
lines changed

src/background/main.ts

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,34 @@ setBaseUrl(client.baseURL)
7777
.then(() => console.info('Started successfully'))
7878

7979
/**
80-
* Keep the service worker alive to prevent Chrome's 5-minute inactivity termination
81-
* This is a workaround for Chrome's behavior of terminating inactive service workers
82-
* https://stackoverflow.com/questions/66618136
80+
* Keep the service worker alive using Offscreen API to prevent Chrome's termination.
8381
*/
84-
if (import.meta.env.VITE_TARGET_BROWSER === 'chrome') {
85-
function setupKeepAlive(): void {
86-
console.debug(
87-
'Setting up keep-alive ping to prevent service worker termination',
88-
)
82+
async function setupOffscreen() {
83+
const _chrome = (globalThis as any).chrome
84+
if (typeof _chrome === 'undefined' || !_chrome.offscreen) return
8985

90-
setInterval(
91-
() => {
92-
console.debug('Keep-alive ping')
93-
// Force some minimal activity
94-
browser.alarms
95-
.get(config.heartbeat.alarmName)
96-
.then(() => console.debug('Keep-alive ping completed'))
97-
.catch((err) => console.error('Keep-alive ping failed:', err))
98-
},
99-
4 * 60 * 1000,
100-
) // 4 minutes (less than Chrome's ~5 minute timeout)
101-
}
86+
if (await _chrome.offscreen.hasDocument()) return
10287

103-
// Start the keep-alive mechanism
104-
setupKeepAlive()
88+
try {
89+
await _chrome.offscreen.createDocument({
90+
url: 'src/offscreen.html',
91+
reasons: ['BLOBS'],
92+
justification: 'Keep service worker alive for heartbeat packets',
93+
})
94+
} catch (e) {
95+
console.error('Failed to create offscreen document:', e)
96+
}
10597
}
98+
99+
browser.runtime.onMessage.addListener((message: any) => {
100+
if (message.type === 'KEEP_ALIVE') {
101+
return Promise.resolve({ status: 'ok' })
102+
}
103+
return undefined
104+
})
105+
106+
// Initialize on startup and installation
107+
browser.runtime.onStartup.addListener(setupOffscreen)
108+
browser.runtime.onInstalled.addListener(setupOffscreen)
109+
110+
setupOffscreen()

src/manifest.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"alarms",
4949
"notifications",
5050
"activeTab",
51+
"offscreen",
5152
"storage"
5253
],
5354
"{{safari}}.permissions": [

src/offscreen.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
</head>
6+
<body>
7+
<script type="module" src="./offscreen.ts"></script>
8+
</body>
9+
</html>

src/offscreen.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import browser from 'webextension-polyfill'
2+
3+
// 每隔 20 秒向 background 发送一次消息,确保 Service Worker 不被杀死
4+
function keepAlive() {
5+
browser.runtime.sendMessage({ type: 'KEEP_ALIVE' }).catch(() => {
6+
// Expected during reload
7+
})
8+
}
9+
10+
setInterval(keepAlive, 20 * 1000)
11+
keepAlive()

vite.config.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ export default defineConfig({
2020
plugins: [
2121
webExtension({
2222
manifest: generateManifest,
23-
additionalInputs: ['src/consent/index.html', 'src/consent/main.ts'],
23+
additionalInputs: [
24+
'src/consent/index.html',
25+
'src/consent/main.ts',
26+
'src/offscreen.html',
27+
],
2428
browser: process.env.VITE_TARGET_BROWSER,
2529
}),
2630
],

0 commit comments

Comments
 (0)