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