forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			3410 lines
		
	
	
	
		
			108 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			3410 lines
		
	
	
	
		
			108 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* 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-globals-from extensionControlled.js */
 | |
| /* import-globals-from preferences.js */
 | |
| 
 | |
| const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 | |
| 
 | |
| const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode";
 | |
| const TRACKING_PROTECTION_PREFS = [
 | |
|   "privacy.trackingprotection.enabled",
 | |
|   "privacy.trackingprotection.pbmode.enabled",
 | |
| ];
 | |
| const CONTENT_BLOCKING_PREFS = [
 | |
|   "privacy.trackingprotection.enabled",
 | |
|   "privacy.trackingprotection.pbmode.enabled",
 | |
|   "network.cookie.cookieBehavior",
 | |
|   "privacy.trackingprotection.fingerprinting.enabled",
 | |
|   "privacy.trackingprotection.cryptomining.enabled",
 | |
|   "privacy.firstparty.isolate",
 | |
|   "privacy.trackingprotection.emailtracking.enabled",
 | |
|   "privacy.trackingprotection.emailtracking.pbmode.enabled",
 | |
|   "privacy.fingerprintingProtection",
 | |
|   "privacy.fingerprintingProtection.pbmode",
 | |
| ];
 | |
| 
 | |
| /*
 | |
|  * Prefs that are unique to sanitizeOnShutdown and are not shared
 | |
|  * with the deleteOnClose mechanism like privacy.clearOnShutdown.cookies, -cache and -offlineApps
 | |
|  */
 | |
| const SANITIZE_ON_SHUTDOWN_PREFS_ONLY = [
 | |
|   "privacy.clearOnShutdown.history",
 | |
|   "privacy.clearOnShutdown.downloads",
 | |
|   "privacy.clearOnShutdown.sessions",
 | |
|   "privacy.clearOnShutdown.formdata",
 | |
|   "privacy.clearOnShutdown.siteSettings",
 | |
| ];
 | |
| 
 | |
| const PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled";
 | |
| const PREF_NORMANDY_ENABLED = "app.normandy.enabled";
 | |
| 
 | |
| const PREF_ADDON_RECOMMENDATIONS_ENABLED = "browser.discovery.enabled";
 | |
| 
 | |
| const PREF_PASSWORD_GENERATION_AVAILABLE = "signon.generation.available";
 | |
| const { BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN } = Ci.nsICookieService;
 | |
| 
 | |
| const PASSWORD_MANAGER_PREF_ID = "services.passwordSavingEnabled";
 | |
| 
 | |
| ChromeUtils.defineLazyGetter(this, "AlertsServiceDND", function () {
 | |
|   try {
 | |
|     let alertsService = Cc["@mozilla.org/alerts-service;1"]
 | |
|       .getService(Ci.nsIAlertsService)
 | |
|       .QueryInterface(Ci.nsIAlertsDoNotDisturb);
 | |
|     // This will throw if manualDoNotDisturb isn't implemented.
 | |
|     alertsService.manualDoNotDisturb;
 | |
|     return alertsService;
 | |
|   } catch (ex) {
 | |
|     return undefined;
 | |
|   }
 | |
| });
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(
 | |
|   this,
 | |
|   "OS_AUTH_ENABLED",
 | |
|   "signon.management.page.os-auth.enabled",
 | |
|   true
 | |
| );
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(
 | |
|   this,
 | |
|   "gIsFirstPartyIsolated",
 | |
|   "privacy.firstparty.isolate",
 | |
|   false
 | |
| );
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(
 | |
|   this,
 | |
|   "useOldClearHistoryDialog",
 | |
|   "privacy.sanitize.useOldClearHistoryDialog",
 | |
|   false
 | |
| );
 | |
| 
 | |
| ChromeUtils.defineESModuleGetters(this, {
 | |
|   DoHConfigController: "resource:///modules/DoHConfig.sys.mjs",
 | |
| });
 | |
| 
 | |
