diff --git a/browser/components/BrowserGlue.sys.mjs b/browser/components/BrowserGlue.sys.mjs index c00e71570b4f..c822b1406cbe 100644 --- a/browser/components/BrowserGlue.sys.mjs +++ b/browser/components/BrowserGlue.sys.mjs @@ -27,6 +27,8 @@ ChromeUtils.defineESModuleGetters(lazy, { BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.sys.mjs", CaptchaDetectionPingUtils: "resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs", + ContentBlockingPrefs: + "moz-src:///browser/components/protections/ContentBlockingPrefs.sys.mjs", ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.sys.mjs", DAPTelemetrySender: "resource://gre/modules/DAPTelemetrySender.sys.mjs", @@ -600,58 +602,7 @@ BrowserGlue.prototype = { delete this._gmpInstallManager; } - Services.prefs.removeObserver( - "privacy.trackingprotection", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "network.cookie.cookieBehavior", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "network.cookie.cookieBehavior.pbmode", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "network.http.referer.disallowCrossSiteRelaxingDefault", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "privacy.partition.network_state.ocsp_cache", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "privacy.query_stripping.enabled", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "privacy.query_stripping.enabled.pbmode", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "privacy.fingerprintingProtection", - this._matchCBCategory - ); - Services.prefs.removeObserver( - "privacy.fingerprintingProtection.pbmode", - this._matchCBCategory - ); - Services.prefs.removeObserver( - ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY, - this._updateCBCategory - ); - Services.prefs.removeObserver( - "privacy.trackingprotection", - this._setPrefExpectations - ); - Services.prefs.removeObserver( - "browser.contentblocking.features.strict", - this._setPrefExpectationsAndUpdate - ); + lazy.ContentBlockingPrefs.uninit(); }, // runs on startup, before the first command line handler is invoked @@ -1096,63 +1047,7 @@ BrowserGlue.prototype = { 16 * aWindow.devicePixelRatio ); - this._setPrefExpectationsAndUpdate(); - this._matchCBCategory(); - - // This observes the entire privacy.trackingprotection.* pref tree. - Services.prefs.addObserver( - "privacy.trackingprotection", - this._matchCBCategory - ); - Services.prefs.addObserver( - "network.cookie.cookieBehavior", - this._matchCBCategory - ); - Services.prefs.addObserver( - "network.cookie.cookieBehavior.pbmode", - this._matchCBCategory - ); - Services.prefs.addObserver( - "network.http.referer.disallowCrossSiteRelaxingDefault", - this._matchCBCategory - ); - Services.prefs.addObserver( - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", - this._matchCBCategory - ); - Services.prefs.addObserver( - "privacy.partition.network_state.ocsp_cache", - this._matchCBCategory - ); - Services.prefs.addObserver( - "privacy.query_stripping.enabled", - this._matchCBCategory - ); - Services.prefs.addObserver( - "privacy.query_stripping.enabled.pbmode", - this._matchCBCategory - ); - Services.prefs.addObserver( - "privacy.fingerprintingProtection", - this._matchCBCategory - ); - Services.prefs.addObserver( - "privacy.fingerprintingProtection.pbmode", - this._matchCBCategory - ); - Services.prefs.addObserver( - ContentBlockingCategoriesPrefs.PREF_CB_CATEGORY, - this._updateCBCategory - ); - Services.prefs.addObserver( - "privacy.trackingprotection", - this._setPrefExpectations - ); - Services.prefs.addObserver( - "browser.contentblocking.features.strict", - this._setPrefExpectationsAndUpdate - ); - + lazy.ContentBlockingPrefs.init(); lazy.CaptchaDetectionPingUtils.init(); this._verifySandboxUserNamespaces(aWindow); @@ -1208,23 +1103,6 @@ BrowserGlue.prototype = { } }, - _setPrefExpectations() { - ContentBlockingCategoriesPrefs.setPrefExpectations(); - }, - - _setPrefExpectationsAndUpdate() { - ContentBlockingCategoriesPrefs.setPrefExpectations(); - ContentBlockingCategoriesPrefs.updateCBCategory(); - }, - - _matchCBCategory() { - ContentBlockingCategoriesPrefs.matchCBCategory(); - }, - - _updateCBCategory() { - ContentBlockingCategoriesPrefs.updateCBCategory(); - }, - /** * Application shutdown handler. * @@ -3539,405 +3417,3 @@ BrowserGlue.prototype = { "nsISupportsWeakReference", ]), }; - -var ContentBlockingCategoriesPrefs = { - PREF_CB_CATEGORY: "browser.contentblocking.category", - PREF_STRICT_DEF: "browser.contentblocking.features.strict", - switchingCategory: false, - - setPrefExpectations() { - // The prefs inside CATEGORY_PREFS are initial values. - // If the pref remains null, then it will expect the default value. - // The "standard" category is defined as expecting default values of the - // listed prefs. The "strict" category lists all prefs that will be set - // according to the strict feature pref. - this.CATEGORY_PREFS = { - strict: { - "network.cookie.cookieBehavior": null, - "network.cookie.cookieBehavior.pbmode": null, - "privacy.trackingprotection.pbmode.enabled": null, - "privacy.trackingprotection.enabled": null, - "privacy.trackingprotection.socialtracking.enabled": null, - "privacy.trackingprotection.fingerprinting.enabled": null, - "privacy.trackingprotection.cryptomining.enabled": null, - "privacy.trackingprotection.emailtracking.enabled": null, - "privacy.trackingprotection.emailtracking.pbmode.enabled": null, - "privacy.trackingprotection.consentmanager.skip.enabled": null, - "privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null, - "privacy.annotate_channels.strict_list.enabled": null, - "network.http.referer.disallowCrossSiteRelaxingDefault": null, - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": - null, - "privacy.partition.network_state.ocsp_cache": null, - "privacy.query_stripping.enabled": null, - "privacy.query_stripping.enabled.pbmode": null, - "privacy.fingerprintingProtection": null, - "privacy.fingerprintingProtection.pbmode": null, - "network.cookie.cookieBehavior.optInPartitioning": null, - "privacy.bounceTrackingProtection.mode": null, - }, - standard: { - "network.cookie.cookieBehavior": null, - "network.cookie.cookieBehavior.pbmode": null, - "privacy.trackingprotection.pbmode.enabled": null, - "privacy.trackingprotection.enabled": null, - "privacy.trackingprotection.socialtracking.enabled": null, - "privacy.trackingprotection.fingerprinting.enabled": null, - "privacy.trackingprotection.cryptomining.enabled": null, - "privacy.trackingprotection.emailtracking.enabled": null, - "privacy.trackingprotection.emailtracking.pbmode.enabled": null, - "privacy.trackingprotection.consentmanager.skip.enabled": null, - "privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null, - "privacy.annotate_channels.strict_list.enabled": null, - "network.http.referer.disallowCrossSiteRelaxingDefault": null, - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": - null, - "privacy.partition.network_state.ocsp_cache": null, - "privacy.query_stripping.enabled": null, - "privacy.query_stripping.enabled.pbmode": null, - "privacy.fingerprintingProtection": null, - "privacy.fingerprintingProtection.pbmode": null, - "network.cookie.cookieBehavior.optInPartitioning": null, - "privacy.bounceTrackingProtection.mode": null, - }, - }; - let type = "strict"; - let rulesArray = Services.prefs - .getStringPref(this.PREF_STRICT_DEF) - .split(","); - for (let item of rulesArray) { - switch (item) { - case "tp": - this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] = - true; - break; - case "-tp": - this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] = - false; - break; - case "tpPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.pbmode.enabled" - ] = true; - break; - case "-tpPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.pbmode.enabled" - ] = false; - break; - case "fp": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.fingerprinting.enabled" - ] = true; - break; - case "-fp": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.fingerprinting.enabled" - ] = false; - break; - case "cryptoTP": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.cryptomining.enabled" - ] = true; - break; - case "-cryptoTP": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.cryptomining.enabled" - ] = false; - break; - case "stp": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.socialtracking.enabled" - ] = true; - break; - case "-stp": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.socialtracking.enabled" - ] = false; - break; - case "emailTP": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.emailtracking.enabled" - ] = true; - break; - case "-emailTP": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.emailtracking.enabled" - ] = false; - break; - case "emailTPPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.emailtracking.pbmode.enabled" - ] = true; - break; - case "-emailTPPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.emailtracking.pbmode.enabled" - ] = false; - break; - case "consentmanagerSkip": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.consentmanager.skip.enabled" - ] = true; - break; - case "-consentmanagerSkip": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.consentmanager.skip.enabled" - ] = false; - break; - case "consentmanagerSkipPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.consentmanager.skip.pbmode.enabled" - ] = true; - break; - case "-consentmanagerSkipPrivate": - this.CATEGORY_PREFS[type][ - "privacy.trackingprotection.consentmanager.skip.pbmode.enabled" - ] = false; - break; - case "lvl2": - this.CATEGORY_PREFS[type][ - "privacy.annotate_channels.strict_list.enabled" - ] = true; - break; - case "-lvl2": - this.CATEGORY_PREFS[type][ - "privacy.annotate_channels.strict_list.enabled" - ] = false; - break; - case "rp": - this.CATEGORY_PREFS[type][ - "network.http.referer.disallowCrossSiteRelaxingDefault" - ] = true; - break; - case "-rp": - this.CATEGORY_PREFS[type][ - "network.http.referer.disallowCrossSiteRelaxingDefault" - ] = false; - break; - case "rpTop": - this.CATEGORY_PREFS[type][ - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" - ] = true; - break; - case "-rpTop": - this.CATEGORY_PREFS[type][ - "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" - ] = false; - break; - case "ocsp": - this.CATEGORY_PREFS[type][ - "privacy.partition.network_state.ocsp_cache" - ] = true; - break; - case "-ocsp": - this.CATEGORY_PREFS[type][ - "privacy.partition.network_state.ocsp_cache" - ] = false; - break; - case "qps": - this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = true; - break; - case "-qps": - this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = false; - break; - case "qpsPBM": - this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] = - true; - break; - case "-qpsPBM": - this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] = - false; - break; - case "fpp": - this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = true; - break; - case "-fpp": - this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = false; - break; - case "fppPrivate": - this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] = - true; - break; - case "-fppPrivate": - this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] = - false; - break; - case "cookieBehavior0": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_ACCEPT; - break; - case "cookieBehavior1": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; - break; - case "cookieBehavior2": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_REJECT; - break; - case "cookieBehavior3": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; - break; - case "cookieBehavior4": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; - break; - case "cookieBehavior5": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = - Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; - break; - case "cookieBehaviorPBM0": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_ACCEPT; - break; - case "cookieBehaviorPBM1": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; - break; - case "cookieBehaviorPBM2": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_REJECT; - break; - case "cookieBehaviorPBM3": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; - break; - case "cookieBehaviorPBM4": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; - break; - case "cookieBehaviorPBM5": - this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = - Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; - break; - case "3pcd": - this.CATEGORY_PREFS[type][ - "network.cookie.cookieBehavior.optInPartitioning" - ] = true; - break; - case "-3pcd": - this.CATEGORY_PREFS[type][ - "network.cookie.cookieBehavior.optInPartitioning" - ] = false; - break; - case "btp": - this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] = - Ci.nsIBounceTrackingProtection.MODE_ENABLED; - break; - case "-btp": - // We currently consider MODE_ENABLED_DRY_RUN the "off" state. See - // nsIBounceTrackingProtection.idl for details. - this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] = - Ci.nsIBounceTrackingProtection.MODE_ENABLED_DRY_RUN; - break; - default: - console.error(`Error: Unknown rule observed ${item}`); - } - } - }, - - /** - * Checks if CB prefs match perfectly with one of our pre-defined categories. - */ - prefsMatch(category) { - // The category pref must be either unset, or match. - if ( - Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) && - Services.prefs.getStringPref(this.PREF_CB_CATEGORY) != category - ) { - return false; - } - for (let pref in this.CATEGORY_PREFS[category]) { - let value = this.CATEGORY_PREFS[category][pref]; - if (value == null) { - if (Services.prefs.prefHasUserValue(pref)) { - return false; - } - } else { - let prefType = Services.prefs.getPrefType(pref); - if ( - (prefType == Services.prefs.PREF_BOOL && - Services.prefs.getBoolPref(pref) != value) || - (prefType == Services.prefs.PREF_INT && - Services.prefs.getIntPref(pref) != value) || - (prefType == Services.prefs.PREF_STRING && - Services.prefs.getStringPref(pref) != value) - ) { - return false; - } - } - } - return true; - }, - - matchCBCategory() { - if (this.switchingCategory) { - return; - } - // If PREF_CB_CATEGORY is not set match users to a Content Blocking category. Check if prefs fit - // perfectly into strict or standard, otherwise match with custom. If PREF_CB_CATEGORY has previously been set, - // a change of one of these prefs necessarily puts us in "custom". - if (this.prefsMatch("standard")) { - Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "standard"); - } else if (this.prefsMatch("strict")) { - Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "strict"); - } else { - Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom"); - } - - // If there is a custom policy which changes a related pref, then put the user in custom so - // they still have access to other content blocking prefs, and to keep our default definitions - // from changing. - let policy = Services.policies.getActivePolicies(); - if (policy && (policy.EnableTrackingProtection || policy.Cookies)) { - Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom"); - } - }, - - updateCBCategory() { - if ( - this.switchingCategory || - !Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) - ) { - return; - } - // Turn on switchingCategory flag, to ensure that when the individual prefs that change as a result - // of the category change do not trigger yet another category change. - this.switchingCategory = true; - let value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY); - this.setPrefsToCategory(value); - this.switchingCategory = false; - }, - - /** - * Sets all user-exposed content blocking preferences to values that match the selected category. - */ - setPrefsToCategory(category) { - // Leave prefs as they were if we are switching to "custom" category. - if (category == "custom") { - return; - } - - for (let pref in this.CATEGORY_PREFS[category]) { - let value = this.CATEGORY_PREFS[category][pref]; - if (!Services.prefs.prefIsLocked(pref)) { - if (value == null) { - Services.prefs.clearUserPref(pref); - } else { - switch (Services.prefs.getPrefType(pref)) { - case Services.prefs.PREF_BOOL: - Services.prefs.setBoolPref(pref, value); - break; - case Services.prefs.PREF_INT: - Services.prefs.setIntPref(pref, value); - break; - case Services.prefs.PREF_STRING: - Services.prefs.setStringPref(pref, value); - break; - } - } - } - } - }, -}; diff --git a/browser/components/protections/ContentBlockingPrefs.sys.mjs b/browser/components/protections/ContentBlockingPrefs.sys.mjs new file mode 100644 index 000000000000..bed45e483cce --- /dev/null +++ b/browser/components/protections/ContentBlockingPrefs.sys.mjs @@ -0,0 +1,483 @@ +/* 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/. */ + +const PREFS_CHANGING_CATEGORY = new Set([ + "network.cookie.cookieBehavior", + "network.cookie.cookieBehavior.pbmode", + "network.http.referer.disallowCrossSiteRelaxingDefault", + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation", + "privacy.partition.network_state.ocsp_cache", + "privacy.query_stripping.enabled", + "privacy.query_stripping.enabled.pbmode", + "privacy.fingerprintingProtection", + "privacy.fingerprintingProtection.pbmode", +]); + +/** + * @class ContentBlockingPrefs + * + * Manages how the content blocking and anti-tracking preferences relate to the + * broad Tracking Protection categories (standard, strict and custom). + * + * @typedef {"standard"|"strict"|"custom"} CBCategory + */ +export let ContentBlockingPrefs = { + PREF_CB_CATEGORY: "browser.contentblocking.category", + PREF_STRICT_DEF: "browser.contentblocking.features.strict", + switchingCategory: false, + + setPrefExpectations() { + // The prefs inside CATEGORY_PREFS are initial values. + // If the pref remains null, then it will expect the default value. + // The "standard" category is defined as expecting default values of the + // listed prefs. The "strict" category lists all prefs that will be set + // according to the strict feature pref. + this.CATEGORY_PREFS = { + strict: { + "network.cookie.cookieBehavior": null, + "network.cookie.cookieBehavior.pbmode": null, + "privacy.trackingprotection.pbmode.enabled": null, + "privacy.trackingprotection.enabled": null, + "privacy.trackingprotection.socialtracking.enabled": null, + "privacy.trackingprotection.fingerprinting.enabled": null, + "privacy.trackingprotection.cryptomining.enabled": null, + "privacy.trackingprotection.emailtracking.enabled": null, + "privacy.trackingprotection.emailtracking.pbmode.enabled": null, + "privacy.trackingprotection.consentmanager.skip.enabled": null, + "privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null, + "privacy.annotate_channels.strict_list.enabled": null, + "network.http.referer.disallowCrossSiteRelaxingDefault": null, + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": + null, + "privacy.partition.network_state.ocsp_cache": null, + "privacy.query_stripping.enabled": null, + "privacy.query_stripping.enabled.pbmode": null, + "privacy.fingerprintingProtection": null, + "privacy.fingerprintingProtection.pbmode": null, + "network.cookie.cookieBehavior.optInPartitioning": null, + "privacy.bounceTrackingProtection.mode": null, + }, + standard: { + "network.cookie.cookieBehavior": null, + "network.cookie.cookieBehavior.pbmode": null, + "privacy.trackingprotection.pbmode.enabled": null, + "privacy.trackingprotection.enabled": null, + "privacy.trackingprotection.socialtracking.enabled": null, + "privacy.trackingprotection.fingerprinting.enabled": null, + "privacy.trackingprotection.cryptomining.enabled": null, + "privacy.trackingprotection.emailtracking.enabled": null, + "privacy.trackingprotection.emailtracking.pbmode.enabled": null, + "privacy.trackingprotection.consentmanager.skip.enabled": null, + "privacy.trackingprotection.consentmanager.skip.pbmode.enabled": null, + "privacy.annotate_channels.strict_list.enabled": null, + "network.http.referer.disallowCrossSiteRelaxingDefault": null, + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation": + null, + "privacy.partition.network_state.ocsp_cache": null, + "privacy.query_stripping.enabled": null, + "privacy.query_stripping.enabled.pbmode": null, + "privacy.fingerprintingProtection": null, + "privacy.fingerprintingProtection.pbmode": null, + "network.cookie.cookieBehavior.optInPartitioning": null, + "privacy.bounceTrackingProtection.mode": null, + }, + }; + let type = "strict"; + let rulesArray = Services.prefs + .getStringPref(this.PREF_STRICT_DEF) + .split(","); + for (let item of rulesArray) { + switch (item) { + case "tp": + this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] = + true; + break; + case "-tp": + this.CATEGORY_PREFS[type]["privacy.trackingprotection.enabled"] = + false; + break; + case "tpPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.pbmode.enabled" + ] = true; + break; + case "-tpPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.pbmode.enabled" + ] = false; + break; + case "fp": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.fingerprinting.enabled" + ] = true; + break; + case "-fp": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.fingerprinting.enabled" + ] = false; + break; + case "cryptoTP": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.cryptomining.enabled" + ] = true; + break; + case "-cryptoTP": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.cryptomining.enabled" + ] = false; + break; + case "stp": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.socialtracking.enabled" + ] = true; + break; + case "-stp": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.socialtracking.enabled" + ] = false; + break; + case "emailTP": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.emailtracking.enabled" + ] = true; + break; + case "-emailTP": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.emailtracking.enabled" + ] = false; + break; + case "emailTPPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.emailtracking.pbmode.enabled" + ] = true; + break; + case "-emailTPPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.emailtracking.pbmode.enabled" + ] = false; + break; + case "consentmanagerSkip": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.consentmanager.skip.enabled" + ] = true; + break; + case "-consentmanagerSkip": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.consentmanager.skip.enabled" + ] = false; + break; + case "consentmanagerSkipPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.consentmanager.skip.pbmode.enabled" + ] = true; + break; + case "-consentmanagerSkipPrivate": + this.CATEGORY_PREFS[type][ + "privacy.trackingprotection.consentmanager.skip.pbmode.enabled" + ] = false; + break; + case "lvl2": + this.CATEGORY_PREFS[type][ + "privacy.annotate_channels.strict_list.enabled" + ] = true; + break; + case "-lvl2": + this.CATEGORY_PREFS[type][ + "privacy.annotate_channels.strict_list.enabled" + ] = false; + break; + case "rp": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault" + ] = true; + break; + case "-rp": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault" + ] = false; + break; + case "rpTop": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" + ] = true; + break; + case "-rpTop": + this.CATEGORY_PREFS[type][ + "network.http.referer.disallowCrossSiteRelaxingDefault.top_navigation" + ] = false; + break; + case "ocsp": + this.CATEGORY_PREFS[type][ + "privacy.partition.network_state.ocsp_cache" + ] = true; + break; + case "-ocsp": + this.CATEGORY_PREFS[type][ + "privacy.partition.network_state.ocsp_cache" + ] = false; + break; + case "qps": + this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = true; + break; + case "-qps": + this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled"] = false; + break; + case "qpsPBM": + this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] = + true; + break; + case "-qpsPBM": + this.CATEGORY_PREFS[type]["privacy.query_stripping.enabled.pbmode"] = + false; + break; + case "fpp": + this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = true; + break; + case "-fpp": + this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection"] = false; + break; + case "fppPrivate": + this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] = + true; + break; + case "-fppPrivate": + this.CATEGORY_PREFS[type]["privacy.fingerprintingProtection.pbmode"] = + false; + break; + case "cookieBehavior0": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_ACCEPT; + break; + case "cookieBehavior1": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; + break; + case "cookieBehavior2": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_REJECT; + break; + case "cookieBehavior3": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; + break; + case "cookieBehavior4": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; + break; + case "cookieBehavior5": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior"] = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; + break; + case "cookieBehaviorPBM0": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_ACCEPT; + break; + case "cookieBehaviorPBM1": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; + break; + case "cookieBehaviorPBM2": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_REJECT; + break; + case "cookieBehaviorPBM3": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; + break; + case "cookieBehaviorPBM4": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; + break; + case "cookieBehaviorPBM5": + this.CATEGORY_PREFS[type]["network.cookie.cookieBehavior.pbmode"] = + Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; + break; + case "3pcd": + this.CATEGORY_PREFS[type][ + "network.cookie.cookieBehavior.optInPartitioning" + ] = true; + break; + case "-3pcd": + this.CATEGORY_PREFS[type][ + "network.cookie.cookieBehavior.optInPartitioning" + ] = false; + break; + case "btp": + this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] = + Ci.nsIBounceTrackingProtection.MODE_ENABLED; + break; + case "-btp": + // We currently consider MODE_ENABLED_DRY_RUN the "off" state. See + // nsIBounceTrackingProtection.idl for details. + this.CATEGORY_PREFS[type]["privacy.bounceTrackingProtection.mode"] = + Ci.nsIBounceTrackingProtection.MODE_ENABLED_DRY_RUN; + break; + default: + console.error(`Error: Unknown rule observed ${item}`); + } + } + }, + + /** + * Checks if CB prefs match perfectly with one of our pre-defined categories. + * + * @param {CBCategory} category + */ + prefsMatch(category) { + // The category pref must be either unset, or match. + if ( + Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) && + Services.prefs.getStringPref(this.PREF_CB_CATEGORY) != category + ) { + return false; + } + for (let pref in this.CATEGORY_PREFS[category]) { + let value = this.CATEGORY_PREFS[category][pref]; + if (value == null) { + if (Services.prefs.prefHasUserValue(pref)) { + return false; + } + } else { + let prefType = Services.prefs.getPrefType(pref); + if ( + (prefType == Services.prefs.PREF_BOOL && + Services.prefs.getBoolPref(pref) != value) || + (prefType == Services.prefs.PREF_INT && + Services.prefs.getIntPref(pref) != value) || + (prefType == Services.prefs.PREF_STRING && + Services.prefs.getStringPref(pref) != value) + ) { + return false; + } + } + } + return true; + }, + + matchCBCategory() { + if (this.switchingCategory) { + return; + } + // If PREF_CB_CATEGORY is not set match users to a Content Blocking category. Check if prefs fit + // perfectly into strict or standard, otherwise match with custom. If PREF_CB_CATEGORY has previously been set, + // a change of one of these prefs necessarily puts us in "custom". + if (this.prefsMatch("standard")) { + Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "standard"); + } else if (this.prefsMatch("strict")) { + Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "strict"); + } else { + Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom"); + } + + // If there is a custom policy which changes a related pref, then put the user in custom so + // they still have access to other content blocking prefs, and to keep our default definitions + // from changing. + let policy = Services.policies.getActivePolicies(); + if (policy && (policy.EnableTrackingProtection || policy.Cookies)) { + Services.prefs.setStringPref(this.PREF_CB_CATEGORY, "custom"); + } + }, + + updateCBCategory() { + if ( + this.switchingCategory || + !Services.prefs.prefHasUserValue(this.PREF_CB_CATEGORY) + ) { + return; + } + // Turn on switchingCategory flag, to ensure that when the individual prefs that change as a result + // of the category change do not trigger yet another category change. + this.switchingCategory = true; + let value = Services.prefs.getStringPref(this.PREF_CB_CATEGORY); + this.setPrefsToCategory(value); + this.switchingCategory = false; + }, + + /** + * Sets all user-exposed content blocking preferences to values that match the selected category. + * + * @param {CBCategory} category + */ + setPrefsToCategory(category) { + // Leave prefs as they were if we are switching to "custom" category. + if (category == "custom") { + return; + } + + for (let pref in this.CATEGORY_PREFS[category]) { + let value = this.CATEGORY_PREFS[category][pref]; + if (!Services.prefs.prefIsLocked(pref)) { + if (value == null) { + Services.prefs.clearUserPref(pref); + } else { + switch (Services.prefs.getPrefType(pref)) { + case Services.prefs.PREF_BOOL: + Services.prefs.setBoolPref(pref, value); + break; + case Services.prefs.PREF_INT: + Services.prefs.setIntPref(pref, value); + break; + case Services.prefs.PREF_STRING: + Services.prefs.setStringPref(pref, value); + break; + } + } + } + } + }, + + setPrefExpectationsAndUpdate() { + this.setPrefExpectations(); + this.updateCBCategory(); + }, + + observe(subject, topic, data) { + if (topic != "nsPref:changed") { + return; + } + + if ( + data.startsWith("privacy.trackingprotection") || + PREFS_CHANGING_CATEGORY.has(data) + ) { + this.matchCBCategory(); + } + + if (data.startsWith("privacy.trackingprotection")) { + this.setPrefExpectations(); + } else if (data == this.PREF_CB_CATEGORY) { + this.updateCBCategory(); + } else if (data == "browser.contentblocking.features.strict") { + this.setPrefExpectationsAndUpdate(); + } + }, + + init() { + this.setPrefExpectationsAndUpdate(); + this.matchCBCategory(); + + for (let prefix of PREF_PREFIXES_TO_OBSERVE) { + Services.prefs.addObserver(prefix, this); + } + }, + + uninit() { + for (let prefix of PREF_PREFIXES_TO_OBSERVE) { + Services.prefs.removeObserver(prefix, this); + } + }, +}; + +const PREF_PREFIXES_TO_OBSERVE = new Set([ + "privacy.trackingprotection", + "network.cookie.cookieBehavior", + "network.http.referer.disallowCrossSiteRelaxingDefault", + "privacy.partition.network_state.ocsp_cache", + "privacy.query_stripping.enabled", + "privacy.fingerprintingProtection", + ContentBlockingPrefs.PREF_CB_CATEGORY, + ContentBlockingPrefs.PREF_STRICT_DEF, +]); + +ContentBlockingPrefs.QueryInterface = ChromeUtils.generateQI([Ci.nsIObserver]); diff --git a/browser/components/protections/moz.build b/browser/components/protections/moz.build index c79fefcc1653..54b99983532d 100644 --- a/browser/components/protections/moz.build +++ b/browser/components/protections/moz.build @@ -8,5 +8,12 @@ BROWSER_CHROME_MANIFESTS += ["test/browser/browser.toml"] JAR_MANIFESTS += ["jar.mn"] +MOZ_SRC_FILES += [ + "ContentBlockingPrefs.sys.mjs", +] + with Files("**"): BUG_COMPONENT = ("Firefox", "Protections UI") + +with Files("ContentBlockingPrefs.sys.mjs"): + BUG_COMPONENT = ("Core", "Privacy: Anti-Tracking")