foxgirl-linux/archive/omni-patching/browser/omni.ja/chrome/browser/content/browser/preferences/in-content/privacy.js
2019-06-12 15:44:32 -05:00

2023 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
// Librefox
if (Services.prefs.prefIsLocked("browser.safebrowsing.downloads.enabled")) {
enableSafeBrowsing.setAttribute("disabled", "true");
}
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;
},
};