| Preferences.addAll([
 | |
|   // Content blocking / Tracking Protection
 | |
|   { id: "privacy.trackingprotection.enabled", type: "bool" },
 | |
|   { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" },
 | |
|   { id: "privacy.trackingprotection.fingerprinting.enabled", type: "bool" },
 | |
|   { id: "privacy.trackingprotection.cryptomining.enabled", type: "bool" },
 | |
|   { id: "privacy.trackingprotection.emailtracking.enabled", type: "bool" },
 | |
|   {
 | |
|     id: "privacy.trackingprotection.emailtracking.pbmode.enabled",
 | |
|     type: "bool",
 | |
|   },
 | |
| 
 | |
|   // Fingerprinting Protection
 | |
|   { id: "privacy.fingerprintingProtection", type: "bool" },
 | |
|   { id: "privacy.fingerprintingProtection.pbmode", type: "bool" },
 | |
| 
 | |
|   // Resist Fingerprinting
 | |
|   { id: "privacy.resistFingerprinting", type: "bool" },
 | |
|   { id: "privacy.resistFingerprinting.pbmode", type: "bool" },
 | |
| 
 | |
|   // Social tracking
 | |
|   { id: "privacy.trackingprotection.socialtracking.enabled", type: "bool" },
 | |
|   { id: "privacy.socialtracking.block_cookies.enabled", type: "bool" },
 | |
| 
 | |
|   // Tracker list
 | |
|   { id: "urlclassifier.trackingTable", type: "string" },
 | |
| 
 | |
|   // Button prefs
 | |
|   { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" },
 | |
|   {
 | |
|     id: "pref.privacy.disable_button.tracking_protection_exceptions",
 | |
|     type: "bool",
 | |
|   },
 | |
| 
 | |
|   // Location Bar
 | |
|   { id: "browser.urlbar.suggest.bookmark", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.clipboard", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.history", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.openpage", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.topsites", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.engines", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.quicksuggest.nonsponsored", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.quicksuggest.sponsored", type: "bool" },
 | |
|   { id: "browser.urlbar.quicksuggest.dataCollection.enabled", type: "bool" },
 | |
|   { id: PREF_URLBAR_QUICKSUGGEST_BLOCKLIST, type: "string" },
 | |
|   { id: PREF_URLBAR_WEATHER_USER_ENABLED, type: "bool" },
 | |
| 
 | |
|   // History
 | |
|   { id: "places.history.enabled", type: "bool" },
 | |
|   { id: "browser.formfill.enable", type: "bool" },
 | |
|   { id: "privacy.history.custom", type: "bool" },
 | |
| 
 | |
|   // Cookies
 | |
|   { id: "network.cookie.cookieBehavior", type: "int" },
 | |
|   { id: "network.cookie.blockFutureCookies", type: "bool" },
 | |
|   // Content blocking category
 | |
|   { id: "browser.contentblocking.category", type: "string" },
 | |
|   { id: "browser.contentblocking.features.strict", type: "string" },
 | |
| 
 | |
|   // Clear Private Data
 | |
|   { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
 | |
|   { id: "privacy.sanitize.timeSpan", type: "int" },
 | |
|   { id: "privacy.clearOnShutdown.cookies", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.cache", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.offlineApps", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.history", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.downloads", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.sessions", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.formdata", type: "bool" },
 | |
|   { id: "privacy.clearOnShutdown.siteSettings", type: "bool" },
 | |
| 
 | |
|   // Do not track
 | |
|   { id: "privacy.donottrackheader.enabled", type: "bool" },
 | |
| 
 | |
|   // Global Privacy Control
 | |
|   { id: "privacy.globalprivacycontrol.enabled", type: "bool" },
 | |
| 
 | |
|   // Media
 | |
|   { id: "media.autoplay.default", type: "int" },
 | |
| 
 | |
|   // Popups
 | |
|   { id: "dom.disable_open_during_load", type: "bool" },
 | |
|   // Passwords
 | |
|   { id: "signon.rememberSignons", type: "bool" },
 | |
|   { id: "signon.generation.enabled", type: "bool" },
 | |
|   { id: "signon.autofillForms", type: "bool" },
 | |
|   { id: "signon.management.page.breach-alerts.enabled", type: "bool" },
 | |
|   { id: "signon.firefoxRelay.feature", type: "string" },
 | |
| 
 | |
|   // Buttons
 | |
|   { id: "pref.privacy.disable_button.view_passwords", type: "bool" },
 | |
|   { id: "pref.privacy.disable_button.view_passwords_exceptions", type: "bool" },
 | |
| 
 | |
|   /* Certificates tab
 | |
|    * security.default_personal_cert
 | |
|    *   - a string:
 | |
|    *       "Select Automatically"   select a certificate automatically when a site
 | |
|    *                                requests one
 | |
|    *       "Ask Every Time"         present a dialog to the user so he can select
 | |
|    *                                the certificate to use on a site which
 | |
|    *                                requests one
 | |
|    */
 | |
|   { id: "security.default_personal_cert", type: "string" },
 | |
| 
 | |
|   { id: "security.disable_button.openCertManager", type: "bool" },
 | |
| 
 | |
|   { id: "security.disable_button.openDeviceManager", type: "bool" },
 | |
| 
 | |
|   { id: "security.OCSP.enabled", type: "int" },
 | |
| 
 | |
|   { id: "security.enterprise_roots.enabled", type: "bool" },
 | |
| 
 | |
|   // Add-ons, malware, phishing
 | |
|   { id: "xpinstall.whitelist.required", type: "bool" },
 | |
| 
 | |
|   { id: "browser.safebrowsing.malware.enabled", type: "bool" },
 | |
|   { id: "browser.safebrowsing.phishing.enabled", type: "bool" },
 | |
| 
 | |
|   { id: "browser.safebrowsing.downloads.enabled", type: "bool" },
 | |
| 
 | |
|   { id: "urlclassifier.malwareTable", type: "string" },
 | |
| 
 | |
|   {
 | |
|     id: "browser.safebrowsing.downloads.remote.block_potentially_unwanted",
 | |
|     type: "bool",
 | |
|   },
 | |
|   { id: "browser.safebrowsing.downloads.remote.block_uncommon", type: "bool" },
 | |
| 
 | |
|   // First-Party Isolation
 | |
|   { id: "privacy.firstparty.isolate", type: "bool" },
 | |
| 
 | |
|   // HTTPS-Only
 | |
|   { id: "dom.security.https_only_mode", type: "bool" },
 | |
|   { id: "dom.security.https_only_mode_pbm", type: "bool" },
 | |
|   { id: "dom.security.https_first", type: "bool" },
 | |
|   { id: "dom.security.https_first_pbm", type: "bool" },
 | |
| 
 | |
|   // Windows SSO
 | |
|   { id: "network.http.windows-sso.enabled", type: "bool" },
 | |
| 
 | |
|   // Quick Actions
 | |
|   { id: "browser.urlbar.quickactions.showPrefs", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.quickactions", type: "bool" },
 | |
| 
 | |
|   // Cookie Banner Handling
 | |
|   { id: "cookiebanners.ui.desktop.enabled", type: "bool" },
 | |
|   { id: "cookiebanners.service.mode.privateBrowsing", type: "int" },
 | |
| 
 | |
|   // DoH
 | |
|   { id: "network.trr.mode", type: "int" },
 | |
|   { id: "network.trr.uri", type: "string" },
 | |
|   { id: "network.trr.default_provider_uri", type: "string" },
 | |
|   { id: "network.trr.custom_uri", type: "string" },
 | |
|   { id: "network.trr_ui.show_fallback_warning_option", type: "bool" },
 | |
|   { id: "network.trr.display_fallback_warning", type: "bool" },
 | |
|   { id: "doh-rollout.disable-heuristics", type: "bool" },
 | |
| ]);
 | |
| 
 | |
| // Study opt out
 | |
| if (AppConstants.MOZ_DATA_REPORTING) {
 | |
|   Preferences.addAll([
 | |
|     // Preference instances for prefs that we need to monitor while the page is open.
 | |
|     { id: PREF_OPT_OUT_STUDIES_ENABLED, type: "bool" },
 | |
|     { id: PREF_ADDON_RECOMMENDATIONS_ENABLED, type: "bool" },
 | |
|     { id: PREF_UPLOAD_ENABLED, type: "bool" },
 | |
|   ]);
 | |
| }
 | |
| // Privacy segmentation section
 | |
| Preferences.add({
 | |
|   id: "browser.dataFeatureRecommendations.enabled",
 | |
|   type: "bool",
 | |
| });
 | |
| 
 | |
| // Data Choices tab
 | |
| if (AppConstants.MOZ_CRASHREPORTER) {
 | |
|   Preferences.add({
 | |
|     id: "browser.crashReports.unsubmittedCheck.autoSubmit2",
 | |
|     type: "bool",
 | |
|   });
 | |
| }
 | |
| 
 | |
| function setEventListener(aId, aEventType, aCallback) {
 | |
|   document
 | |
|     .getElementById(aId)
 | |
|     .addEventListener(aEventType, aCallback.bind(gPrivacyPane));
 | |
| }
 | |
| 
 | |
| function setSyncFromPrefListener(aId, aCallback) {
 | |
|   Preferences.addSyncFromPrefListener(document.getElementById(aId), aCallback);
 | |
| }
 | |
| 
 | |
| function setSyncToPrefListener(aId, aCallback) {
 | |
|   Preferences.addSyncToPrefListener(document.getElementById(aId), aCallback);
 | |
| }
 | |
| 
 | |
| function dataCollectionCheckboxHandler({
 | |
|   checkbox,
 | |
|   pref,
 | |
|   matchPref = () => true,
 | |
|   isDisabled = () => false,
 | |
| }) {
 | |
|   function updateCheckbox() {
 | |
|     let collectionEnabled = Services.prefs.getBoolPref(
 | |
|       PREF_UPLOAD_ENABLED,
 | |
|       false
 | |
|     );
 | |
| 
 | |
|     if (collectionEnabled && matchPref()) {
 | |
|       if (Services.prefs.getBoolPref(pref, false)) {
 | |
|         checkbox.setAttribute("checked", "true");
 | |
|       } else {
 | |
|         checkbox.removeAttribute("checked");
 | |
|       }
 | |
|       checkbox.setAttribute("preference", pref);
 | |
|     } else {
 | |
|       checkbox.removeAttribute("preference");
 | |
|       checkbox.removeAttribute("checked");
 | |
|     }
 | |
| 
 | |
|     checkbox.disabled =
 | |
|       !collectionEnabled || Services.prefs.prefIsLocked(pref) || isDisabled();
 | |
|   }
 | |
| 
 | |
|   Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateCheckbox);
 | |
|   updateCheckbox();
 | |
| }
 | |
| 
 | |
| // Sets the "Learn how" SUMO link in the Strict/Custom options of Content Blocking.
 | |
| function setUpContentBlockingWarnings() {
 | |
|   document.getElementById("fpiIncompatibilityWarning").hidden =
 | |
|     !gIsFirstPartyIsolated;
 | |
| 
 | |
|   document.getElementById("rfpIncompatibilityWarning").hidden =
 | |
|     !Preferences.get("privacy.resistFingerprinting").value &&
 | |
|     !Preferences.get("privacy.resistFingerprinting.pbmode").value;
 | |
| }
 | |
| 
 | |
| function initTCPStandardSection() {
 | |
|   let cookieBehaviorPref = Preferences.get("network.cookie.cookieBehavior");
 | |
|   let updateTCPSectionVisibilityState = () => {
 | |
|     document.getElementById("etpStandardTCPBox").hidden =
 | |
|       cookieBehaviorPref.value !=
 | |
|       Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
 | |
|   };
 | |
| 
 | |
|   cookieBehaviorPref.on("change", updateTCPSectionVisibilityState);
 | |
| 
 | |
|   updateTCPSectionVisibilityState();
 | |
| }
 | |
| 
 | |
| var gPrivacyPane = {
 | |
|   _pane: null,
 | |
| 
 | |
|   /**
 | |
|    * Variable that controls when we update sites for the new
 | |
|    * clear history dialog
 | |
|    */
 | |
|   shouldUpdateSiteUsageDataForSanitizeDialog: false,
 | |
| 
 | |
|   /**
 | |
|    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
 | |
|    */
 | |
|   _shouldPromptForRestart: true,
 | |
| 
 | |
|   /**
 | |
|    * Update the tracking protection UI to deal with extension control.
 | |
|    */
 | |
|   _updateTrackingProtectionUI() {
 | |
|     let cBPrefisLocked = CONTENT_BLOCKING_PREFS.some(pref =>
 | |
|       Services.prefs.prefIsLocked(pref)
 | |
|     );
 | |
|     let tPPrefisLocked = TRACKING_PROTECTION_PREFS.some(pref =>
 | |
|       Services.prefs.prefIsLocked(pref)
 | |
|     );
 | |
| 
 | |
|     function setInputsDisabledState(isControlled) {
 | |
|       let tpDisabled = tPPrefisLocked || isControlled;
 | |
|       let disabled = cBPrefisLocked || isControlled;
 | |
|       let tpCheckbox = document.getElementById(
 | |
|         "contentBlockingTrackingProtectionCheckbox"
 | |
|       );
 | |
|       // Only enable the TP menu if Detect All Trackers is enabled.
 | |
|       document.getElementById("trackingProtectionMenu").disabled =
 | |
|         tpDisabled || !tpCheckbox.checked;
 | |
|       tpCheckbox.disabled = tpDisabled;
 | |
| 
 | |
|       document.getElementById("standardRadio").disabled = disabled;
 | |
|       document.getElementById("strictRadio").disabled = disabled;
 | |
|       document
 | |
|         .getElementById("contentBlockingOptionStrict")
 | |
|         .classList.toggle("disabled", disabled);
 | |
|       document
 | |
|         .getElementById("contentBlockingOptionStandard")
 | |
|         .classList.toggle("disabled", disabled);
 | |
|       let arrowButtons = document.querySelectorAll("button.arrowhead");
 | |
|       for (let button of arrowButtons) {
 | |
|         button.disabled = disabled;
 | |
|       }
 | |
| 
 | |
|       // Notify observers that the TP UI has been updated.
 | |
|       // This is needed since our tests need to be notified about the
 | |
|       // trackingProtectionMenu element getting disabled/enabled at the right time.
 | |
|       Services.obs.notifyObservers(window, "privacy-pane-tp-ui-updated");
 | |
|     }
 | |
| 
 | |
|     let policy = Services.policies.getActivePolicies();
 | |
|     if (
 | |
|       policy &&
 | |
|       ((policy.EnableTrackingProtection &&
 | |
|         policy.EnableTrackingProtection.Locked) ||
 | |
|         (policy.Cookies && policy.Cookies.Locked))
 | |
|     ) {
 | |
|       setInputsDisabledState(true);
 | |
|     }
 | |
|     if (tPPrefisLocked) {
 | |
|       // An extension can't control this setting if either pref is locked.
 | |
|       hideControllingExtension(TRACKING_PROTECTION_KEY);
 | |
|       setInputsDisabledState(false);
 | |
|     } else {
 | |
|       handleControllingExtension(
 | |
|         PREF_SETTING_TYPE,
 | |
|         TRACKING_PROTECTION_KEY
 | |
|       ).then(setInputsDisabledState);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Hide the "Change Block List" link for trackers/tracking content in the
 | |
|    * custom Content Blocking/ETP panel. By default, it will not be visible.
 | |
|    */
 | |
|   _showCustomBlockList() {
 | |
|     let prefValue = Services.prefs.getBoolPref(
 | |
|       "browser.contentblocking.customBlockList.preferences.ui.enabled"
 | |
|     );
 | |
|     if (!prefValue) {
 | |
|       document.getElementById("changeBlockListLink").style.display = "none";
 | |
|     } else {
 | |
|       setEventListener("changeBlockListLink", "click", this.showBlockLists);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Set up handlers for showing and hiding controlling extension info
 | |
|    * for tracking protection.
 | |
|    */
 | |
|   _initTrackingProtectionExtensionControl() {
 | |
|     setEventListener(
 | |
|       "contentBlockingDisableTrackingProtectionExtension",
 | |
|       "command",
 | |
|       makeDisableControllingExtension(
 | |
|         PREF_SETTING_TYPE,
 | |
|         TRACKING_PROTECTION_KEY
 | |
|       )
 | |
|     );
 | |
| 
 | |
|     let trackingProtectionObserver = {
 | |
|       observe(subject, topic, data) {
 | |
|         gPrivacyPane._updateTrackingProtectionUI();
 | |
|       },
 | |
|     };
 | |
| 
 | |
|     for (let pref of TRACKING_PROTECTION_PREFS) {
 | |
|       Services.prefs.addObserver(pref, trackingProtectionObserver);
 | |
|     }
 | |
|     window.addEventListener("unload", () => {
 | |
|       for (let pref of TRACKING_PROTECTION_PREFS) {
 | |
|         Services.prefs.removeObserver(pref, trackingProtectionObserver);
 | |
|       }
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   _initThirdPartyCertsToggle() {
 | |
|     // Third-party certificate import is only implemented for Windows and Mac,
 | |
|     // and we should not expose this as a user-configurable setting if there's
 | |
|     // an enterprise policy controlling it (either to enable _or_ disable it).
 | |
|     let canConfigureThirdPartyCerts =
 | |
|       (AppConstants.platform == "win" || AppConstants.platform == "macosx") &&
 | |
|       typeof Services.policies.getActivePolicies()?.Certificates
 | |
|         ?.ImportEnterpriseRoots == "undefined";
 | |
| 
 | |
|     document.getElementById("certEnableThirdPartyToggleBox").hidden =
 | |
|       !canConfigureThirdPartyCerts;
 | |
|   },
 | |
| 
 | |
|   syncFromHttpsOnlyPref() {
 | |
|     let httpsOnlyOnPref = Services.prefs.getBoolPref(
 | |
|       "dom.security.https_only_mode"
 | |
|     );
 | |
|     let httpsOnlyOnPBMPref = Services.prefs.getBoolPref(
 | |
|       "dom.security.https_only_mode_pbm"
 | |
|     );
 | |
|     let httpsFirstOnPref = Services.prefs.getBoolPref(
 | |
|       "dom.security.https_first"
 | |
|     );
 | |
|     let httpsFirstOnPBMPref = Services.prefs.getBoolPref(
 | |
|       "dom.security.https_first_pbm"
 | |
|     );
 | |
|     let httpsOnlyRadioGroup = document.getElementById("httpsOnlyRadioGroup");
 | |
|     let httpsOnlyExceptionButton = document.getElementById(
 | |
|       "httpsOnlyExceptionButton"
 | |
|     );
 | |
| 
 | |
|     if (httpsOnlyOnPref) {
 | |
|       httpsOnlyRadioGroup.value = "enabled";
 | |
|     } else if (httpsOnlyOnPBMPref) {
 | |
|       httpsOnlyRadioGroup.value = "privateOnly";
 | |
|     } else {
 | |
|       httpsOnlyRadioGroup.value = "disabled";
 | |
|     }
 | |
| 
 | |
|     httpsOnlyExceptionButton.disabled =
 | |
|       !httpsOnlyOnPref &&
 | |
|       !httpsFirstOnPref &&
 | |
|       !httpsOnlyOnPBMPref &&
 | |
|       !httpsFirstOnPBMPref;
 | |
| 
 | |
|     if (
 | |
|       Services.prefs.prefIsLocked("dom.security.https_only_mode") ||
 | |
|       Services.prefs.prefIsLocked("dom.security.https_only_mode_pbm")
 | |
|     ) {
 | |
|       httpsOnlyRadioGroup.disabled = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   syncToHttpsOnlyPref() {
 | |
|     let value = document.getElementById("httpsOnlyRadioGroup").value;
 | |
|     Services.prefs.setBoolPref(
 | |
|       "dom.security.https_only_mode_pbm",
 | |
|       value == "privateOnly"
 | |
|     );
 | |
|     Services.prefs.setBoolPref(
 | |
|       "dom.security.https_only_mode",
 | |
|       value == "enabled"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Init HTTPS-Only mode and corresponding prefs
 | |
|    */
 | |
|   initHttpsOnly() {
 | |
|     // Set radio-value based on the pref value
 | |
|     this.syncFromHttpsOnlyPref();
 | |
| 
 | |
|     // Create event listener for when the user clicks
 | |
|     // on one of the radio buttons
 | |
|     setEventListener(
 | |
|       "httpsOnlyRadioGroup",
 | |
|       "command",
 | |
|       this.syncToHttpsOnlyPref
 | |
|     );
 | |
|     // Update radio-value when the pref changes
 | |
|     Preferences.get("dom.security.https_only_mode").on("change", () =>
 | |
|       this.syncFromHttpsOnlyPref()
 | |
|     );
 | |
|     Preferences.get("dom.security.https_only_mode_pbm").on("change", () =>
 | |
|       this.syncFromHttpsOnlyPref()
 | |
|     );
 | |
|     Preferences.get("dom.security.https_first").on("change", () =>
 | |
|       this.syncFromHttpsOnlyPref()
 | |
|     );
 | |
|     Preferences.get("dom.security.https_first_pbm").on("change", () =>
 | |
|       this.syncFromHttpsOnlyPref()
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   get dnsOverHttpsResolvers() {
 | |
|     let providers = DoHConfigController.currentConfig.providerList;
 | |
|     // if there's no default, we'll hold its position with an empty string
 | |
|     let defaultURI = DoHConfigController.currentConfig.fallbackProviderURI;
 | |
|     let defaultIndex = providers.findIndex(p => p.uri == defaultURI);
 | |
|     if (defaultIndex == -1 && defaultURI) {
 | |
|       // the default value for the pref isn't included in the resolvers list
 | |
|       // so we'll make a stub for it. Without an id, we'll have to use the url as the label
 | |
|       providers.unshift({ uri: defaultURI });
 | |
|     }
 | |
|     return providers;
 | |
|   },
 | |
| 
 | |
|   updateDoHResolverList(mode) {
 | |
|     let resolvers = this.dnsOverHttpsResolvers;
 | |
|     let currentURI = Preferences.get("network.trr.uri").value;
 | |
|     if (!currentURI) {
 | |
|       currentURI = Preferences.get("network.trr.default_provider_uri").value;
 | |
|     }
 | |
|     let menu = document.getElementById(`${mode}ResolverChoices`);
 | |
| 
 | |
|     let selectedIndex = currentURI
 | |
|       ? resolvers.findIndex(r => r.uri == currentURI)
 | |
|       : 0;
 | |
|     if (selectedIndex == -1) {
 | |
|       // select the last "Custom" item
 | |
|       selectedIndex = menu.itemCount - 1;
 | |
|     }
 | |
|     menu.selectedIndex = selectedIndex;
 | |
| 
 | |
|     let customInput = document.getElementById(`${mode}InputField`);
 | |
|     customInput.hidden = menu.value != "custom";
 | |
|   },
 | |
| 
 | |
|   populateDoHResolverList(mode) {
 | |
|     let resolvers = this.dnsOverHttpsResolvers;
 | |
|     let defaultURI = DoHConfigController.currentConfig.fallbackProviderURI;
 | |
|     let menu = document.getElementById(`${mode}ResolverChoices`);
 | |
| 
 | |
|     // populate the DNS-Over-HTTPS resolver list
 | |
|     menu.removeAllItems();
 | |
|     for (let resolver of resolvers) {
 | |
|       let item = menu.appendItem(undefined, resolver.uri);
 | |
|       if (resolver.uri == defaultURI) {
 | |
|         document.l10n.setAttributes(
 | |
|           item,
 | |
|           "connection-dns-over-https-url-item-default",
 | |
|           {
 | |
|             name: resolver.UIName || resolver.uri,
 | |
|           }
 | |
|         );
 | |
|       } else {
 | |
|         item.label = resolver.UIName || resolver.uri;
 | |
|       }
 | |
|     }
 | |
|     let lastItem = menu.appendItem(undefined, "custom");
 | |
|     document.l10n.setAttributes(
 | |
|       lastItem,
 | |
|       "connection-dns-over-https-url-custom"
 | |
|     );
 | |
| 
 | |
|     // set initial selection in the resolver provider picker
 | |
|     this.updateDoHResolverList(mode);
 | |
| 
 | |
|     let customInput = document.getElementById(`${mode}InputField`);
 | |
| 
 | |
|     function updateURIPref() {
 | |
|       if (customInput.value == "") {
 | |
|         // Setting the pref to empty string will make it have the default
 | |
|         // pref value which makes us fallback to using the default TRR
 | |
|         // resolver in network.trr.default_provider_uri.
 | |
|         // If the input is empty we set it to "(space)" which is essentially
 | |
|         // the same.
 | |
|         Services.prefs.setStringPref("network.trr.uri", " ");
 | |
|       } else {
 | |
|         Services.prefs.setStringPref("network.trr.uri", customInput.value);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     menu.addEventListener("command", () => {
 | |
|       if (menu.value == "custom") {
 | |
|         customInput.hidden = false;
 | |
|         updateURIPref();
 | |
|       } else {
 | |
|         customInput.hidden = true;
 | |
|         Services.prefs.setStringPref("network.trr.uri", menu.value);
 | |
|       }
 | |
|       Services.telemetry.recordEvent(
 | |
|         "security.doh.settings",
 | |
|         "provider_choice",
 | |
|         "value",
 | |
|         menu.value
 | |
|       );
 | |
| 
 | |
|       // Update other menu too.
 | |
|       let otherMode = mode == "dohEnabled" ? "dohStrict" : "dohEnabled";
 | |
|       let otherMenu = document.getElementById(`${otherMode}ResolverChoices`);
 | |
|       let otherInput = document.getElementById(`${otherMode}InputField`);
 | |
|       otherMenu.value = menu.value;
 | |
|       otherInput.hidden = otherMenu.value != "custom";
 | |
|     });
 | |
| 
 | |
|     // Change the URL when you press ENTER in the input field it or loses focus
 | |
|     customInput.addEventListener("change", () => {
 | |
|       updateURIPref();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   async updateDoHStatus() {
 | |
|     let trrURI = Services.dns.currentTrrURI;
 | |
|     let hostname = "";
 | |
|     try {
 | |
|       hostname = new URL(trrURI).hostname;
 | |
|     } catch (e) {
 | |
|       hostname = await document.l10n.formatValue("preferences-doh-bad-url");
 | |
|     }
 | |
| 
 | |
|     let steering = document.getElementById("dohSteeringStatus");
 | |
|     steering.hidden = true;
 | |
| 
 | |
|     let dohResolver = document.getElementById("dohResolver");
 | |
|     dohResolver.hidden = true;
 | |
| 
 | |
|     let status = document.getElementById("dohStatus");
 | |
| 
 | |
|     async function setStatus(localizedStringName, options) {
 | |
|       let opts = options || {};
 | |
|       let statusString = await document.l10n.formatValue(
 | |
|         localizedStringName,
 | |
|         opts
 | |
|       );
 | |
|       document.l10n.setAttributes(status, "preferences-doh-status", {
 | |
|         status: statusString,
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     function computeStatus() {
 | |
|       let mode = Services.dns.currentTrrMode;
 | |
|       let confirmationState = Services.dns.currentTrrConfirmationState;
 | |
|       if (
 | |
|         mode == Ci.nsIDNSService.MODE_TRRFIRST ||
 | |
|         mode == Ci.nsIDNSService.MODE_TRRONLY
 | |
|       ) {
 | |
|         switch (confirmationState) {
 | |
|           case Ci.nsIDNSService.CONFIRM_TRYING_OK:
 | |
|           case Ci.nsIDNSService.CONFIRM_OK:
 | |
|           case Ci.nsIDNSService.CONFIRM_DISABLED:
 | |
|             return "preferences-doh-status-active";
 | |
|           default:
 | |
|             return "preferences-doh-status-not-active";
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return "preferences-doh-status-disabled";
 | |
|     }
 | |
| 
 | |
|     let errReason = "";
 | |
|     let confirmationStatus = Services.dns.lastConfirmationStatus;
 | |
|     if (confirmationStatus != Cr.NS_OK) {
 | |
|       errReason = ChromeUtils.getXPCOMErrorName(confirmationStatus);
 | |
|     } else {
 | |
|       errReason = Services.dns.getTRRSkipReasonName(
 | |
|         Services.dns.lastConfirmationSkipReason
 | |
|       );
 | |
|     }
 | |
|     let statusLabel = computeStatus();
 | |
|     // setStatus will format and set the statusLabel asynchronously.
 | |
|     setStatus(statusLabel, { reason: errReason });
 | |
|     dohResolver.hidden = statusLabel == "preferences-doh-status-disabled";
 | |
| 
 | |
|     let statusLearnMore = document.getElementById("dohStatusLearnMore");
 | |
|     statusLearnMore.hidden = statusLabel != "preferences-doh-status-not-active";
 | |
| 
 | |
|     // No need to set the resolver name since we're not going to show it.
 | |
|     if (statusLabel == "preferences-doh-status-disabled") {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     function nameOrDomain() {
 | |
|       for (let resolver of DoHConfigController.currentConfig.providerList) {
 | |
|         if (resolver.uri == trrURI) {
 | |
|           return resolver.UIName || hostname || trrURI;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       // Also check if this is a steering provider.
 | |
|       for (let resolver of DoHConfigController.currentConfig.providerSteering
 | |
|         .providerList) {
 | |
|         if (resolver.uri == trrURI) {
 | |
|           steering.hidden = false;
 | |
|           return resolver.UIName || hostname || trrURI;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return hostname;
 | |
|     }
 | |
| 
 | |
|     let resolverNameOrDomain = nameOrDomain();
 | |
|     document.l10n.setAttributes(dohResolver, "preferences-doh-resolver", {
 | |
|       name: resolverNameOrDomain,
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   highlightDoHCategoryAndUpdateStatus() {
 | |
|     let value = Preferences.get("network.trr.mode").value;
 | |
|     let defaultOption = document.getElementById("dohOptionDefault");
 | |
|     let enabledOption = document.getElementById("dohOptionEnabled");
 | |
|     let strictOption = document.getElementById("dohOptionStrict");
 | |
|     let offOption = document.getElementById("dohOptionOff");
 | |
|     defaultOption.classList.remove("selected");
 | |
|     enabledOption.classList.remove("selected");
 | |
|     strictOption.classList.remove("selected");
 | |
|     offOption.classList.remove("selected");
 | |
| 
 | |
|     switch (value) {
 | |
|       case Ci.nsIDNSService.MODE_NATIVEONLY:
 | |
|         defaultOption.classList.add("selected");
 | |
|         break;
 | |
|       case Ci.nsIDNSService.MODE_TRRFIRST:
 | |
|         enabledOption.classList.add("selected");
 | |
|         break;
 | |
|       case Ci.nsIDNSService.MODE_TRRONLY:
 | |
|         strictOption.classList.add("selected");
 | |
|         break;
 | |
|       case Ci.nsIDNSService.MODE_TRROFF:
 | |
|         offOption.classList.add("selected");
 | |
|         break;
 | |
|       default:
 | |
|         // The pref is set to a random value.
 | |
|         // This shouldn't happen, but let's make sure off is selected.
 | |
|         offOption.classList.add("selected");
 | |
|         document.getElementById("dohCategoryRadioGroup").selectedIndex = 3;
 | |
|         break;
 | |
|     }
 | |
| 
 | |
|     // When the mode is set to 0 we need to clear the URI so
 | |
|     // doh-rollout can kick in.
 | |
|     if (value == Ci.nsIDNSService.MODE_NATIVEONLY) {
 | |
|       Services.prefs.clearUserPref("network.trr.uri");
 | |
|       Services.prefs.clearUserPref("doh-rollout.disable-heuristics");
 | |
|     }
 | |
| 
 | |
|     gPrivacyPane.updateDoHStatus();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Init DoH corresponding prefs
 | |
|    */
 | |
|   initDoH() {
 | |
|     Services.telemetry.setEventRecordingEnabled("security.doh.settings", true);
 | |
| 
 | |
|     setEventListener("dohDefaultArrow", "command", this.toggleExpansion);
 | |
|     setEventListener("dohEnabledArrow", "command", this.toggleExpansion);
 | |
|     setEventListener("dohStrictArrow", "command", this.toggleExpansion);
 | |
| 
 | |
|     function modeButtonPressed(e) {
 | |
|       // Clicking the active mode again should not generate another event
 | |
|       if (
 | |
|         parseInt(e.target.value) == Preferences.get("network.trr.mode").value
 | |
|       ) {
 | |
|         return;
 | |
|       }
 | |
|       Services.telemetry.recordEvent(
 | |
|         "security.doh.settings",
 | |
|         "mode_changed",
 | |
|         "button",
 | |
|         e.target.id
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     setEventListener("dohDefaultRadio", "command", modeButtonPressed);
 | |
|     setEventListener("dohEnabledRadio", "command", modeButtonPressed);
 | |
|     setEventListener("dohStrictRadio", "command", modeButtonPressed);
 | |
|     setEventListener("dohOffRadio", "command", modeButtonPressed);
 | |
| 
 | |
|     function warnCheckboxClicked(e) {
 | |
|       Services.telemetry.recordEvent(
 | |
|         "security.doh.settings",
 | |
|         "warn_checkbox",
 | |
|         "checkbox",
 | |
|         `${e.target.checked}`
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     setEventListener("dohWarnCheckbox1", "command", warnCheckboxClicked);
 | |
|     setEventListener("dohWarnCheckbox2", "command", warnCheckboxClicked);
 | |
| 
 | |
|     this.populateDoHResolverList("dohEnabled");
 | |
|     this.populateDoHResolverList("dohStrict");
 | |
| 
 | |
|     Preferences.get("network.trr.uri").on("change", () => {
 | |
|       gPrivacyPane.updateDoHResolverList("dohEnabled");
 | |
|       gPrivacyPane.updateDoHResolverList("dohStrict");
 | |
|       gPrivacyPane.updateDoHStatus();
 | |
|     });
 | |
| 
 | |
|     // Update status box and hightlightling when the pref changes
 | |
|     Preferences.get("network.trr.mode").on(
 | |
|       "change",
 | |
|       gPrivacyPane.highlightDoHCategoryAndUpdateStatus
 | |
|     );
 | |
|     this.highlightDoHCategoryAndUpdateStatus();
 | |
| 
 | |
|     Services.obs.addObserver(this, "network:trr-uri-changed");
 | |
|     Services.obs.addObserver(this, "network:trr-mode-changed");
 | |
|     Services.obs.addObserver(this, "network:trr-confirmation");
 | |
|     let unload = () => {
 | |
|       Services.obs.removeObserver(this, "network:trr-uri-changed");
 | |
|       Services.obs.removeObserver(this, "network:trr-mode-changed");
 | |
|       Services.obs.removeObserver(this, "network:trr-confirmation");
 | |
|     };
 | |
|     window.addEventListener("unload", unload, { once: true });
 | |
| 
 | |
|     if (Preferences.get("network.trr_ui.show_fallback_warning_option").value) {
 | |
|       document.getElementById("dohWarningBox1").hidden = false;
 | |
|       document.getElementById("dohWarningBox2").hidden = false;
 | |
|     }
 | |
| 
 | |
|     let uriPref = Services.prefs.getStringPref("network.trr.uri");
 | |
|     // If the value isn't one of the providers, we need to update the
 | |
|     // custom_uri pref to make sure the input box contains the correct URL.
 | |
|     if (uriPref && !this.dnsOverHttpsResolvers.some(e => e.uri == uriPref)) {
 | |
|       Services.prefs.setStringPref(
 | |
|         "network.trr.custom_uri",
 | |
|         Services.prefs.getStringPref("network.trr.uri")
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     if (Services.prefs.prefIsLocked("network.trr.mode")) {
 | |
|       document.getElementById("dohCategoryRadioGroup").disabled = true;
 | |
|       Services.prefs.setStringPref("network.trr.custom_uri", uriPref);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   initWebAuthn() {
 | |
|     document.getElementById("openWindowsPasskeySettings").hidden =
 | |
|       !Services.prefs.getBoolPref(
 | |
|         "security.webauthn.show_ms_settings_link",
 | |
|         true
 | |
|       );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Sets up the UI for the number of days of history to keep, and updates the
 | |
|    * label of the "Clear Now..." button.
 | |
|    */
 | |
|   init() {
 | |
|     this._updateSanitizeSettingsButton();
 | |
|     this.initDeleteOnCloseBox();
 | |
|     this.syncSanitizationPrefsWithDeleteOnClose();
 | |
|     this.initializeHistoryMode();
 | |
|     this.updateHistoryModePane();
 | |
|     this.updatePrivacyMicroControls();
 | |
|     this.initAutoStartPrivateBrowsingReverter();
 | |
| 
 | |
|     /* Initialize Content Blocking */
 | |
|     this.initContentBlocking();
 | |
| 
 | |
|     this._showCustomBlockList();
 | |
|     this.trackingProtectionReadPrefs();
 | |
|     this.fingerprintingProtectionReadPrefs();
 | |
|     this.networkCookieBehaviorReadPrefs();
 | |
|     this._initTrackingProtectionExtensionControl();
 | |
|     this._initThirdPartyCertsToggle();
 | |
| 
 | |
|     Services.telemetry.setEventRecordingEnabled("privacy.ui.fpp", true);
 | |
| 
 | |
|     Services.telemetry.setEventRecordingEnabled("pwmgr", true);
 | |
| 
 | |
|     Preferences.get("privacy.trackingprotection.enabled").on(
 | |
|       "change",
 | |
|       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("privacy.trackingprotection.pbmode.enabled").on(
 | |
|       "change",
 | |
|       gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
| 
 | |
|     // Watch all of the prefs that the new Cookies & Site Data UI depends on
 | |
|     Preferences.get("network.cookie.cookieBehavior").on(
 | |
|       "change",
 | |
|       gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("browser.privatebrowsing.autostart").on(
 | |
|       "change",
 | |
|       gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("privacy.firstparty.isolate").on(
 | |
|       "change",
 | |
|       gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
| 
 | |
|     Preferences.get("privacy.fingerprintingProtection").on(
 | |
|       "change",
 | |
|       gPrivacyPane.fingerprintingProtectionReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("privacy.fingerprintingProtection.pbmode").on(
 | |
|       "change",
 | |
|       gPrivacyPane.fingerprintingProtectionReadPrefs.bind(gPrivacyPane)
 | |
|     );
 | |
| 
 | |
|     setEventListener(
 | |
|       "trackingProtectionExceptions",
 | |
|       "command",
 | |
|       gPrivacyPane.showTrackingProtectionExceptions
 | |
|     );
 | |
| 
 | |
|     Preferences.get("privacy.sanitize.sanitizeOnShutdown").on(
 | |
|       "change",
 | |
|       gPrivacyPane._updateSanitizeSettingsButton.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("browser.privatebrowsing.autostart").on(
 | |
|       "change",
 | |
|       gPrivacyPane.updatePrivacyMicroControls.bind(gPrivacyPane)
 | |
|     );
 | |
|     setEventListener("historyMode", "command", function () {
 | |
|       gPrivacyPane.updateHistoryModePane();
 | |
|       gPrivacyPane.updateHistoryModePrefs();
 | |
|       gPrivacyPane.updatePrivacyMicroControls();
 | |
|       gPrivacyPane.updateAutostart();
 | |
|     });
 | |
|     setEventListener("clearHistoryButton", "command", function () {
 | |
|       let historyMode = document.getElementById("historyMode");
 | |
|       // Select "everything" in the clear history dialog if the
 | |
|       // user has set their history mode to never remember history.
 | |
|       gPrivacyPane.clearPrivateDataNow(historyMode.value == "dontremember");
 | |
|     });
 | |
|     setEventListener(
 | |
|       "privateBrowsingAutoStart",
 | |
|       "command",
 | |
|       gPrivacyPane.updateAutostart
 | |
|     );
 | |
|     setEventListener(
 | |
|       "cookieExceptions",
 | |
|       "command",
 | |
|       gPrivacyPane.showCookieExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "httpsOnlyExceptionButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showHttpsOnlyModeExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "dohExceptionsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showDoHExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "clearDataSettings",
 | |
|       "command",
 | |
|       gPrivacyPane.showClearPrivateDataSettings
 | |
|     );
 | |
|     setEventListener(
 | |
|       "passwordExceptions",
 | |
|       "command",
 | |
|       gPrivacyPane.showPasswordExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "useMasterPassword",
 | |
|       "command",
 | |
|       gPrivacyPane.updateMasterPasswordButton
 | |
|     );
 | |
|     setEventListener(
 | |
|       "changeMasterPassword",
 | |
|       "command",
 | |
|       gPrivacyPane.changeMasterPassword
 | |
|     );
 | |
|     setEventListener("showPasswords", "command", gPrivacyPane.showPasswords);
 | |
|     setEventListener(
 | |
|       "addonExceptions",
 | |
|       "command",
 | |
|       gPrivacyPane.showAddonExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "viewCertificatesButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showCertificates
 | |
|     );
 | |
|     setEventListener(
 | |
|       "viewSecurityDevicesButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showSecurityDevices
 | |
|     );
 | |
| 
 | |
|     this._pane = document.getElementById("panePrivacy");
 | |
| 
 | |
|     this._initGlobalPrivacyControlUI();
 | |
|     this._initPasswordGenerationUI();
 | |
|     this._initRelayIntegrationUI();
 | |
|     this._initMasterPasswordUI();
 | |
| 
 | |
|     this.initListenersForExtensionControllingPasswordManager();
 | |
| 
 | |
|     this._initSafeBrowsing();
 | |
| 
 | |
|     setEventListener(
 | |
|       "autoplaySettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showAutoplayMediaExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "notificationSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showNotificationExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "locationSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showLocationExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "xrSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showXRExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "cameraSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showCameraExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "microphoneSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showMicrophoneExceptions
 | |
|     );
 | |
|     document.getElementById("speakerSettingsRow").hidden =
 | |
|       !Services.prefs.getBoolPref("media.setsinkid.enabled", false);
 | |
|     setEventListener(
 | |
|       "speakerSettingsButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showSpeakerExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "popupPolicyButton",
 | |
|       "command",
 | |
|       gPrivacyPane.showPopupExceptions
 | |
|     );
 | |
|     setEventListener(
 | |
|       "notificationsDoNotDisturb",
 | |
|       "command",
 | |
|       gPrivacyPane.toggleDoNotDisturbNotifications
 | |
|     );
 | |
| 
 | |
|     setSyncFromPrefListener("contentBlockingBlockCookiesCheckbox", () =>
 | |
|       this.readBlockCookies()
 | |
|     );
 | |
|     setSyncToPrefListener("contentBlockingBlockCookiesCheckbox", () =>
 | |
|       this.writeBlockCookies()
 | |
|     );
 | |
|     setSyncFromPrefListener("blockCookiesMenu", () =>
 | |
|       this.readBlockCookiesFrom()
 | |
|     );
 | |
|     setSyncToPrefListener("blockCookiesMenu", () =>
 | |
|       this.writeBlockCookiesFrom()
 | |
|     );
 | |
| 
 | |
|     setSyncFromPrefListener("savePasswords", () => this.readSavePasswords());
 | |
| 
 | |
|     let microControlHandler = el =>
 | |
|       this.ensurePrivacyMicroControlUncheckedWhenDisabled(el);
 | |
|     setSyncFromPrefListener("rememberHistory", microControlHandler);
 | |
|     setSyncFromPrefListener("rememberForms", microControlHandler);
 | |
|     setSyncFromPrefListener("alwaysClear", microControlHandler);
 | |
| 
 | |
|     setSyncFromPrefListener("popupPolicy", () =>
 | |
|       this.updateButtons("popupPolicyButton", "dom.disable_open_during_load")
 | |
|     );
 | |
|     setSyncFromPrefListener("warnAddonInstall", () =>
 | |
|       this.readWarnAddonInstall()
 | |
|     );
 | |
|     setSyncFromPrefListener("enableOCSP", () => this.readEnableOCSP());
 | |
|     setSyncToPrefListener("enableOCSP", () => this.writeEnableOCSP());
 | |
| 
 | |
|     if (AlertsServiceDND) {
 | |
|       let notificationsDoNotDisturbBox = document.getElementById(
 | |
|         "notificationsDoNotDisturbBox"
 | |
|       );
 | |
|       notificationsDoNotDisturbBox.removeAttribute("hidden");
 | |
|       let checkbox = document.getElementById("notificationsDoNotDisturb");
 | |
|       document.l10n.setAttributes(checkbox, "permissions-notification-pause");
 | |
|       if (AlertsServiceDND.manualDoNotDisturb) {
 | |
|         let notificationsDoNotDisturb = document.getElementById(
 | |
|           "notificationsDoNotDisturb"
 | |
|         );
 | |
|         notificationsDoNotDisturb.setAttribute("checked", true);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     let onNimbus = () => this._updateFirefoxSuggestToggle();
 | |
|     NimbusFeatures.urlbar.onUpdate(onNimbus);
 | |
|     this._updateFirefoxSuggestToggle(true);
 | |
|     window.addEventListener("unload", () => {
 | |
|       NimbusFeatures.urlbar.offUpdate(onNimbus);
 | |
|     });
 | |
| 
 | |
|     this.initSiteDataControls();
 | |
|     setEventListener(
 | |
|       "clearSiteDataButton",
 | |
|       "command",
 | |
|       gPrivacyPane.clearSiteData
 | |
|     );
 | |
|     setEventListener(
 | |
|       "siteDataSettings",
 | |
|       "command",
 | |
|       gPrivacyPane.showSiteDataSettings
 | |
|     );
 | |
| 
 | |
|     this.initCookieBannerHandling();
 | |
| 
 | |
|     this.initDataCollection();
 | |
| 
 | |
|     if (AppConstants.MOZ_DATA_REPORTING) {
 | |
|       if (AppConstants.MOZ_CRASHREPORTER) {
 | |
|         this.initSubmitCrashes();
 | |
|       }
 | |
|       this.initSubmitHealthReport();
 | |
|       setEventListener(
 | |
|         "submitHealthReportBox",
 | |
|         "command",
 | |
|         gPrivacyPane.updateSubmitHealthReport
 | |
|       );
 | |
|       setEventListener(
 | |
|         "telemetryDataDeletionLearnMore",
 | |
|         "click",
 | |
|         gPrivacyPane.showDataDeletion
 | |
|       );
 | |
|       if (AppConstants.MOZ_NORMANDY) {
 | |
|         this.initOptOutStudyCheckbox();
 | |
|       }
 | |
|       this.initAddonRecommendationsCheckbox();
 | |
|     }
 | |
| 
 | |
|     let signonBundle = document.getElementById("signonBundle");
 | |
|     let pkiBundle = document.getElementById("pkiBundle");
 | |
|     appendSearchKeywords("showPasswords", [
 | |
|       signonBundle.getString("loginsDescriptionAll2"),
 | |
|     ]);
 | |
|     appendSearchKeywords("viewSecurityDevicesButton", [
 | |
|       pkiBundle.getString("enable_fips"),
 | |
|     ]);
 | |
| 
 | |
|     if (!PrivateBrowsingUtils.enabled) {
 | |
|       document.getElementById("privateBrowsingAutoStart").hidden = true;
 | |
|       document.querySelector("menuitem[value='dontremember']").hidden = true;
 | |
|     }
 | |
| 
 | |
|     /* init HTTPS-Only mode */
 | |
|     this.initHttpsOnly();
 | |
| 
 | |
|     this.initDoH();
 | |
| 
 | |
|     this.initWebAuthn();
 | |
| 
 | |
|     // Notify observers that the UI is now ready
 | |
|     Services.obs.notifyObservers(window, "privacy-pane-loaded");
 | |
|   },
 | |
| 
 | |
|   initSiteDataControls() {
 | |
|     Services.obs.addObserver(this, "sitedatamanager:sites-updated");
 | |
|     Services.obs.addObserver(this, "sitedatamanager:updating-sites");
 | |
|     let unload = () => {
 | |
|       window.removeEventListener("unload", unload);
 | |
|       Services.obs.removeObserver(this, "sitedatamanager:sites-updated");
 | |
|       Services.obs.removeObserver(this, "sitedatamanager:updating-sites");
 | |
|     };
 | |
|     window.addEventListener("unload", unload);
 | |
|     SiteDataManager.updateSites();
 | |
|   },
 | |
| 
 | |
|   // CONTENT BLOCKING
 | |
| 
 | |
|   /**
 | |
|    * Initializes the content blocking section.
 | |
|    */
 | |
|   initContentBlocking() {
 | |
|     setEventListener(
 | |
|       "contentBlockingTrackingProtectionCheckbox",
 | |
|       "command",
 | |
|       this.trackingProtectionWritePrefs
 | |
|     );
 | |
|     setEventListener(
 | |
|       "contentBlockingTrackingProtectionCheckbox",
 | |
|       "command",
 | |
|       this._updateTrackingProtectionUI
 | |
|     );
 | |
|     setEventListener(
 | |
|       "contentBlockingCryptominersCheckbox",
 | |
|       "command",
 | |
|       this.updateCryptominingLists
 | |
|     );
 | |
|     setEventListener(
 | |
|       "contentBlockingFingerprintersCheckbox",
 | |
|       "command",
 | |
|       this.updateFingerprintingLists
 | |
|     );
 | |
|     setEventListener(
 | |
|       "trackingProtectionMenu",
 | |
|       "command",
 | |
|       this.trackingProtectionWritePrefs
 | |
|     );
 | |
|     setEventListener(
 | |
|       "contentBlockingFingerprintingProtectionCheckbox",
 | |
|       "command",
 | |
|       e => {
 | |
|         const extra = { checked: e.target.checked };
 | |
|         Glean.privacyUiFppClick.checkbox.record(extra);
 | |
|         this.fingerprintingProtectionWritePrefs();
 | |
|       }
 | |
|     );
 | |
|     setEventListener("fingerprintingProtectionMenu", "command", e => {
 | |
|       const extra = { value: e.target.value };
 | |
|       Glean.privacyUiFppClick.menu.record(extra);
 | |
|       this.fingerprintingProtectionWritePrefs();
 | |
|     });
 | |
|     setEventListener("standardArrow", "command", this.toggleExpansion);
 | |
|     setEventListener("strictArrow", "command", this.toggleExpansion);
 | |
|     setEventListener("customArrow", "command", this.toggleExpansion);
 | |
| 
 | |
|     Preferences.get("network.cookie.cookieBehavior").on(
 | |
|       "change",
 | |
|       gPrivacyPane.readBlockCookies.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("browser.contentblocking.category").on(
 | |
|       "change",
 | |
|       gPrivacyPane.highlightCBCategory
 | |
|     );
 | |
| 
 | |
|     // If any relevant content blocking pref changes, show a warning that the changes will
 | |
|     // not be implemented until they refresh their tabs.
 | |
|     for (let pref of CONTENT_BLOCKING_PREFS) {
 | |
|       Preferences.get(pref).on("change", gPrivacyPane.maybeNotifyUserToReload);
 | |
|       // If the value changes, run populateCategoryContents, since that change might have been
 | |
|       // triggered by a default value changing in the standard category.
 | |
|       Preferences.get(pref).on("change", gPrivacyPane.populateCategoryContents);
 | |
|     }
 | |
|     Preferences.get("urlclassifier.trackingTable").on(
 | |
|       "change",
 | |
|       gPrivacyPane.maybeNotifyUserToReload
 | |
|     );
 | |
|     for (let button of document.querySelectorAll(".reload-tabs-button")) {
 | |
|       button.addEventListener("command", gPrivacyPane.reloadAllOtherTabs);
 | |
|     }
 | |
| 
 | |
|     let cryptoMinersOption = document.getElementById(
 | |
|       "contentBlockingCryptominersOption"
 | |
|     );
 | |
|     let fingerprintersOption = document.getElementById(
 | |
|       "contentBlockingFingerprintersOption"
 | |
|     );
 | |
|     let trackingAndIsolateOption = document.querySelector(
 | |
|       "#blockCookiesMenu menuitem[value='trackers-plus-isolate']"
 | |
|     );
 | |
|     cryptoMinersOption.hidden = !Services.prefs.getBoolPref(
 | |
|       "browser.contentblocking.cryptomining.preferences.ui.enabled"
 | |
|     );
 | |
|     fingerprintersOption.hidden = !Services.prefs.getBoolPref(
 | |
|       "browser.contentblocking.fingerprinting.preferences.ui.enabled"
 | |
|     );
 | |
|     let updateTrackingAndIsolateOption = () => {
 | |
|       trackingAndIsolateOption.hidden =
 | |
|         !Services.prefs.getBoolPref(
 | |
|           "browser.contentblocking.reject-and-isolate-cookies.preferences.ui.enabled",
 | |
|           false
 | |
|         ) || gIsFirstPartyIsolated;
 | |
|     };
 | |
|     Preferences.get("privacy.firstparty.isolate").on(
 | |
|       "change",
 | |
|       updateTrackingAndIsolateOption
 | |
|     );
 | |
|     updateTrackingAndIsolateOption();
 | |
| 
 | |
|     Preferences.get("browser.contentblocking.features.strict").on(
 | |
|       "change",
 | |
|       this.populateCategoryContents
 | |
|     );
 | |
|     this.populateCategoryContents();
 | |
|     this.highlightCBCategory();
 | |
|     this.readBlockCookies();
 | |
| 
 | |
|     // Toggles the text "Cross-site and social media trackers" based on the
 | |
|     // social tracking pref. If the pref is false, the text reads
 | |
|     // "Cross-site trackers".
 | |
|     const STP_COOKIES_PREF = "privacy.socialtracking.block_cookies.enabled";
 | |
|     if (Services.prefs.getBoolPref(STP_COOKIES_PREF)) {
 | |
|       let contentBlockOptionSocialMedia = document.getElementById(
 | |
|         "blockCookiesSocialMedia"
 | |
|       );
 | |
| 
 | |
|       document.l10n.setAttributes(
 | |
|         contentBlockOptionSocialMedia,
 | |
|         "sitedata-option-block-cross-site-tracking-cookies"
 | |
|       );
 | |
|     }
 | |
| 
 | |
|     Preferences.get("privacy.resistFingerprinting").on(
 | |
|       "change",
 | |
|       setUpContentBlockingWarnings
 | |
|     );
 | |
|     Preferences.get("privacy.resistFingerprinting.pbmode").on(
 | |
|       "change",
 | |
|       setUpContentBlockingWarnings
 | |
|     );
 | |
| 
 | |
|     setUpContentBlockingWarnings();
 | |
| 
 | |
|     initTCPStandardSection();
 | |
|   },
 | |
| 
 | |
|   populateCategoryContents() {
 | |
|     for (let type of ["strict", "standard"]) {
 | |
|       let rulesArray = [];
 | |
|       let selector;
 | |
|       if (type == "strict") {
 | |
|         selector = "#contentBlockingOptionStrict";
 | |
|         rulesArray = Services.prefs
 | |
|           .getStringPref("browser.contentblocking.features.strict")
 | |
|           .split(",");
 | |
|         if (gIsFirstPartyIsolated) {
 | |
|           let idx = rulesArray.indexOf("cookieBehavior5");
 | |
|           if (idx != -1) {
 | |
|             rulesArray[idx] = "cookieBehavior4";
 | |
|           }
 | |
|         }
 | |
|       } else {
 | |
|         selector = "#contentBlockingOptionStandard";
 | |
|         // In standard show/hide UI items based on the default values of the relevant prefs.
 | |
|         let defaults = Services.prefs.getDefaultBranch("");
 | |
| 
 | |
|         let cookieBehavior = defaults.getIntPref(
 | |
|           "network.cookie.cookieBehavior"
 | |
|         );
 | |
|         switch (cookieBehavior) {
 | |
|           case Ci.nsICookieService.BEHAVIOR_ACCEPT:
 | |
|             rulesArray.push("cookieBehavior0");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|             rulesArray.push("cookieBehavior1");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|             rulesArray.push("cookieBehavior2");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|             rulesArray.push("cookieBehavior3");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|             rulesArray.push("cookieBehavior4");
 | |
|             break;
 | |
|           case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
 | |
|             rulesArray.push(
 | |
|               gIsFirstPartyIsolated ? "cookieBehavior4" : "cookieBehavior5"
 | |
|             );
 | |
|             break;
 | |
|         }
 | |
|         let cookieBehaviorPBM = defaults.getIntPref(
 | |
|           "network.cookie.cookieBehavior.pbmode"
 | |
|         );
 | |
|         switch (cookieBehaviorPBM) {
 | |
|           case Ci.nsICookieService.BEHAVIOR_ACCEPT:
 | |
|             rulesArray.push("cookieBehaviorPBM0");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|             rulesArray.push("cookieBehaviorPBM1");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|             rulesArray.push("cookieBehaviorPBM2");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|             rulesArray.push("cookieBehaviorPBM3");
 | |
|             break;
 | |
|           case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|             rulesArray.push("cookieBehaviorPBM4");
 | |
|             break;
 | |
|           case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
 | |
|             rulesArray.push(
 | |
|               gIsFirstPartyIsolated
 | |
|                 ? "cookieBehaviorPBM4"
 | |
|                 : "cookieBehaviorPBM5"
 | |
|             );
 | |
|             break;
 | |
|         }
 | |
|         rulesArray.push(
 | |
|           defaults.getBoolPref(
 | |
|             "privacy.trackingprotection.cryptomining.enabled"
 | |
|           )
 | |
|             ? "cm"
 | |
|             : "-cm"
 | |
|         );
 | |
|         rulesArray.push(
 | |
|           defaults.getBoolPref(
 | |
|             "privacy.trackingprotection.fingerprinting.enabled"
 | |
|           )
 | |
|             ? "fp"
 | |
|             : "-fp"
 | |
|         );
 | |
|         rulesArray.push(
 | |
|           Services.prefs.getBoolPref(
 | |
|             "privacy.socialtracking.block_cookies.enabled"
 | |
|           )
 | |
|             ? "stp"
 | |
|             : "-stp"
 | |
|         );
 | |
|         rulesArray.push(
 | |
|           defaults.getBoolPref("privacy.trackingprotection.enabled")
 | |
|             ? "tp"
 | |
|             : "-tp"
 | |
|         );
 | |
|         rulesArray.push(
 | |
|           defaults.getBoolPref("privacy.trackingprotection.pbmode.enabled")
 | |
|             ? "tpPrivate"
 | |
|             : "-tpPrivate"
 | |
|         );
 | |
|       }
 | |
| 
 | |
|       // Hide all cookie options first, until we learn which one should be showing.
 | |
|       document.querySelector(selector + " .all-cookies-option").hidden = true;
 | |
|       document.querySelector(
 | |
|         selector + " .unvisited-cookies-option"
 | |
|       ).hidden = true;
 | |
|       document.querySelector(
 | |
|         selector + " .cross-site-cookies-option"
 | |
|       ).hidden = true;
 | |
|       document.querySelector(
 | |
|         selector + " .third-party-tracking-cookies-option"
 | |
|       ).hidden = true;
 | |
|       document.querySelector(
 | |
|         selector + " .all-third-party-cookies-private-windows-option"
 | |
|       ).hidden = true;
 | |
|       document.querySelector(
 | |
|         selector + " .all-third-party-cookies-option"
 | |
|       ).hidden = true;
 | |
|       document.querySelector(selector + " .social-media-option").hidden = true;
 | |
| 
 | |
|       for (let item of rulesArray) {
 | |
|         // Note "cookieBehavior0", will result in no UI changes, so is not listed here.
 | |
|         switch (item) {
 | |
|           case "tp":
 | |
|             document.querySelector(
 | |
|               selector + " .trackers-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "-tp":
 | |
|             document.querySelector(
 | |
|               selector + " .trackers-option"
 | |
|             ).hidden = true;
 | |
|             break;
 | |
|           case "tpPrivate":
 | |
|             document.querySelector(
 | |
|               selector + " .pb-trackers-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "-tpPrivate":
 | |
|             document.querySelector(
 | |
|               selector + " .pb-trackers-option"
 | |
|             ).hidden = true;
 | |
|             break;
 | |
|           case "fp":
 | |
|             document.querySelector(
 | |
|               selector + " .fingerprinters-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "-fp":
 | |
|             document.querySelector(
 | |
|               selector + " .fingerprinters-option"
 | |
|             ).hidden = true;
 | |
|             break;
 | |
|           case "cm":
 | |
|             document.querySelector(
 | |
|               selector + " .cryptominers-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "-cm":
 | |
|             document.querySelector(
 | |
|               selector + " .cryptominers-option"
 | |
|             ).hidden = true;
 | |
|             break;
 | |
|           case "stp":
 | |
|             // Store social tracking cookies pref
 | |
|             const STP_COOKIES_PREF =
 | |
|               "privacy.socialtracking.block_cookies.enabled";
 | |
| 
 | |
|             if (Services.prefs.getBoolPref(STP_COOKIES_PREF)) {
 | |
|               document.querySelector(
 | |
|                 selector + " .social-media-option"
 | |
|               ).hidden = false;
 | |
|             }
 | |
|             break;
 | |
|           case "-stp":
 | |
|             // Store social tracking cookies pref
 | |
|             document.querySelector(
 | |
|               selector + " .social-media-option"
 | |
|             ).hidden = true;
 | |
|             break;
 | |
|           case "cookieBehavior1":
 | |
|             document.querySelector(
 | |
|               selector + " .all-third-party-cookies-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "cookieBehavior2":
 | |
|             document.querySelector(
 | |
|               selector + " .all-cookies-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "cookieBehavior3":
 | |
|             document.querySelector(
 | |
|               selector + " .unvisited-cookies-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "cookieBehavior4":
 | |
|             document.querySelector(
 | |
|               selector + " .third-party-tracking-cookies-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "cookieBehavior5":
 | |
|             document.querySelector(
 | |
|               selector + " .cross-site-cookies-option"
 | |
|             ).hidden = false;
 | |
|             break;
 | |
|           case "cookieBehaviorPBM5":
 | |
|             // We only need to show the cookie option for private windows if the
 | |
|             // cookieBehaviors are different between regular windows and private
 | |
|             // windows.
 | |
|             if (!rulesArray.includes("cookieBehavior5")) {
 | |
|               document.querySelector(
 | |
|                 selector + " .all-third-party-cookies-private-windows-option"
 | |
|               ).hidden = false;
 | |
|             }
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|       // Hide the "tracking protection in private browsing" list item
 | |
|       // if the "tracking protection enabled in all windows" list item is showing.
 | |
|       if (!document.querySelector(selector + " .trackers-option").hidden) {
 | |
|         document.querySelector(selector + " .pb-trackers-option").hidden = true;
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   highlightCBCategory() {
 | |
|     let value = Preferences.get("browser.contentblocking.category").value;
 | |
|     let standardEl = document.getElementById("contentBlockingOptionStandard");
 | |
|     let strictEl = document.getElementById("contentBlockingOptionStrict");
 | |
|     let customEl = document.getElementById("contentBlockingOptionCustom");
 | |
|     standardEl.classList.remove("selected");
 | |
|     strictEl.classList.remove("selected");
 | |
|     customEl.classList.remove("selected");
 | |
| 
 | |
|     switch (value) {
 | |
|       case "strict":
 | |
|         strictEl.classList.add("selected");
 | |
|         break;
 | |
|       case "custom":
 | |
|         customEl.classList.add("selected");
 | |
|         break;
 | |
|       case "standard":
 | |
|       /* fall through */
 | |
|       default:
 | |
|         standardEl.classList.add("selected");
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   updateCryptominingLists() {
 | |
|     let listPrefs = [
 | |
|       "urlclassifier.features.cryptomining.blacklistTables",
 | |
|       "urlclassifier.features.cryptomining.whitelistTables",
 | |
|     ];
 | |
| 
 | |
|     let listValue = listPrefs
 | |
|       .map(l => Services.prefs.getStringPref(l))
 | |
|       .join(",");
 | |
|     listManager.forceUpdates(listValue);
 | |
|   },
 | |
| 
 | |
|   updateFingerprintingLists() {
 | |
|     let listPrefs = [
 | |
|       "urlclassifier.features.fingerprinting.blacklistTables",
 | |
|       "urlclassifier.features.fingerprinting.whitelistTables",
 | |
|     ];
 | |
| 
 | |
|     let listValue = listPrefs
 | |
|       .map(l => Services.prefs.getStringPref(l))
 | |
|       .join(",");
 | |
|     listManager.forceUpdates(listValue);
 | |
|   },
 | |
| 
 | |
|   // TRACKING PROTECTION MODE
 | |
| 
 | |
|   /**
 | |
|    * Selects the right item of the Tracking Protection menulist and checkbox.
 | |
|    */
 | |
|   trackingProtectionReadPrefs() {
 | |
|     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
 | |
|     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
 | |
|     let tpMenu = document.getElementById("trackingProtectionMenu");
 | |
|     let tpCheckbox = document.getElementById(
 | |
|       "contentBlockingTrackingProtectionCheckbox"
 | |
|     );
 | |
| 
 | |
|     this._updateTrackingProtectionUI();
 | |
| 
 | |
|     // Global enable takes precedence over enabled in Private Browsing.
 | |
|     if (enabledPref.value) {
 | |
|       tpMenu.value = "always";
 | |
|       tpCheckbox.checked = true;
 | |
|     } else if (pbmPref.value) {
 | |
|       tpMenu.value = "private";
 | |
|       tpCheckbox.checked = true;
 | |
|     } else {
 | |
|       tpMenu.value = "never";
 | |
|       tpCheckbox.checked = false;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Selects the right item of the Fingerprinting Protection menulist and
 | |
|    * checkbox.
 | |
|    */
 | |
|   fingerprintingProtectionReadPrefs() {
 | |
|     let enabledPref = Preferences.get("privacy.fingerprintingProtection");
 | |
|     let pbmPref = Preferences.get("privacy.fingerprintingProtection.pbmode");
 | |
|     let fppMenu = document.getElementById("fingerprintingProtectionMenu");
 | |
|     let fppCheckbox = document.getElementById(
 | |
|       "contentBlockingFingerprintingProtectionCheckbox"
 | |
|     );
 | |
| 
 | |
|     // Global enable takes precedence over enabled in Private Browsing.
 | |
|     if (enabledPref.value) {
 | |
|       fppMenu.value = "always";
 | |
|       fppCheckbox.checked = true;
 | |
|     } else if (pbmPref.value) {
 | |
|       fppMenu.value = "private";
 | |
|       fppCheckbox.checked = true;
 | |
|     } else {
 | |
|       fppMenu.value = "never";
 | |
|       fppCheckbox.checked = false;
 | |
|     }
 | |
|     fppMenu.disabled = !fppCheckbox.checked;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Selects the right items of the new Cookies & Site Data UI.
 | |
|    */
 | |
|   networkCookieBehaviorReadPrefs() {
 | |
|     let behavior = Services.cookies.getCookieBehavior(false);
 | |
|     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
 | |
|     let deleteOnCloseCheckbox = document.getElementById("deleteOnClose");
 | |
|     let deleteOnCloseNote = document.getElementById("deleteOnCloseNote");
 | |
|     let blockCookies = behavior != Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|     let cookieBehaviorLocked = Services.prefs.prefIsLocked(
 | |
|       "network.cookie.cookieBehavior"
 | |
|     );
 | |
|     let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked;
 | |
|     blockCookiesMenu.disabled = blockCookiesControlsDisabled;
 | |
| 
 | |
|     let completelyBlockCookies =
 | |
|       behavior == Ci.nsICookieService.BEHAVIOR_REJECT;
 | |
|     let privateBrowsing = Preferences.get(
 | |
|       "browser.privatebrowsing.autostart"
 | |
|     ).value;
 | |
|     deleteOnCloseCheckbox.disabled = privateBrowsing || completelyBlockCookies;
 | |
|     deleteOnCloseNote.hidden = !privateBrowsing;
 | |
| 
 | |
|     switch (behavior) {
 | |
|       case Ci.nsICookieService.BEHAVIOR_ACCEPT:
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|         blockCookiesMenu.value = "all-third-parties";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|         blockCookiesMenu.value = "always";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|         blockCookiesMenu.value = "unvisited";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|         blockCookiesMenu.value = "trackers";
 | |
|         break;
 | |
|       case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
 | |
|         blockCookiesMenu.value = "trackers-plus-isolate";
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Sets the pref values based on the selected item of the radiogroup.
 | |
|    */
 | |
|   trackingProtectionWritePrefs() {
 | |
|     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
 | |
|     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
 | |
|     let stpPref = Preferences.get(
 | |
|       "privacy.trackingprotection.socialtracking.enabled"
 | |
|     );
 | |
|     let stpCookiePref = Preferences.get(
 | |
|       "privacy.socialtracking.block_cookies.enabled"
 | |
|     );
 | |
|     // Currently, we don't expose the email tracking protection setting on our
 | |
|     // privacy UI. Instead, we use the existing tracking protection checkbox to
 | |
|     // control the email tracking protection.
 | |
|     let emailTPPref = Preferences.get(
 | |
|       "privacy.trackingprotection.emailtracking.enabled"
 | |
|     );
 | |
|     let emailTPPBMPref = Preferences.get(
 | |
|       "privacy.trackingprotection.emailtracking.pbmode.enabled"
 | |
|     );
 | |
|     let tpMenu = document.getElementById("trackingProtectionMenu");
 | |
|     let tpCheckbox = document.getElementById(
 | |
|       "contentBlockingTrackingProtectionCheckbox"
 | |
|     );
 | |
| 
 | |
|     let value;
 | |
|     if (tpCheckbox.checked) {
 | |
|       if (tpMenu.value == "never") {
 | |
|         tpMenu.value = "private";
 | |
|       }
 | |
|       value = tpMenu.value;
 | |
|     } else {
 | |
|       tpMenu.value = "never";
 | |
|       value = "never";
 | |
|     }
 | |
| 
 | |
|     switch (value) {
 | |
|       case "always":
 | |
|         enabledPref.value = true;
 | |
|         pbmPref.value = true;
 | |
|         emailTPPref.value = true;
 | |
|         emailTPPBMPref.value = true;
 | |
|         if (stpCookiePref.value) {
 | |
|           stpPref.value = true;
 | |
|         }
 | |
|         break;
 | |
|       case "private":
 | |
|         enabledPref.value = false;
 | |
|         pbmPref.value = true;
 | |
|         emailTPPref.value = false;
 | |
|         emailTPPBMPref.value = true;
 | |
|         if (stpCookiePref.value) {
 | |
|           stpPref.value = false;
 | |
|         }
 | |
|         break;
 | |
|       case "never":
 | |
|         enabledPref.value = false;
 | |
|         pbmPref.value = false;
 | |
|         emailTPPref.value = false;
 | |
|         emailTPPBMPref.value = false;
 | |
|         if (stpCookiePref.value) {
 | |
|           stpPref.value = false;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   fingerprintingProtectionWritePrefs() {
 | |
|     let enabledPref = Preferences.get("privacy.fingerprintingProtection");
 | |
|     let pbmPref = Preferences.get("privacy.fingerprintingProtection.pbmode");
 | |
|     let fppMenu = document.getElementById("fingerprintingProtectionMenu");
 | |
|     let fppCheckbox = document.getElementById(
 | |
|       "contentBlockingFingerprintingProtectionCheckbox"
 | |
|     );
 | |
| 
 | |
|     let value;
 | |
|     if (fppCheckbox.checked) {
 | |
|       if (fppMenu.value == "never") {
 | |
|         fppMenu.value = "private";
 | |
|       }
 | |
|       value = fppMenu.value;
 | |
|     } else {
 | |
|       fppMenu.value = "never";
 | |
|       value = "never";
 | |
|     }
 | |
| 
 | |
|     fppMenu.disabled = !fppCheckbox.checked;
 | |
| 
 | |
|     switch (value) {
 | |
|       case "always":
 | |
|         enabledPref.value = true;
 | |
|         pbmPref.value = true;
 | |
|         break;
 | |
|       case "private":
 | |
|         enabledPref.value = false;
 | |
|         pbmPref.value = true;
 | |
|         break;
 | |
|       case "never":
 | |
|         enabledPref.value = false;
 | |
|         pbmPref.value = false;
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   toggleExpansion(e) {
 | |
|     let carat = e.target;
 | |
|     carat.classList.toggle("up");
 | |
|     carat.closest(".privacy-detailedoption").classList.toggle("expanded");
 | |
|     carat.setAttribute(
 | |
|       "aria-expanded",
 | |
|       carat.getAttribute("aria-expanded") === "false"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // HISTORY MODE
 | |
| 
 | |
|   /**
 | |
|    * The list of preferences which affect the initial history mode settings.
 | |
|    * If the auto start private browsing mode pref is active, the initial
 | |
|    * history mode would be set to "Don't remember anything".
 | |
|    * If ALL of these preferences are set to the values that correspond
 | |
|    * to keeping some part of history, and the auto-start
 | |
|    * private browsing mode is not active, the initial history mode would be
 | |
|    * set to "Remember everything".
 | |
|    * Otherwise, the initial history mode would be set to "Custom".
 | |
|    *
 | |
|    * Extensions adding their own preferences can set values here if needed.
 | |
|    */
 | |
|   prefsForKeepingHistory: {
 | |
|     "places.history.enabled": true, // History is enabled
 | |
|     "browser.formfill.enable": true, // Form information is saved
 | |
|     "privacy.sanitize.sanitizeOnShutdown": false, // Private date is NOT cleared on shutdown
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * The list of control IDs which are dependent on the auto-start private
 | |
|    * browsing setting, such that in "Custom" mode they would be disabled if
 | |
|    * the auto-start private browsing checkbox is checked, and enabled otherwise.
 | |
|    *
 | |
|    * Extensions adding their own controls can append their IDs to this array if needed.
 | |
|    */
 | |
|   dependentControls: [
 | |
|     "rememberHistory",
 | |
|     "rememberForms",
 | |
|     "alwaysClear",
 | |
|     "clearDataSettings",
 | |
|   ],
 | |
| 
 | |
|   /**
 | |
|    * Check whether preferences values are set to keep history
 | |
|    *
 | |
|    * @param aPrefs an array of pref names to check for
 | |
|    * @returns boolean true if all of the prefs are set to keep history,
 | |
|    *                  false otherwise
 | |
|    */
 | |
|   _checkHistoryValues(aPrefs) {
 | |
|     for (let pref of Object.keys(aPrefs)) {
 | |
|       if (Preferences.get(pref).value != aPrefs[pref]) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initialize the history mode menulist based on the privacy preferences
 | |
|    */
 | |
|   initializeHistoryMode() {
 | |
|     let mode;
 | |
|     let getVal = aPref => Preferences.get(aPref).value;
 | |
| 
 | |
|     if (getVal("privacy.history.custom")) {
 | |
|       mode = "custom";
 | |
|     } else if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
 | |
|       if (getVal("browser.privatebrowsing.autostart")) {
 | |
|         mode = "dontremember";
 | |
|       } else {
 | |
|         mode = "remember";
 | |
|       }
 | |
|     } else {
 | |
|       mode = "custom";
 | |
|     }
 | |
| 
 | |
|     document.getElementById("historyMode").value = mode;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Update the selected pane based on the history mode menulist
 | |
|    */
 | |
|   updateHistoryModePane() {
 | |
|     let selectedIndex = -1;
 | |
|     switch (document.getElementById("historyMode").value) {
 | |
|       case "remember":
 | |
|         selectedIndex = 0;
 | |
|         break;
 | |
|       case "dontremember":
 | |
|         selectedIndex = 1;
 | |
|         break;
 | |
|       case "custom":
 | |
|         selectedIndex = 2;
 | |
|         break;
 | |
|     }
 | |
|     document.getElementById("historyPane").selectedIndex = selectedIndex;
 | |
|     Preferences.get("privacy.history.custom").value = selectedIndex == 2;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Update the private browsing auto-start pref and the history mode
 | |
|    * micro-management prefs based on the history mode menulist
 | |
|    */
 | |
|   updateHistoryModePrefs() {
 | |
|     let pref = Preferences.get("browser.privatebrowsing.autostart");
 | |
|     switch (document.getElementById("historyMode").value) {
 | |
|       case "remember":
 | |
|         if (pref.value) {
 | |
|           pref.value = false;
 | |
|         }
 | |
| 
 | |
|         // select the remember history option if needed
 | |
|         Preferences.get("places.history.enabled").value = true;
 | |
| 
 | |
|         // select the remember forms history option
 | |
|         Preferences.get("browser.formfill.enable").value = true;
 | |
| 
 | |
|         // select the clear on close option
 | |
|         Preferences.get("privacy.sanitize.sanitizeOnShutdown").value = false;
 | |
|         break;
 | |
|       case "dontremember":
 | |
|         if (!pref.value) {
 | |
|           pref.value = true;
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Update the privacy micro-management controls based on the
 | |
|    * value of the private browsing auto-start preference.
 | |
|    */
 | |
|   updatePrivacyMicroControls() {
 | |
|     let clearDataSettings = document.getElementById("clearDataSettings");
 | |
| 
 | |
|     if (document.getElementById("historyMode").value == "custom") {
 | |
|       let disabled = Preferences.get("browser.privatebrowsing.autostart").value;
 | |
|       this.dependentControls.forEach(aElement => {
 | |
|         let control = document.getElementById(aElement);
 | |
|         let preferenceId = control.getAttribute("preference");
 | |
|         if (!preferenceId) {
 | |
|           let dependentControlId = control.getAttribute("control");
 | |
|           if (dependentControlId) {
 | |
|             let dependentControl = document.getElementById(dependentControlId);
 | |
|             preferenceId = dependentControl.getAttribute("preference");
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         let preference = preferenceId ? Preferences.get(preferenceId) : {};
 | |
|         control.disabled = disabled || preference.locked;
 | |
|         if (control != clearDataSettings) {
 | |
|           this.ensurePrivacyMicroControlUncheckedWhenDisabled(control);
 | |
|         }
 | |
|       });
 | |
| 
 | |
|       clearDataSettings.removeAttribute("hidden");
 | |
| 
 | |
|       if (!disabled) {
 | |
|         // adjust the Settings button for sanitizeOnShutdown
 | |
|         this._updateSanitizeSettingsButton();
 | |
|       }
 | |
|     } else {
 | |
|       clearDataSettings.hidden = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   ensurePrivacyMicroControlUncheckedWhenDisabled(el) {
 | |
|     if (Preferences.get("browser.privatebrowsing.autostart").value) {
 | |
|       // Set checked to false when called from updatePrivacyMicroControls
 | |
|       el.checked = false;
 | |
|       // return false for the onsyncfrompreference case:
 | |
|       return false;
 | |
|     }
 | |
|     return undefined; // tell preferencesBindings to assign the 'right' value.
 | |
|   },
 | |
| 
 | |
|   // CLEAR PRIVATE DATA
 | |
| 
 | |
|   /*
 | |
|    * Preferences:
 | |
|    *
 | |
|    * privacy.sanitize.sanitizeOnShutdown
 | |
|    * - true if the user's private data is cleared on startup according to the
 | |
|    *   Clear Private Data settings, false otherwise
 | |
|    */
 | |
| 
 | |
|   /**
 | |
|    * Displays the Clear Private Data settings dialog.
 | |
|    */
 | |
|   showClearPrivateDataSettings() {
 | |
|     let dialogFile = useOldClearHistoryDialog
 | |
|       ? "chrome://browser/content/preferences/dialogs/sanitize.xhtml"
 | |
|       : "chrome://browser/content/sanitize_v2.xhtml";
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       dialogFile,
 | |
|       {
 | |
|         features: "resizable=no",
 | |
|       },
 | |
|       {
 | |
|         mode: "clearOnShutdown",
 | |
|         updateUsageData: this.shouldUpdateSiteUsageDataForSanitizeDialog,
 | |
|       }
 | |
|     );
 | |
| 
 | |
|     // Since we've opened this once we should
 | |
|     // update sites within the dialog everytime now
 | |
|     this.shouldUpdateSiteUsageDataForSanitizeDialog = true;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog from which individual parts of private data may be
 | |
|    * cleared.
 | |
|    */
 | |
|   clearPrivateDataNow(aClearEverything) {
 | |
|     var ts = Preferences.get("privacy.sanitize.timeSpan");
 | |
|     var timeSpanOrig = ts.value;
 | |
| 
 | |
|     if (aClearEverything) {
 | |
|       ts.value = 0;
 | |
|     }
 | |
| 
 | |
|     // Bug 1856418 We intend to remove the old dialog box
 | |
|     let dialogFile = useOldClearHistoryDialog
 | |
|       ? "chrome://browser/content/sanitize.xhtml"
 | |
|       : "chrome://browser/content/sanitize_v2.xhtml";
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       dialogFile,
 | |
|       {
 | |
|         features: "resizable=no",
 | |
|         closingCallback: () => {
 | |
|           // reset the timeSpan pref
 | |
|           if (aClearEverything) {
 | |
|             ts.value = timeSpanOrig;
 | |
|           }
 | |
| 
 | |
|           Services.obs.notifyObservers(null, "clear-private-data");
 | |
|         },
 | |
|       },
 | |
|       {
 | |
|         updateUsageData: this.shouldUpdateSiteUsageDataForSanitizeDialog,
 | |
|       }
 | |
|     );
 | |
| 
 | |
|     // Since we've opened this once we should  update sites within the
 | |
|     // dialog everytime now to show the most up to date data sizes
 | |
|     this.shouldUpdateSiteUsageDataForSanitizeDialog = true;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * On loading the page, assigns the state to the deleteOnClose checkbox that fits the pref selection
 | |
|    */
 | |
|   initDeleteOnCloseBox() {
 | |
|     let deleteOnCloseBox = document.getElementById("deleteOnClose");
 | |
|     deleteOnCloseBox.checked =
 | |
|       (Preferences.get("privacy.sanitize.sanitizeOnShutdown").value &&
 | |
|         Preferences.get("privacy.clearOnShutdown.cookies").value &&
 | |
|         Preferences.get("privacy.clearOnShutdown.cache").value &&
 | |
|         Preferences.get("privacy.clearOnShutdown.offlineApps").value) ||
 | |
|       Preferences.get("browser.privatebrowsing.autostart").value;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Keeps the state of the deleteOnClose checkbox in sync with the pref selection
 | |
|    */
 | |
|   syncSanitizationPrefsWithDeleteOnClose() {
 | |
|     let deleteOnCloseBox = document.getElementById("deleteOnClose");
 | |
|     let historyMode = Preferences.get("privacy.history.custom");
 | |
|     let sanitizeOnShutdownPref = Preferences.get(
 | |
|       "privacy.sanitize.sanitizeOnShutdown"
 | |
|     );
 | |
|     // ClearOnClose cleaning categories
 | |
|     let cookiePref = Preferences.get("privacy.clearOnShutdown.cookies");
 | |
|     let cachePref = Preferences.get("privacy.clearOnShutdown.cache");
 | |
|     let offlineAppsPref = Preferences.get(
 | |
|       "privacy.clearOnShutdown.offlineApps"
 | |
|     );
 | |
| 
 | |
|     // Sync the cleaning prefs with the deleteOnClose box
 | |
|     deleteOnCloseBox.addEventListener("command", () => {
 | |
|       let { checked } = deleteOnCloseBox;
 | |
|       cookiePref.value = checked;
 | |
|       cachePref.value = checked;
 | |
|       offlineAppsPref.value = checked;
 | |
|       // Forget the current pref selection if sanitizeOnShutdown is disabled,
 | |
|       // to not over clear when it gets enabled by the sync mechanism
 | |
|       if (!sanitizeOnShutdownPref.value) {
 | |
|         this._resetCleaningPrefs();
 | |
|       }
 | |
|       // If no other cleaning category is selected, sanitizeOnShutdown gets synced with deleteOnClose
 | |
|       sanitizeOnShutdownPref.value =
 | |
|         this._isCustomCleaningPrefPresent() || checked;
 | |
| 
 | |
|       // Update the view of the history settings
 | |
|       if (checked && !historyMode.value) {
 | |
|         historyMode.value = "custom";
 | |
|         this.initializeHistoryMode();
 | |
|         this.updateHistoryModePane();
 | |
|         this.updatePrivacyMicroControls();
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     cookiePref.on("change", this._onSanitizePrefChangeSyncClearOnClose);
 | |
|     cachePref.on("change", this._onSanitizePrefChangeSyncClearOnClose);
 | |
|     offlineAppsPref.on("change", this._onSanitizePrefChangeSyncClearOnClose);
 | |
|     sanitizeOnShutdownPref.on(
 | |
|       "change",
 | |
|       this._onSanitizePrefChangeSyncClearOnClose
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Sync the deleteOnClose box to its cleaning prefs
 | |
|    */
 | |
|   _onSanitizePrefChangeSyncClearOnClose() {
 | |
|     let deleteOnCloseBox = document.getElementById("deleteOnClose");
 | |
|     deleteOnCloseBox.checked =
 | |
|       Preferences.get("privacy.clearOnShutdown.cookies").value &&
 | |
|       Preferences.get("privacy.clearOnShutdown.cache").value &&
 | |
|       Preferences.get("privacy.clearOnShutdown.offlineApps").value &&
 | |
|       Preferences.get("privacy.sanitize.sanitizeOnShutdown").value;
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    * Unsets cleaning prefs that do not belong to DeleteOnClose
 | |
|    */
 | |
|   _resetCleaningPrefs() {
 | |
|     SANITIZE_ON_SHUTDOWN_PREFS_ONLY.forEach(
 | |
|       pref => (Preferences.get(pref).value = false)
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /*
 | |
|    Checks if the user set cleaning prefs that do not belong to DeleteOnClose
 | |
|    */
 | |
|   _isCustomCleaningPrefPresent() {
 | |
|     return SANITIZE_ON_SHUTDOWN_PREFS_ONLY.some(
 | |
|       pref => Preferences.get(pref).value
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Enables or disables the "Settings..." button depending
 | |
|    * on the privacy.sanitize.sanitizeOnShutdown preference value
 | |
|    */
 | |
|   _updateSanitizeSettingsButton() {
 | |
|     var settingsButton = document.getElementById("clearDataSettings");
 | |
|     var sanitizeOnShutdownPref = Preferences.get(
 | |
|       "privacy.sanitize.sanitizeOnShutdown"
 | |
|     );
 | |
| 
 | |
|     settingsButton.disabled = !sanitizeOnShutdownPref.value;
 | |
|   },
 | |
| 
 | |
|   toggleDoNotDisturbNotifications(event) {
 | |
|     AlertsServiceDND.manualDoNotDisturb = event.target.checked;
 | |
|   },
 | |
| 
 | |
|   // PRIVATE BROWSING
 | |
| 
 | |
|   /**
 | |
|    * Initialize the starting state for the auto-start private browsing mode pref reverter.
 | |
|    */
 | |
|   initAutoStartPrivateBrowsingReverter() {
 | |
|     // We determine the mode in initializeHistoryMode, which is guaranteed to have been
 | |
|     // called before now, so this is up-to-date.
 | |
|     let mode = document.getElementById("historyMode");
 | |
|     this._lastMode = mode.selectedIndex;
 | |
|     // The value of the autostart pref, on the other hand, is gotten from Preferences,
 | |
|     // which updates the DOM asynchronously, so we can't rely on the DOM. Get it directly
 | |
|     // from the prefs.
 | |
|     this._lastCheckState = Preferences.get(
 | |
|       "browser.privatebrowsing.autostart"
 | |
|     ).value;
 | |
|   },
 | |
| 
 | |
|   _lastMode: null,
 | |
|   _lastCheckState: null,
 | |
|   async updateAutostart() {
 | |
|     let mode = document.getElementById("historyMode");
 | |
|     let autoStart = document.getElementById("privateBrowsingAutoStart");
 | |
|     let pref = Preferences.get("browser.privatebrowsing.autostart");
 | |
|     if (
 | |
|       (mode.value == "custom" && this._lastCheckState == autoStart.checked) ||
 | |
|       (mode.value == "remember" && !this._lastCheckState) ||
 | |
|       (mode.value == "dontremember" && this._lastCheckState)
 | |
|     ) {
 | |
|       // These are all no-op changes, so we don't need to prompt.
 | |
|       this._lastMode = mode.selectedIndex;
 | |
|       this._lastCheckState = autoStart.hasAttribute("checked");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     if (!this._shouldPromptForRestart) {
 | |
|       // We're performing a revert. Just let it happen.
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     let buttonIndex = await confirmRestartPrompt(
 | |
|       autoStart.checked,
 | |
|       1,
 | |
|       true,
 | |
|       false
 | |
|     );
 | |
|     if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
 | |
|       pref.value = autoStart.hasAttribute("checked");
 | |
|       Services.startup.quit(
 | |
|         Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
 | |
|       );
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     this._shouldPromptForRestart = false;
 | |
| 
 | |
|     if (this._lastCheckState) {
 | |
|       autoStart.checked = "checked";
 | |
|     } else {
 | |
|       autoStart.removeAttribute("checked");
 | |
|     }
 | |
|     pref.value = autoStart.hasAttribute("checked");
 | |
|     mode.selectedIndex = this._lastMode;
 | |
|     mode.doCommand();
 | |
| 
 | |
|     this._shouldPromptForRestart = true;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays fine-grained, per-site preferences for tracking protection.
 | |
|    */
 | |
|   showTrackingProtectionExceptions() {
 | |
|     let params = {
 | |
|       permissionType: "trackingprotection",
 | |
|       disableETPVisible: true,
 | |
|       prefilledHost: "",
 | |
|       hideStatusColumn: true,
 | |
|     };
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       undefined,
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the available block lists for tracking protection.
 | |
|    */
 | |
|   showBlockLists() {
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/blocklists.xhtml"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // COOKIES AND SITE DATA
 | |
| 
 | |
|   /*
 | |
|    * Preferences:
 | |
|    *
 | |
|    * network.cookie.cookieBehavior
 | |
|    * - determines how the browser should handle cookies:
 | |
|    *     0   means enable all cookies
 | |
|    *     1   means reject all third party cookies
 | |
|    *     2   means disable all cookies
 | |
|    *     3   means reject third party cookies unless at least one is already set for the eTLD
 | |
|    *     4   means reject all trackers
 | |
|    *     5   means reject all trackers and partition third-party cookies
 | |
|    *         see netwerk/cookie/src/CookieService.cpp for details
 | |
|    */
 | |
| 
 | |
|   /**
 | |
|    * Reads the network.cookie.cookieBehavior preference value and
 | |
|    * enables/disables the "blockCookiesMenu" menulist accordingly.
 | |
|    */
 | |
|   readBlockCookies() {
 | |
|     let bcControl = document.getElementById("blockCookiesMenu");
 | |
|     bcControl.disabled =
 | |
|       Services.cookies.getCookieBehavior(false) ==
 | |
|       Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Updates the "accept third party cookies" menu based on whether the
 | |
|    * "contentBlockingBlockCookiesCheckbox" checkbox is checked.
 | |
|    */
 | |
|   writeBlockCookies() {
 | |
|     let block = document.getElementById("contentBlockingBlockCookiesCheckbox");
 | |
|     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
 | |
| 
 | |
|     if (block.checked) {
 | |
|       // Automatically select 'third-party trackers' as the default.
 | |
|       blockCookiesMenu.selectedIndex = 0;
 | |
|       return this.writeBlockCookiesFrom();
 | |
|     }
 | |
|     return Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|   },
 | |
| 
 | |
|   readBlockCookiesFrom() {
 | |
|     switch (Services.cookies.getCookieBehavior(false)) {
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|         return "all-third-parties";
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|         return "always";
 | |
|       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|         return "unvisited";
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|         return "trackers";
 | |
|       case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN:
 | |
|         return "trackers-plus-isolate";
 | |
|       default:
 | |
|         return undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   writeBlockCookiesFrom() {
 | |
|     let block = document.getElementById("blockCookiesMenu").selectedItem;
 | |
|     switch (block.value) {
 | |
|       case "trackers":
 | |
|         return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
 | |
|       case "unvisited":
 | |
|         return Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN;
 | |
|       case "always":
 | |
|         return Ci.nsICookieService.BEHAVIOR_REJECT;
 | |
|       case "all-third-parties":
 | |
|         return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
 | |
|       case "trackers-plus-isolate":
 | |
|         return Ci.nsICookieService
 | |
|           .BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN;
 | |
|       default:
 | |
|         return undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Discard the browsers of all tabs in all windows. Pinned tabs, as
 | |
|    * well as tabs for which discarding doesn't succeed (e.g. selected
 | |
|    * tabs, tabs with beforeunload listeners), are reloaded.
 | |
|    */
 | |
|   reloadAllOtherTabs() {
 | |
|     let ourTab = BrowserWindowTracker.getTopWindow().gBrowser.selectedTab;
 | |
|     BrowserWindowTracker.orderedWindows.forEach(win => {
 | |
|       let otherGBrowser = win.gBrowser;
 | |
|       for (let tab of otherGBrowser.tabs) {
 | |
|         if (tab == ourTab) {
 | |
|           // Don't reload our preferences tab.
 | |
|           continue;
 | |
|         }
 | |
| 
 | |
|         if (tab.pinned || tab.selected) {
 | |
|           otherGBrowser.reloadTab(tab);
 | |
|         } else {
 | |
|           otherGBrowser.discardBrowser(tab);
 | |
|         }
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     for (let notification of document.querySelectorAll(".reload-tabs")) {
 | |
|       notification.hidden = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * If there are more tabs than just the preferences tab, show a warning to the user that
 | |
|    * they need to reload their tabs to apply the setting.
 | |
|    */
 | |
|   maybeNotifyUserToReload() {
 | |
|     let shouldShow = false;
 | |
|     if (window.BrowserWindowTracker.orderedWindows.length > 1) {
 | |
|       shouldShow = true;
 | |
|     } else {
 | |
|       let tabbrowser = window.BrowserWindowTracker.getTopWindow().gBrowser;
 | |
|       if (tabbrowser.tabs.length > 1) {
 | |
|         shouldShow = true;
 | |
|       }
 | |
|     }
 | |
|     if (shouldShow) {
 | |
|       for (let notification of document.querySelectorAll(".reload-tabs")) {
 | |
|         notification.hidden = false;
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays fine-grained, per-site preferences for cookies.
 | |
|    */
 | |
|   showCookieExceptions() {
 | |
|     var params = {
 | |
|       blockVisible: true,
 | |
|       sessionVisible: true,
 | |
|       allowVisible: true,
 | |
|       prefilledHost: "",
 | |
|       permissionType: "cookie",
 | |
|     };
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       undefined,
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays per-site preferences for HTTPS-Only Mode exceptions.
 | |
|    */
 | |
|   showHttpsOnlyModeExceptions() {
 | |
|     var params = {
 | |
|       blockVisible: false,
 | |
|       sessionVisible: true,
 | |
|       allowVisible: false,
 | |
|       prefilledHost: "",
 | |
|       permissionType: "https-only-load-insecure",
 | |
|       forcedHTTP: true,
 | |
|     };
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       undefined,
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   showDoHExceptions() {
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/dohExceptions.xhtml",
 | |
|       undefined
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   showSiteDataSettings() {
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   toggleSiteData(shouldShow) {
 | |
|     let clearButton = document.getElementById("clearSiteDataButton");
 | |
|     let settingsButton = document.getElementById("siteDataSettings");
 | |
|     clearButton.disabled = !shouldShow;
 | |
|     settingsButton.disabled = !shouldShow;
 | |
|   },
 | |
| 
 | |
|   showSiteDataLoading() {
 | |
|     let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
 | |
|     document.l10n.setAttributes(
 | |
|       totalSiteDataSizeLabel,
 | |
|       "sitedata-total-size-calculating"
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   updateTotalDataSizeLabel(siteDataUsage) {
 | |
|     SiteDataManager.getCacheSize().then(function (cacheUsage) {
 | |
|       let totalSiteDataSizeLabel = document.getElementById("totalSiteDataSize");
 | |
|       let totalUsage = siteDataUsage + cacheUsage;
 | |
|       let [value, unit] = DownloadUtils.convertByteUnits(totalUsage);
 | |
|       document.l10n.setAttributes(
 | |
|         totalSiteDataSizeLabel,
 | |
|         "sitedata-total-size",
 | |
|         {
 | |
|           value,
 | |
|           unit,
 | |
|         }
 | |
|       );
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   clearSiteData() {
 | |
|     // We have to use the full path name to avoid getting errors in
 | |
|     // browser/base/content/test/static/browser_all_files_referenced.js
 | |
|     let dialogFile = useOldClearHistoryDialog
 | |
|       ? "chrome://browser/content/preferences/dialogs/clearSiteData.xhtml"
 | |
|       : "chrome://browser/content/sanitize_v2.xhtml";
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       dialogFile,
 | |
|       {
 | |
|         features: "resizable=no",
 | |
|       },
 | |
|       {
 | |
|         mode: "clearSiteData",
 | |
|         updateUsageData: this.shouldUpdateSiteUsageDataForSanitizeDialog,
 | |
|       }
 | |
|     );
 | |
|     // Since we've opened this once we should
 | |
|     // update sites within the dialog everytime now
 | |
|     this.shouldUpdateSiteUsageDataForSanitizeDialog = true;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initializes the cookie banner handling subgroup on the privacy pane.
 | |
|    *
 | |
|    * This UI is shown if the "cookiebanners.ui.desktop.enabled" pref is true.
 | |
|    *
 | |
|    * The cookie banner handling checkbox reflects the cookie banner feature
 | |
|    * state. It is enabled when the service enabled via the
 | |
|    * cookiebanners.service.mode pref. If detection-only mode is enabled the
 | |
|    * checkbox is unchecked, since in this mode no banners are handled. It is
 | |
|    * only used for detection for banners which means we may prompt the user to
 | |
|    * enable the feature via other UI surfaces such as the onboarding doorhanger.
 | |
|    *
 | |
|    * If the user checks the checkbox, the pref value is set to
 | |
|    * nsICookieBannerService.MODE_REJECT_OR_ACCEPT.
 | |
|    *
 | |
|    * If the user unchecks the checkbox, the mode pref value is set to
 | |
|    * nsICookieBannerService.MODE_DISABLED.
 | |
|    *
 | |
|    * Advanced users can choose other int-valued modes via about:config.
 | |
|    */
 | |
|   initCookieBannerHandling() {
 | |
|     setSyncFromPrefListener("handleCookieBanners", () =>
 | |
|       this.readCookieBannerMode()
 | |
|     );
 | |
|     setSyncToPrefListener("handleCookieBanners", () =>
 | |
|       this.writeCookieBannerMode()
 | |
|     );
 | |
| 
 | |
|     let preference = Preferences.get("cookiebanners.ui.desktop.enabled");
 | |
|     preference.on("change", () => this.updateCookieBannerHandlingVisibility());
 | |
| 
 | |
|     this.updateCookieBannerHandlingVisibility();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Reads the cookiebanners.service.mode.privateBrowsing pref,
 | |
|    * interpreting the multiple modes as a true/false value
 | |
|    */
 | |
|   readCookieBannerMode() {
 | |
|     return (
 | |
|       Preferences.get("cookiebanners.service.mode.privateBrowsing").value !=
 | |
|       Ci.nsICookieBannerService.MODE_DISABLED
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Translates user clicks on the cookie banner handling checkbox to the
 | |
|    * corresponding integer-valued cookie banner mode preference.
 | |
|    */
 | |
|   writeCookieBannerMode() {
 | |
|     let checkbox = document.getElementById("handleCookieBanners");
 | |
|     if (!checkbox.checked) {
 | |
|       /* because we removed UI control for the non-PBM pref, disabling it here
 | |
|          provides an off-ramp for profiles where it had previously been enabled from the UI */
 | |
|       Services.prefs.setIntPref(
 | |
|         "cookiebanners.service.mode",
 | |
|         Ci.nsICookieBannerService.MODE_DISABLED
 | |
|       );
 | |
|       return Ci.nsICookieBannerService.MODE_DISABLED;
 | |
|     }
 | |
|     return Ci.nsICookieBannerService.MODE_REJECT;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Shows or hides the cookie banner handling section based on the value of
 | |
|    * the "cookiebanners.ui.desktop.enabled" pref.
 | |
|    */
 | |
|   updateCookieBannerHandlingVisibility() {
 | |
|     let groupbox = document.getElementById("cookieBannerHandlingGroup");
 | |
|     let isEnabled = Preferences.get("cookiebanners.ui.desktop.enabled").value;
 | |
| 
 | |
|     // Because the top-level pane showing code unsets the hidden attribute, we
 | |
|     // manually hide the section when cookie banner handling is preffed off.
 | |
|     if (isEnabled) {
 | |
|       groupbox.removeAttribute("style");
 | |
|     } else {
 | |
|       groupbox.setAttribute("style", "display: none !important");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Updates the visibility of the Firefox Suggest Privacy Container
 | |
|    * based on the user's Quick Suggest settings.
 | |
|    *
 | |
|    * @param {boolean} [onInit]
 | |
|    *   Pass true when calling this when initializing the pane.
 | |
|    */
 | |
|   _updateFirefoxSuggestToggle(onInit = false) {
 | |
|     let container = document.getElementById("firefoxSuggestPrivacyContainer");
 | |
| 
 | |
|     if (UrlbarPrefs.get("quickSuggestEnabled")) {
 | |
|       container.removeAttribute("hidden");
 | |
|     } else if (!onInit) {
 | |
|       container.setAttribute("hidden", "true");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // GEOLOCATION
 | |
| 
 | |
|   /**
 | |
|    * Displays the location exceptions dialog where specific site location
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showLocationExceptions() {
 | |
|     let params = { permissionType: "geo" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // XR
 | |
| 
 | |
|   /**
 | |
|    * Displays the XR exceptions dialog where specific site XR
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showXRExceptions() {
 | |
|     let params = { permissionType: "xr" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // CAMERA
 | |
| 
 | |
|   /**
 | |
|    * Displays the camera exceptions dialog where specific site camera
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showCameraExceptions() {
 | |
|     let params = { permissionType: "camera" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // MICROPHONE
 | |
| 
 | |
|   /**
 | |
|    * Displays the microphone exceptions dialog where specific site microphone
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showMicrophoneExceptions() {
 | |
|     let params = { permissionType: "microphone" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // SPEAKER
 | |
| 
 | |
|   /**
 | |
|    * Displays the speaker exceptions dialog where specific site speaker
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showSpeakerExceptions() {
 | |
|     let params = { permissionType: "speaker" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // NOTIFICATIONS
 | |
| 
 | |
|   /**
 | |
|    * Displays the notifications exceptions dialog where specific site notification
 | |
|    * preferences can be set.
 | |
|    */
 | |
|   showNotificationExceptions() {
 | |
|     let params = { permissionType: "desktop-notification" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // MEDIA
 | |
| 
 | |
|   showAutoplayMediaExceptions() {
 | |
|     var params = { permissionType: "autoplay-media" };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // POP-UPS
 | |
| 
 | |
|   /**
 | |
|    * Displays the popup exceptions dialog where specific site popup preferences
 | |
|    * can be set.
 | |
|    */
 | |
|   showPopupExceptions() {
 | |
|     var params = {
 | |
|       blockVisible: false,
 | |
|       sessionVisible: false,
 | |
|       allowVisible: true,
 | |
|       prefilledHost: "",
 | |
|       permissionType: "popup",
 | |
|     };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       { features: "resizable=yes" },
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   // UTILITY FUNCTIONS
 | |
| 
 | |
|   /**
 | |
|    * Utility function to enable/disable the button specified by aButtonID based
 | |
|    * on the value of the Boolean preference specified by aPreferenceID.
 | |
|    */
 | |
|   updateButtons(aButtonID, aPreferenceID) {
 | |
|     var button = document.getElementById(aButtonID);
 | |
|     var preference = Preferences.get(aPreferenceID);
 | |
|     button.disabled = !preference.value || preference.locked;
 | |
|     return undefined;
 | |
|   },
 | |
| 
 | |
|   // BEGIN UI CODE
 | |
| 
 | |
|   /*
 | |
|    * Preferences:
 | |
|    *
 | |
|    * dom.disable_open_during_load
 | |
|    * - true if popups are blocked by default, false otherwise
 | |
|    */
 | |
| 
 | |
|   // POP-UPS
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog in which the user can view and modify the list of sites
 | |
|    * where passwords are never saved.
 | |
|    */
 | |
|   showPasswordExceptions() {
 | |
|     var params = {
 | |
|       blockVisible: true,
 | |
|       sessionVisible: false,
 | |
|       allowVisible: false,
 | |
|       hideStatusColumn: true,
 | |
|       prefilledHost: "",
 | |
|       permissionType: "login-saving",
 | |
|     };
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       undefined,
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initializes master password UI: the "use master password" checkbox, selects
 | |
|    * the master password button to show, and enables/disables it as necessary.
 | |
|    * The master password is controlled by various bits of NSS functionality, so
 | |
|    * the UI for it can't be controlled by the normal preference bindings.
 | |
|    */
 | |
|   _initMasterPasswordUI() {
 | |
|     var noMP = !LoginHelper.isPrimaryPasswordSet();
 | |
| 
 | |
|     var button = document.getElementById("changeMasterPassword");
 | |
|     button.disabled = noMP;
 | |
| 
 | |
|     var checkbox = document.getElementById("useMasterPassword");
 | |
|     checkbox.checked = !noMP;
 | |
|     checkbox.disabled =
 | |
|       (noMP && !Services.policies.isAllowed("createMasterPassword")) ||
 | |
|       (!noMP && !Services.policies.isAllowed("removeMasterPassword"));
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Enables/disables the master password button depending on the state of the
 | |
|    * "use master password" checkbox, and prompts for master password removal if
 | |
|    * one is set.
 | |
|    */
 | |
|   async updateMasterPasswordButton() {
 | |
|     var checkbox = document.getElementById("useMasterPassword");
 | |
|     var button = document.getElementById("changeMasterPassword");
 | |
|     button.disabled = !checkbox.checked;
 | |
| 
 | |
|     // unchecking the checkbox should try to immediately remove the master
 | |
|     // password, because it's impossible to non-destructively remove the master
 | |
|     // password used to encrypt all the passwords without providing it (by
 | |
|     // design), and it would be extremely odd to pop up that dialog when the
 | |
|     // user closes the prefwindow and saves his settings
 | |
|     if (!checkbox.checked) {
 | |
|       await this._removeMasterPassword();
 | |
|     } else {
 | |
|       await this.changeMasterPassword();
 | |
|     }
 | |
| 
 | |
|     this._initMasterPasswordUI();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the "remove master password" dialog to allow the user to remove
 | |
|    * the current master password.  When the dialog is dismissed, master password
 | |
|    * UI is automatically updated.
 | |
|    */
 | |
|   async _removeMasterPassword() {
 | |
|     var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(
 | |
|       Ci.nsIPKCS11ModuleDB
 | |
|     );
 | |
|     if (secmodDB.isFIPSEnabled) {
 | |
|       let title = document.getElementById("fips-title").textContent;
 | |
|       let desc = document.getElementById("fips-desc").textContent;
 | |
|       Services.prompt.alert(window, title, desc);
 | |
|       this._initMasterPasswordUI();
 | |
|     } else {
 | |
|       gSubDialog.open("chrome://mozapps/content/preferences/removemp.xhtml", {
 | |
|         closingCallback: this._initMasterPasswordUI.bind(this),
 | |
|       });
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog in which the primary password may be changed.
 | |
|    */
 | |
|   async changeMasterPassword() {
 | |
|     // Require OS authentication before the user can set a Primary Password.
 | |
|     // OS reauthenticate functionality is not available on Linux yet (bug 1527745)
 | |
|     if (
 | |
|       !LoginHelper.isPrimaryPasswordSet() &&
 | |
|       OS_AUTH_ENABLED &&
 | |
|       OSKeyStore.canReauth()
 | |
|     ) {
 | |
|       // Uses primary-password-os-auth-dialog-message-win and
 | |
|       // primary-password-os-auth-dialog-message-macosx via concatenation:
 | |
|       let messageId =
 | |
|         "primary-password-os-auth-dialog-message-" + AppConstants.platform;
 | |
|       let [messageText, captionText] = await document.l10n.formatMessages([
 | |
|         {
 | |
|           id: messageId,
 | |
|         },
 | |
|         {
 | |
|           id: "master-password-os-auth-dialog-caption",
 | |
|         },
 | |
|       ]);
 | |
|       let win = Services.wm.getMostRecentBrowserWindow();
 | |
|       let loggedIn = await OSKeyStore.ensureLoggedIn(
 | |
|         messageText.value,
 | |
|         captionText.value,
 | |
|         win,
 | |
|         false
 | |
|       );
 | |
|       if (!loggedIn.authenticated) {
 | |
|         return;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     gSubDialog.open("chrome://mozapps/content/preferences/changemp.xhtml", {
 | |
|       features: "resizable=no",
 | |
|       closingCallback: this._initMasterPasswordUI.bind(this),
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Set up the initial state for the GPC/DNT UI.
 | |
|    * The GPC part should only appear if the functionality is
 | |
|    * enabled.
 | |
|    */
 | |
|   _initGlobalPrivacyControlUI() {
 | |
|     let gpcEnabledPrefValue = Services.prefs.getBoolPref(
 | |
|       "privacy.globalprivacycontrol.functionality.enabled",
 | |
|       false
 | |
|     );
 | |
|     document.getElementById("globalPrivacyControlBox").hidden =
 | |
|       !gpcEnabledPrefValue;
 | |
|     document.getElementById("doNotTrackBox").hidden = !gpcEnabledPrefValue;
 | |
|     document.getElementById("legacyDoNotTrackBox").hidden = gpcEnabledPrefValue;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Set up the initial state for the password generation UI.
 | |
|    * It will be hidden unless the .available pref is true
 | |
|    */
 | |
|   _initPasswordGenerationUI() {
 | |
|     // we don't watch the .available pref for runtime changes
 | |
|     let prefValue = Services.prefs.getBoolPref(
 | |
|       PREF_PASSWORD_GENERATION_AVAILABLE,
 | |
|       false
 | |
|     );
 | |
|     document.getElementById("generatePasswordsBox").hidden = !prefValue;
 | |
|   },
 | |
| 
 | |
|   toggleRelayIntegration() {
 | |
|     const checkbox = document.getElementById("relayIntegration");
 | |
|     if (checkbox.checked) {
 | |
|       FirefoxRelay.markAsAvailable();
 | |
|       FirefoxRelayTelemetry.recordRelayPrefEvent("enabled");
 | |
|     } else {
 | |
|       FirefoxRelay.markAsDisabled();
 | |
|       FirefoxRelayTelemetry.recordRelayPrefEvent("disabled");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _updateRelayIntegrationUI() {
 | |
|     document.getElementById("relayIntegrationBox").hidden =
 | |
|       !FirefoxRelay.isAvailable;
 | |
|     document.getElementById("relayIntegration").checked =
 | |
|       FirefoxRelay.isAvailable && !FirefoxRelay.isDisabled;
 | |
|   },
 | |
| 
 | |
|   _initRelayIntegrationUI() {
 | |
|     document
 | |
|       .getElementById("relayIntegrationLearnMoreLink")
 | |
|       .setAttribute("href", FirefoxRelay.learnMoreUrl);
 | |
| 
 | |
|     setEventListener(
 | |
|       "relayIntegration",
 | |
|       "command",
 | |
|       gPrivacyPane.toggleRelayIntegration.bind(gPrivacyPane)
 | |
|     );
 | |
|     Preferences.get("signon.firefoxRelay.feature").on(
 | |
|       "change",
 | |
|       gPrivacyPane._updateRelayIntegrationUI.bind(gPrivacyPane)
 | |
|     );
 | |
| 
 | |
|     this._updateRelayIntegrationUI();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Shows the sites where the user has saved passwords and the associated login
 | |
|    * information.
 | |
|    */
 | |
|   showPasswords() {
 | |
|     let loginManager = window.windowGlobalChild.getActor("LoginManager");
 | |
|     loginManager.sendAsyncMessage("PasswordManager:OpenPreferences", {
 | |
|       entryPoint: "preferences",
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Enables/disables dependent controls related to password saving
 | |
|    * When password saving is not enabled, we need to also disable the password generation checkbox
 | |
|    * The Exceptions button is used to configure sites where passwords are never saved.
 | |
|    */
 | |
|   readSavePasswords() {
 | |
|     var prefValue = Preferences.get("signon.rememberSignons").value;
 | |
|     document.getElementById("passwordExceptions").disabled = !prefValue;
 | |
|     document.getElementById("generatePasswords").disabled = !prefValue;
 | |
|     document.getElementById("passwordAutofillCheckbox").disabled = !prefValue;
 | |
|     document.getElementById("relayIntegration").disabled =
 | |
|       !prefValue || Services.prefs.prefIsLocked("signon.firefoxRelay.feature");
 | |
|     // don't override pref value in UI
 | |
|     return undefined;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initalizes pref listeners for the password manager.
 | |
|    *
 | |
|    * This ensures that the user is always notified if an extension is controlling the password manager.
 | |
|    */
 | |
|   initListenersForExtensionControllingPasswordManager() {
 | |
|     this._passwordManagerCheckbox = document.getElementById("savePasswords");
 | |
|     this._disableExtensionButton = document.getElementById(
 | |
|       "disablePasswordManagerExtension"
 | |
|     );
 | |
| 
 | |
|     this._disableExtensionButton.addEventListener(
 | |
|       "command",
 | |
|       makeDisableControllingExtension(
 | |
|         PREF_SETTING_TYPE,
 | |
|         PASSWORD_MANAGER_PREF_ID
 | |
|       )
 | |
|     );
 | |
| 
 | |
|     initListenersForPrefChange(
 | |
|       PREF_SETTING_TYPE,
 | |
|       PASSWORD_MANAGER_PREF_ID,
 | |
|       this._passwordManagerCheckbox
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Enables/disables the add-ons Exceptions button depending on whether
 | |
|    * or not add-on installation warnings are displayed.
 | |
|    */
 | |
|   readWarnAddonInstall() {
 | |
|     var warn = Preferences.get("xpinstall.whitelist.required");
 | |
|     var exceptions = document.getElementById("addonExceptions");
 | |
| 
 | |
|     exceptions.disabled = !warn.value || warn.locked;
 | |
| 
 | |
|     // don't override the preference value
 | |
|     return undefined;
 | |
|   },
 | |
| 
 | |
|   _initSafeBrowsing() {
 | |
|     let enableSafeBrowsing = document.getElementById("enableSafeBrowsing");
 | |
|     let blockDownloads = document.getElementById("blockDownloads");
 | |
|     let blockUncommonUnwanted = document.getElementById(
 | |
|       "blockUncommonUnwanted"
 | |
|     );
 | |
| 
 | |
|     let safeBrowsingPhishingPref = Preferences.get(
 | |
|       "browser.safebrowsing.phishing.enabled"
 | |
|     );
 | |
|     let safeBrowsingMalwarePref = Preferences.get(
 | |
|       "browser.safebrowsing.malware.enabled"
 | |
|     );
 | |
| 
 | |
|     let blockDownloadsPref = Preferences.get(
 | |
|       "browser.safebrowsing.downloads.enabled"
 | |
|     );
 | |
|     let malwareTable = Preferences.get("urlclassifier.malwareTable");
 | |
| 
 | |
|     let blockUnwantedPref = Preferences.get(
 | |
|       "browser.safebrowsing.downloads.remote.block_potentially_unwanted"
 | |
|     );
 | |
|     let blockUncommonPref = Preferences.get(
 | |
|       "browser.safebrowsing.downloads.remote.block_uncommon"
 | |
|     );
 | |
| 
 | |
|     enableSafeBrowsing.addEventListener("command", function () {
 | |
|       safeBrowsingPhishingPref.value = enableSafeBrowsing.checked;
 | |
|       safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;
 | |
| 
 | |
|       blockDownloads.disabled =
 | |
|         !enableSafeBrowsing.checked || blockDownloadsPref.locked;
 | |
|       blockUncommonUnwanted.disabled =
 | |
|         !blockDownloads.checked ||
 | |
|         !enableSafeBrowsing.checked ||
 | |
|         blockUnwantedPref.locked ||
 | |
|         blockUncommonPref.locked;
 | |
|     });
 | |
| 
 | |
|     blockDownloads.addEventListener("command", function () {
 | |
|       blockDownloadsPref.value = blockDownloads.checked;
 | |
|       blockUncommonUnwanted.disabled =
 | |
|         !blockDownloads.checked ||
 | |
|         blockUnwantedPref.locked ||
 | |
|         blockUncommonPref.locked;
 | |
|     });
 | |
| 
 | |
|     blockUncommonUnwanted.addEventListener("command", function () {
 | |
|       blockUnwantedPref.value = blockUncommonUnwanted.checked;
 | |
|       blockUncommonPref.value = blockUncommonUnwanted.checked;
 | |
| 
 | |
|       let malware = malwareTable.value
 | |
|         .split(",")
 | |
|         .filter(
 | |
|           x =>
 | |
|             x !== "goog-unwanted-proto" &&
 | |
|             x !== "goog-unwanted-shavar" &&
 | |
|             x !== "moztest-unwanted-simple"
 | |
|         );
 | |
| 
 | |
|       if (blockUncommonUnwanted.checked) {
 | |
|         if (malware.includes("goog-malware-shavar")) {
 | |
|           malware.push("goog-unwanted-shavar");
 | |
|         } else {
 | |
|           malware.push("goog-unwanted-proto");
 | |
|         }
 | |
| 
 | |
|         malware.push("moztest-unwanted-simple");
 | |
|       }
 | |
| 
 | |
|       // sort alphabetically to keep the pref consistent
 | |
|       malware.sort();
 | |
| 
 | |
|       malwareTable.value = malware.join(",");
 | |
| 
 | |
|       // Force an update after changing the malware table.
 | |
|       listManager.forceUpdates(malwareTable.value);
 | |
|     });
 | |
| 
 | |
|     // set initial values
 | |
| 
 | |
|     enableSafeBrowsing.checked =
 | |
|       safeBrowsingPhishingPref.value && safeBrowsingMalwarePref.value;
 | |
|     if (!enableSafeBrowsing.checked) {
 | |
|       blockDownloads.setAttribute("disabled", "true");
 | |
|       blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|     }
 | |
| 
 | |
|     blockDownloads.checked = blockDownloadsPref.value;
 | |
|     if (!blockDownloadsPref.value) {
 | |
|       blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|     }
 | |
|     blockUncommonUnwanted.checked =
 | |
|       blockUnwantedPref.value && blockUncommonPref.value;
 | |
| 
 | |
|     if (safeBrowsingPhishingPref.locked || safeBrowsingMalwarePref.locked) {
 | |
|       enableSafeBrowsing.disabled = true;
 | |
|     }
 | |
|     if (blockDownloadsPref.locked) {
 | |
|       blockDownloads.disabled = true;
 | |
|     }
 | |
|     if (blockUnwantedPref.locked || blockUncommonPref.locked) {
 | |
|       blockUncommonUnwanted.disabled = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the exceptions lists for add-on installation warnings.
 | |
|    */
 | |
|   showAddonExceptions() {
 | |
|     var params = this._addonParams;
 | |
| 
 | |
|     gSubDialog.open(
 | |
|       "chrome://browser/content/preferences/dialogs/permissions.xhtml",
 | |
|       undefined,
 | |
|       params
 | |
|     );
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Parameters for the add-on install permissions dialog.
 | |
|    */
 | |
|   _addonParams: {
 | |
|     blockVisible: false,
 | |
|     sessionVisible: false,
 | |
|     allowVisible: true,
 | |
|     prefilledHost: "",
 | |
|     permissionType: "install",
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * readEnableOCSP is used by the preferences UI to determine whether or not
 | |
|    * the checkbox for OCSP fetching should be checked (it returns true if it
 | |
|    * should be checked and false otherwise). The about:config preference
 | |
|    * "security.OCSP.enabled" is an integer rather than a boolean, so it can't be
 | |
|    * directly mapped from {true,false} to {checked,unchecked}. The possible
 | |
|    * values for "security.OCSP.enabled" are:
 | |
|    * 0: fetching is disabled
 | |
|    * 1: fetch for all certificates
 | |
|    * 2: fetch only for EV certificates
 | |
|    * Hence, if "security.OCSP.enabled" is non-zero, the checkbox should be
 | |
|    * checked. Otherwise, it should be unchecked.
 | |
|    */
 | |
|   readEnableOCSP() {
 | |
|     var preference = Preferences.get("security.OCSP.enabled");
 | |
|     // This is the case if the preference is the default value.
 | |
|     if (preference.value === undefined) {
 | |
|       return true;
 | |
|     }
 | |
|     return preference.value != 0;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * writeEnableOCSP is used by the preferences UI to map the checked/unchecked
 | |
|    * state of the OCSP fetching checkbox to the value that the preference
 | |
|    * "security.OCSP.enabled" should be set to (it returns that value). See the
 | |
|    * readEnableOCSP documentation for more background. We unfortunately don't
 | |
|    * have enough information to map from {true,false} to all possible values for
 | |
|    * "security.OCSP.enabled", but a reasonable alternative is to map from
 | |
|    * {true,false} to {<the default value>,0}. That is, if the box is checked,
 | |
|    * "security.OCSP.enabled" will be set to whatever default it should be, given
 | |
|    * the platform and channel. If the box is unchecked, the preference will be
 | |
|    * set to 0. Obviously this won't work if the default is 0, so we will have to
 | |
|    * revisit this if we ever set it to 0.
 | |
|    */
 | |
|   writeEnableOCSP() {
 | |
|     var checkbox = document.getElementById("enableOCSP");
 | |
|     var defaults = Services.prefs.getDefaultBranch(null);
 | |
|     var defaultValue = defaults.getIntPref("security.OCSP.enabled");
 | |
|     return checkbox.checked ? defaultValue : 0;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the user's certificates and associated options.
 | |
|    */
 | |
|   showCertificates() {
 | |
|     gSubDialog.open("chrome://pippki/content/certManager.xhtml");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog from which the user can manage his security devices.
 | |
|    */
 | |
|   showSecurityDevices() {
 | |
|     gSubDialog.open("chrome://pippki/content/device_manager.xhtml");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the learn more health report page when a user opts out of data collection.
 | |
|    */
 | |
|   showDataDeletion() {
 | |
|     let url =
 | |
|       Services.urlFormatter.formatURLPref("app.support.baseURL") +
 | |
|       "telemetry-clientid";
 | |
|     window.open(url, "_blank");
 | |
|   },
 | |
| 
 | |
|   initDataCollection() {
 | |
|     if (
 | |
|       !AppConstants.MOZ_DATA_REPORTING &&
 | |
|       !NimbusFeatures.majorRelease2022.getVariable(
 | |
|         "feltPrivacyShowPreferencesSection"
 | |
|       )
 | |
|     ) {
 | |
|       // Nothing to control in the data collection section, remove it.
 | |
|       document.getElementById("dataCollectionCategory").remove();
 | |
|       document.getElementById("dataCollectionGroup").remove();
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     this._setupLearnMoreLink(
 | |
|       "toolkit.datacollection.infoURL",
 | |
|       "dataCollectionPrivacyNotice"
 | |
|     );
 | |
|     this.initPrivacySegmentation();
 | |
|   },
 | |
| 
 | |
|   initSubmitCrashes() {
 | |
|     this._setupLearnMoreLink(
 | |
|       "toolkit.crashreporter.infoURL",
 | |
|       "crashReporterLearnMore"
 | |
|     );
 | |
|     setEventListener("crashReporterLabel", "click", function (event) {
 | |
|       if (event.target.localName == "a") {
 | |
|         return;
 | |
|       }
 | |
|       const checkboxId = event.target.getAttribute("for");
 | |
|       document.getElementById(checkboxId).click();
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   initPrivacySegmentation() {
 | |
|     // Section visibility
 | |
|     let section = document.getElementById("privacySegmentationSection");
 | |
|     let updatePrivacySegmentationSectionVisibilityState = () => {
 | |
|       section.hidden = !NimbusFeatures.majorRelease2022.getVariable(
 | |
|         "feltPrivacyShowPreferencesSection"
 | |
|       );
 | |
|     };
 | |
| 
 | |
|     NimbusFeatures.majorRelease2022.onUpdate(
 | |
|       updatePrivacySegmentationSectionVisibilityState
 | |
|     );
 | |
|     window.addEventListener("unload", () => {
 | |
|       NimbusFeatures.majorRelease2022.offUpdate(
 | |
|         updatePrivacySegmentationSectionVisibilityState
 | |
|       );
 | |
|     });
 | |
| 
 | |
|     updatePrivacySegmentationSectionVisibilityState();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Set up or hide the Learn More links for various data collection options
 | |
|    */
 | |
|   _setupLearnMoreLink(pref, element) {
 | |
|     // set up the Learn More link with the correct URL
 | |
|     let url = Services.urlFormatter.formatURLPref(pref);
 | |
|     let el = document.getElementById(element);
 | |
| 
 | |
|     if (url) {
 | |
|       el.setAttribute("href", url);
 | |
|     } else {
 | |
|       el.hidden = true;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initialize the health report service reference and checkbox.
 | |
|    */
 | |
|   initSubmitHealthReport() {
 | |
|     this._setupLearnMoreLink(
 | |
|       "datareporting.healthreport.infoURL",
 | |
|       "FHRLearnMore"
 | |
|     );
 | |
| 
 | |
|     let checkbox = document.getElementById("submitHealthReportBox");
 | |
| 
 | |
|     // Telemetry is only sending data if MOZ_TELEMETRY_REPORTING is defined.
 | |
|     // We still want to display the preferences panel if that's not the case, but
 | |
|     // we want it to be disabled and unchecked.
 | |
|     if (
 | |
|       Services.prefs.prefIsLocked(PREF_UPLOAD_ENABLED) ||
 | |
|       !AppConstants.MOZ_TELEMETRY_REPORTING
 | |
|     ) {
 | |
|       checkbox.setAttribute("disabled", "true");
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     checkbox.checked =
 | |
|       Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED) &&
 | |
|       AppConstants.MOZ_TELEMETRY_REPORTING;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Update the health report preference with state from checkbox.
 | |
|    */
 | |
|   updateSubmitHealthReport() {
 | |
|     let checkbox = document.getElementById("submitHealthReportBox");
 | |
|     let telemetryContainer = document.getElementById("telemetry-container");
 | |
| 
 | |
|     Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked);
 | |
|     telemetryContainer.hidden = checkbox.checked;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initialize the opt-out-study preference checkbox into about:preferences and
 | |
|    * handles events coming from the UI for it.
 | |
|    */
 | |
|   initOptOutStudyCheckbox(doc) {
 | |
|     // The checkbox should be disabled if any of the below are true. This
 | |
|     // prevents the user from changing the value in the box.
 | |
|     //
 | |
|     // * the policy forbids shield
 | |
|     // * Normandy is disabled
 | |
|     //
 | |
|     // The checkbox should match the value of the preference only if all of
 | |
|     // these are true. Otherwise, the checkbox should remain unchecked. This
 | |
|     // is because in these situations, Shield studies are always disabled, and
 | |
|     // so showing a checkbox would be confusing.
 | |
|     //
 | |
|     // * the policy allows Shield
 | |
|     // * Normandy is enabled
 | |
| 
 | |
|     const allowedByPolicy = Services.policies.isAllowed("Shield");
 | |
|     const checkbox = document.getElementById("optOutStudiesEnabled");
 | |
| 
 | |
|     if (
 | |
|       allowedByPolicy &&
 | |
|       Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false)
 | |
|     ) {
 | |
|       if (Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false)) {
 | |
|         checkbox.setAttribute("checked", "true");
 | |
|       } else {
 | |
|         checkbox.removeAttribute("checked");
 | |
|       }
 | |
|       checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED);
 | |
|       checkbox.removeAttribute("disabled");
 | |
|     } else {
 | |
|       checkbox.removeAttribute("preference");
 | |
|       checkbox.removeAttribute("checked");
 | |
|       checkbox.setAttribute("disabled", "true");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   initAddonRecommendationsCheckbox() {
 | |
|     // Setup the checkbox.
 | |
|     dataCollectionCheckboxHandler({
 | |
|       checkbox: document.getElementById("addonRecommendationEnabled"),
 | |
|       pref: PREF_ADDON_RECOMMENDATIONS_ENABLED,
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   observe(aSubject, aTopic, aData) {
 | |
|     switch (aTopic) {
 | |
|       case "sitedatamanager:updating-sites":
 | |
|         // While updating, we want to disable this section and display loading message until updated
 | |
|         this.toggleSiteData(false);
 | |
|         this.showSiteDataLoading();
 | |
|         break;
 | |
| 
 | |
|       case "sitedatamanager:sites-updated":
 | |
|         this.toggleSiteData(true);
 | |
|         SiteDataManager.getTotalUsage().then(
 | |
|           this.updateTotalDataSizeLabel.bind(this)
 | |
|         );
 | |
|         break;
 | |
|       case "network:trr-uri-changed":
 | |
|       case "network:trr-mode-changed":
 | |
|       case "network:trr-confirmation":
 | |
|         gPrivacyPane.updateDoHStatus();
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| };
 | 
