/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; const lazy = {}; ChromeUtils.defineESModuleGetters(lazy, { ClientID: "resource://gre/modules/ClientID.sys.mjs", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.sys.mjs", }); const RECOMMENDATION_ENABLED = "browser.discovery.enabled"; const TELEMETRY_ENABLED = "datareporting.healthreport.uploadEnabled"; const TAAR_COOKIE_NAME = "taarId"; export const Discovery = { set enabled(val) { val = !!val; if (val && !lazy.gTelemetryEnabled) { throw Error("unable to turn on recommendations"); } Services.prefs.setBoolPref(RECOMMENDATION_ENABLED, val); }, get enabled() { return lazy.gTelemetryEnabled && lazy.gRecommendationEnabled; }, reset() { return DiscoveryInternal.update(true); }, update() { return DiscoveryInternal.update(); }, }; XPCOMUtils.defineLazyPreferenceGetter( lazy, "gRecommendationEnabled", RECOMMENDATION_ENABLED, false, Discovery.update ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "gTelemetryEnabled", TELEMETRY_ENABLED, false, Discovery.update ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "gCachedClientID", "toolkit.telemetry.cachedClientID", "", Discovery.reset ); XPCOMUtils.defineLazyPreferenceGetter( lazy, "gContainersEnabled", "browser.discovery.containers.enabled", false, Discovery.reset ); Services.obs.addObserver(Discovery.update, "contextual-identity-created"); const DiscoveryInternal = { get sites() { delete this.sites; this.sites = Services.prefs .getCharPref("browser.discovery.sites", "") .split(","); return this.sites; }, getContextualIDs() { // There is never a zero id, this is just for use in update. let IDs = [0]; if (lazy.gContainersEnabled) { lazy.ContextualIdentityService.getPublicIdentities().forEach(identity => { IDs.push(identity.userContextId); }); } return IDs; }, async update(reset = false) { if (reset || !Discovery.enabled) { for (let site of this.sites) { Services.cookies.remove(site, TAAR_COOKIE_NAME, "/", {}); lazy.ContextualIdentityService.getPublicIdentities().forEach( identity => { let { userContextId } = identity; Services.cookies.remove(site, TAAR_COOKIE_NAME, "/", { userContextId, }); } ); } } if (Discovery.enabled) { // If the client id is not cached, wait for the notification that it is // cached. This will happen shortly after startup in TelemetryController.sys.mjs. // When that happens, we'll get a pref notification for the cached id, // which will call update again. if (!lazy.gCachedClientID) { return; } let id = await lazy.ClientID.getClientIdHash(); for (let site of this.sites) { // This cookie gets tied down as much as possible. Specifically, // SameSite, Secure, HttpOnly and non-PrivateBrowsing. for (let userContextId of this.getContextualIDs()) { let originAttributes = { privateBrowsingId: 0 }; if (userContextId > 0) { originAttributes.userContextId = userContextId; } if ( Services.cookies.cookieExists( site, "/", TAAR_COOKIE_NAME, originAttributes ) ) { continue; } Services.cookies.add( site, "/", TAAR_COOKIE_NAME, id, true, // secure true, // httpOnly true, // session Date.now(), originAttributes, Ci.nsICookie.SAMESITE_LAX, Ci.nsICookie.SCHEME_HTTPS ); } } } }, };