In Manifest V3, Chrome extensions can no longer use background pages in favor of service workers. Chrome will mark a service worker as inactive even if an active WebSocket connection is made. This causes Ably to disconnect and new messages will not be received. Unfortunately, this appears to be intended design so workarounds are needed to keep the service worker alive in order to continue receiving messages. Multiple workarounds are available, depending on your use-case.
Alarms will reset the service worker's inactivity timer, or wake up an inactive service worker.
The alarm must have a period of less than 5 minutes in order to keep a service worker active consistently.
The minimum period required depends on the actions that your service worker performs, but it has to be at most 4.9 minutes.
Requires permission alarms
to be added to manifest.json:
{
// ...
"permissions": ["alarms"]
}
chrome.alarms.create({ periodInMinutes: 0.3 });
chrome.alarms.onAlarm.addListener(() => {
// This function will be called once the SW wakes up
});
If your Chrome extension connects to a native application, a service worker which is attached to a native application will not be marked as inactive as long as the native application is alive. To ensure that the native application exiting does not mark your service worker as inactive, make sure the connection can survive a restart of the native application.
var port = chrome.runtime.connectNative('com.example.extension');
port.onDisconnect.addListener(function () {
// Reconnect to avoid being marked as inactive
});
If your Chrome extension requires broad host permissions (e.g. *://*/*
), connecting to a tab will keep your service worker active.
Requires scripting
permission to be added to manifest.json:
{
// ...
"permissions": ["scripting"]
}
findTab();
chrome.runtime.onConnect.addListener((port) => {
if (port.name === 'keepAlive') {
setTimeout(() => port.disconnect(), 250e3);
port.onDisconnect.addListener(() => findTab());
}
});
const onUpdate = (tabId, info, tab) => /^https?:/.test(info.url) && findTab([tab]);
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);
}
If you connect to a port, the service worker's inactivity timer will be reset. However, the port must be reconnected at an interval less than 5 minutes or the SW will be marked as inactive.
chrome.runtime.onConnect.addListener((port) => {
if (port.name !== 'foo') return;
port.onMessage.addListener(() => {});
port.onDisconnect.addListener(deleteTimer);
port._timer = setTimeout(forceReconnect, 250e3, port);
});
function forceReconnect(port) {
deleteTimer(port);
port.disconnect();
}
function deleteTimer(port) {
if (port._timer) {
clearTimeout(port._timer);
delete port._timer;
}
}
Enterprise extensions will continue to work on manifest v2 until January 2024. If your extension is installed as part of an enterprise policy, this means that you will have until January 2024 to continue using manifest v2.