Skip to content

Instantly share code, notes, and snippets.

@guest271314
Created February 8, 2022 01:35
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guest271314/74f35ff0a1271592d6f7e3cf792b357f to your computer and use it in GitHub Desktop.
Save guest271314/74f35ff0a1271592d6f7e3cf792b357f to your computer and use it in GitHub Desktop.
Persistent Manifest Version 3 (MV3) ServiceWorker https://bugs.chromium.org/p/chromium/issues/detail?id=1152255
async function persistServiceWorker(src) {
return new Promise((resolve) => {
const removeFrames = (close = false) => {
for (const frame of document.querySelectorAll(
`[src="${src}index.html"]`
)) {
frame.parentNode.removeChild(frame);
}
if (close) {
port.postMessage('close');
}
};
removeFrames();
onbeforeunload = (e) => {
removeFrames(true);
};
onmessage = async (e) => {
if (e.origin === new URL(src).origin) {
console.log(e);
if (e.ports.length) {
[port] = e.ports;
port.onmessage = (e) => {
console.log(e.data);
};
port.onmessageerror = (e) => {
console.error(e);
};
if (iframe.parentNode) {
// TODO: remove iframe when port is defined
// iframe.parentNode.removeChild(iframe);
}
while (port) {
port.postMessage(null);
await new Promise((r) => setTimeout(r, 1000 * 5));
}
} else if (e.data === 'close') {
if (iframe.parentNode) {
iframe.parentNode.removeChild(iframe);
onmessage = null;
port = null;
}
}
}
};
iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.name = location.origin;
document.body.appendChild(iframe);
iframe.onload = () => resolve();
iframe.src = `${src}index.html`;
});
}
function persistentServiceWorkerActive(url) {
alert(`Persistent ServiceWorker already running on ${url}`);
}
chrome.action.onClicked.addListener(async (tab) => {
if (!currentPersistentClient) {
currentPersistentClient = tab.url;
await chrome.scripting.executeScript({
target: {
tabId: tab.id,
},
world: 'MAIN',
args: [chrome.runtime.getURL('')],
func: persistServiceWorker,
});
(
await self.clients.matchAll({ includeUncontrolled: true })
)[0].postMessage(null, [port2]);
} else {
await chrome.scripting.executeScript({
target: {
tabId: tab.id,
},
world: 'MAIN',
args: [currentPersistentClient],
func: persistentServiceWorkerActive,
});
}
});
oninstall = async (event) => {
console.log(event);
event.waitUntil(self.skipWaiting());
};
onactivate = async (event) => {
console.log(event);
event.waitUntil(self.clients.claim());
};
onfetch = async (event) => {
console.log(event);
};
// persistent data
const start = new Date();
let currentPersistentClient = null;
let lastEventId = 0;
// MessageChannel between external source and extension ServiceWorkerGlobalScope
let { port1, port2 } = new MessageChannel();
port1.onmessageerror = (err) => {
console.error(err);
currentPersistentClient = null;
port1.close();
chrome.runtime.reload();
};
port1.onmessage = async (e) => {
if (e.data === 'close') {
console.log(e);
port1.close();
chrome.runtime.reload();
}
if (e.data === 'port2') {
port1.postMessage('port1');
return;
}
port1.postMessage({
lastEventId,
start,
now: new Date(),
minutes: (new Date() - start) / 60000,
message: `Message from ${e.target.constructor.name} in ${globalThis.constructor.name}`,
});
++lastEventId;
};
onmessage = (e) => {
if (e.data === 'close') {
port1.close();
chrome.runtime.reload();
}
};
<script type="module" src="./index.js"></script>
function closeMessageChannel() {
parent.postMessage('close', name);
}
navigator.serviceWorker.oncontrollerchange = async (e) => {
console.log(e);
closeMessageChannel();
};
navigator.serviceWorker.onmessage = async (e) => {
parent.postMessage(e.data, name, e.ports);
try {
while ((await navigator.serviceWorker.ready).active) {
(await navigator.serviceWorker.ready).active.postMessage(null);
await new Promise((resolve) => setTimeout(resolve, 1000 * 15));
}
closeMessageChannel();
} catch (err) {
console.error(err);
closeMessageChannel();
}
};
console.log(chrome.runtime);
{
"name": "Persistent ServiceWorker - web_accessible_resources, iframe, MessageEvent",
"version": "1.0",
"manifest_version": 3,
"permissions": ["scripting"],
"host_permissions": ["<all_urls>"],
"background": {
"service_worker": "background.js",
"type": "module"
},
"web_accessible_resources": [{
"resources": ["*.html", "*.js"],
"matches": ["<all_urls>"],
"extensions": [ ]
}],
"action": {},
"author": "guest271314"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment