// 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. 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 if it does not already exist. * * 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. * * @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) 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, };