fix: drive background refresh and phishing update from alarms (closes #158)
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
This commit was merged in pull request #208.
This commit is contained in:
114
src/shared/alarms.js
Normal file
114
src/shared/alarms.js
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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<boolean>} 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<string, function>} 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,
|
||||
};
|
||||
Reference in New Issue
Block a user