MV3 service workers that never terminate because of setInterval or persistent WebSocket connections consume CPU and memory continuously — for every browser session, on every user's machine. ISO 25010:2011 performance-efficiency.resource-utilisation treats unkillable background processes as a resource defect. Chrome aggressively terminates service workers that misbehave, but a setInterval pattern keeps the worker alive indefinitely, preventing Chrome's idle eviction from ever firing. This drains battery on laptops and degrades overall system performance.
High because a background script that never terminates imposes a permanent CPU and memory tax on every user's browser, measurably degrading battery life.
Replace setInterval with chrome.alarms so the service worker performs its work and exits, relying on Chrome to wake it when the next alarm fires.
// BAD: prevents service worker termination
setInterval(() => checkForUpdates(), 60_000);
// GOOD: service worker wakes on alarm, runs, then idles naturally
chrome.alarms.create('updateCheck', { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'updateCheck') checkForUpdates();
});
For MV2 extensions, set "persistent": false in manifest.json unless the extension genuinely requires a persistent WebSocket or audio context — document the reason in a comment if true is unavoidable.
ID: extension-ux-performance.bundle-memory.background-terminates
Severity: high
What to look for: For Manifest V3 extensions, examine the service worker file for patterns that prevent termination. Look for setInterval calls, setTimeout chains (re-scheduling before expiry), chrome.alarms misuse, or open WebSocket connections kept alive indefinitely. Check whether the service worker holds resources that block idle termination. For Manifest V2 extensions, check whether a persistent background page is used when an event page would suffice ("persistent": false in manifest). Before evaluating, extract and quote the service worker or background script lifecycle handlers to verify proper termination patterns. Count all instances found and enumerate each.
Pass criteria: MV3 service worker does not use setInterval or persistent setTimeout chains. Recurring tasks use chrome.alarms instead. The service worker completes its work and allows itself to terminate. MV2 extensions use event pages ("persistent": false) rather than persistent background pages unless there is a documented reason. At least 1 implementation must be confirmed.
Fail criteria: MV3 service worker uses setInterval (which prevents idle termination). Long-running setTimeout chains re-register before completion. Open WebSocket connections or long-polling patterns prevent the service worker from going idle. MV2 extension uses "persistent": true with no documented justification.
Skip (N/A) when: The extension has no background script (purely content-script or popup-only extension).
Cross-reference: The memory-under-50mb check verifies that memory usage stays bounded even when the background script is actively running.
Detail on fail: Describe the termination blocker. Example: "Service worker uses setInterval(checkForUpdates, 60000) — prevents idle termination, keeps background alive indefinitely" or "MV2 manifest uses 'persistent': true with no feature that requires a persistent background page."
Remediation: Replace setInterval with chrome.alarms for recurring tasks:
// BAD: prevents service worker termination
setInterval(() => checkForUpdates(), 60_000);
// GOOD: use chrome.alarms (service worker wakes on alarm, then idles)
chrome.alarms.create('updateCheck', { periodInMinutes: 1 });
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'updateCheck') checkForUpdates();
});
For MV2, set "persistent": false unless you genuinely need a persistent WebSocket or audio context.