diff options
Diffstat (limited to 'comm/mail/components/preferences/privacy.js')
-rw-r--r-- | comm/mail/components/preferences/privacy.js | 562 |
1 files changed, 562 insertions, 0 deletions
diff --git a/comm/mail/components/preferences/privacy.js b/comm/mail/components/preferences/privacy.js new file mode 100644 index 0000000000..2bb8cba6ad --- /dev/null +++ b/comm/mail/components/preferences/privacy.js @@ -0,0 +1,562 @@ +/* -*- Mode: JavaScript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +"use strict"; + +/* import-globals-from preferences.js */ + +var { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +ChromeUtils.defineESModuleGetters(this, { + LoginHelper: "resource://gre/modules/LoginHelper.sys.mjs", + OSKeyStore: "resource://gre/modules/OSKeyStore.sys.mjs", +}); + +const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; + +Preferences.addAll([ + { id: "mail.spam.manualMark", type: "bool" }, + { id: "mail.spam.manualMarkMode", type: "int" }, + { id: "mail.spam.markAsReadOnSpam", type: "bool" }, + { id: "mail.spam.logging.enabled", type: "bool" }, + { id: "mail.phishing.detection.enabled", type: "bool" }, + { id: "browser.safebrowsing.enabled", type: "bool" }, + { id: "mailnews.downloadToTempFile", type: "bool" }, + { id: "pref.privacy.disable_button.view_passwords", type: "bool" }, + { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" }, + { id: "pref.privacy.disable_button.view_cookies", type: "bool" }, + { + id: "mailnews.message_display.disable_remote_image", + type: "bool", + inverted: "true", + }, + { id: "places.history.enabled", type: "bool" }, + { id: "network.cookie.cookieBehavior", type: "int" }, + { id: "network.cookie.blockFutureCookies", type: "bool" }, + { id: "privacy.donottrackheader.enabled", type: "bool" }, + { id: "security.default_personal_cert", type: "string" }, + { id: "security.disable_button.openCertManager", type: "bool" }, + { id: "security.disable_button.openDeviceManager", type: "bool" }, + { id: "security.OCSP.enabled", type: "int" }, + { id: "mail.e2ee.auto_enable", type: "bool" }, + { id: "mail.e2ee.auto_disable", type: "bool" }, + { id: "mail.e2ee.notify_on_auto_disable", type: "bool" }, +]); + +if (AppConstants.MOZ_DATA_REPORTING) { + Preferences.addAll([ + // Preference instances for prefs that we need to monitor while the page is open. + { id: PREF_UPLOAD_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)); +} + +var gPrivacyPane = { + init() { + this.updateManualMarkMode(Preferences.get("mail.spam.manualMark").value); + this.updateJunkLogButton( + Preferences.get("mail.spam.logging.enabled").value + ); + + this._initMasterPasswordUI(); + + if (AppConstants.MOZ_DATA_REPORTING) { + this.initDataCollection(); + if (AppConstants.MOZ_CRASHREPORTER) { + this.initSubmitCrashes(); + } + this.initSubmitHealthReport(); + setEventListener( + "submitHealthReportBox", + "command", + gPrivacyPane.updateSubmitHealthReport + ); + setEventListener( + "telemetryDataDeletionLearnMore", + "command", + gPrivacyPane.showDataDeletion + ); + } + + this.readAcceptCookies(); + let element = document.getElementById("acceptCookies"); + Preferences.addSyncFromPrefListener(element, () => + this.readAcceptCookies() + ); + Preferences.addSyncToPrefListener(element, () => this.writeAcceptCookies()); + + element = document.getElementById("acceptThirdPartyMenu"); + Preferences.addSyncFromPrefListener(element, () => + this.readAcceptThirdPartyCookies() + ); + Preferences.addSyncToPrefListener(element, () => + this.writeAcceptThirdPartyCookies() + ); + + element = document.getElementById("enableOCSP"); + Preferences.addSyncFromPrefListener(element, () => this.readEnableOCSP()); + Preferences.addSyncToPrefListener(element, () => this.writeEnableOCSP()); + + this.initE2eeCheckboxes(); + }, + + /** + * Reload the current message after a preference affecting the view + * has been changed. + */ + reloadMessageInOpener() { + if (window.opener && typeof window.opener.ReloadMessage == "function") { + window.opener.ReloadMessage(); + } + }, + + /** + * Reads the network.cookie.cookieBehavior preference value and + * enables/disables the rest of the cookie UI accordingly, returning true + * if cookies are enabled. + */ + readAcceptCookies() { + let pref = Preferences.get("network.cookie.cookieBehavior"); + let exceptionsButton = document.getElementById("cookieExceptions"); + let acceptThirdPartyLabel = document.getElementById( + "acceptThirdPartyLabel" + ); + let acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu"); + let showCookiesButton = document.getElementById("showCookiesButton"); + + // enable the rest of the UI for anything other than "disable all cookies" + let acceptCookies = pref.value != 2; + let cookieBehaviorLocked = Services.prefs.prefIsLocked( + "network.cookie.cookieBehavior" + ); + + exceptionsButton.disabled = cookieBehaviorLocked; + acceptThirdPartyLabel.disabled = acceptThirdPartyMenu.disabled = + !acceptCookies || cookieBehaviorLocked; + showCookiesButton.disabled = cookieBehaviorLocked; + + return acceptCookies; + }, + + /** + * Enables/disables the "keep until" label and menulist in response to the + * "accept cookies" checkbox being checked or unchecked. + * + * @returns 0 if cookies are accepted, 2 if they are not; + * the value network.cookie.cookieBehavior should get + */ + writeAcceptCookies() { + let accept = document.getElementById("acceptCookies"); + let acceptThirdPartyMenu = document.getElementById("acceptThirdPartyMenu"); + // if we're enabling cookies, automatically select 'accept third party always' + if (accept.checked) { + acceptThirdPartyMenu.selectedIndex = 0; + } + + return accept.checked ? 0 : 2; + }, + + /** + * Displays fine-grained, per-site preferences for cookies. + */ + showCookieExceptions() { + let bundle = document.getElementById("bundlePreferences"); + let params = { + blockVisible: true, + sessionVisible: true, + allowVisible: true, + prefilledHost: "", + permissionType: "cookie", + windowTitle: bundle.getString("cookiepermissionstitle"), + introText: bundle.getString("cookiepermissionstext"), + }; + gSubDialog.open( + "chrome://messenger/content/preferences/permissions.xhtml", + undefined, + params + ); + }, + + /** + * Displays all the user's cookies in a dialog. + */ + showCookies(aCategory) { + gSubDialog.open("chrome://messenger/content/preferences/cookies.xhtml"); + }, + + /** + * Converts between network.cookie.cookieBehavior and the third-party cookie UI + */ + readAcceptThirdPartyCookies() { + let pref = Preferences.get("network.cookie.cookieBehavior"); + switch (pref.value) { + case 0: + return "always"; + case 1: + return "never"; + case 2: + return "never"; + case 3: + return "visited"; + default: + return undefined; + } + }, + + writeAcceptThirdPartyCookies() { + let accept = document.getElementById("acceptThirdPartyMenu").selectedItem; + switch (accept.value) { + case "always": + return 0; + case "visited": + return 3; + case "never": + return 1; + default: + return undefined; + } + }, + + /** + * Displays fine-grained, per-site preferences for remote content. + * We use the "image" type for that, but it can also be stylesheets or + * iframes. + */ + showRemoteContentExceptions() { + let bundle = document.getElementById("bundlePreferences"); + let params = { + blockVisible: true, + sessionVisible: false, + allowVisible: true, + prefilledHost: "", + permissionType: "image", + windowTitle: bundle.getString("imagepermissionstitle"), + introText: bundle.getString("imagepermissionstext"), + }; + gSubDialog.open( + "chrome://messenger/content/preferences/permissions.xhtml", + undefined, + params + ); + }, + updateManualMarkMode(aEnableRadioGroup) { + document.getElementById("manualMarkMode").disabled = !aEnableRadioGroup; + }, + + updateJunkLogButton(aEnableButton) { + document.getElementById("openJunkLogButton").disabled = !aEnableButton; + }, + + openJunkLog() { + // The junk log dialog can't work as a sub-dialog, because that means + // loading it in a browser, and we can't load a chrome: page containing a + // file: page in a browser. Open it as a real dialog instead. + window.browsingContext.topChromeWindow.openDialog( + "chrome://messenger/content/junkLog.xhtml" + ); + }, + + resetTrainingData() { + // make sure the user really wants to do this + var bundle = document.getElementById("bundlePreferences"); + var title = bundle.getString("confirmResetJunkTrainingTitle"); + var text = bundle.getString("confirmResetJunkTrainingText"); + + // if the user says no, then just fall out + if (!Services.prompt.confirm(window, title, text)) { + return; + } + + // otherwise go ahead and remove the training data + MailServices.junk.resetTrainingData(); + }, + + /** + * Initializes primary password UI: the "use primary password" checkbox, selects + * the primary password button to show, and enables/disables it as necessary. + * The primary 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 primary password button depending on the state of the + * "use primary password" checkbox, and prompts for primary 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 primary password" dialog to allow the user to remove + * the current primary password. When the dialog is dismissed, primary 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), + }); + } + this._initMasterPasswordUI(); + }, + + /** + * Displays a dialog in which the primary password may be changed. + */ + async changeMasterPassword() { + // OS reauthenticate functionality is not available on Linux yet (bug 1527745) + if ( + !LoginHelper.isPrimaryPasswordSet() && + Services.prefs.getBoolPref("signon.management.page.os-auth.enabled") && + AppConstants.platform != "linux" + ) { + 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.getMostRecentWindow(""); + let loggedIn = await OSKeyStore.ensureLoggedIn( + messageText.value, + captionText.value, + win, + false + ); + if (!loggedIn.authenticated) { + return; + } + } + + gSubDialog.open("chrome://mozapps/content/preferences/changemp.xhtml", { + closingCallback: this._initMasterPasswordUI.bind(this), + }); + }, + + /** + * Shows the sites where the user has saved passwords and the associated + * login information. + */ + showPasswords() { + gSubDialog.open( + "chrome://messenger/content/preferences/passwordManager.xhtml" + ); + }, + + updateDownloadedPhishingListState() { + document.getElementById("useDownloadedList").disabled = + !document.getElementById("enablePhishingDetector").checked; + }, + + /** + * Display the user's certificates and associated options. + */ + showCertificates() { + gSubDialog.open("chrome://pippki/content/certManager.xhtml"); + }, + + /** + * security.OCSP.enabled is an integer value for legacy reasons. + * A value of 1 means OCSP is enabled. Any other value means it is disabled. + */ + 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 == 1; + }, + + /** + * See documentation for readEnableOCSP. + */ + writeEnableOCSP() { + var checkbox = document.getElementById("enableOCSP"); + return checkbox.checked ? 1 : 0; + }, + + /** + * Display 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() { + this._setupLearnMoreLink( + "toolkit.datacollection.infoURL", + "dataCollectionPrivacyNotice" + ); + }, + + initSubmitCrashes() { + this._setupLearnMoreLink( + "toolkit.crashreporter.infoURL", + "crashReporterLearnMore" + ); + }, + + /** + * Set up or hide the Learn More links for various data collection options + */ + _setupLearnMoreLink(pref, element) { + // set up the Learn More link with the correct URL + let url = Services.urlFormatter.formatURLPref(pref); + let el = document.getElementById(element); + + if (url) { + el.setAttribute("href", url); + } else { + el.setAttribute("hidden", "true"); + } + }, + + /** + * Initialize the health report service reference and checkbox. + */ + initSubmitHealthReport() { + this._setupLearnMoreLink( + "datareporting.healthreport.infoURL", + "FHRLearnMore" + ); + + let checkbox = document.getElementById("submitHealthReportBox"); + + // Telemetry is only sending data if MOZ_TELEMETRY_REPORTING is defined. + // We still want to display the preferences panel if that's not the case, but + // we want it to be disabled and unchecked. + if ( + Services.prefs.prefIsLocked(PREF_UPLOAD_ENABLED) || + !AppConstants.MOZ_TELEMETRY_REPORTING + ) { + checkbox.setAttribute("disabled", "true"); + return; + } + + checkbox.checked = + Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED) && + AppConstants.MOZ_TELEMETRY_REPORTING; + }, + + /** + * Update the health report preference with state from checkbox. + */ + updateSubmitHealthReport() { + let checkbox = document.getElementById("submitHealthReportBox"); + + Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked); + + // If allow telemetry is checked, hide the box saying you're no longer + // allowing it. + document.getElementById("telemetry-container").hidden = checkbox.checked; + }, + + initE2eeCheckboxes() { + let on = document.getElementById("emailE2eeAutoEnable"); + let off = document.getElementById("emailE2eeAutoDisable"); + let notify = document.getElementById("emailE2eeAutoDisableNotify"); + + on.checked = Preferences.get("mail.e2ee.auto_enable").value; + off.checked = Preferences.get("mail.e2ee.auto_disable").value; + notify.checked = Preferences.get("mail.e2ee.notify_on_auto_disable").value; + + if (!on.checked) { + off.disabled = true; + notify.disabled = true; + } else { + off.disabled = false; + notify.disabled = !off.checked; + } + }, + + updateE2eeCheckboxes() { + let on = document.getElementById("emailE2eeAutoEnable"); + let off = document.getElementById("emailE2eeAutoDisable"); + let notify = document.getElementById("emailE2eeAutoDisableNotify"); + + if (!on.checked) { + off.disabled = true; + notify.disabled = true; + } else { + off.disabled = false; + notify.disabled = !off.checked; + } + }, +}; + +Preferences.get("mailnews.message_display.disable_remote_image").on( + "change", + gPrivacyPane.reloadMessageInOpener +); +Preferences.get("mail.phishing.detection.enabled").on( + "change", + gPrivacyPane.reloadMessageInOpener +); |