// Periodic scheduling for the background context. // // The Chrome MV3 service worker is terminated after roughly 30 seconds idle, // which takes every setInterval/setTimeout with it. The extension alarms API // is the mechanism that survives: the browser holds the schedule and wakes // the worker to deliver onAlarm. Firefox MV2 runs a persistent background // page where timers would survive, but alarms behave identically there, so // both targets share this path and both manifests declare the "alarms" // permission. // // Periods are whole minutes at or above the browser-enforced one-minute // minimum, so nothing here is silently clamped to a slower cadence. // // Trap for anyone changing a period: each job also carries a freshness guard // that can veto its own scheduled tick. A guard timed to the alarm period // halves the real cadence, because the guard is measured from when the last // run finished and the alarm fires one run-duration earlier than that. Every // guard must therefore either be strictly shorter than the period it gates or // be bypassed on the scheduled tick — see backgroundRefresh() in // src/background/index.js and updatePhishingList() in shared/phishingDomains.js. const BALANCE_REFRESH_ALARM = "autistmask-balance-refresh"; const PHISHING_REFRESH_ALARM = "autistmask-phishing-refresh"; const MIN_ALARM_PERIOD_MINUTES = 1; const BALANCE_REFRESH_PERIOD_MINUTES = 1; const PHISHING_REFRESH_PERIOD_MINUTES = 24 * 60; // Resolved on use rather than captured at module load: the worker is torn // down and re-evaluated repeatedly, and tests install a stub after requiring // the module. function alarmsApi() { if (typeof browser !== "undefined" && browser.alarms) return browser.alarms; if (typeof chrome !== "undefined" && chrome.alarms) return chrome.alarms; return null; } /** * Create an alarm unless one with the requested period already exists. * * The existence check is load-bearing: creating an alarm resets its schedule, * and this runs on every worker wake. Creating unconditionally would push the * next fire time out on every incoming message, so a busy extension would * never see the alarm fire at all. * * The period comparison is equally load-bearing in the other direction: an * alarm created by an older version keeps its old period forever unless a * changed constant re-creates it, so a period edit would never reach an * existing install. Re-creating on a period change happens once and then * settles into the existence check above. * * @param {string} name * @param {number} periodInMinutes * @returns {Promise} true if the alarm was created by this call. */ async function ensureAlarm(name, periodInMinutes) { const api = alarmsApi(); if (!api) return false; const period = Math.max(periodInMinutes, MIN_ALARM_PERIOD_MINUTES); const existing = await api.get(name); if (existing && existing.periodInMinutes === period) return false; api.create(name, { periodInMinutes: period, delayInMinutes: period, }); return true; } /** * Ensure both recurring background jobs are scheduled. Safe to call on every * worker start, on onInstalled and on onStartup. * * @returns {Promise<{balance: boolean, phishing: boolean}>} which alarms this * call had to create. */ async function ensureRecurringAlarms() { const balance = await ensureAlarm( BALANCE_REFRESH_ALARM, BALANCE_REFRESH_PERIOD_MINUTES, ); const phishing = await ensureAlarm( PHISHING_REFRESH_ALARM, PHISHING_REFRESH_PERIOD_MINUTES, ); return { balance, phishing }; } /** * Register per-alarm handlers. One listener dispatches by alarm name so the * worker only ever installs a single onAlarm listener. * * @param {Object} handlers * @returns {boolean} true if the listener was installed. */ function registerAlarmHandlers(handlers) { const api = alarmsApi(); if (!api || !api.onAlarm) return false; api.onAlarm.addListener((alarm) => { const handler = handlers[alarm && alarm.name]; if (handler) handler(); }); return true; } module.exports = { BALANCE_REFRESH_ALARM, PHISHING_REFRESH_ALARM, MIN_ALARM_PERIOD_MINUTES, BALANCE_REFRESH_PERIOD_MINUTES, PHISHING_REFRESH_PERIOD_MINUTES, ensureAlarm, ensureRecurringAlarms, registerAlarmHandlers, };