forked from mirrors/gecko-dev
		
	 2289894cfb
			
		
	
	
		2289894cfb
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D7782 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			2016 lines
		
	
	
	
		
			71 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			2016 lines
		
	
	
	
		
			71 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 */
 | |
| 
 | |
| /* FIXME: ESlint globals workaround should be removed once bug 1395426 gets fixed */
 | |
| /* globals DownloadUtils, LoadContextInfo */
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 | |
| ChromeUtils.import("resource://gre/modules/PluralForm.jsm");
 | |
| 
 | |
| ChromeUtils.defineModuleGetter(this, "PluralForm",
 | |
|   "resource://gre/modules/PluralForm.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "LoginHelper",
 | |
|   "resource://gre/modules/LoginHelper.jsm");
 | |
| ChromeUtils.defineModuleGetter(this, "SiteDataManager",
 | |
|   "resource:///modules/SiteDataManager.jsm");
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingUiEnabled",
 | |
|                                       "browser.contentblocking.ui.enabled");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingCookiesAndSiteDataRejectTrackersRecommended",
 | |
|                                       "browser.contentblocking.cookies-site-data.ui.reject-trackers.recommended");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingCookiesAndSiteDataRejectTrackersEnabled",
 | |
|                                       "browser.contentblocking.cookies-site-data.ui.reject-trackers.enabled");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingFastBlockUiEnabled",
 | |
|                                       "browser.contentblocking.fastblock.ui.enabled");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingTrackingProtectionUiEnabled",
 | |
|                                       "browser.contentblocking.trackingprotection.ui.enabled");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingRejectTrackersUiEnabled",
 | |
|                                       "browser.contentblocking.rejecttrackers.ui.enabled");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingRejectTrackersRecommended",
 | |
|                                       "browser.contentblocking.rejecttrackers.ui.recommended");
 | |
| 
 | |
| XPCOMUtils.defineLazyPreferenceGetter(this, "contentBlockingEnabled",
 | |
|                                       "browser.contentblocking.enabled");
 | |
| 
 | |
| 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 PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled";
 | |
| const PREF_NORMANDY_ENABLED = "app.normandy.enabled";
 | |
| 
 | |
| XPCOMUtils.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;
 | |
|   }
 | |
| });
 | |
| 
 | |
| Preferences.addAll([
 | |
|   // Content Blocking
 | |
|   { id: "browser.contentblocking.enabled", type: "bool" },
 | |
| 
 | |
|   // FastBlock
 | |
|   { id: "browser.fastblock.enabled", type: "bool" },
 | |
| 
 | |
|   // Tracking Protection
 | |
|   { id: "privacy.trackingprotection.enabled", type: "bool" },
 | |
|   { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" },
 | |
|   // This isn't a configuration pref, rather it's for saving the previous state
 | |
|   // of the UI when we turn off the TP controls when the user checks off the
 | |
|   // All Detected Trackers under Content Blocking.  This pref isn't listed in
 | |
|   // all.js/firefox.js to make sure it doesn't appear in about:config by default.
 | |
|   { id: "browser.privacy.trackingprotection.menu", type: "string" },
 | |
| 
 | |
|   // Button prefs
 | |
|   { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" },
 | |
|   { id: "pref.privacy.disable_button.view_cookies", type: "bool" },
 | |
|   { id: "pref.privacy.disable_button.change_blocklist", type: "bool" },
 | |
|   { id: "pref.privacy.disable_button.tracking_protection_exceptions", type: "bool" },
 | |
| 
 | |
|   // Location Bar
 | |
|   { id: "browser.urlbar.autocomplete.enabled", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.bookmark", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.history", type: "bool" },
 | |
|   { id: "browser.urlbar.suggest.openpage", 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.lifetimePolicy", type: "int" },
 | |
|   { id: "network.cookie.blockFutureCookies", type: "bool" },
 | |
|   // Clear Private Data
 | |
|   { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" },
 | |
|   { id: "privacy.sanitize.timeSpan", type: "int" },
 | |
|   // Do not track
 | |
|   { id: "privacy.donottrackheader.enabled", type: "bool" },
 | |
| 
 | |
|   // Media
 | |
|   { id: "media.autoplay.default", type: "int" },
 | |
|   { id: "media.autoplay.enabled.ask-permission", type: "bool" },
 | |
|   { id: "media.autoplay.enabled.user-gestures-needed", type: "bool" },
 | |
| 
 | |
|   // Popups
 | |
|   { id: "dom.disable_open_during_load", type: "bool" },
 | |
|   // Passwords
 | |
|   { id: "signon.rememberSignons", type: "bool" },
 | |
| 
 | |
|   // 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" },
 | |
| 
 | |
|   // 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" },
 | |
| 
 | |
| ]);
 | |
| 
 | |
| // 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_UPLOAD_ENABLED, type: "bool" },
 | |
|   ]);
 | |
| }
 | |
| 
 | |
| // Data Choices tab
 | |
| if (AppConstants.NIGHTLY_BUILD) {
 | |
|   Preferences.add({ id: "browser.chrome.errorReporter.enabled", type: "bool" });
 | |
| }
 | |
| 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));
 | |
| }
 | |
| 
 | |
