/* 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]);