diff --git a/extensions/chromium/manifest.json b/extensions/chromium/manifest.json index ee9f4e736..8fd2649c1 100644 --- a/extensions/chromium/manifest.json +++ b/extensions/chromium/manifest.json @@ -10,6 +10,7 @@ "16": "icon16.png" }, "permissions": [ + "alarms", "declarativeNetRequestWithHostAccess", "webRequest", "webRequestBlocking", diff --git a/extensions/chromium/telemetry.js b/extensions/chromium/telemetry.js index 7a985e3a1..b0cb34ef4 100644 --- a/extensions/chromium/telemetry.js +++ b/extensions/chromium/telemetry.js @@ -42,8 +42,35 @@ limitations under the License. return; } - maybeSendPing(); - setInterval(maybeSendPing, 36e5); + // The localStorage API is unavailable in service workers. We store data in + // chrome.storage.local and use this "localStorage" object to enable + // synchronous access in the logic. + const localStorage = { + telemetryLastTime: 0, + telemetryDeduplicationId: "", + telemetryLastVersion: "", + }; + + chrome.alarms.onAlarm.addListener(alarm => { + if (alarm.name === "maybeSendPing") { + maybeSendPing(); + } + }); + chrome.storage.session.get({ didPingCheck: false }, async items => { + if (items?.didPingCheck) { + return; + } + maybeSendPing(); + await chrome.alarms.clear("maybeSendPing"); + await chrome.alarms.create("maybeSendPing", { periodInMinutes: 60 }); + chrome.storage.session.set({ didPingCheck: true }); + }); + + function updateLocalStorage(key, value) { + localStorage[key] = value; + // Note: We mirror the data in localStorage because the following is async. + chrome.storage.local.set({ [key]: value }); + } function maybeSendPing() { getLoggingPref(function (didOptOut) { @@ -61,12 +88,20 @@ limitations under the License. // send more pings. return; } + doSendPing(); + }); + } + + function doSendPing() { + chrome.storage.local.get(localStorage, items => { + Object.assign(localStorage, items); + var lastTime = parseInt(localStorage.telemetryLastTime) || 0; var wasUpdated = didUpdateSinceLastCheck(); if (!wasUpdated && Date.now() - lastTime < MINIMUM_TIME_BETWEEN_PING) { return; } - localStorage.telemetryLastTime = Date.now(); + updateLocalStorage("telemetryLastTime", Date.now()); var deduplication_id = getDeduplicationId(wasUpdated); var extension_version = chrome.runtime.getManifest().version; @@ -104,7 +139,7 @@ limitations under the License. for (const c of buf) { id += (c >>> 4).toString(16) + (c & 0xf).toString(16); } - localStorage.telemetryDeduplicationId = id; + updateLocalStorage("telemetryDeduplicationId", id); } return id; } @@ -119,7 +154,7 @@ limitations under the License. if (!chromeVersion || localStorage.telemetryLastVersion === chromeVersion) { return false; } - localStorage.telemetryLastVersion = chromeVersion; + updateLocalStorage("telemetryLastVersion", chromeVersion); return true; }