| var gPrivacyPane = {
 | |
|   _pane: null,
 | |
| 
 | |
|   /**
 | |
|    * Whether the prompt to restart Firefox should appear when changing the autostart pref.
 | |
|    */
 | |
|   _shouldPromptForRestart: true,
 | |
| 
 | |
|   /**
 | |
|    * Initialize the tracking protection prefs and linkify its Learn More link.
 | |
|    */
 | |
|   _initTrackingProtection() {
 | |
|     setEventListener("trackingProtectionRadioGroup", "command",
 | |
|       this.trackingProtectionWritePrefs);
 | |
|     setEventListener("changeBlockList", "command", this.showBlockLists);
 | |
| 
 | |
|     let link = document.getElementById("trackingProtectionLearnMore");
 | |
|     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
 | |
|     link.setAttribute("href", url);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Update the tracking protection UI to deal with extension control.
 | |
|    */
 | |
|   _updateTrackingProtectionUI() {
 | |
|     let isLocked = TRACKING_PROTECTION_PREFS.some(
 | |
|       pref => Services.prefs.prefIsLocked(pref));
 | |
| 
 | |
|     function setInputsDisabledState(isControlled) {
 | |
|       let disabled = isLocked || isControlled;
 | |
|       if (contentBlockingUiEnabled) {
 | |
|         let tpCheckbox =
 | |
|           document.getElementById("contentBlockingTrackingProtectionCheckbox");
 | |
|         // Only enable the TP menu if content blocking and Detect All Trackers
 | |
|         // are enabled.
 | |
|         document.getElementById("trackingProtectionMenu").disabled = disabled ||
 | |
|           !tpCheckbox.checked ||
 | |
|           !contentBlockingEnabled;
 | |
|         // Only enable the TP category checkbox if content blocking is enabled.
 | |
|         tpCheckbox.disabled = disabled || !contentBlockingEnabled;
 | |
|       } else {
 | |
|         document.querySelectorAll("#trackingProtectionRadioGroup > radio")
 | |
|           .forEach((element) => {
 | |
|             element.disabled = disabled;
 | |
|           });
 | |
|         document.querySelector("#trackingProtectionDesc > label")
 | |
|           .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");
 | |
|     }
 | |
| 
 | |
|     if (isLocked) {
 | |
|       // 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);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Set up handlers for showing and hiding controlling extension info
 | |
|    * for tracking protection.
 | |
|    */
 | |
|   _initTrackingProtectionExtensionControl() {
 | |
|     let disableButton = contentBlockingUiEnabled ?
 | |
|       "contentBlockingDisableTrackingProtectionExtension" : "disableTrackingProtectionExtension";
 | |
|     setEventListener(disableButton, "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);
 | |
|       }
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Initialize autocomplete to ensure prefs are in sync.
 | |
|    */
 | |
|   _initAutocomplete() {
 | |
|     Cc["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
 | |
|       .getService(Ci.mozIPlacesAutoComplete);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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.initializeHistoryMode();
 | |
|     this.initAutoplay();
 | |
|     this.updateAutoplayMediaControlsVisibility();
 | |
|     this.updateHistoryModePane();
 | |
|     this.updatePrivacyMicroControls();
 | |
|     this.initAutoStartPrivateBrowsingReverter();
 | |
|     this._initAutocomplete();
 | |
| 
 | |
|     /* Initialize Content Blocking / Tracking Protection */
 | |
| 
 | |
|     if (contentBlockingUiEnabled) {
 | |
|       this.initContentBlocking();
 | |
|     } else {
 | |
|       this._initTrackingProtection();
 | |
|     }
 | |
| 
 | |
|     this.trackingProtectionReadPrefs();
 | |
|     this.networkCookieBehaviorReadPrefs();
 | |
|     this._initTrackingProtectionExtensionControl();
 | |
| 
 | |
|     this.updateContentBlockingVisibility();
 | |
| 
 | |
|     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("network.cookie.lifetimePolicy").on("change",
 | |
|       gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane));
 | |
|     Preferences.get("browser.privatebrowsing.autostart").on("change",
 | |
|       gPrivacyPane.networkCookieBehaviorReadPrefs.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));
 | |
|     Preferences.get("media.autoplay.enabled.ask-permission").on("change",
 | |
|      gPrivacyPane.updateAutoplayMediaControlsVisibility.bind(gPrivacyPane));
 | |
|     Preferences.get("media.autoplay.enabled.user-gestures-needed").on("change",
 | |
|      gPrivacyPane.updateAutoplayMediaControlsVisibility.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("openSearchEnginePreferences", "click", function(event) {
 | |
|       if (event.button == 0) {
 | |
|         gotoPref("search");
 | |
|       }
 | |
|       return false;
 | |
|     });
 | |
|     setEventListener("privateBrowsingAutoStart", "command",
 | |
|       gPrivacyPane.updateAutostart);
 | |
|     setEventListener("cookieExceptions", "command",
 | |
|       gPrivacyPane.showCookieExceptions);
 | |
|     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._initMasterPasswordUI();
 | |
|     this._initSafeBrowsing();
 | |
| 
 | |
|     setEventListener("notificationSettingsButton", "command",
 | |
|       gPrivacyPane.showNotificationExceptions);
 | |
|     setEventListener("locationSettingsButton", "command",
 | |
|       gPrivacyPane.showLocationExceptions);
 | |
|     setEventListener("cameraSettingsButton", "command",
 | |
|       gPrivacyPane.showCameraExceptions);
 | |
|     setEventListener("microphoneSettingsButton", "command",
 | |
|       gPrivacyPane.showMicrophoneExceptions);
 | |
|     setEventListener("popupPolicyButton", "command",
 | |
|       gPrivacyPane.showPopupExceptions);
 | |
|     setEventListener("autoplayMediaCheckbox", "command",
 | |
|       gPrivacyPane.toggleAutoplayMedia);
 | |
|     setEventListener("autoplayMediaPolicyButton", "command",
 | |
|       gPrivacyPane.showAutoplayMediaExceptions);
 | |
|     setEventListener("autoplayMediaPolicyComboboxButton", "command",
 | |
|       gPrivacyPane.showAutoplayMediaExceptions);
 | |
|     setEventListener("notificationsDoNotDisturb", "command",
 | |
|       gPrivacyPane.toggleDoNotDisturbNotifications);
 | |
| 
 | |
|     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);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     this.initSiteDataControls();
 | |
|     setEventListener("clearSiteDataButton", "command",
 | |
|       gPrivacyPane.clearSiteData);
 | |
|     setEventListener("siteDataSettings", "command",
 | |
|       gPrivacyPane.showSiteDataSettings);
 | |
|     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "storage-permissions";
 | |
|     document.getElementById("siteDataLearnMoreLink").setAttribute("href", url);
 | |
| 
 | |
|     let notificationInfoURL =
 | |
|       Services.urlFormatter.formatURLPref("app.support.baseURL") + "push";
 | |
|     document.getElementById("notificationPermissionsLearnMore").setAttribute("href",
 | |
|       notificationInfoURL);
 | |
|     let drmInfoURL =
 | |
|       Services.urlFormatter.formatURLPref("app.support.baseURL") + "drm-content";
 | |
|     document.getElementById("playDRMContentLink").setAttribute("href", drmInfoURL);
 | |
|     let emeUIEnabled = Services.prefs.getBoolPref("browser.eme.ui.enabled");
 | |
|     // Force-disable/hide on WinXP:
 | |
|     if (navigator.platform.toLowerCase().startsWith("win")) {
 | |
|       emeUIEnabled = emeUIEnabled && parseFloat(Services.sysinfo.get("version")) >= 6;
 | |
|     }
 | |
|     if (!emeUIEnabled) {
 | |
|       // Don't want to rely on .hidden for the toplevel groupbox because
 | |
|       // of the pane hiding/showing code potentially interfering:
 | |
|       document.getElementById("drmGroup").setAttribute("style", "display: none !important");
 | |
|     }
 | |
| 
 | |
|     if (AppConstants.MOZ_DATA_REPORTING) {
 | |
|       this.initDataCollection();
 | |
|       if (AppConstants.NIGHTLY_BUILD) {
 | |
|         this.initCollectBrowserErrors();
 | |
|       }
 | |
|       if (AppConstants.MOZ_CRASHREPORTER) {
 | |
|         this.initSubmitCrashes();
 | |
|       }
 | |
|       this.initSubmitHealthReport();
 | |
|       setEventListener("submitHealthReportBox", "command",
 | |
|         gPrivacyPane.updateSubmitHealthReport);
 | |
|       this.initOptOutStudyCheckbox();
 | |
|     }
 | |
|     this._initA11yState();
 | |
|     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;
 | |
|     }
 | |
| 
 | |
|     // 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() {
 | |
|     let contentBlockingCheckbox = document.getElementById("contentBlockingCheckbox");
 | |
|     setEventListener("contentBlockingToggle", "command",
 | |
|       () => contentBlockingCheckbox.click());
 | |
|     setEventListener("contentBlockingToggle", "command",
 | |
|       this.updateContentBlockingControls);
 | |
|     setEventListener("changeBlockListLink", "click", this.showBlockLists);
 | |
|     setEventListener("contentBlockingRestoreDefaults", "command",
 | |
|       this.restoreContentBlockingPrefs);
 | |
|     setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
 | |
|       this.trackingProtectionWritePrefs);
 | |
|     setEventListener("contentBlockingTrackingProtectionCheckbox", "command",
 | |
|       this._updateTrackingProtectionUI);
 | |
|     setEventListener("trackingProtectionMenu", "command",
 | |
|       this.trackingProtectionWritePrefs);
 | |
|     setEventListener("contentBlockingChangeCookieSettings", "command",
 | |
|       this.changeCookieSettings);
 | |
|     setEventListener("contentBlockingBlockCookiesCheckbox", "command",
 | |
|       this.writeBlockCookiesCheckbox);
 | |
| 
 | |
|     Preferences.get("network.cookie.cookieBehavior").on("change",
 | |
|       gPrivacyPane.readBlockCookiesCheckbox.bind(gPrivacyPane));
 | |
| 
 | |
|     this.readBlockCookiesCheckbox();
 | |
| 
 | |
|     let link = document.getElementById("contentBlockingLearnMore");
 | |
|     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
 | |
|     link.setAttribute("href", url);
 | |
| 
 | |
|     // Honour our Content Blocking category UI prefs. If each pref is set to false,
 | |
|     // Make all descendants of the corresponding selector hidden.
 | |
|     let selectorPrefMap = {
 | |
|       ".fast-block-ui": contentBlockingFastBlockUiEnabled,
 | |
|       ".tracking-protection-ui": contentBlockingTrackingProtectionUiEnabled,
 | |
|       ".reject-trackers-ui": contentBlockingRejectTrackersUiEnabled,
 | |
|     };
 | |
| 
 | |
|     for (let selector in selectorPrefMap) {
 | |
|       let pref = selectorPrefMap[selector];
 | |
|       if (!pref) {
 | |
|         let elements = document.querySelectorAll(selector);
 | |
|         for (let element of elements) {
 | |
|           element.hidden = true;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Allow turning off the "(recommended)" label using a pref
 | |
|     let blockCookiesFromTrackers = document.getElementById("blockCookiesFromTrackersCB");
 | |
|     if (contentBlockingRejectTrackersRecommended) {
 | |
|       document.l10n.setAttributes(blockCookiesFromTrackers, "content-blocking-reject-trackers-block-trackers-option-recommended");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Resets all user-exposed content blocking preferences to their default values.
 | |
|    */
 | |
|   async restoreContentBlockingPrefs() {
 | |
|     function clearIfNotLocked(pref) {
 | |
|       if (!Services.prefs.prefIsLocked(pref)) {
 | |
|         Services.prefs.clearUserPref(pref);
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     clearIfNotLocked("browser.contentblocking.enabled");
 | |
|     clearIfNotLocked("browser.fastblock.enabled");
 | |
|     clearIfNotLocked("urlclassifier.trackingTable");
 | |
|     clearIfNotLocked("network.cookie.cookieBehavior");
 | |
|     clearIfNotLocked("network.cookie.lifetimePolicy");
 | |
| 
 | |
|     let controllingExtension = await getControllingExtension(
 | |
|       PREF_SETTING_TYPE, TRACKING_PROTECTION_KEY);
 | |
|     if (!controllingExtension) {
 | |
|       for (let preference of TRACKING_PROTECTION_PREFS) {
 | |
|         clearIfNotLocked(preference);
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Highlights the Cookies & Site Data UI section.
 | |
|    */
 | |
|   changeCookieSettings() {
 | |
|     gotoPref("privacy-sitedata");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Changes the visibility of elements in the TP/CB section depending on the
 | |
|    * content blocking UI pref.
 | |
|    */
 | |
|   updateContentBlockingVisibility() {
 | |
|     // First, update the content blocking UI.
 | |
|     let visibleState = {
 | |
|       "contentBlockingHeader": true,
 | |
|       "contentBlockingDescription": true,
 | |
|       "contentBlockingLearnMore": true,
 | |
|       "contentBlockingRestoreDefaults": true,
 | |
|       "contentBlockingCheckboxContainer": true,
 | |
|       "contentBlockingCategories": true,
 | |
| 
 | |
|       "trackingProtectionHeader": false,
 | |
|       "trackingProtectionDescription": false,
 | |
|       "trackingProtectionBox": false,
 | |
|     };
 | |
|     for (let id in visibleState) {
 | |
|       document.getElementById(id).hidden = contentBlockingUiEnabled != visibleState[id];
 | |
|     }
 | |
| 
 | |
|     if (contentBlockingUiEnabled) {
 | |
|       // Update the Do Not Track section to not mention "Tracking Protection".
 | |
|       let dntDefaultRadioItem =
 | |
|         document.querySelector("#doNotTrackRadioGroup > radio[value=false]");
 | |
|       document.l10n.setAttributes(
 | |
|         dntDefaultRadioItem, "do-not-track-option-default-content-blocking");
 | |
| 
 | |
|       // Potentially hide the global toggle.
 | |
|       document.getElementById("contentBlockingCheckboxContainer").hidden =
 | |
|         !Services.prefs.getBoolPref("browser.contentblocking.global-toggle.enabled", true);
 | |
|     }
 | |
| 
 | |
|     // Allow turning off the "(recommended)" label using a pref
 | |
|     let blockCookiesFromTrackers = document.getElementById("blockCookiesFromTrackers");
 | |
|     if (contentBlockingCookiesAndSiteDataRejectTrackersRecommended) {
 | |
|       document.l10n.setAttributes(blockCookiesFromTrackers, "sitedata-block-trackers-option-recommended");
 | |
|     }
 | |
| 
 | |
|     // Allow hiding the Reject Trackers option based on a pref
 | |
|     if (!contentBlockingCookiesAndSiteDataRejectTrackersEnabled) {
 | |
|       blockCookiesFromTrackers.remove();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Updates the preferences UI to reflect the browser.contentblocking.enabled pref.
 | |
|    * This affects the button to toggle the pref and the disabled state of the dependent controls.
 | |
|    */
 | |
|   updateContentBlockingToggle() {
 | |
|     let onOrOff = contentBlockingEnabled ? "on" : "off";
 | |
|     let contentBlockingToggle = document.getElementById("contentBlockingToggle");
 | |
|     let contentBlockingToggleLabel = document.getElementById("contentBlockingToggleLabel");
 | |
| 
 | |
|     document.l10n.setAttributes(contentBlockingToggle,
 | |
|       "content-blocking-toggle-" + onOrOff);
 | |
|     contentBlockingToggle.setAttribute("aria-pressed", contentBlockingEnabled);
 | |
|     document.l10n.setAttributes(contentBlockingToggleLabel,
 | |
|       "content-blocking-toggle-label-" + onOrOff);
 | |
| 
 | |
|     this.updateContentBlockingControls();
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Changes the disabled state of controls that depend on the browser.contentblocking.enabled pref.
 | |
|    */
 | |
|   updateContentBlockingControls() {
 | |
|     let dependentControls = [
 | |
|       "#content-blocking-categories-label",
 | |
|       ".content-blocking-checkbox",
 | |
|       "#changeBlockListLink",
 | |
|       "#contentBlockingChangeCookieSettings",
 | |
|       "#blockCookiesCB, #blockCookiesCB > radio",
 | |
|       "#blockCookies, #blockCookies > radio",
 | |
|     ];
 | |
| 
 | |
|     this._toggleControls(dependentControls, contentBlockingEnabled);
 | |
| 
 | |
|     // The list of dependent controls here would normally include #blockCookiesLabel,
 | |
|     // #blockCookiesMenu, #keepUntil and #keepCookiesUntil, but in order to avoid
 | |
|     // having two parts of the code responsible for figuring out whether these
 | |
|     // controls must be enabled or disabled, we offload that responsibility to
 | |
|     // networkCookieBehaviorReadPrefs() which already knows how to deal with it.
 | |
|     this.networkCookieBehaviorReadPrefs();
 | |
| 
 | |
|     // If Content Blocking gets disabled, show the warning in the Cookies and Site Data section.
 | |
|     let blockCookiesWarning = document.getElementById("blockCookiesWarning");
 | |
|     blockCookiesWarning.hidden = contentBlockingEnabled;
 | |
| 
 | |
|     // Need to make sure we account for pref locking/extension overrides when enabling the TP menu.
 | |
|     this._updateTrackingProtectionUI();
 | |
| 
 | |
|     // If we are turning Content Blocking on, we may need to keep some parts of the Third-Party Cookies
 | |
|     // UI off, depending on the value of the cookieBehavior pref.  readBlockCookiesCheckbox() can do
 | |
|     // the work that is needed for that.
 | |
|     this.readBlockCookiesCheckbox();
 | |
|   },
 | |
| 
 | |
|   _toggleControls(dependentControls, enabled) {
 | |
|     for (let selector of dependentControls) {
 | |
|       let controls = document.querySelectorAll(selector);
 | |
| 
 | |
|       for (let control of controls) {
 | |
|         if (enabled) {
 | |
|           control.removeAttribute("disabled");
 | |
|         } else {
 | |
|           control.setAttribute("disabled", "true");
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // TRACKING PROTECTION MODE
 | |
| 
 | |
|   /**
 | |
|    * Selects the right item of the Tracking Protection radiogroup.
 | |
|    */
 | |
|   trackingProtectionReadPrefs() {
 | |
|     let enabledPref = Preferences.get("privacy.trackingprotection.enabled");
 | |
|     let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled");
 | |
|     let btpmPref = Preferences.get("browser.privacy.trackingprotection.menu");
 | |
|     let tpControl,
 | |
|         tpCheckbox;
 | |
|     if (contentBlockingUiEnabled) {
 | |
|       tpControl = document.getElementById("trackingProtectionMenu");
 | |
|       tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
 | |
|     } else {
 | |
|       tpControl = document.getElementById("trackingProtectionRadioGroup");
 | |
|     }
 | |
| 
 | |
|     let savedMenuValue;
 | |
|     if (contentBlockingUiEnabled) {
 | |
|       // Only look at the backup pref when restoring the checkbox next to
 | |
|       // "All Detected Trackers".
 | |
|       if (["always", "private"].includes(btpmPref.value) &&
 | |
|           tpCheckbox.checked) {
 | |
|         savedMenuValue = btpmPref.value;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     this._updateTrackingProtectionUI();
 | |
| 
 | |
|     // Global enable takes precedence over enabled in Private Browsing.
 | |
|     if (enabledPref.value) {
 | |
|       tpControl.value = "always";
 | |
|       if (tpCheckbox) {
 | |
|         tpCheckbox.checked = true;
 | |
|       }
 | |
|     } else if (pbmPref.value) {
 | |
|       tpControl.value = "private";
 | |
|       if (tpCheckbox) {
 | |
|         tpCheckbox.checked = true;
 | |
|       }
 | |
|     } else if (!tpCheckbox) {
 | |
|       tpControl.value = "never";
 | |
|     } else {
 | |
|       if (savedMenuValue) {
 | |
|         tpControl.value = savedMenuValue;
 | |
|       }
 | |
|       tpCheckbox.checked = false;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Selects the right items of the new Cookies & Site Data UI.
 | |
|    */
 | |
|   networkCookieBehaviorReadPrefs() {
 | |
|     let behavior = Preferences.get("network.cookie.cookieBehavior").value;
 | |
|     let blockCookiesCtrl = document.getElementById("blockCookies");
 | |
|     let blockCookiesLabel = document.getElementById("blockCookiesLabel");
 | |
|     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
 | |
|     let keepUntilLabel = document.getElementById("keepUntil");
 | |
|     let keepUntilMenu = document.getElementById("keepCookiesUntil");
 | |
| 
 | |
|     let disabledByCB = contentBlockingUiEnabled ? !contentBlockingEnabled : false;
 | |
|     let blockCookies = (behavior != 0);
 | |
|     let cookieBehaviorLocked = Services.prefs.prefIsLocked("network.cookie.cookieBehavior");
 | |
|     let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked || disabledByCB;
 | |
|     blockCookiesLabel.disabled = blockCookiesMenu.disabled = blockCookiesControlsDisabled;
 | |
| 
 | |
|     let completelyBlockCookies = (behavior == 2);
 | |
|     let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
 | |
|     let cookieExpirationLocked = Services.prefs.prefIsLocked("network.cookie.lifetimePolicy");
 | |
|     let keepUntilControlsDisabled = privateBrowsing || completelyBlockCookies ||
 | |
|                                     cookieExpirationLocked || disabledByCB;
 | |
|     keepUntilLabel.disabled = keepUntilMenu.disabled = keepUntilControlsDisabled;
 | |
| 
 | |
|     switch (behavior) {
 | |
|       case Ci.nsICookieService.BEHAVIOR_ACCEPT:
 | |
|         blockCookiesCtrl.value = "allow";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|         blockCookiesCtrl.value = "disallow";
 | |
|         blockCookiesMenu.value = "all-third-parties";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|         blockCookiesCtrl.value = "disallow";
 | |
|         blockCookiesMenu.value = "always";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|         blockCookiesCtrl.value = "disallow";
 | |
|         blockCookiesMenu.value = "unvisited";
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|         blockCookiesCtrl.value = "disallow";
 | |
|         blockCookiesMenu.value = "trackers";
 | |
|         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 btpmPref = Preferences.get("browser.privacy.trackingprotection.menu");
 | |
|     let tpControl,
 | |
|         tpCheckbox;
 | |
|     if (contentBlockingUiEnabled) {
 | |
|       tpControl = document.getElementById("trackingProtectionMenu");
 | |
|       tpCheckbox = document.getElementById("contentBlockingTrackingProtectionCheckbox");
 | |
|     } else {
 | |
|       tpControl = document.getElementById("trackingProtectionRadioGroup");
 | |
|     }
 | |
| 
 | |
|     let value;
 | |
|     if (tpCheckbox) {
 | |
|       if (tpCheckbox.checked) {
 | |
|         value = tpControl.value;
 | |
|         btpmPref.value = value;
 | |
|       } else {
 | |
|         value = "never";
 | |
|       }
 | |
|     } else {
 | |
|       value = tpControl.value;
 | |
|     }
 | |
| 
 | |
|     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;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // 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() {
 | |
|     // Set "Keep cookies until..." to "I close Nightly" and disable the setting
 | |
|     // when we're in auto private mode (or reset it back otherwise).
 | |
|     document.getElementById("keepCookiesUntil").value = this.readKeepCookiesUntil();
 | |
| 
 | |
|     let clearDataSettings = document.getElementById("clearDataSettings");
 | |
| 
 | |
|     if (document.getElementById("historyMode").value == "custom") {
 | |
|       let disabled = Preferences.get("browser.privatebrowsing.autostart").value;
 | |
|       this.dependentControls.forEach(function(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;
 | |
|       });
 | |
| 
 | |
|       clearDataSettings.removeAttribute("hidden");
 | |
| 
 | |
|       // adjust the checked state of the sanitizeOnShutdown checkbox
 | |
|       document.getElementById("alwaysClear").checked = disabled ? false :
 | |
|         Preferences.get("privacy.sanitize.sanitizeOnShutdown").value;
 | |
| 
 | |
|       // adjust the checked state of the remember history checkboxes
 | |
|       document.getElementById("rememberHistory").checked = disabled ? false :
 | |
|         Preferences.get("places.history.enabled").value;
 | |
|       document.getElementById("rememberForms").checked = disabled ? false :
 | |
|         Preferences.get("browser.formfill.enable").value;
 | |
| 
 | |
|       if (!disabled) {
 | |
|         // adjust the Settings button for sanitizeOnShutdown
 | |
|         this._updateSanitizeSettingsButton();
 | |
|       }
 | |
|     } else {
 | |
|       clearDataSettings.setAttribute("hidden", "true");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // 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() {
 | |
|     gSubDialog.open("chrome://browser/content/preferences/sanitize.xul", "resizable=no");
 | |
|   },
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * 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;
 | |
|     }
 | |
| 
 | |
|     gSubDialog.open("chrome://browser/content/sanitize.xul", "resizable=no", null, () => {
 | |
|       // reset the timeSpan pref
 | |
|       if (aClearEverything) {
 | |
|         ts.value = timeSpanOrig;
 | |
|       }
 | |
| 
 | |
|       Services.obs.notifyObservers(null, "clear-private-data");
 | |
|     });
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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() {
 | |
|     let mode = document.getElementById("historyMode");
 | |
|     let autoStart = document.getElementById("privateBrowsingAutoStart");
 | |
|     this._lastMode = mode.selectedIndex;
 | |
|     this._lastCheckState = autoStart.hasAttribute("checked");
 | |
|   },
 | |
| 
 | |
|   _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",
 | |
|       hideStatusColumn: true,
 | |
|     };
 | |
|     gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
 | |
|       null, params);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the available block lists for tracking protection.
 | |
|    */
 | |
|   showBlockLists() {
 | |
|     gSubDialog.open("chrome://browser/content/preferences/blocklists.xul", null);
 | |
|   },
 | |
| 
 | |
|   // 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
 | |
|    *         see netwerk/cookie/src/nsCookieService.cpp for details
 | |
|    * network.cookie.lifetimePolicy
 | |
|    * - determines how long cookies are stored:
 | |
|    *     0   means keep cookies until they expire
 | |
|    *     2   means keep cookies until the browser is closed
 | |
|    */
 | |
| 
 | |
|   readKeepCookiesUntil() {
 | |
|     let privateBrowsing = Preferences.get("browser.privatebrowsing.autostart").value;
 | |
|     if (privateBrowsing) {
 | |
|       return Ci.nsICookieService.ACCEPT_SESSION;
 | |
|     }
 | |
| 
 | |
|     let lifetimePolicy = Preferences.get("network.cookie.lifetimePolicy").value;
 | |
|     if (lifetimePolicy == Ci.nsICookieService.ACCEPT_SESSION) {
 | |
|       return Ci.nsICookieService.ACCEPT_SESSION;
 | |
|     }
 | |
| 
 | |
|     // network.cookie.lifetimePolicy can be set to any value, but we just
 | |
|     // support ACCEPT_SESSION and ACCEPT_NORMALLY. Let's force ACCEPT_NORMALLY.
 | |
|     return Ci.nsICookieService.ACCEPT_NORMALLY;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Reads the network.cookie.cookieBehavior preference value and
 | |
|    * enables/disables the rest of the new cookie & site data UI accordingly.
 | |
|    *
 | |
|    * Returns "allow" if cookies are accepted and "disallow" if they are entirely
 | |
|    * disabled.
 | |
|    */
 | |
|   readBlockCookies() {
 | |
|     // enable the rest of the UI for anything other than "accept all cookies"
 | |
|     let pref = Preferences.get("network.cookie.cookieBehavior");
 | |
|     let blockCookies = (pref.value != 0);
 | |
| 
 | |
|     // Our top-level setting is a radiogroup that only sets "enable all"
 | |
|     // and "disable all", so convert the pref value accordingly.
 | |
|     return blockCookies ? "disallow" : "allow";
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Updates the "accept third party cookies" menu based on whether the
 | |
|    * "accept cookies" or "block cookies" radio buttons are selected.
 | |
|    */
 | |
|   writeBlockCookies() {
 | |
|     let block = document.getElementById("blockCookies");
 | |
|     let blockCookiesMenu = document.getElementById("blockCookiesMenu");
 | |
| 
 | |
|     // if we're disabling cookies, automatically select 'third-party trackers'
 | |
|     if (block.value == "disallow") {
 | |
|       blockCookiesMenu.selectedIndex = 0;
 | |
|       return this.writeBlockCookiesFrom();
 | |
|     }
 | |
| 
 | |
|     return Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|   },
 | |
| 
 | |
|   enableThirdPartyCookiesUI() {
 | |
|     document.getElementById("blockCookiesCBDeck").selectedIndex = 0;
 | |
|     document.getElementById("contentBlockingChangeCookieSettings").hidden = true;
 | |
| 
 | |
|     let dependentControls = [
 | |
|       ".reject-trackers-ui .content-blocking-checkbox",
 | |
|       "#blockCookiesCB, #blockCookiesCB > radio",
 | |
|       "#blockCookiesCBDeck",
 | |
|     ];
 | |
| 
 | |
|     this._toggleControls(dependentControls, contentBlockingEnabled);
 | |
|   },
 | |
| 
 | |
|   disableThirdPartyCookiesUI(reason) {
 | |
|     let deckIndex = 0;
 | |
|     switch (reason) {
 | |
|       case "always":
 | |
|         deckIndex = 1;
 | |
|         break;
 | |
|       case "unvisited":
 | |
|         deckIndex = 2;
 | |
|         break;
 | |
|     }
 | |
|     document.getElementById("blockCookiesCBDeck").selectedIndex = deckIndex;
 | |
|     document.getElementById("contentBlockingChangeCookieSettings").hidden = false;
 | |
| 
 | |
|     let dependentControls = [
 | |
|       ".reject-trackers-ui .content-blocking-checkbox",
 | |
|       "#blockCookiesCB, #blockCookiesCB > radio",
 | |
|       "#blockCookiesCBDeck",
 | |
|     ];
 | |
| 
 | |
|     this._toggleControls(dependentControls, false);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Converts between network.cookie.cookieBehavior and the new content blocking UI
 | |
|    */
 | |
|   readBlockCookiesCB() {
 | |
|     let pref = Preferences.get("network.cookie.cookieBehavior");
 | |
|     switch (pref.value) {
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|         return "all-third-parties";
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|         return "trackers";
 | |
|       default:
 | |
|         return undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   writeBlockCookiesCB() {
 | |
|     let block = document.getElementById("blockCookiesCB").selectedItem;
 | |
|     switch (block.value) {
 | |
|       case "trackers":
 | |
|         return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER;
 | |
|       case "all-third-parties":
 | |
|         return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN;
 | |
|       default:
 | |
|         return undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   writeBlockCookiesCheckbox() {
 | |
|     let pref = Preferences.get("network.cookie.cookieBehavior");
 | |
|     let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
 | |
|     let bcControl = document.getElementById("blockCookiesCB");
 | |
| 
 | |
|     let value;
 | |
|     if (bcCheckbox.checked) {
 | |
|       value = bcControl.selectedItem.value;
 | |
|     } else {
 | |
|       value = "none";
 | |
|     }
 | |
| 
 | |
|     switch (value) {
 | |
|       case "trackers":
 | |
|       case "all-third-parties":
 | |
|         bcControl.disabled = false;
 | |
|         pref.value = this.writeBlockCookiesCB();
 | |
|         break;
 | |
|       default:
 | |
|         bcControl.disabled = true;
 | |
|         pref.value = Ci.nsICookieService.BEHAVIOR_ACCEPT;
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   readBlockCookiesCheckbox() {
 | |
|     let pref = Preferences.get("network.cookie.cookieBehavior");
 | |
|     let bcCheckbox = document.getElementById("contentBlockingBlockCookiesCheckbox");
 | |
|     let bcControl = document.getElementById("blockCookiesCB");
 | |
| 
 | |
|     switch (pref.value) {
 | |
|       case Ci.nsICookieService.BEHAVIOR_ACCEPT:
 | |
|         this.enableThirdPartyCookiesUI();
 | |
|         bcCheckbox.checked = false;
 | |
|         bcControl.disabled = true;
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT:
 | |
|         this.disableThirdPartyCookiesUI("always");
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN:
 | |
|         this.disableThirdPartyCookiesUI("unvisited");
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN:
 | |
|         this.enableThirdPartyCookiesUI();
 | |
|         bcCheckbox.checked = true;
 | |
|         bcControl.disabled = !contentBlockingEnabled;
 | |
|         break;
 | |
|       case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER:
 | |
|         this.enableThirdPartyCookiesUI();
 | |
|         bcCheckbox.checked = true;
 | |
|         bcControl.disabled = !contentBlockingEnabled;
 | |
|         break;
 | |
|       default:
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Converts between network.cookie.cookieBehavior and the new third-party cookies UI
 | |
|    */
 | |
|   readBlockCookiesFrom() {
 | |
|     let pref = Preferences.get("network.cookie.cookieBehavior");
 | |
|     switch (pref.value) {
 | |
|       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";
 | |
|       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;
 | |
|       default:
 | |
|         return undefined;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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/permissions.xul",
 | |
|       null, params);
 | |
|   },
 | |
| 
 | |
|   showSiteDataSettings() {
 | |
|     gSubDialog.open("chrome://browser/content/preferences/siteDataSettings.xul");
 | |
|   },
 | |
| 
 | |
|   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() {
 | |
|     gSubDialog.open("chrome://browser/content/preferences/clearSiteData.xul");
 | |
|   },
 | |
| 
 | |
|   // 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/sitePermissions.xul",
 | |
|       "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/sitePermissions.xul",
 | |
|       "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/sitePermissions.xul",
 | |
|       "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/sitePermissions.xul",
 | |
|       "resizable=yes", params);
 | |
| 
 | |
|     try {
 | |
|       Services.telemetry
 | |
|         .getHistogramById("WEB_NOTIFICATION_EXCEPTIONS_OPENED").add();
 | |
|     } catch (e) { }
 | |
|   },
 | |
| 
 | |
| 
 | |
|   // MEDIA
 | |
| 
 | |
|   initAutoplay() {
 | |
|     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") +
 | |
|       "block-autoplay";
 | |
|     document.getElementById("autoplayLearnMoreLink").setAttribute("href", url);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * The checkbox enabled sets the pref to BLOCKED
 | |
|    */
 | |
|   toggleAutoplayMedia(event) {
 | |
|     let blocked = event.target.checked ? Ci.nsIAutoplay.BLOCKED : Ci.nsIAutoplay.ALLOWED;
 | |
|     Services.prefs.setIntPref("media.autoplay.default", blocked);
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * If user-gestures-needed is false we do not show any UI for configuring autoplay,
 | |
|    * if user-gestures-needed is false and ask-permission is false we show a checkbox
 | |
|    * which only allows the user to block autoplay
 | |
|    * if user-gestures-needed and ask-permission are true we show a combobox that
 | |
|    * allows the user to block / allow or prompt for autoplay
 | |
|    * We will be performing a shield study to determine the behaviour to be
 | |
|    * shipped, at which point we can remove these pref switches.
 | |
|    * https://bugzilla.mozilla.org/show_bug.cgi?id=1475099
 | |
|    */
 | |
|   updateAutoplayMediaControlsVisibility() {
 | |
|     let askPermission =
 | |
|       Services.prefs.getBoolPref("media.autoplay.ask-permission", false);
 | |
|     let userGestures =
 | |
|         Services.prefs.getBoolPref("media.autoplay.enabled.user-gestures-needed", false);
 | |
|     // Hide the combobox if we don't let the user ask for permission.
 | |
|     document.getElementById("autoplayMediaComboboxWrapper").hidden =
 | |
|       !userGestures || !askPermission;
 | |
|     // If the user may ask for permission, hide the checkbox instead.
 | |
|     document.getElementById("autoplayMediaCheckboxWrapper").hidden =
 | |
|       !userGestures || askPermission;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the autoplay exceptions dialog where specific site autoplay preferences
 | |
|    * can be set.
 | |
|    */
 | |
|   showAutoplayMediaExceptions() {
 | |
|     var params = {
 | |
|       blockVisible: true, sessionVisible: false, allowVisible: true,
 | |
|       prefilledHost: "", permissionType: "autoplay-media",
 | |
|     };
 | |
| 
 | |
|     gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
 | |
|       "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/permissions.xul",
 | |
|       "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;
 | |
|     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/permissions.xul",
 | |
|       null, 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.isMasterPasswordSet();
 | |
| 
 | |
|     var button = document.getElementById("changeMasterPassword");
 | |
|     button.disabled = noMP;
 | |
| 
 | |
|     var checkbox = document.getElementById("useMasterPassword");
 | |
|     checkbox.checked = !noMP;
 | |
|     checkbox.disabled = noMP && !Services.policies.isAllowed("createMasterPassword");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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.
 | |
|    */
 | |
|   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)
 | |
|       this._removeMasterPassword();
 | |
|     else
 | |
|       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.
 | |
|    */
 | |
|   _removeMasterPassword() {
 | |
|     var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].
 | |
|       getService(Ci.nsIPKCS11ModuleDB);
 | |
|     if (secmodDB.isFIPSEnabled) {
 | |
|       var bundle = document.getElementById("bundlePreferences");
 | |
|       Services.prompt.alert(window,
 | |
|         bundle.getString("pw_change_failed_title"),
 | |
|         bundle.getString("pw_change2empty_in_fips_mode"));
 | |
|       this._initMasterPasswordUI();
 | |
|     } else {
 | |
|       gSubDialog.open("chrome://mozapps/content/preferences/removemp.xul",
 | |
|         null, null, this._initMasterPasswordUI.bind(this));
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog in which the master password may be changed.
 | |
|    */
 | |
|   changeMasterPassword() {
 | |
|     gSubDialog.open("chrome://mozapps/content/preferences/changemp.xul",
 | |
|       "resizable=no", null, this._initMasterPasswordUI.bind(this));
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|  * Shows the sites where the user has saved passwords and the associated login
 | |
|  * information.
 | |
|  */
 | |
|   showPasswords() {
 | |
|     gSubDialog.open("chrome://passwordmgr/content/passwordManager.xul");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Enables/disables the Exceptions button used to configure sites where
 | |
|    * passwords are never saved. When browser is set to start in Private
 | |
|    * Browsing mode, the "Remember passwords" UI is useless, so we disable it.
 | |
|    */
 | |
|   readSavePasswords() {
 | |
|     var pref = Preferences.get("signon.rememberSignons");
 | |
|     var excepts = document.getElementById("passwordExceptions");
 | |
| 
 | |
|     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
 | |
|       document.getElementById("savePasswords").disabled = true;
 | |
|       excepts.disabled = true;
 | |
|       return false;
 | |
|     }
 | |
|     excepts.disabled = !pref.value;
 | |
|     // don't override pref value in UI
 | |
|     return undefined;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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;
 | |
| 
 | |
|     // 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");
 | |
| 
 | |
|     let learnMoreLink = document.getElementById("enableSafeBrowsingLearnMore");
 | |
|     let phishingUrl = Services.urlFormatter.formatURLPref("app.support.baseURL") + "phishing-malware";
 | |
|     learnMoreLink.setAttribute("href", phishingUrl);
 | |
| 
 | |
|     enableSafeBrowsing.addEventListener("command", function() {
 | |
|       safeBrowsingPhishingPref.value = enableSafeBrowsing.checked;
 | |
|       safeBrowsingMalwarePref.value = enableSafeBrowsing.checked;
 | |
| 
 | |
|       if (enableSafeBrowsing.checked) {
 | |
|         if (blockDownloads) {
 | |
|           blockDownloads.removeAttribute("disabled");
 | |
|           if (blockDownloads.checked) {
 | |
|             blockUncommonUnwanted.removeAttribute("disabled");
 | |
|           }
 | |
|         } else {
 | |
|           blockUncommonUnwanted.removeAttribute("disabled");
 | |
|         }
 | |
|       } else {
 | |
|         if (blockDownloads) {
 | |
|           blockDownloads.setAttribute("disabled", "true");
 | |
|         }
 | |
|         blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     if (blockDownloads) {
 | |
|       blockDownloads.addEventListener("command", function() {
 | |
|         blockDownloadsPref.value = blockDownloads.checked;
 | |
|         if (blockDownloads.checked) {
 | |
|           blockUncommonUnwanted.removeAttribute("disabled");
 | |
|         } else {
 | |
|           blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     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 !== "test-unwanted-simple");
 | |
| 
 | |
|       if (blockUncommonUnwanted.checked) {
 | |
|         if (malware.includes("goog-malware-shavar")) {
 | |
|           malware.push("goog-unwanted-shavar");
 | |
|         } else {
 | |
|           malware.push("goog-unwanted-proto");
 | |
|         }
 | |
| 
 | |
|         malware.push("test-unwanted-simple");
 | |
|       }
 | |
| 
 | |
|       // sort alphabetically to keep the pref consistent
 | |
|       malware.sort();
 | |
| 
 | |
|       malwareTable.value = malware.join(",");
 | |
| 
 | |
|       // Force an update after changing the malware table.
 | |
|       let listmanager = Cc["@mozilla.org/url-classifier/listmanager;1"]
 | |
|                         .getService(Ci.nsIUrlListManager);
 | |
|       if (listmanager) {
 | |
|         listmanager.forceUpdates(malwareTable.value);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     // set initial values
 | |
| 
 | |
|     enableSafeBrowsing.checked = safeBrowsingPhishingPref.value && safeBrowsingMalwarePref.value;
 | |
|     if (!enableSafeBrowsing.checked) {
 | |
|       if (blockDownloads) {
 | |
|         blockDownloads.setAttribute("disabled", "true");
 | |
|       }
 | |
| 
 | |
|       blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|     }
 | |
| 
 | |
|     if (blockDownloads) {
 | |
|       blockDownloads.checked = blockDownloadsPref.value;
 | |
|       if (!blockDownloadsPref.value) {
 | |
|         blockUncommonUnwanted.setAttribute("disabled", "true");
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     blockUncommonUnwanted.checked = blockUnwantedPref.value && blockUncommonPref.value;
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays the exceptions lists for add-on installation warnings.
 | |
|    */
 | |
|   showAddonExceptions() {
 | |
|     var params = this._addonParams;
 | |
| 
 | |
|     gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
 | |
|       null, 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.xul");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * Displays a dialog from which the user can manage his security devices.
 | |
|    */
 | |
|   showSecurityDevices() {
 | |
|     gSubDialog.open("chrome://pippki/content/device_manager.xul");
 | |
|   },
 | |
| 
 | |
|   initDataCollection() {
 | |
|     this._setupLearnMoreLink("toolkit.datacollection.infoURL",
 | |
|       "dataCollectionPrivacyNotice");
 | |
|   },
 | |
| 
 | |
|   initCollectBrowserErrors() {
 | |
|     this._setupLearnMoreLink("browser.chrome.errorReporter.infoURL",
 | |
|       "collectBrowserErrorsLearnMore");
 | |
|   },
 | |
| 
 | |
|   initSubmitCrashes() {
 | |
|     this._setupLearnMoreLink("toolkit.crashreporter.infoURL",
 | |
|       "crashReporterLearnMore");
 | |
|   },
 | |
| 
 | |
|   /**
 | |
|    * 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.setAttribute("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");
 | |
|     Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked);
 | |
|   },
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Initialize the opt-out-study preference checkbox into about:preferences and
 | |
|    * handles events coming from the UI for it.
 | |
|    */
 | |
|   initOptOutStudyCheckbox(doc) {
 | |
|     const allowedByPolicy = Services.policies.isAllowed("Shield");
 | |
|     const checkbox = document.getElementById("optOutStudiesEnabled");
 | |
| 
 | |
|     function updateStudyCheckboxState() {
 | |
|       // 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
 | |
|       // * the Shield Study preference is locked
 | |
|       // * the FHR pref is false
 | |
|       //
 | |
|       // 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
 | |
|       // * the FHR pref is true
 | |
|       // * Normandy is enabled
 | |
| 
 | |
|       const checkboxMatchesPref = (
 | |
|         allowedByPolicy &&
 | |
|         Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false) &&
 | |
|         Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false)
 | |
|       );
 | |
| 
 | |
|       if (checkboxMatchesPref) {
 | |
|         if (Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false)) {
 | |
|           checkbox.setAttribute("checked", "checked");
 | |
|         } else {
 | |
|           checkbox.removeAttribute("checked");
 | |
|         }
 | |
|         checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED);
 | |
|       } else {
 | |
|         checkbox.removeAttribute("preference");
 | |
|         checkbox.removeAttribute("checked");
 | |
|       }
 | |
| 
 | |
|       const isDisabled = (
 | |
|         !allowedByPolicy ||
 | |
|         Services.prefs.prefIsLocked(PREF_OPT_OUT_STUDIES_ENABLED) ||
 | |
|         !Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false)
 | |
|       );
 | |
| 
 | |
|       // We can't use checkbox.disabled here because the XBL binding may not be present,
 | |
|       // in which case setting the property won't work properly.
 | |
|       if (isDisabled) {
 | |
|         checkbox.setAttribute("disabled", "true");
 | |
|       } else {
 | |
|         checkbox.removeAttribute("disabled");
 | |
|       }
 | |
|     }
 | |
|     Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateStudyCheckboxState);
 | |
|     updateStudyCheckboxState();
 | |
|   },
 | |
| 
 | |
|   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;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   // Accessibility checkbox helpers
 | |
|   _initA11yState() {
 | |
|     this._initA11yString();
 | |
|     let checkbox = document.getElementById("a11yPrivacyCheckbox");
 | |
|     switch (Services.prefs.getIntPref("accessibility.force_disabled")) {
 | |
|       case 1: // access blocked
 | |
|         checkbox.checked = true;
 | |
|         break;
 | |
|       case -1: // a11y is forced on for testing
 | |
|       case 0: // access allowed
 | |
|         checkbox.checked = false;
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _initA11yString() {
 | |
|     let a11yLearnMoreLink =
 | |
|       Services.urlFormatter.formatURLPref("accessibility.support.url");
 | |
|     document.getElementById("a11yLearnMoreLink")
 | |
|       .setAttribute("href", a11yLearnMoreLink);
 | |
|   },
 | |
| 
 | |
|   async updateA11yPrefs(checked) {
 | |
|     let buttonIndex = await confirmRestartPrompt(checked, 0, true, false);
 | |
|     if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) {
 | |
|       Services.prefs.setIntPref("accessibility.force_disabled", checked ? 1 : 0);
 | |
|       Services.telemetry.scalarSet("preferences.prevent_accessibility_services", true);
 | |
|       Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
 | |
|     }
 | |
| 
 | |
|     // Revert the checkbox in case we didn't quit
 | |
|     document.getElementById("a11yPrivacyCheckbox").checked = !checked;
 | |
|   },
 | |
| };
 |