Skip to content

Commit 7138219

Browse files
committed
fix(Chrome): Prevent chrome from killing our worker while it's syncing
fixes #2096 Signed-off-by: Marcel Klehr <mklehr@gmx.net>
1 parent 951acd7 commit 7138219

File tree

7 files changed

+70
-2
lines changed

7 files changed

+70
-2
lines changed

gulpfile.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ const js = async function() {
118118
const html = function(statsJson) {
119119
fs.mkdirSync('dist/html/', { recursive: true })
120120
let html, scripts, bgScript, addition
121-
;['index.html', 'options.html', 'background.html', 'test.html'].forEach(htmlFile => {
121+
;['index.html', 'options.html', 'background.html', 'test.html', 'offscreen.html'].forEach(htmlFile => {
122122
switch (htmlFile) {
123123
case 'index.html':
124124
html = fs.readFileSync('html/' + htmlFile, 'utf8')
@@ -139,6 +139,10 @@ const html = function(statsJson) {
139139
html = html.replace('{{ scripts }}', scripts)
140140
fs.writeFileSync('dist/html/' + htmlFile, html)
141141
break
142+
case 'offscreen.html':
143+
html = fs.readFileSync('html/' + htmlFile, 'utf8')
144+
fs.writeFileSync('dist/html/' + htmlFile, html)
145+
break
142146
case 'background.html':
143147
html = fs.readFileSync('html/' + htmlFile, 'utf8')
144148
scripts = statsJson.entrypoints['background-script'].assets.map(asset => `<script src="../js/${asset.name}"></script>`).join('\n')

html/offscreen.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<!DOCTYPE html>
2+
<script src="../js/offscreen.js"></script>

manifest.chrome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
"default_locale": "en",
1414

15-
"permissions": ["alarms", "bookmarks", "storage", "unlimitedStorage", "tabs", "tabGroups", "identity"],
15+
"permissions": ["alarms", "bookmarks", "storage", "unlimitedStorage", "tabs", "tabGroups", "identity", "offscreen"],
1616
"optional_permissions": ["history"],
1717
"host_permissions": [
1818
"*://*/*"

src/entries/offscreen.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// eslint-disable-next-line no-use-before-define
2+
if (typeof chrome === 'undefined') {
3+
var chrome = {}
4+
}
5+
// Keep the connection alive by sending periodic messages
6+
setInterval(() => {
7+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
8+
// @ts-ignore
9+
chrome.runtime.sendMessage({
10+
type: 'ping-service-worker'
11+
}).catch((err) => {
12+
console.debug('Failed to ping service worker:', err)
13+
})
14+
}, 25000) // Send message every 25 seconds
15+
16+
// Listen for messages from service worker
17+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
18+
// @ts-ignore
19+
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
20+
if (message.type === 'sync-progress') {
21+
sendResponse({ success: true })
22+
}
23+
return true // Keep message channel open
24+
})

src/lib/Account.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { setUser, setContext, withScope, captureException } from '@sentry/browse
1515
import AsyncLock from 'async-lock'
1616
import CachingTreeWrapper from './CachingTreeWrapper'
1717
import { UnexpectedFolderPathError } from '../errors/Error'
18+
import { createOffscreen, destroyOffscreen } from './offscreen'
1819

1920
declare const DEBUG: boolean
2021

@@ -84,6 +85,7 @@ export default class Account {
8485
protected localTabs: TLocalTree
8586
protected lockTimeout: number
8687

88+
private offscreenPingInterval: any = null
8789
private localCachingResource: CachingTreeWrapper
8890

8991
constructor(id:string, storageAdapter:IAccountStorage, serverAdapter: TAdapter, treeAdapter:TLocalTree) {
@@ -191,6 +193,16 @@ export default class Account {
191193
if (oldPath && newPath !== oldPath) {
192194
throw new UnexpectedFolderPathError(oldPath, newPath)
193195
}
196+
197+
// eslint-disable-next-line no-undef
198+
if (self.constructor.name === 'ServiceWorkerGlobalScope' || (typeof chrome !== 'undefined' && 'offscreen' in chrome)) {
199+
// Create an offscreen page in chrome and ping it regularly to prevent this worker from getting killed
200+
await createOffscreen()
201+
this.offscreenPingInterval = setInterval(() => {
202+
// eslint-disable-next-line no-undef
203+
chrome.runtime.sendMessage({type: 'sync-progress'})
204+
}, 20000)
205+
}
194206
}
195207

196208
if (this.server.onSyncStart) {
@@ -389,6 +401,12 @@ export default class Account {
389401
await this.init()
390402
}
391403
}
404+
// eslint-disable-next-line no-undef
405+
if (self.constructor.name === 'ServiceWorkerGlobalScope' || (typeof chrome !== 'undefined' && 'offscreen' in chrome)) {
406+
// We destroy the offscreen page when the sync is done to allow the worker to be killed
407+
await destroyOffscreen()
408+
}
409+
clearInterval(this.offscreenPingInterval)
392410
this.syncProcess = null
393411
this.localCachingResource = null
394412
await Logger.persist()
@@ -422,6 +440,10 @@ export default class Account {
422440
if (!this.syncProcess) {
423441
return
424442
}
443+
if (self.constructor.name === 'ServiceWorkerGlobalScope') {
444+
// eslint-disable-next-line no-undef
445+
chrome.runtime.sendMessage({ type: 'sync-progress' })
446+
}
425447
if (actionsDone) {
426448
if (this.server.isAtomic()) {
427449
const cache = (await this.localCachingResource.getCacheTree()).clone(false)

src/lib/offscreen.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import browserApi from './browser-api'
2+
export async function createOffscreen() {
3+
if (await browserApi.offscreen.hasDocument()) return
4+
5+
await browserApi.offscreen.createDocument({
6+
url: '/dist/html/offscreen.html',
7+
reasons: ['WORKERS'],
8+
justification: 'In order to sync your bookmarks uninterrupted without displaying a visible window.'
9+
})
10+
}
11+
12+
export async function destroyOffscreen() {
13+
if (!(await browserApi.offscreen.hasDocument())) return
14+
await browserApi.offscreen.closeDocument()
15+
}

webpack.common.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module.exports = {
1414
options: path.join(__dirname, 'src', 'entries', 'options.js'),
1515
test: path.join(__dirname, 'src', 'entries', 'test.js'),
1616
native: path.join(__dirname, 'src', 'entries', 'native.js'),
17+
offscreen: path.join(__dirname, 'src', 'entries', 'offscreen.js'),
1718
},
1819
output: {
1920
path: path.resolve(__dirname, 'dist', 'js'),

0 commit comments

Comments
 (0)