privacy.js (174195B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /* import-globals-from extensionControlled.js */ 6 /* import-globals-from preferences.js */ 7 8 const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; 9 10 const TRACKING_PROTECTION_KEY = "websites.trackingProtectionMode"; 11 const TRACKING_PROTECTION_PREFS = [ 12 "privacy.trackingprotection.enabled", 13 "privacy.trackingprotection.pbmode.enabled", 14 ]; 15 const CONTENT_BLOCKING_PREFS = [ 16 "privacy.trackingprotection.enabled", 17 "privacy.trackingprotection.pbmode.enabled", 18 "network.cookie.cookieBehavior", 19 "privacy.trackingprotection.fingerprinting.enabled", 20 "privacy.trackingprotection.cryptomining.enabled", 21 "privacy.firstparty.isolate", 22 "privacy.trackingprotection.emailtracking.enabled", 23 "privacy.trackingprotection.emailtracking.pbmode.enabled", 24 "privacy.fingerprintingProtection", 25 "privacy.fingerprintingProtection.pbmode", 26 "privacy.trackingprotection.allow_list.baseline.enabled", 27 "privacy.trackingprotection.allow_list.convenience.enabled", 28 ]; 29 30 const PREF_OPT_OUT_STUDIES_ENABLED = "app.shield.optoutstudies.enabled"; 31 const PREF_NORMANDY_ENABLED = "app.normandy.enabled"; 32 33 const PREF_ADDON_RECOMMENDATIONS_ENABLED = "browser.discovery.enabled"; 34 35 const PREF_PASSWORD_GENERATION_AVAILABLE = "signon.generation.available"; 36 const { BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN } = Ci.nsICookieService; 37 38 const PASSWORD_MANAGER_PREF_ID = "services.passwordSavingEnabled"; 39 40 ChromeUtils.defineLazyGetter(this, "AlertsServiceDND", function () { 41 try { 42 let alertsService = Cc["@mozilla.org/alerts-service;1"] 43 .getService(Ci.nsIAlertsService) 44 .QueryInterface(Ci.nsIAlertsDoNotDisturb); 45 // This will throw if manualDoNotDisturb isn't implemented. 46 alertsService.manualDoNotDisturb; 47 return alertsService; 48 } catch (ex) { 49 return undefined; 50 } 51 }); 52 53 ChromeUtils.defineLazyGetter(lazy, "AboutLoginsL10n", () => { 54 return new Localization(["branding/brand.ftl", "browser/aboutLogins.ftl"]); 55 }); 56 57 ChromeUtils.defineLazyGetter(lazy, "gParentalControlsService", () => 58 "@mozilla.org/parental-controls-service;1" in Cc 59 ? Cc["@mozilla.org/parental-controls-service;1"].getService( 60 Ci.nsIParentalControlsService 61 ) 62 : null 63 ); 64 65 XPCOMUtils.defineLazyScriptGetter( 66 this, 67 ["OnionServicesAuthPreferences"], 68 "chrome://browser/content/onionservices/authPreferences.js" 69 ); 70 71 // TODO: module import via ChromeUtils.defineModuleGetter 72 XPCOMUtils.defineLazyScriptGetter( 73 this, 74 ["SecurityLevelPreferences"], 75 "chrome://browser/content/securitylevel/securityLevel.js" 76 ); 77 78 XPCOMUtils.defineLazyServiceGetter( 79 lazy, 80 "TrackingDBService", 81 "@mozilla.org/tracking-db-service;1", 82 Ci.nsITrackingDBService 83 ); 84 85 XPCOMUtils.defineLazyPreferenceGetter( 86 this, 87 "gIsFirstPartyIsolated", 88 "privacy.firstparty.isolate", 89 false 90 ); 91 92 XPCOMUtils.defineLazyPreferenceGetter( 93 this, 94 "useOldClearHistoryDialog", 95 "privacy.sanitize.useOldClearHistoryDialog", 96 false 97 ); 98 99 ChromeUtils.defineESModuleGetters(this, { 100 AppUpdater: "resource://gre/modules/AppUpdater.sys.mjs", 101 DoHConfigController: "moz-src:///toolkit/components/doh/DoHConfig.sys.mjs", 102 Sanitizer: "resource:///modules/Sanitizer.sys.mjs", 103 SelectableProfileService: 104 "resource:///modules/profiles/SelectableProfileService.sys.mjs", 105 }); 106 107 const SANITIZE_ON_SHUTDOWN_MAPPINGS = { 108 history: "privacy.clearOnShutdown.history", 109 downloads: "privacy.clearOnShutdown.downloads", 110 formdata: "privacy.clearOnShutdown.formdata", 111 sessions: "privacy.clearOnShutdown.sessions", 112 siteSettings: "privacy.clearOnShutdown.siteSettings", 113 cookies: "privacy.clearOnShutdown.cookies", 114 cache: "privacy.clearOnShutdown.cache", 115 offlineApps: "privacy.clearOnShutdown.offlineApps", 116 }; 117 118 /* 119 * Prefs that are unique to sanitizeOnShutdown and are not shared 120 * with the deleteOnClose mechanism like privacy.clearOnShutdown.cookies, -cache and -offlineApps 121 */ 122 const SANITIZE_ON_SHUTDOWN_PREFS_ONLY = [ 123 "privacy.clearOnShutdown.history", 124 "privacy.clearOnShutdown.downloads", 125 "privacy.clearOnShutdown.sessions", 126 "privacy.clearOnShutdown.formdata", 127 "privacy.clearOnShutdown.siteSettings", 128 ]; 129 130 const SANITIZE_ON_SHUTDOWN_PREFS_ONLY_V2 = [ 131 "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads", 132 "privacy.clearOnShutdown_v2.siteSettings", 133 ]; 134 135 const SECURITY_PRIVACY_STATUS_CARD_ENABLED = 136 Services.prefs.getBoolPref("browser.settings-redesign.enabled", false) || 137 Services.prefs.getBoolPref( 138 "browser.settings-redesign.securityPrivacyStatus.enabled", 139 false 140 ); 141 142 Preferences.addAll([ 143 // Content blocking / Tracking Protection 144 { id: "privacy.trackingprotection.enabled", type: "bool" }, 145 { id: "privacy.trackingprotection.pbmode.enabled", type: "bool" }, 146 { id: "privacy.trackingprotection.fingerprinting.enabled", type: "bool" }, 147 { id: "privacy.trackingprotection.cryptomining.enabled", type: "bool" }, 148 { id: "privacy.trackingprotection.emailtracking.enabled", type: "bool" }, 149 { 150 id: "privacy.trackingprotection.emailtracking.pbmode.enabled", 151 type: "bool", 152 }, 153 { 154 id: "privacy.trackingprotection.allow_list.baseline.enabled", 155 type: "bool", 156 }, 157 { 158 id: "privacy.trackingprotection.allow_list.convenience.enabled", 159 type: "bool", 160 }, 161 162 // Fingerprinting Protection 163 { id: "privacy.fingerprintingProtection", type: "bool" }, 164 { id: "privacy.fingerprintingProtection.pbmode", type: "bool" }, 165 166 // Resist Fingerprinting 167 { id: "privacy.resistFingerprinting", type: "bool" }, 168 { id: "privacy.resistFingerprinting.pbmode", type: "bool" }, 169 170 // Social tracking 171 { id: "privacy.trackingprotection.socialtracking.enabled", type: "bool" }, 172 { id: "privacy.socialtracking.block_cookies.enabled", type: "bool" }, 173 174 // Tracker list 175 { id: "urlclassifier.trackingTable", type: "string" }, 176 177 // Button prefs 178 { id: "pref.privacy.disable_button.cookie_exceptions", type: "bool" }, 179 { 180 id: "pref.privacy.disable_button.tracking_protection_exceptions", 181 type: "bool", 182 }, 183 184 // History 185 { id: "places.history.enabled", type: "bool" }, 186 { id: "browser.formfill.enable", type: "bool" }, 187 { id: "privacy.history.custom", type: "bool" }, 188 189 // Cookies 190 { id: "network.cookie.cookieBehavior", type: "int" }, 191 { id: "network.cookie.blockFutureCookies", type: "bool" }, 192 // Content blocking category 193 { id: "browser.contentblocking.category", type: "string" }, 194 { id: "browser.contentblocking.features.strict", type: "string" }, 195 196 // Clear Private Data 197 { id: "privacy.sanitize.sanitizeOnShutdown", type: "bool" }, 198 { id: "privacy.sanitize.timeSpan", type: "int" }, 199 { id: "privacy.clearOnShutdown.cookies", type: "bool" }, 200 { id: "privacy.clearOnShutdown_v2.cookiesAndStorage", type: "bool" }, 201 { id: "privacy.clearOnShutdown.cache", type: "bool" }, 202 { id: "privacy.clearOnShutdown_v2.cache", type: "bool" }, 203 { id: "privacy.clearOnShutdown.offlineApps", type: "bool" }, 204 { id: "privacy.clearOnShutdown.history", type: "bool" }, 205 { 206 id: "privacy.clearOnShutdown_v2.browsingHistoryAndDownloads", 207 type: "bool", 208 }, 209 { id: "privacy.clearOnShutdown.downloads", type: "bool" }, 210 { id: "privacy.clearOnShutdown.sessions", type: "bool" }, 211 { id: "privacy.clearOnShutdown.formdata", type: "bool" }, 212 { id: "privacy.clearOnShutdown.siteSettings", type: "bool" }, 213 { id: "privacy.clearOnShutdown_v2.siteSettings", type: "bool" }, 214 215 // Do not track and Global Privacy Control 216 { id: "privacy.donottrackheader.enabled", type: "bool" }, 217 { id: "privacy.globalprivacycontrol.functionality.enabled", type: "bool" }, 218 { id: "privacy.globalprivacycontrol.enabled", type: "bool" }, 219 { 220 id: "browser.preferences.config_warning.donottrackheader.dismissed", 221 type: "bool", 222 }, 223 224 // Firefox VPN 225 { id: "browser.ipProtection.enabled", type: "bool" }, 226 { id: "browser.ipProtection.features.siteExceptions", type: "bool" }, 227 { id: "browser.ipProtection.features.autoStart", type: "bool" }, 228 { id: "browser.ipProtection.autoStartEnabled", type: "bool" }, 229 { id: "browser.ipProtection.autoStartPrivateEnabled", type: "bool" }, 230 231 // Media 232 { id: "media.autoplay.default", type: "int" }, 233 234 // Popups 235 { id: "dom.disable_open_during_load", type: "bool" }, 236 237 // Passwords 238 { id: "signon.rememberSignons", type: "bool" }, 239 { id: "signon.generation.enabled", type: "bool" }, 240 { id: "signon.autofillForms", type: "bool" }, 241 { id: "signon.management.page.breach-alerts.enabled", type: "bool" }, 242 { id: "signon.firefoxRelay.feature", type: "string" }, 243 244 // Buttons 245 { id: "pref.privacy.disable_button.view_passwords", type: "bool" }, 246 { id: "pref.privacy.disable_button.view_passwords_exceptions", type: "bool" }, 247 248 /* Certificates tab 249 * security.default_personal_cert 250 * - a string: 251 * "Select Automatically" select a certificate automatically when a site 252 * requests one 253 * "Ask Every Time" present a dialog to the user so he can select 254 * the certificate to use on a site which 255 * requests one 256 */ 257 { id: "security.default_personal_cert", type: "string" }, 258 259 { id: "security.disable_button.openCertManager", type: "bool" }, 260 261 { id: "security.disable_button.openDeviceManager", type: "bool" }, 262 263 { id: "security.enterprise_roots.enabled", type: "bool" }, 264 265 // Add-ons, malware, phishing 266 { id: "xpinstall.whitelist.required", type: "bool" }, 267 268 { id: "browser.safebrowsing.malware.enabled", type: "bool" }, 269 { id: "browser.safebrowsing.phishing.enabled", type: "bool" }, 270 271 { id: "browser.safebrowsing.downloads.enabled", type: "bool" }, 272 273 { id: "urlclassifier.malwareTable", type: "string" }, 274 275 { 276 id: "browser.safebrowsing.downloads.remote.block_potentially_unwanted", 277 type: "bool", 278 }, 279 { id: "browser.safebrowsing.downloads.remote.block_uncommon", type: "bool" }, 280 281 // First-Party Isolation 282 { id: "privacy.firstparty.isolate", type: "bool" }, 283 284 // HTTPS-Only 285 { id: "dom.security.https_only_mode", type: "bool" }, 286 { id: "dom.security.https_only_mode_pbm", type: "bool" }, 287 { id: "dom.security.https_first", type: "bool" }, 288 { id: "dom.security.https_first_pbm", type: "bool" }, 289 290 // Windows SSO 291 { id: "network.http.windows-sso.enabled", type: "bool" }, 292 293 // Cookie Banner Handling 294 { id: "cookiebanners.ui.desktop.enabled", type: "bool" }, 295 { id: "cookiebanners.service.mode.privateBrowsing", type: "int" }, 296 297 // DoH 298 { id: "network.trr.mode", type: "int" }, 299 { id: "network.trr.uri", type: "string" }, 300 { id: "network.trr.default_provider_uri", type: "string" }, 301 { id: "network.trr.custom_uri", type: "string" }, 302 { id: "network.trr_ui.fallback_was_checked", type: "bool" }, 303 { id: "doh-rollout.disable-heuristics", type: "bool" }, 304 305 // Local Network Access 306 { id: "network.lna.blocking", type: "bool" }, 307 308 // Permissions 309 { id: "media.setsinkid.enabled", type: "bool" }, 310 ]); 311 312 if (SECURITY_PRIVACY_STATUS_CARD_ENABLED) { 313 Preferences.addAll([ 314 // Security and Privacy Warnings 315 { id: "browser.preferences.config_warning.dismissAll", type: "bool" }, 316 { id: "privacy.ui.status_card.testing.show_issue", type: "bool" }, 317 { 318 id: "browser.preferences.config_warning.warningTest.dismissed", 319 type: "bool", 320 }, 321 { 322 id: "browser.preferences.config_warning.warningAllowFingerprinters.dismissed", 323 type: "bool", 324 }, 325 { 326 id: "browser.preferences.config_warning.warningThirdPartyCookies.dismissed", 327 type: "bool", 328 }, 329 { 330 id: "browser.preferences.config_warning.warningPasswordManager.dismissed", 331 type: "bool", 332 }, 333 { 334 id: "browser.preferences.config_warning.warningPopupBlocker.dismissed", 335 type: "bool", 336 }, 337 { 338 id: "browser.preferences.config_warning.warningExtensionInstall.dismissed", 339 type: "bool", 340 }, 341 { 342 id: "browser.preferences.config_warning.warningSafeBrowsing.dismissed", 343 type: "bool", 344 }, 345 { 346 id: "browser.preferences.config_warning.warningDoH.dismissed", 347 type: "bool", 348 }, 349 { 350 id: "browser.preferences.config_warning.warningECH.dismissed", 351 type: "bool", 352 }, 353 { 354 id: "browser.preferences.config_warning.warningCT.dismissed", 355 type: "bool", 356 }, 357 { 358 id: "browser.preferences.config_warning.warningCRLite.dismissed", 359 type: "bool", 360 }, 361 { 362 id: "browser.preferences.config_warning.warningCertificatePinning.dismissed", 363 type: "bool", 364 }, 365 { 366 id: "browser.preferences.config_warning.warningTLSMin.dismissed", 367 type: "bool", 368 }, 369 { 370 id: "browser.preferences.config_warning.warningTLSMax.dismissed", 371 type: "bool", 372 }, 373 { 374 id: "browser.preferences.config_warning.warningProxyAutodetection.dismissed", 375 type: "bool", 376 }, 377 { 378 id: "browser.preferences.config_warning.warningPrivilegedConstraint.dismissed", 379 type: "bool", 380 }, 381 { 382 id: "browser.preferences.config_warning.warningProcessSandbox.dismissed", 383 type: "bool", 384 }, 385 { 386 id: "browser.preferences.config_warning.warningContentResourceURI.dismissed", 387 type: "bool", 388 }, 389 { 390 id: "browser.preferences.config_warning.warningWorkerMIME.dismissed", 391 type: "bool", 392 }, 393 { 394 id: "browser.preferences.config_warning.warningTopLevelDataURI.dismissed", 395 type: "bool", 396 }, 397 { 398 id: "browser.preferences.config_warning.warningActiveMixedContent.dismissed", 399 type: "bool", 400 }, 401 { 402 id: "browser.preferences.config_warning.warningInnerHTMLltgt.dismissed", 403 type: "bool", 404 }, 405 { 406 id: "browser.preferences.config_warning.warningFileURIOrigin.dismissed", 407 type: "bool", 408 }, 409 { 410 id: "services.passwordSavingEnabled", 411 type: "bool", 412 }, 413 { 414 id: "network.dns.echconfig.enabled", 415 type: "bool", 416 }, 417 { 418 id: "network.dns.http3_echconfig.enabled", 419 type: "bool", 420 }, 421 { 422 id: "security.pki.certificate_transparency.mode", 423 type: "int", 424 }, 425 { 426 id: "security.pki.crlite_mode", 427 type: "int", 428 }, 429 { 430 id: "security.cert_pinning.enforcement_level", 431 type: "int", 432 }, 433 { 434 id: "security.tls.version.min", 435 type: "int", 436 }, 437 { 438 id: "security.tls.version.fallback-limit", 439 type: "int", 440 }, 441 { 442 id: "security.tls.version.enable-deprecated", 443 type: "bool", 444 }, 445 { 446 id: "security.tls.version.max", 447 type: "int", 448 }, 449 { 450 id: "network.proxy.type", 451 type: "int", 452 }, 453 { 454 id: "security.all_resource_uri_content_accessible", 455 type: "bool", 456 }, 457 { 458 id: "security.block_Worker_with_wrong_mime", 459 type: "bool", 460 }, 461 { 462 id: "security.data_uri.block_toplevel_data_uri_navigations", 463 type: "bool", 464 }, 465 { 466 id: "security.mixed_content.block_active_content", 467 type: "bool", 468 }, 469 { 470 id: "dom.security.html_serialization_escape_lt_gt", 471 type: "bool", 472 }, 473 { 474 id: "security.fileuri.strict_origin_policy", 475 type: "bool", 476 }, 477 { 478 id: "dom.security.skip_html_fragment_assertion", 479 type: "bool", 480 }, 481 { 482 id: "security.browser_xhtml_csp.enabled", 483 type: "bool", 484 }, 485 { 486 id: "security.allow_unsafe_dangerous_privileged_evil_eval", 487 type: "bool", 488 }, 489 { 490 id: "security.allow_eval_in_parent_process", 491 type: "bool", 492 }, 493 { 494 id: "security.allow_eval_with_system_principal", 495 type: "bool", 496 }, 497 { 498 id: "security.allow_unsafe_parent_loads", 499 type: "bool", 500 }, 501 { 502 id: "security.allow_parent_unrestricted_js_loads", 503 type: "bool", 504 }, 505 { 506 id: "dom.security.skip_remote_script_assertion_in_system_priv_context", 507 type: "bool", 508 }, 509 { 510 id: "security.sandbox.content.mac.disconnect-windowserver", 511 type: "bool", 512 }, 513 { 514 id: "security.sandbox.content.write_path_whitelist", 515 type: "string", 516 }, 517 { 518 id: "security.sandbox.content.read_path_whitelist", 519 type: "string", 520 }, 521 { 522 id: "security.sandbox.content.syscall_whitelist", 523 type: "string", 524 }, 525 { 526 id: "security.sandbox.content.level", 527 type: "int", 528 }, 529 { 530 id: "security.sandbox.socket.process.level", 531 type: "int", 532 }, 533 { 534 id: "security.sandbox.gpu.level", 535 type: "int", 536 }, 537 { 538 id: "security.sandbox.content.win32k-disable", 539 type: "bool", 540 }, 541 { 542 id: "security.sandbox.gmp.win32k-disable", 543 type: "bool", 544 }, 545 { 546 id: "security.sandbox.gmp.acg.enabled", 547 type: "bool", 548 }, 549 { 550 id: "security.sandbox.socket.win32k-disable", 551 type: "bool", 552 }, 553 { 554 id: "security.sandbox.rdd.shadow-stack.enabled", 555 type: "bool", 556 }, 557 { 558 id: "security.sandbox.socket.shadow-stack.enabled", 559 type: "bool", 560 }, 561 { 562 id: "security.sandbox.gpu.shadow-stack.enabled", 563 type: "bool", 564 }, 565 { 566 id: "security.sandbox.gmp.shadow-stack.enabled", 567 type: "bool", 568 }, 569 { 570 id: "security.sandbox.utility-wmf-cdm.lpac.enabled", 571 type: "bool", 572 }, 573 { 574 id: "security.sandbox.rdd.acg.enabled", 575 type: "bool", 576 }, 577 { 578 id: "security.sandbox.utility-wmf.acg.enabled", 579 type: "bool", 580 }, 581 ]); 582 583 Preferences.addSetting({ 584 id: "etpStrictEnabled", 585 pref: "browser.contentblocking.category", 586 get: prefValue => prefValue == "strict", 587 }); 588 Preferences.addSetting( 589 /** @type {{ cachedValue: number, loadTrackerCount: (emitChange: SettingEmitChange) => Promise<void> } & SettingConfig} */ ({ 590 id: "trackerCount", 591 cachedValue: null, 592 async loadTrackerCount(emitChange) { 593 const now = Date.now(); 594 const aMonthAgo = new Date(now - 30 * 24 * 60 * 60 * 1000); 595 /** @type {{ getResultByName: (_: string) => number }[]} */ 596 const events = await lazy.TrackingDBService.getEventsByDateRange( 597 now, 598 aMonthAgo 599 ); 600 601 const total = events.reduce((acc, day) => { 602 return acc + day.getResultByName("count"); 603 }, 0); 604 this.cachedValue = total; 605 emitChange(); 606 }, 607 setup(emitChange) { 608 this.loadTrackerCount(emitChange); 609 }, 610 get() { 611 return this.cachedValue; 612 }, 613 }) 614 ); 615 Preferences.addSetting( 616 /** @type {{ cachedValue: any } & SettingConfig} */ ({ 617 id: "appUpdateStatus", 618 cachedValue: AppUpdater.STATUS.NO_UPDATER, 619 setup(emitChange) { 620 if (AppConstants.MOZ_UPDATER && !gIsPackagedApp) { 621 let appUpdater = new AppUpdater(); 622 /** 623 * @param {number} status 624 * @param {any[]} _args 625 */ 626 let listener = (status, ..._args) => { 627 this.cachedValue = status; 628 emitChange(); 629 }; 630 appUpdater.addListener(listener); 631 appUpdater.check(); 632 return () => { 633 appUpdater.removeListener(listener); 634 appUpdater.stop(); 635 }; 636 } 637 return () => {}; 638 }, 639 get() { 640 return this.cachedValue; 641 }, 642 set(value) { 643 this.cachedValue = value; 644 }, 645 }) 646 ); 647 } 648 649 Preferences.addSetting({ 650 id: "savePasswords", 651 pref: "signon.rememberSignons", 652 controllingExtensionInfo: { 653 storeId: "services.passwordSavingEnabled", 654 l10nId: "extension-controlling-password-saving", 655 }, 656 }); 657 658 Preferences.addSetting({ 659 id: "managePasswordExceptions", 660 onUserClick: () => { 661 gPrivacyPane.showPasswordExceptions(); 662 }, 663 }); 664 665 Preferences.addSetting({ 666 id: "fillUsernameAndPasswords", 667 pref: "signon.autofillForms", 668 }); 669 670 Preferences.addSetting({ 671 id: "suggestStrongPasswords", 672 pref: "signon.generation.enabled", 673 visible: () => Services.prefs.getBoolPref("signon.generation.available"), 674 }); 675 676 Preferences.addSetting({ 677 id: "requireOSAuthForPasswords", 678 visible: () => OSKeyStore.canReauth(), 679 get: () => LoginHelper.getOSAuthEnabled(), 680 async set(checked) { 681 const [messageText, captionText] = await Promise.all([ 682 lazy.AboutLoginsL10n.formatValue("about-logins-os-auth-dialog-message"), 683 lazy.AboutLoginsL10n.formatValue("about-logins-os-auth-dialog-caption"), 684 ]); 685 686 await LoginHelper.trySetOSAuthEnabled( 687 window, 688 checked, 689 messageText, 690 captionText 691 ); 692 693 // Trigger change event to keep checkbox UI in sync with pref value 694 Services.obs.notifyObservers(null, "PasswordsOSAuthEnabledChange"); 695 }, 696 setup: emitChange => { 697 Services.obs.addObserver(emitChange, "PasswordsOSAuthEnabledChange"); 698 return () => 699 Services.obs.removeObserver(emitChange, "PasswordsOSAuthEnabledChange"); 700 }, 701 }); 702 703 Preferences.addSetting({ 704 id: "allowWindowSSO", 705 pref: "network.http.windows-sso.enabled", 706 visible: () => AppConstants.platform === "win", 707 }); 708 709 Preferences.addSetting({ 710 id: "manageSavedPasswords", 711 onUserClick: ({ target }) => { 712 target.ownerGlobal.gPrivacyPane.showPasswords(); 713 }, 714 }); 715 716 Preferences.addSetting({ 717 id: "additionalProtectionsGroup", 718 }); 719 720 Preferences.addSetting({ 721 id: "primaryPasswordNotSet", 722 setup(emitChange) { 723 const topic = "passwordmgr-primary-pw-changed"; 724 Services.obs.addObserver(emitChange, topic); 725 return () => Services.obs.removeObserver(emitChange, topic); 726 }, 727 visible: () => { 728 return !LoginHelper.isPrimaryPasswordSet(); 729 }, 730 }); 731 732 Preferences.addSetting({ 733 id: "usePrimaryPassword", 734 deps: ["primaryPasswordNotSet"], 735 }); 736 737 Preferences.addSetting({ 738 id: "addPrimaryPassword", 739 deps: ["primaryPasswordNotSet"], 740 onUserClick: ({ target }) => { 741 target.ownerGlobal.gPrivacyPane.changeMasterPassword(); 742 }, 743 disabled: () => { 744 return !Services.policies.isAllowed("createMasterPassword"); 745 }, 746 }); 747 748 Preferences.addSetting({ 749 id: "primaryPasswordSet", 750 setup(emitChange) { 751 const topic = "passwordmgr-primary-pw-changed"; 752 Services.obs.addObserver(emitChange, topic); 753 return () => Services.obs.removeObserver(emitChange, topic); 754 }, 755 visible: () => { 756 return LoginHelper.isPrimaryPasswordSet(); 757 }, 758 }); 759 760 Preferences.addSetting({ 761 id: "statusPrimaryPassword", 762 deps: ["primaryPasswordSet"], 763 onUserClick: e => { 764 if (e.target.localName == "moz-button") { 765 e.target.ownerGlobal.gPrivacyPane._removeMasterPassword(); 766 } 767 }, 768 getControlConfig(config) { 769 config.options[0].controlAttrs = { 770 ...config.options[0].controlAttrs, 771 ...(!Services.policies.isAllowed("removeMasterPassword") 772 ? { disabled: "" } 773 : {}), 774 }; 775 return config; 776 }, 777 }); 778 779 Preferences.addSetting({ 780 id: "changePrimaryPassword", 781 deps: ["primaryPasswordSet"], 782 onUserClick: ({ target }) => { 783 target.ownerGlobal.gPrivacyPane.changeMasterPassword(); 784 }, 785 }); 786 787 Preferences.addSetting({ 788 id: "breachAlerts", 789 pref: "signon.management.page.breach-alerts.enabled", 790 }); 791 792 /** 793 * This class is used to create Settings that are used to warn the user about 794 * potential misconfigurations. It should be passed into Preferences.addSetting 795 * to create the Preference for a <moz-box-item> because it creates 796 * separate members on pref.config 797 * 798 * @implements {SettingConfig} 799 */ 800 class WarningSettingConfig { 801 /** 802 * This callback type specifies the most important part of a WarningSettingConfig: how to know 803 * when to warn. 804 * 805 * @callback problematicCallback 806 * @param {WarningSettingConfig} self - this is a Setting config created by the constructor below, 807 * that has been `setup` and not yet cleaned up. Its prefMapping is setup into its properties. 808 * @returns {boolean} Should this Setting show a warning to the user if not yet dismissed? 809 */ 810 811 /** 812 * 813 * @param {string} id - The unique setting ID for the setting created by this config 814 * @param {{[key: string]: string}} prefMapping - A map from member name (to be used in the 815 * `problematic` arg's arg) to pref string, containing all of the preferences this Setting 816 * relies upon. On setup, this object will create properties for each entry here, where the 817 * value is the result of Preferences.get(key). 818 * @param {problematicCallback} problematic - How we determine whether or not to show this 819 * setting initially 820 * @param {boolean} isDismissable - A boolean indicating whether or not we should support dismissing 821 * this setting 822 */ 823 constructor(id, prefMapping, problematic, isDismissable) { 824 this.id = id; 825 this.prefMapping = prefMapping; 826 if (isDismissable) { 827 this.dismissedPrefId = `browser.preferences.config_warning.${this.id}.dismissed`; 828 this.prefMapping.dismissed = this.dismissedPrefId; 829 this.dismissAllPrefId = `browser.preferences.config_warning.dismissAll`; 830 this.prefMapping.dismissAll = this.dismissAllPrefId; 831 } 832 this.problematic = problematic; 833 } 834 835 /** 836 * This item in a warning moz-box-group should be visible if the `problematic` argument 837 * from the constructor says we should, and it isn't hidden. 838 * 839 * @returns {boolean} Whether or not to show this configuration as a warning to the user 840 */ 841 visible() { 842 return ( 843 !this.dismissAll?.value && 844 !this.dismissed?.value && 845 this.problematic(this) 846 ); 847 } 848 849 /** 850 * This resets all of the preferernces in the `prefMapping` from the constructor that have 851 * user-specified values. This includes the dismiss pref as well. 852 */ 853 reset() { 854 for (let getter of Object.keys(this.prefMapping)) { 855 if (this[getter].hasUserValue) { 856 this[getter].reset(); 857 } 858 } 859 } 860 861 /** 862 * When invoked, this sets a pref that persistently hides this setting. See visible(). 863 */ 864 dismiss() { 865 if (this.dismissed) { 866 this.dismissed.value = true; 867 } 868 } 869 870 /** 871 * This initializes the Setting created with this config, starting listeners for all dependent 872 * Preferences and providing a cleanup callback to remove them 873 * 874 * @param {() => any} emitChange - a callback to be invoked any time that the Setting created 875 * with this config is changed 876 * @returns {() => any} a function that cleans up the state from this Setting, namely pref change listeners. 877 */ 878 setup(emitChange) { 879 for (let [getter, prefId] of Object.entries(this.prefMapping)) { 880 this[getter] = Preferences.get(prefId); 881 this[getter].on("change", emitChange); 882 } 883 return () => { 884 for (let getter of Object.keys(this.prefMapping)) { 885 this[getter].off(emitChange); 886 } 887 }; 888 } 889 890 /** 891 * Setting helper to handle clicks of our warning. They may be a "reset" or 892 * "dismiss" action depending on the target, and those callbacks are defined 893 * in this class. 894 * 895 * @param {PointerEvent} event - The event for the user click 896 */ 897 onUserClick(event) { 898 switch (event.target.id) { 899 case "reset": { 900 this.reset(); 901 Glean.securityPreferencesWarnings.warningFixed.record(); 902 break; 903 } 904 case "dismiss": { 905 this.dismiss(); 906 Glean.securityPreferencesWarnings.warningDismissed.record(); 907 break; 908 } 909 } 910 } 911 } 912 913 if (SECURITY_PRIVACY_STATUS_CARD_ENABLED) { 914 Preferences.addSetting( 915 new WarningSettingConfig( 916 "warningTest", 917 { 918 showIssue: "privacy.ui.status_card.testing.show_issue", 919 }, 920 ({ showIssue }) => showIssue.hasUserValue && !showIssue.locked, 921 true 922 ) 923 ); 924 925 Preferences.addSetting( 926 new WarningSettingConfig( 927 "warningAllowFingerprinters", 928 { 929 fingerprintingEnabled: 930 "privacy.trackingprotection.fingerprinting.enabled", 931 }, 932 ({ fingerprintingEnabled }) => 933 !fingerprintingEnabled.value && !fingerprintingEnabled.locked, 934 true 935 ) 936 ); 937 938 Preferences.addSetting( 939 new WarningSettingConfig( 940 "warningThirdPartyCookies", 941 { 942 cookieBehavior: "network.cookie.cookieBehavior", 943 }, 944 ({ cookieBehavior }) => 945 (cookieBehavior.value == 0 || 946 cookieBehavior.value == 3 || 947 cookieBehavior.value == 4) && 948 !cookieBehavior.locked, 949 true 950 ) 951 ); 952 953 Preferences.addSetting( 954 new WarningSettingConfig( 955 "warningPasswordManager", 956 { 957 enabled: "signon.rememberSignons", 958 extentionAllows: "services.passwordSavingEnabled", 959 }, 960 ({ enabled, extentionAllows }) => 961 !enabled.value && !enabled.locked && !extentionAllows.value, 962 true 963 ) 964 ); 965 966 Preferences.addSetting( 967 new WarningSettingConfig( 968 "warningPopupBlocker", 969 { 970 enabled: "dom.disable_open_during_load", 971 }, 972 ({ enabled }) => !enabled.value && !enabled.locked, 973 true 974 ) 975 ); 976 977 Preferences.addSetting( 978 new WarningSettingConfig( 979 "warningExtensionInstall", 980 { 981 blockInstalls: "xpinstall.whitelist.required", 982 }, 983 ({ blockInstalls }) => !blockInstalls.value && !blockInstalls.locked, 984 true 985 ) 986 ); 987 988 Preferences.addSetting( 989 new WarningSettingConfig( 990 "warningSafeBrowsing", 991 { 992 malware: "browser.safebrowsing.malware.enabled", 993 phishing: "browser.safebrowsing.phishing.enabled", 994 downloads: "browser.safebrowsing.downloads.enabled", 995 unwantedDownloads: 996 "browser.safebrowsing.downloads.remote.block_potentially_unwanted", 997 uncommonDownloads: 998 "browser.safebrowsing.downloads.remote.block_potentially_unwanted", 999 }, 1000 ({ 1001 malware, 1002 phishing, 1003 downloads, 1004 unwantedDownloads, 1005 uncommonDownloads, 1006 }) => 1007 (!malware.value && !malware.locked) || 1008 (!phishing.value && !phishing.locked) || 1009 (!downloads.value && !downloads.locked) || 1010 (!unwantedDownloads.value && !unwantedDownloads.locked) || 1011 (!uncommonDownloads.value && !uncommonDownloads.locked), 1012 true 1013 ) 1014 ); 1015 1016 Preferences.addSetting( 1017 new WarningSettingConfig( 1018 "warningDoH", 1019 { 1020 dohMode: "network.trr.mode", 1021 }, 1022 ({ dohMode }) => dohMode.value == 5 && !dohMode.locked, 1023 true 1024 ) 1025 ); 1026 1027 Preferences.addSetting( 1028 new WarningSettingConfig( 1029 "warningECH", 1030 { 1031 echEnabled: "network.dns.echconfig.enabled", 1032 https3echEnabled: "network.dns.http3_echconfig.enabled", 1033 }, 1034 ({ echEnabled, https3echEnabled }) => 1035 (!echEnabled.value && !echEnabled.locked) || 1036 (!https3echEnabled.value && !https3echEnabled.locked), 1037 true 1038 ) 1039 ); 1040 1041 Preferences.addSetting( 1042 new WarningSettingConfig( 1043 "warningCT", 1044 { 1045 ctMode: "security.pki.certificate_transparency.mode", 1046 }, 1047 ({ ctMode }) => ctMode.value != 2 && !ctMode.locked, 1048 true 1049 ) 1050 ); 1051 1052 Preferences.addSetting( 1053 new WarningSettingConfig( 1054 "warningCRLite", 1055 { 1056 crliteMode: "security.pki.crlite_mode", 1057 }, 1058 ({ crliteMode }) => crliteMode.value != 2 && !crliteMode.locked, 1059 true 1060 ) 1061 ); 1062 1063 Preferences.addSetting( 1064 new WarningSettingConfig( 1065 "warningCertificatePinning", 1066 { 1067 pinningLevel: "security.cert_pinning.enforcement_level", 1068 }, 1069 ({ pinningLevel }) => pinningLevel.value < 1 && !pinningLevel.locked, 1070 true 1071 ) 1072 ); 1073 1074 Preferences.addSetting( 1075 new WarningSettingConfig( 1076 "warningTLSMin", 1077 { 1078 tlsMin: "security.tls.version.min", 1079 enableDeprecated: "security.tls.version.enable-deprecated", 1080 fallbackLimit: "security.tls.version.fallback-limit", 1081 }, 1082 ({ tlsMin, enableDeprecated, fallbackLimit }) => 1083 (tlsMin.value < 3 && !tlsMin.locked) || 1084 (enableDeprecated.value && !tlsMin.locked) || 1085 (fallbackLimit.value < 4 && !tlsMin.locked), 1086 true 1087 ) 1088 ); 1089 1090 Preferences.addSetting( 1091 new WarningSettingConfig( 1092 "warningTLSMax", 1093 { 1094 tlsMax: "security.tls.version.max", 1095 }, 1096 ({ tlsMax }) => tlsMax.value < 4 && !tlsMax.locked, 1097 true 1098 ) 1099 ); 1100 1101 Preferences.addSetting( 1102 new WarningSettingConfig( 1103 "warningProxyAutodetection", 1104 { 1105 proxyType: "network.proxy.type", 1106 }, 1107 ({ proxyType }) => proxyType.value == 2 && !proxyType.locked, 1108 true 1109 ) 1110 ); 1111 1112 Preferences.addSetting( 1113 new WarningSettingConfig( 1114 "warningContentResourceURI", 1115 { 1116 contentResourceURIAccessible: 1117 "security.all_resource_uri_content_accessible", 1118 }, 1119 ({ contentResourceURIAccessible }) => 1120 contentResourceURIAccessible.value && 1121 !contentResourceURIAccessible.locked, 1122 true 1123 ) 1124 ); 1125 1126 Preferences.addSetting( 1127 new WarningSettingConfig( 1128 "warningWorkerMIME", 1129 { 1130 workerMimeTypeBlock: "security.block_Worker_with_wrong_mime", 1131 }, 1132 ({ workerMimeTypeBlock }) => 1133 !workerMimeTypeBlock.value && !workerMimeTypeBlock.locked, 1134 true 1135 ) 1136 ); 1137 1138 Preferences.addSetting( 1139 new WarningSettingConfig( 1140 "warningTopLevelDataURI", 1141 { 1142 blockNav: "security.data_uri.block_toplevel_data_uri_navigations", 1143 }, 1144 ({ blockNav }) => !blockNav.value && !blockNav.locked, 1145 true 1146 ) 1147 ); 1148 1149 Preferences.addSetting( 1150 new WarningSettingConfig( 1151 "warningActiveMixedContent", 1152 { 1153 blockedMixedContent: "security.mixed_content.block_active_content", 1154 }, 1155 ({ blockedMixedContent }) => 1156 !blockedMixedContent.value && !blockedMixedContent.locked, 1157 true 1158 ) 1159 ); 1160 1161 Preferences.addSetting( 1162 new WarningSettingConfig( 1163 "warningInnerHTMLltgt", 1164 { 1165 escapeLtGt: "dom.security.html_serialization_escape_lt_gt", 1166 }, 1167 ({ escapeLtGt }) => !escapeLtGt.value && !escapeLtGt.locked, 1168 true 1169 ) 1170 ); 1171 1172 Preferences.addSetting( 1173 new WarningSettingConfig( 1174 "warningFileURIOrigin", 1175 { 1176 fileURIStrictOrigin: "security.fileuri.strict_origin_policy", 1177 }, 1178 ({ fileURIStrictOrigin }) => 1179 !fileURIStrictOrigin.value && !fileURIStrictOrigin.locked, 1180 true 1181 ) 1182 ); 1183 1184 Preferences.addSetting( 1185 new WarningSettingConfig( 1186 "warningPrivilegedConstraint", 1187 { 1188 shfa: "dom.security.skip_html_fragment_assertion", 1189 xhtmlcsp: "security.browser_xhtml_csp.enabled", 1190 allowUDPEE: "security.allow_unsafe_dangerous_privileged_evil_eval", 1191 allowEvalInParent: "security.allow_eval_in_parent_process", 1192 allowEvalBySystem: "security.allow_eval_with_system_principal", 1193 allowUnsafeParentLoads: "security.allow_unsafe_parent_loads", 1194 allowParentUnrestrictedJSLoads: 1195 "security.allow_parent_unrestricted_js_loads", 1196 skipRemoteScriptAssertionInSystem: 1197 "dom.security.skip_remote_script_assertion_in_system_priv_context", 1198 }, 1199 ({ 1200 shfa, 1201 xhtmlcsp, 1202 allowUDPEE, 1203 allowEvalInParent, 1204 allowEvalBySystem, 1205 allowUnsafeParentLoads, 1206 allowParentUnrestrictedJSLoads, 1207 skipRemoteScriptAssertionInSystem, 1208 }) => 1209 (!xhtmlcsp.value && !xhtmlcsp.locked) || 1210 (shfa.value && !shfa.locked) || 1211 (allowUDPEE.value && !allowUDPEE.locked) || 1212 (allowEvalInParent.value && !allowEvalInParent.locked) || 1213 (allowEvalBySystem.value && !allowEvalBySystem.locked) || 1214 (allowUnsafeParentLoads.value && !allowUnsafeParentLoads.locked) || 1215 (allowParentUnrestrictedJSLoads.value && 1216 !allowParentUnrestrictedJSLoads.locked) || 1217 (skipRemoteScriptAssertionInSystem.value && 1218 !skipRemoteScriptAssertionInSystem.locked), 1219 true 1220 ) 1221 ); 1222 1223 Preferences.addSetting( 1224 new WarningSettingConfig( 1225 "warningProcessSandbox", 1226 { 1227 macNoWindowServer: 1228 "security.sandbox.content.mac.disconnect-windowserver", 1229 contentWriteWhitelist: "security.sandbox.content.write_path_whitelist", 1230 contentReadWhitelist: "security.sandbox.content.read_path_whitelist", 1231 contentSyscallWhitelist: "security.sandbox.content.syscall_whitelist", 1232 contentSandboxLevel: "security.sandbox.content.level", 1233 socketSandboxLevel: "security.sandbox.socket.process.level", 1234 gpuSandboxLevel: "security.sandbox.gpu.level", 1235 content32kDisable: "security.sandbox.content.win32k-disable", 1236 gmp32kDisable: "security.sandbox.gmp.win32k-disable", 1237 gmpACGEnable: "security.sandbox.gmp.acg.enabled", 1238 socket32kDisable: "security.sandbox.socket.win32k-disable", 1239 rddShadowStackEnabled: "security.sandbox.rdd.shadow-stack.enabled", 1240 socketShadowStackEnabled: 1241 "security.sandbox.socket.shadow-stack.enabled", 1242 gpuShadowStackEnabled: "security.sandbox.gpu.shadow-stack.enabled", 1243 gmpShadowStackEnabled: "security.sandbox.gmp.shadow-stack.enabled", 1244 utilityWmfCdmLpacEnabled: 1245 "security.sandbox.utility-wmf-cdm.lpac.enabled", 1246 rddACGEnabled: "security.sandbox.rdd.acg.enabled", 1247 utilityWmfACGEnabled: "security.sandbox.utility-wmf.acg.enabled", 1248 }, 1249 ({ 1250 macNoWindowServer, 1251 contentWriteWhitelist, 1252 contentReadWhitelist, 1253 contentSyscallWhitelist, 1254 contentSandboxLevel, 1255 socketSandboxLevel, 1256 gpuSandboxLevel, 1257 content32kDisable, 1258 gmp32kDisable, 1259 gmpACGEnable, 1260 socket32kDisable, 1261 rddShadowStackEnabled, 1262 socketShadowStackEnabled, 1263 gpuShadowStackEnabled, 1264 gmpShadowStackEnabled, 1265 utilityWmfCdmLpacEnabled, 1266 rddACGEnabled, 1267 utilityWmfACGEnabled, 1268 }) => 1269 (macNoWindowServer.hasUserValue && !macNoWindowServer.locked) || 1270 (contentWriteWhitelist.hasUserValue && !contentWriteWhitelist.locked) || 1271 (contentReadWhitelist.hasUserValue && !contentReadWhitelist.locked) || 1272 (contentSyscallWhitelist.hasUserValue && 1273 !contentSyscallWhitelist.locked) || 1274 (contentSandboxLevel.hasUserValue && !contentSandboxLevel.locked) || 1275 (socketSandboxLevel.hasUserValue && !socketSandboxLevel.locked) || 1276 (gpuSandboxLevel.hasUserValue && !gpuSandboxLevel.locked) || 1277 (content32kDisable.hasUserValue && !content32kDisable.locked) || 1278 (gmp32kDisable.hasUserValue && !gmp32kDisable.locked) || 1279 (gmpACGEnable.hasUserValue && !gmpACGEnable.locked) || 1280 (socket32kDisable.hasUserValue && !socket32kDisable.locked) || 1281 (rddShadowStackEnabled.hasUserValue && !rddShadowStackEnabled.locked) || 1282 (socketShadowStackEnabled.hasUserValue && 1283 !socketShadowStackEnabled.locked) || 1284 (gpuShadowStackEnabled.hasUserValue && !gpuShadowStackEnabled.locked) || 1285 (gmpShadowStackEnabled.hasUserValue && !gmpShadowStackEnabled.locked) || 1286 (utilityWmfCdmLpacEnabled.hasUserValue && 1287 !utilityWmfCdmLpacEnabled.locked) || 1288 (rddACGEnabled.hasUserValue && !rddACGEnabled.locked) || 1289 (utilityWmfACGEnabled.hasUserValue && !utilityWmfACGEnabled.locked), 1290 1291 true 1292 ) 1293 ); 1294 1295 /** @type {SettingControlConfig[]} */ 1296 const SECURITY_WARNINGS = [ 1297 { 1298 l10nId: "security-privacy-issue-warning-test", 1299 id: "warningTest", 1300 }, 1301 { 1302 l10nId: "security-privacy-issue-warning-fingerprinters", 1303 id: "warningAllowFingerprinters", 1304 }, 1305 { 1306 l10nId: "security-privacy-issue-warning-third-party-cookies", 1307 id: "warningThirdPartyCookies", 1308 }, 1309 { 1310 l10nId: "security-privacy-issue-warning-password-manager", 1311 id: "warningPasswordManager", 1312 }, 1313 { 1314 l10nId: "security-privacy-issue-warning-popup-blocker", 1315 id: "warningPopupBlocker", 1316 }, 1317 { 1318 l10nId: "security-privacy-issue-warning-extension-install", 1319 id: "warningExtensionInstall", 1320 }, 1321 { 1322 l10nId: "security-privacy-issue-warning-safe-browsing", 1323 id: "warningSafeBrowsing", 1324 }, 1325 { 1326 l10nId: "security-privacy-issue-warning-doh", 1327 id: "warningDoH", 1328 }, 1329 { 1330 l10nId: "security-privacy-issue-warning-ech", 1331 id: "warningECH", 1332 }, 1333 { 1334 l10nId: "security-privacy-issue-warning-ct", 1335 id: "warningCT", 1336 }, 1337 { 1338 l10nId: "security-privacy-issue-warning-crlite", 1339 id: "warningCRLite", 1340 }, 1341 { 1342 l10nId: "security-privacy-issue-warning-certificate-pinning", 1343 id: "warningCertificatePinning", 1344 }, 1345 { 1346 l10nId: "security-privacy-issue-warning-tlsmin", 1347 id: "warningTLSMin", 1348 }, 1349 { 1350 l10nId: "security-privacy-issue-warning-tlsmax", 1351 id: "warningTLSMax", 1352 }, 1353 { 1354 l10nId: "security-privacy-issue-warning-proxy-autodetection", 1355 id: "warningProxyAutodetection", 1356 }, 1357 { 1358 l10nId: "security-privacy-issue-warning-content-resource-uri", 1359 id: "warningContentResourceURI", 1360 }, 1361 { 1362 l10nId: "security-privacy-issue-warning-worker-mime", 1363 id: "warningWorkerMIME", 1364 }, 1365 { 1366 l10nId: "security-privacy-issue-warning-top-level-data-uri", 1367 id: "warningTopLevelDataURI", 1368 }, 1369 { 1370 l10nId: "security-privacy-issue-warning-active-mixed-content", 1371 id: "warningActiveMixedContent", 1372 }, 1373 { 1374 l10nId: "security-privacy-issue-warning-inner-html-ltgt", 1375 id: "warningInnerHTMLltgt", 1376 }, 1377 { 1378 l10nId: "security-privacy-issue-warning-file-uri-origin", 1379 id: "warningFileURIOrigin", 1380 }, 1381 { 1382 l10nId: "security-privacy-issue-warning-privileged-constraint", 1383 id: "warningPrivilegedConstraint", 1384 }, 1385 { 1386 l10nId: "security-privacy-issue-warning-process-sandbox", 1387 id: "warningProcessSandbox", 1388 }, 1389 ]; 1390 1391 Preferences.addSetting( 1392 /** @type {{ makeSecurityWarningItems: () => SettingControlConfig[] } & SettingConfig} */ ({ 1393 id: "securityWarningsGroup", 1394 makeSecurityWarningItems() { 1395 return SECURITY_WARNINGS.map(({ id, l10nId }) => ({ 1396 id, 1397 l10nId, 1398 control: "moz-box-item", 1399 options: [ 1400 { 1401 control: "moz-button", 1402 l10nId: "issue-card-reset-button", 1403 controlAttrs: { slot: "actions", size: "small", id: "reset" }, 1404 }, 1405 { 1406 control: "moz-button", 1407 l10nId: "issue-card-dismiss-button", 1408 controlAttrs: { 1409 slot: "actions", 1410 size: "small", 1411 iconsrc: "chrome://global/skin/icons/close.svg", 1412 id: "dismiss", 1413 }, 1414 }, 1415 ], 1416 })); 1417 }, 1418 getControlConfig(config) { 1419 if (!config.items) { 1420 return { ...config, items: this.makeSecurityWarningItems() }; 1421 } 1422 return config; 1423 }, 1424 }) 1425 ); 1426 1427 Preferences.addSetting({ 1428 id: "privacyCard", 1429 deps: [ 1430 "appUpdateStatus", 1431 "trackerCount", 1432 "etpStrictEnabled", 1433 ...SECURITY_WARNINGS.map(warning => warning.id), 1434 ], 1435 }); 1436 1437 Preferences.addSetting({ 1438 id: "warningCard", 1439 deps: SECURITY_WARNINGS.map(warning => warning.id), 1440 visible: deps => { 1441 const count = Object.values(deps).filter( 1442 depSetting => depSetting.visible 1443 ).length; 1444 if (!this._telemetrySent) { 1445 Glean.securityPreferencesWarnings.warningsShown.record({ count }); 1446 this._telemetrySent = true; 1447 } 1448 return count > 0; 1449 }, 1450 }); 1451 } 1452 1453 Preferences.addSetting({ 1454 id: "ipProtectionVisible", 1455 pref: "browser.ipProtection.enabled", 1456 }); 1457 Preferences.addSetting({ 1458 id: "ipProtectionSiteExceptionsFeatureEnabled", 1459 pref: "browser.ipProtection.features.siteExceptions", 1460 }); 1461 Preferences.addSetting({ 1462 id: "ipProtectionExceptions", 1463 deps: ["ipProtectionVisible", "ipProtectionSiteExceptionsFeatureEnabled"], 1464 visible: ({ 1465 ipProtectionVisible, 1466 ipProtectionSiteExceptionsFeatureEnabled, 1467 }) => 1468 ipProtectionVisible.value && ipProtectionSiteExceptionsFeatureEnabled.value, 1469 }); 1470 1471 Preferences.addSetting({ 1472 id: "ipProtectionExceptionAllListButton", 1473 deps: ["ipProtectionVisible", "ipProtectionSiteExceptionsFeatureEnabled"], 1474 setup(emitChange) { 1475 let permObserver = { 1476 observe(subject, topic, _data) { 1477 if (subject && topic === "perm-changed") { 1478 let permission = subject.QueryInterface(Ci.nsIPermission); 1479 if (permission.type === "ipp-vpn") { 1480 emitChange(); 1481 } 1482 } 1483 }, 1484 }; 1485 Services.obs.addObserver(permObserver, "perm-changed"); 1486 return () => { 1487 Services.obs.removeObserver(permObserver, "perm-changed"); 1488 }; 1489 }, 1490 visible: ({ 1491 ipProtectionVisible, 1492 ipProtectionSiteExceptionsFeatureEnabled, 1493 }) => 1494 ipProtectionVisible.value && ipProtectionSiteExceptionsFeatureEnabled.value, 1495 onUserClick() { 1496 let params = { 1497 addVisible: true, 1498 hideStatusColumn: true, 1499 prefilledHost: "", 1500 permissionType: "ipp-vpn", 1501 capabilityFilter: Ci.nsIPermissionManager.DENY_ACTION, 1502 }; 1503 1504 gSubDialog.open( 1505 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 1506 { features: "resizable=yes" }, 1507 params 1508 ); 1509 }, 1510 getControlConfig(config) { 1511 let l10nId = "ip-protection-site-exceptions-all-sites-button"; 1512 1513 let savedExceptions = Services.perms.getAllByTypes(["ipp-vpn"]); 1514 let numberOfExclusions = savedExceptions.filter( 1515 perm => perm.capability === Ci.nsIPermissionManager.DENY_ACTION 1516 ).length; 1517 1518 let l10nArgs = { 1519 count: numberOfExclusions, 1520 }; 1521 1522 return { 1523 ...config, 1524 l10nId, 1525 l10nArgs, 1526 }; 1527 }, 1528 }); 1529 Preferences.addSetting({ 1530 id: "ipProtectionAutoStartFeatureEnabled", 1531 pref: "browser.ipProtection.features.autoStart", 1532 get: prefVal => prefVal, 1533 }); 1534 Preferences.addSetting({ 1535 id: "ipProtectionAutoStart", 1536 deps: ["ipProtectionVisible", "ipProtectionAutoStartFeatureEnabled"], 1537 visible: ({ ipProtectionVisible, ipProtectionAutoStartFeatureEnabled }) => 1538 ipProtectionVisible.value && ipProtectionAutoStartFeatureEnabled.value, 1539 }); 1540 Preferences.addSetting({ 1541 id: "ipProtectionAutoStartCheckbox", 1542 pref: "browser.ipProtection.autoStartEnabled", 1543 deps: ["ipProtectionVisible", "ipProtectionAutoStart"], 1544 visible: ({ ipProtectionVisible }) => ipProtectionVisible.value, 1545 }); 1546 Preferences.addSetting({ 1547 id: "ipProtectionAutoStartPrivateCheckbox", 1548 pref: "browser.ipProtection.autoStartPrivateEnabled", 1549 deps: ["ipProtectionVisible", "ipProtectionAutoStart"], 1550 visible: ({ ipProtectionVisible }) => ipProtectionVisible.value, 1551 }); 1552 Preferences.addSetting({ 1553 id: "ipProtectionAdditionalLinks", 1554 deps: ["ipProtectionVisible"], 1555 visible: ({ ipProtectionVisible }) => ipProtectionVisible.value, 1556 }); 1557 1558 // Study opt out 1559 if (AppConstants.MOZ_DATA_REPORTING) { 1560 Preferences.addAll([ 1561 // Preference instances for prefs that we need to monitor while the page is open. 1562 { id: PREF_OPT_OUT_STUDIES_ENABLED, type: "bool" }, 1563 { id: PREF_ADDON_RECOMMENDATIONS_ENABLED, type: "bool" }, 1564 { id: PREF_UPLOAD_ENABLED, type: "bool" }, 1565 { id: "datareporting.usage.uploadEnabled", type: "bool" }, 1566 { id: "dom.private-attribution.submission.enabled", type: "bool" }, 1567 ]); 1568 } 1569 // Privacy segmentation section 1570 Preferences.add({ 1571 id: "browser.dataFeatureRecommendations.enabled", 1572 type: "bool", 1573 }); 1574 1575 // Data Choices tab 1576 if (AppConstants.MOZ_CRASHREPORTER) { 1577 Preferences.add({ 1578 id: "browser.crashReports.unsubmittedCheck.autoSubmit2", 1579 type: "bool", 1580 }); 1581 } 1582 1583 Preferences.addSetting({ 1584 id: "gpcFunctionalityEnabled", 1585 pref: "privacy.globalprivacycontrol.functionality.enabled", 1586 }); 1587 Preferences.addSetting({ 1588 id: "gpcEnabled", 1589 pref: "privacy.globalprivacycontrol.enabled", 1590 deps: ["gpcFunctionalityEnabled"], 1591 visible: ({ gpcFunctionalityEnabled }) => { 1592 return gpcFunctionalityEnabled.value; 1593 }, 1594 }); 1595 Preferences.addSetting({ 1596 id: "relayFeature", 1597 pref: "signon.firefoxRelay.feature", 1598 }); 1599 Preferences.addSetting({ 1600 id: "relayIntegration", 1601 deps: ["savePasswords", "relayFeature"], 1602 visible: () => { 1603 return FirefoxRelay.isAvailable; 1604 }, 1605 disabled: ({ savePasswords, relayFeature }) => { 1606 return !savePasswords.value || relayFeature.pref.locked; 1607 }, 1608 get() { 1609 return FirefoxRelay.isAvailable && !FirefoxRelay.isDisabled; 1610 }, 1611 set(checked) { 1612 if (checked) { 1613 FirefoxRelay.markAsAvailable(); 1614 } else { 1615 FirefoxRelay.markAsDisabled(); 1616 } 1617 }, 1618 onUserChange(checked) { 1619 if (checked) { 1620 Glean.relayIntegration.enabledPrefChange.record(); 1621 } else { 1622 Glean.relayIntegration.disabledPrefChange.record(); 1623 } 1624 }, 1625 }); 1626 Preferences.addSetting({ 1627 id: "dntHeaderEnabled", 1628 pref: "privacy.donottrackheader.enabled", 1629 }); 1630 Preferences.addSetting({ 1631 id: "dntRemoval", 1632 pref: "browser.preferences.config_warning.donottrackheader.dismissed", 1633 deps: ["dntHeaderEnabled"], 1634 visible: ({ dntHeaderEnabled }, setting) => { 1635 return dntHeaderEnabled.value && !setting.value; 1636 }, 1637 onUserClick: (event, _deps, setting) => { 1638 let dismissButton = event.target?.shadowRoot?.querySelector(".close"); 1639 if ( 1640 dismissButton?.shadowRoot && 1641 event.originalTarget && 1642 dismissButton.shadowRoot.contains(event.originalTarget) 1643 ) { 1644 setting.value = true; 1645 } 1646 }, 1647 }); 1648 1649 Preferences.addSetting({ 1650 id: "httpsOnlyEnabled", 1651 pref: "dom.security.https_only_mode", 1652 }); 1653 Preferences.addSetting({ 1654 id: "httpsOnlyEnabledPBM", 1655 pref: "dom.security.https_only_mode_pbm", 1656 }); 1657 Preferences.addSetting({ 1658 id: "httpsOnlyRadioGroup", 1659 deps: ["httpsOnlyEnabled", "httpsOnlyEnabledPBM"], 1660 get: (_value, deps) => { 1661 if (deps.httpsOnlyEnabled.value) { 1662 return "enabled"; 1663 } 1664 if (deps.httpsOnlyEnabledPBM.value) { 1665 return "privateOnly"; 1666 } 1667 return "disabled"; 1668 }, 1669 set: (value, deps) => { 1670 if (value == "enabled") { 1671 deps.httpsOnlyEnabled.value = true; 1672 deps.httpsOnlyEnabledPBM.value = false; 1673 } else if (value == "privateOnly") { 1674 deps.httpsOnlyEnabled.value = false; 1675 deps.httpsOnlyEnabledPBM.value = true; 1676 } else if (value == "disabled") { 1677 deps.httpsOnlyEnabled.value = false; 1678 deps.httpsOnlyEnabledPBM.value = false; 1679 } 1680 }, 1681 disabled: deps => { 1682 return deps.httpsOnlyEnabled.locked || deps.httpsOnlyEnabledPBM.locked; 1683 }, 1684 }); 1685 Preferences.addSetting({ 1686 id: "httpsFirstEnabled", 1687 pref: "dom.security.https_first", 1688 }); 1689 Preferences.addSetting({ 1690 id: "httpsFirstEnabledPBM", 1691 pref: "dom.security.https_first_pbm", 1692 }); 1693 Preferences.addSetting({ 1694 id: "httpsOnlyExceptionButton", 1695 deps: [ 1696 "httpsOnlyEnabled", 1697 "httpsOnlyEnabledPBM", 1698 "httpsFirstEnabled", 1699 "httpsFirstEnabledPBM", 1700 ], 1701 disabled: deps => { 1702 return ( 1703 !deps.httpsOnlyEnabled.value && 1704 !deps.httpsOnlyEnabledPBM.value && 1705 !deps.httpsFirstEnabled.value && 1706 !deps.httpsFirstEnabledPBM.value 1707 ); 1708 }, 1709 onUserClick: () => { 1710 gPrivacyPane.showHttpsOnlyModeExceptions(); 1711 }, 1712 }); 1713 1714 Preferences.addSetting({ 1715 id: "enableSafeBrowsingPhishing", 1716 pref: "browser.safebrowsing.phishing.enabled", 1717 }); 1718 Preferences.addSetting({ 1719 id: "enableSafeBrowsingMalware", 1720 pref: "browser.safebrowsing.malware.enabled", 1721 }); 1722 Preferences.addSetting({ 1723 id: "enableSafeBrowsing", 1724 deps: ["enableSafeBrowsingPhishing", "enableSafeBrowsingMalware"], 1725 get: (_value, deps) => { 1726 return ( 1727 deps.enableSafeBrowsingPhishing.value && 1728 deps.enableSafeBrowsingMalware.value 1729 ); 1730 }, 1731 set: (value, deps) => { 1732 deps.enableSafeBrowsingPhishing.value = value; 1733 deps.enableSafeBrowsingMalware.value = value; 1734 }, 1735 disabled: deps => { 1736 return ( 1737 deps.enableSafeBrowsingPhishing.locked || 1738 deps.enableSafeBrowsingMalware.locked 1739 ); 1740 }, 1741 }); 1742 Preferences.addSetting({ 1743 id: "blockDownloads", 1744 pref: "browser.safebrowsing.downloads.enabled", 1745 deps: ["enableSafeBrowsing"], 1746 disabled: (deps, self) => { 1747 return !deps.enableSafeBrowsing.value || self.locked; 1748 }, 1749 }); 1750 Preferences.addSetting({ 1751 id: "malwareTable", 1752 pref: "urlclassifier.malwareTable", 1753 }); 1754 Preferences.addSetting({ 1755 id: "blockUncommonDownloads", 1756 pref: "browser.safebrowsing.downloads.remote.block_uncommon", 1757 }); 1758 Preferences.addSetting({ 1759 id: "blockUnwantedDownloads", 1760 pref: "browser.safebrowsing.downloads.remote.block_potentially_unwanted", 1761 }); 1762 Preferences.addSetting({ 1763 id: "blockUncommonUnwanted", 1764 deps: [ 1765 "enableSafeBrowsing", 1766 "blockDownloads", 1767 "blockUncommonDownloads", 1768 "blockUnwantedDownloads", 1769 ], 1770 get: (_value, deps) => { 1771 return ( 1772 deps.blockUncommonDownloads.value && deps.blockUnwantedDownloads.value 1773 ); 1774 }, 1775 set: (value, deps) => { 1776 deps.blockUncommonDownloads.value = value; 1777 deps.blockUnwantedDownloads.value = value; 1778 1779 let malwareTable = Preferences.get("urlclassifier.malwareTable"); 1780 let malware = /** @type {string} */ (malwareTable.value) 1781 .split(",") 1782 .filter( 1783 x => 1784 x !== "goog-unwanted-proto" && 1785 x !== "goog-unwanted-shavar" && 1786 x !== "moztest-unwanted-simple" 1787 ); 1788 1789 if (value) { 1790 if (malware.includes("goog-malware-shavar")) { 1791 malware.push("goog-unwanted-shavar"); 1792 } else { 1793 malware.push("goog-unwanted-proto"); 1794 } 1795 malware.push("moztest-unwanted-simple"); 1796 } 1797 1798 // sort alphabetically to keep the pref consistent 1799 malware.sort(); 1800 malwareTable.value = malware.join(","); 1801 1802 // Force an update after changing the malware table. 1803 listManager.forceUpdates(malwareTable.value); 1804 }, 1805 disabled: deps => { 1806 return ( 1807 !deps.enableSafeBrowsing.value || 1808 !deps.blockDownloads.value || 1809 deps.blockUncommonDownloads.locked || 1810 deps.blockUnwantedDownloads.locked 1811 ); 1812 }, 1813 }); 1814 Preferences.addSetting({ 1815 id: "manageDataSettingsGroup", 1816 }); 1817 Preferences.addSetting( 1818 /** @type {{ isUpdatingSites: boolean, usage: { value: number, unit: string } | void } & SettingConfig} */ ({ 1819 id: "siteDataSize", 1820 usage: null, 1821 isUpdatingSites: false, 1822 setup(emitChange) { 1823 let onUsageChanged = async () => { 1824 let [siteDataUsage, cacheUsage] = await Promise.all([ 1825 SiteDataManager.getTotalUsage(), 1826 SiteDataManager.getCacheSize(), 1827 ]); 1828 let totalUsage = siteDataUsage + cacheUsage; 1829 let [value, unit] = DownloadUtils.convertByteUnits(totalUsage); 1830 this.usage = { value, unit }; 1831 1832 this.isUpdatingSites = false; 1833 emitChange(); 1834 }; 1835 1836 let onUpdatingSites = () => { 1837 this.isUpdatingSites = true; 1838 emitChange(); 1839 }; 1840 1841 Services.obs.addObserver(onUsageChanged, "sitedatamanager:sites-updated"); 1842 Services.obs.addObserver( 1843 onUpdatingSites, 1844 "sitedatamanager:updating-sites" 1845 ); 1846 1847 return () => { 1848 Services.obs.removeObserver( 1849 onUsageChanged, 1850 "sitedatamanager:sites-updated" 1851 ); 1852 Services.obs.removeObserver( 1853 onUpdatingSites, 1854 "sitedatamanager:updating-sites" 1855 ); 1856 }; 1857 }, 1858 getControlConfig(config) { 1859 if (this.isUpdatingSites || !this.usage) { 1860 // Data not retrieved yet, show a loading state. 1861 return { 1862 ...config, 1863 l10nId: "sitedata-total-size-calculating", 1864 }; 1865 } 1866 1867 let { value, unit } = this.usage; 1868 return { 1869 ...config, 1870 l10nId: "sitedata-total-size2", 1871 l10nArgs: { 1872 value, 1873 unit, 1874 }, 1875 }; 1876 }, 1877 }) 1878 ); 1879 1880 Preferences.addSetting({ 1881 id: "deleteOnCloseInfo", 1882 deps: ["privateBrowsingAutoStart"], 1883 visible({ privateBrowsingAutoStart }) { 1884 return privateBrowsingAutoStart.value; 1885 }, 1886 }); 1887 1888 Preferences.addSetting( 1889 /** @type {{ isUpdatingSites: boolean } & SettingConfig} */ ({ 1890 id: "clearSiteDataButton", 1891 isUpdatingSites: false, 1892 setup(emitChange) { 1893 let onSitesUpdated = async () => { 1894 this.isUpdatingSites = false; 1895 emitChange(); 1896 }; 1897 1898 let onUpdatingSites = () => { 1899 this.isUpdatingSites = true; 1900 emitChange(); 1901 }; 1902 1903 Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); 1904 Services.obs.addObserver( 1905 onUpdatingSites, 1906 "sitedatamanager:updating-sites" 1907 ); 1908 1909 return () => { 1910 Services.obs.removeObserver( 1911 onSitesUpdated, 1912 "sitedatamanager:sites-updated" 1913 ); 1914 Services.obs.removeObserver( 1915 onUpdatingSites, 1916 "sitedatamanager:updating-sites" 1917 ); 1918 }; 1919 }, 1920 onUserClick() { 1921 let uri; 1922 if (useOldClearHistoryDialog) { 1923 uri = 1924 "chrome://browser/content/preferences/dialogs/clearSiteData.xhtml"; 1925 } else { 1926 uri = "chrome://browser/content/sanitize_v2.xhtml"; 1927 } 1928 1929 gSubDialog.open( 1930 uri, 1931 { 1932 features: "resizable=no", 1933 }, 1934 { 1935 mode: "clearSiteData", 1936 } 1937 ); 1938 }, 1939 disabled() { 1940 return this.isUpdatingSites; 1941 }, 1942 }) 1943 ); 1944 Preferences.addSetting( 1945 /** @type {{ isUpdatingSites: boolean } & SettingConfig} */ ({ 1946 id: "siteDataSettings", 1947 isUpdatingSites: false, 1948 setup(emitChange) { 1949 let onSitesUpdated = async () => { 1950 this.isUpdatingSites = false; 1951 emitChange(); 1952 }; 1953 1954 let onUpdatingSites = () => { 1955 this.isUpdatingSites = true; 1956 emitChange(); 1957 }; 1958 1959 Services.obs.addObserver(onSitesUpdated, "sitedatamanager:sites-updated"); 1960 Services.obs.addObserver( 1961 onUpdatingSites, 1962 "sitedatamanager:updating-sites" 1963 ); 1964 1965 return () => { 1966 Services.obs.removeObserver( 1967 onSitesUpdated, 1968 "sitedatamanager:sites-updated" 1969 ); 1970 Services.obs.removeObserver( 1971 onUpdatingSites, 1972 "sitedatamanager:updating-sites" 1973 ); 1974 }; 1975 }, 1976 onUserClick() { 1977 gSubDialog.open( 1978 "chrome://browser/content/preferences/dialogs/siteDataSettings.xhtml" 1979 ); 1980 }, 1981 disabled() { 1982 return this.isUpdatingSites; 1983 }, 1984 }) 1985 ); 1986 Preferences.addSetting({ 1987 id: "cookieExceptions", 1988 onUserClick() { 1989 gSubDialog.open( 1990 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 1991 {}, 1992 { 1993 blockVisible: true, 1994 sessionVisible: true, 1995 allowVisible: true, 1996 prefilledHost: "", 1997 permissionType: "cookie", 1998 } 1999 ); 2000 }, 2001 }); 2002 2003 function isCookiesAndStorageClearingOnShutdown() { 2004 // We have to branch between the old clear on shutdown prefs and new prefs after the clear history revamp (Bug 1853996) 2005 // Once the old dialog is deprecated, we can remove these branches. 2006 if (useOldClearHistoryDialog) { 2007 return ( 2008 Preferences.get("privacy.sanitize.sanitizeOnShutdown").value && 2009 Preferences.get("privacy.clearOnShutdown.cookies").value && 2010 Preferences.get("privacy.clearOnShutdown.cache").value && 2011 Preferences.get("privacy.clearOnShutdown.offlineApps").value 2012 ); 2013 } 2014 return ( 2015 Preferences.get("privacy.sanitize.sanitizeOnShutdown").value && 2016 Preferences.get("privacy.clearOnShutdown_v2.cookiesAndStorage").value && 2017 Preferences.get("privacy.clearOnShutdown_v2.cache").value 2018 ); 2019 } 2020 2021 /* 2022 * Unsets cleaning prefs that do not belong to DeleteOnClose 2023 */ 2024 function resetCleaningPrefs() { 2025 let sanitizeOnShutdownPrefsArray = useOldClearHistoryDialog 2026 ? SANITIZE_ON_SHUTDOWN_PREFS_ONLY 2027 : SANITIZE_ON_SHUTDOWN_PREFS_ONLY_V2; 2028 2029 return sanitizeOnShutdownPrefsArray.forEach( 2030 pref => (Preferences.get(pref).value = false) 2031 ); 2032 } 2033 2034 Preferences.addSetting({ 2035 id: "clearOnCloseCookies", 2036 pref: useOldClearHistoryDialog 2037 ? "privacy.clearOnShutdown.cookies" 2038 : "privacy.clearOnShutdown_v2.cookiesAndStorage", 2039 }); 2040 Preferences.addSetting({ 2041 id: "clearOnCloseCache", 2042 pref: useOldClearHistoryDialog 2043 ? "privacy.clearOnShutdown.cache" 2044 : "privacy.clearOnShutdown_v2.cache", 2045 }); 2046 Preferences.addSetting({ 2047 id: "clearOnCloseStorage", 2048 pref: useOldClearHistoryDialog 2049 ? "privacy.clearOnShutdown.offlineApps" 2050 : "privacy.clearOnShutdown_v2.cookiesAndStorage", 2051 }); 2052 Preferences.addSetting({ 2053 id: "sanitizeOnShutdown", 2054 pref: "privacy.sanitize.sanitizeOnShutdown", 2055 }); 2056 Preferences.addSetting({ 2057 id: "historyModeCustom", 2058 pref: "privacy.history.custom", 2059 }); 2060 Preferences.addSetting({ 2061 id: "cookieBehavior", 2062 pref: "network.cookie.cookieBehavior", 2063 }); 2064 Preferences.addSetting({ 2065 id: "deleteOnClose", 2066 deps: [ 2067 "clearOnCloseCookies", 2068 "clearOnCloseCache", 2069 "clearOnCloseStorage", 2070 "sanitizeOnShutdown", 2071 "privateBrowsingAutoStart", 2072 "cookieBehavior", 2073 "alwaysClear", 2074 ], 2075 setup() { 2076 // Make sure to do the migration for the clear history dialog before implementing logic for delete on close 2077 // This needs to be done to make sure the migration is done before any pref changes are made to avoid unintentionally 2078 // overwriting prefs 2079 Sanitizer.maybeMigratePrefs("clearOnShutdown"); 2080 }, 2081 disabled({ privateBrowsingAutoStart, cookieBehavior }) { 2082 return ( 2083 privateBrowsingAutoStart.value || 2084 cookieBehavior.value == Ci.nsICookieService.BEHAVIOR_REJECT 2085 ); 2086 }, 2087 get(_, { privateBrowsingAutoStart }) { 2088 return ( 2089 isCookiesAndStorageClearingOnShutdown() || privateBrowsingAutoStart.value 2090 ); 2091 }, 2092 set( 2093 value, 2094 { 2095 clearOnCloseCookies, 2096 clearOnCloseCache, 2097 clearOnCloseStorage, 2098 sanitizeOnShutdown, 2099 } 2100 ) { 2101 clearOnCloseCookies.value = value; 2102 clearOnCloseCache.value = value; 2103 clearOnCloseStorage.value = value; 2104 2105 // Sync the cleaning prefs with the deleteOnClose box. 2106 2107 // Forget the current pref selection if sanitizeOnShutdown is disabled, 2108 // to not over clear when it gets enabled by the sync mechanism 2109 if (!sanitizeOnShutdown.value) { 2110 resetCleaningPrefs(); 2111 } 2112 // If no other cleaning category is selected, sanitizeOnShutdown gets synced with deleteOnClose 2113 sanitizeOnShutdown.value = 2114 gPrivacyPane._isCustomCleaningPrefPresent() || value; 2115 }, 2116 }); 2117 2118 Preferences.addSetting({ 2119 id: "historyModeCustom", 2120 pref: "privacy.history.custom", 2121 }); 2122 Preferences.addSetting({ 2123 id: "historyEnabled", 2124 pref: "places.history.enabled", 2125 }); 2126 Preferences.addSetting({ 2127 id: "formFillEnabled", 2128 pref: "browser.formfill.enable", 2129 }); 2130 2131 // Store this on the window so tests can suppress the prompt. 2132 window._shouldPromptForRestartPBM = true; 2133 async function onChangePrivateBrowsingAutoStart(value, revertFn) { 2134 if (!window._shouldPromptForRestartPBM) { 2135 return false; 2136 } 2137 2138 // The PBM autostart pref has changed so we need to prompt for restart. 2139 let buttonIndex = await confirmRestartPrompt(value, 1, true, false); 2140 2141 // User accepts, restart the browser. 2142 if (buttonIndex == CONFIRM_RESTART_PROMPT_RESTART_NOW) { 2143 Services.startup.quit( 2144 Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart 2145 ); 2146 return false; 2147 } 2148 2149 // Don't prompt for the revert operation itself. 2150 window._shouldPromptForRestartPBM = false; 2151 revertFn(); 2152 window._shouldPromptForRestartPBM = true; 2153 2154 // User cancels, do nothing. The caller will clean up the pref change. 2155 return true; 2156 } 2157 2158 Preferences.addSetting({ 2159 id: "historyMode", 2160 deps: [ 2161 "historyModeCustom", 2162 "privateBrowsingAutoStart", 2163 "historyEnabled", 2164 "formFillEnabled", 2165 "sanitizeOnShutdown", 2166 ], 2167 get( 2168 _, 2169 { 2170 historyModeCustom, 2171 privateBrowsingAutoStart, 2172 historyEnabled, 2173 formFillEnabled, 2174 sanitizeOnShutdown, 2175 } 2176 ) { 2177 if (historyModeCustom.value) { 2178 return "custom"; 2179 } 2180 2181 if (privateBrowsingAutoStart.value) { 2182 return "dontremember"; 2183 } 2184 2185 if ( 2186 historyEnabled.value && 2187 formFillEnabled.value && 2188 !sanitizeOnShutdown.value 2189 ) { 2190 return "remember"; 2191 } 2192 2193 return "custom"; 2194 }, 2195 set( 2196 value, 2197 { 2198 historyModeCustom, 2199 privateBrowsingAutoStart, 2200 historyEnabled, 2201 formFillEnabled, 2202 sanitizeOnShutdown, 2203 } 2204 ) { 2205 let lastHistoryModeCustom = historyModeCustom.value; 2206 let lastHistoryEnabled = historyEnabled.value; 2207 let lastFormFillEnabled = formFillEnabled.value; 2208 let lastSanitizeOnShutdown = sanitizeOnShutdown.value; 2209 let lastPrivateBrowsingAutoStart = privateBrowsingAutoStart.value; 2210 2211 historyModeCustom.value = value == "custom"; 2212 2213 if (value == "remember") { 2214 historyEnabled.value = true; 2215 formFillEnabled.value = true; 2216 sanitizeOnShutdown.value = false; 2217 privateBrowsingAutoStart.value = false; 2218 } else if (value == "dontremember") { 2219 privateBrowsingAutoStart.value = true; 2220 } 2221 2222 if (privateBrowsingAutoStart.value !== lastPrivateBrowsingAutoStart) { 2223 // The PBM autostart pref has changed so we need to prompt for restart. 2224 onChangePrivateBrowsingAutoStart(privateBrowsingAutoStart.value, () => { 2225 // User cancelled the action, revert the change. 2226 // Simply reverting the setting value itself is not enough, because a 2227 // state transition to "custom" does not override any of the sub-prefs. 2228 // We need to update them all manually. 2229 historyModeCustom.value = lastHistoryModeCustom; 2230 historyEnabled.value = lastHistoryEnabled; 2231 formFillEnabled.value = lastFormFillEnabled; 2232 sanitizeOnShutdown.value = lastSanitizeOnShutdown; 2233 privateBrowsingAutoStart.value = lastPrivateBrowsingAutoStart; 2234 }); 2235 } 2236 }, 2237 disabled({ privateBrowsingAutoStart }) { 2238 // Disable history dropdown if PBM autostart is locked on. 2239 return privateBrowsingAutoStart.locked && privateBrowsingAutoStart.value; 2240 }, 2241 getControlConfig(config, { privateBrowsingAutoStart }, setting) { 2242 let l10nId = undefined; 2243 if (!srdSectionEnabled("history2")) { 2244 if (setting.value == "remember") { 2245 l10nId = "history-remember-description3"; 2246 } else if (setting.value == "dontremember") { 2247 l10nId = "history-dontremember-description3"; 2248 } else if (setting.value == "custom") { 2249 l10nId = "history-custom-description3"; 2250 } 2251 } 2252 2253 let dontRememberOption = config.options.find( 2254 opt => opt.value == "dontremember" 2255 ); 2256 2257 // If PBM is unavailable hide the "Never remember history" option. 2258 dontRememberOption.hidden = !PrivateBrowsingUtils.enabled; 2259 2260 // If the PBM autostart pref is locked disable the "Never remember history" 2261 // option. 2262 dontRememberOption.disabled = 2263 privateBrowsingAutoStart.locked && !privateBrowsingAutoStart.value; 2264 2265 return { 2266 ...config, 2267 l10nId, 2268 }; 2269 }, 2270 }); 2271 2272 Preferences.addSetting({ 2273 id: "customHistoryButton", 2274 onUserClick(e) { 2275 e.preventDefault(); 2276 gotoPref("paneHistory"); 2277 }, 2278 }); 2279 2280 Preferences.addSetting({ 2281 id: "privateBrowsingAutoStart", 2282 pref: "browser.privatebrowsing.autostart", 2283 deps: ["historyMode"], 2284 onUserChange(value, _, setting) { 2285 onChangePrivateBrowsingAutoStart(value, () => { 2286 // User cancelled the action, revert the setting. 2287 setting.value = !value; 2288 }); 2289 }, 2290 visible({ historyMode }) { 2291 return PrivateBrowsingUtils.enabled && historyMode.value == "custom"; 2292 }, 2293 }); 2294 Preferences.addSetting({ 2295 id: "rememberHistory", 2296 pref: "places.history.enabled", 2297 deps: ["historyMode", "privateBrowsingAutoStart"], 2298 visible({ historyMode }) { 2299 return historyMode.value == "custom"; 2300 }, 2301 disabled({ privateBrowsingAutoStart }) { 2302 return privateBrowsingAutoStart.value; 2303 }, 2304 }); 2305 Preferences.addSetting({ 2306 id: "rememberForms", 2307 pref: "browser.formfill.enable", 2308 deps: ["historyMode", "privateBrowsingAutoStart"], 2309 visible({ historyMode }) { 2310 return historyMode.value == "custom"; 2311 }, 2312 disabled({ privateBrowsingAutoStart }) { 2313 return privateBrowsingAutoStart.value; 2314 }, 2315 }); 2316 Preferences.addSetting({ 2317 id: "alwaysClear", 2318 pref: "privacy.sanitize.sanitizeOnShutdown", 2319 deps: ["historyMode", "privateBrowsingAutoStart"], 2320 visible({ historyMode }) { 2321 return historyMode.value == "custom"; 2322 }, 2323 disabled({ privateBrowsingAutoStart }) { 2324 return privateBrowsingAutoStart.value; 2325 }, 2326 }); 2327 2328 Preferences.addSetting({ 2329 id: "clearDataSettings", 2330 deps: ["historyMode", "alwaysClear"], 2331 visible({ historyMode }) { 2332 return historyMode.value == "custom"; 2333 }, 2334 disabled({ alwaysClear }) { 2335 return !alwaysClear.value || alwaysClear.disabled; 2336 }, 2337 onUserClick() { 2338 let dialogFile = useOldClearHistoryDialog 2339 ? "chrome://browser/content/preferences/dialogs/sanitize.xhtml" 2340 : "chrome://browser/content/sanitize_v2.xhtml"; 2341 2342 gSubDialog.open( 2343 dialogFile, 2344 { 2345 features: "resizable=no", 2346 }, 2347 { 2348 mode: "clearOnShutdown", 2349 } 2350 ); 2351 }, 2352 }); 2353 2354 Preferences.addSetting({ 2355 id: "clearHistoryButton", 2356 deps: ["historyMode"], 2357 onUserClick(_, { historyMode }) { 2358 gPrivacyPane.clearPrivateDataNow(historyMode.value == "dontremember"); 2359 }, 2360 }); 2361 2362 Preferences.addSetting({ 2363 id: "certificateButtonGroup", 2364 }); 2365 Preferences.addSetting({ 2366 id: "disableOpenCertManager", 2367 pref: "security.disable_button.openCertManager", 2368 }); 2369 Preferences.addSetting({ 2370 id: "disableOpenDeviceManager", 2371 pref: "security.disable_button.openDeviceManager", 2372 }); 2373 Preferences.addSetting({ 2374 id: "viewCertificatesButton", 2375 deps: ["disableOpenCertManager"], 2376 disabled: deps => { 2377 return deps.disableOpenCertManager.value; 2378 }, 2379 onUserClick: () => { 2380 gPrivacyPane.showCertificates(); 2381 }, 2382 }); 2383 Preferences.addSetting({ 2384 id: "viewSecurityDevicesButton", 2385 deps: ["disableOpenDeviceManager"], 2386 disabled: deps => { 2387 return deps.disableOpenDeviceManager.value; 2388 }, 2389 onUserClick: () => { 2390 gPrivacyPane.showSecurityDevices(); 2391 }, 2392 }); 2393 Preferences.addSetting({ 2394 id: "certEnableThirdPartyToggle", 2395 pref: "security.enterprise_roots.enabled", 2396 visible: () => { 2397 // Third-party certificate import is only implemented for Windows and Mac, 2398 // and we should not expose this as a user-configurable setting if there's 2399 // an enterprise policy controlling it (either to enable _or_ disable it). 2400 return ( 2401 (AppConstants.platform == "win" || AppConstants.platform == "macosx") && 2402 typeof Services.policies.getActivePolicies()?.Certificates 2403 ?.ImportEnterpriseRoots == "undefined" && 2404 !AppConstants.BASE_BROWSER_VERSION 2405 ); 2406 }, 2407 }); 2408 2409 Preferences.addSetting({ 2410 id: "permissionBox", 2411 }); 2412 Preferences.addSetting({ 2413 id: "popupPolicy", 2414 pref: "dom.disable_open_during_load", 2415 }); 2416 Preferences.addSetting({ 2417 id: "popupPolicyButton", 2418 deps: ["popupPolicy"], 2419 onUserClick: () => gPrivacyPane.showPopupExceptions(), 2420 disabled: ({ popupPolicy }) => { 2421 return !popupPolicy.value || popupPolicy.locked; 2422 }, 2423 }); 2424 Preferences.addSetting({ 2425 id: "warnAddonInstall", 2426 pref: "xpinstall.whitelist.required", 2427 }); 2428 Preferences.addSetting({ 2429 id: "addonExceptions", 2430 deps: ["warnAddonInstall"], 2431 onUserClick: () => gPrivacyPane.showAddonExceptions(), 2432 disabled: ({ warnAddonInstall }) => { 2433 return !warnAddonInstall.value || warnAddonInstall.locked; 2434 }, 2435 }); 2436 Preferences.addSetting({ 2437 id: "notificationsDoNotDisturb", 2438 get: () => { 2439 return AlertsServiceDND?.manualDoNotDisturb ?? false; 2440 }, 2441 set: value => { 2442 if (AlertsServiceDND) { 2443 AlertsServiceDND.manualDoNotDisturb = value; 2444 } 2445 }, 2446 visible: () => { 2447 return AlertsServiceDND != undefined; 2448 }, 2449 }); 2450 Preferences.addSetting({ 2451 id: "locationSettingsButton", 2452 onUserClick: () => gPrivacyPane.showLocationExceptions(), 2453 }); 2454 Preferences.addSetting({ 2455 id: "cameraSettingsButton", 2456 onUserClick: () => gPrivacyPane.showCameraExceptions(), 2457 }); 2458 Preferences.addSetting({ 2459 id: "enabledLNA", 2460 pref: "network.lna.blocking", 2461 }); 2462 Preferences.addSetting({ 2463 id: "localNetworkSettingsButton", 2464 onUserClick: () => gPrivacyPane.showLocalNetworkExceptions(), 2465 deps: ["enabledLNA"], 2466 visible: deps => { 2467 return deps.enabledLNA.value; 2468 }, 2469 }); 2470 Preferences.addSetting({ 2471 id: "localHostSettingsButton", 2472 onUserClick: () => gPrivacyPane.showLocalHostExceptions(), 2473 deps: ["enabledLNA"], 2474 visible: deps => { 2475 return deps.enabledLNA.value; 2476 }, 2477 }); 2478 Preferences.addSetting({ 2479 id: "microphoneSettingsButton", 2480 onUserClick: () => gPrivacyPane.showMicrophoneExceptions(), 2481 }); 2482 Preferences.addSetting({ 2483 id: "enabledSpeakerControl", 2484 pref: "media.setsinkid.enabled", 2485 }); 2486 Preferences.addSetting({ 2487 id: "speakerSettingsButton", 2488 onUserClick: () => gPrivacyPane.showSpeakerExceptions(), 2489 deps: ["enabledSpeakerControl"], 2490 visible: ({ enabledSpeakerControl }) => { 2491 return enabledSpeakerControl.value; 2492 }, 2493 }); 2494 Preferences.addSetting({ 2495 id: "notificationSettingsButton", 2496 onUserClick: () => gPrivacyPane.showNotificationExceptions(), 2497 }); 2498 Preferences.addSetting({ 2499 id: "autoplaySettingsButton", 2500 onUserClick: () => gPrivacyPane.showAutoplayMediaExceptions(), 2501 }); 2502 Preferences.addSetting({ 2503 id: "xrSettingsButton", 2504 onUserClick: () => gPrivacyPane.showXRExceptions(), 2505 }); 2506 2507 Preferences.addSetting({ 2508 id: "dohBox", 2509 }); 2510 2511 Preferences.addSetting({ 2512 id: "dohAdvancedButton", 2513 onUserClick(e) { 2514 e.preventDefault(); 2515 gotoPref("paneDnsOverHttps"); 2516 }, 2517 }); 2518 2519 Preferences.addSetting({ 2520 id: "dohExceptionsButton", 2521 onUserClick: () => gPrivacyPane.showDoHExceptions(), 2522 }); 2523 2524 Preferences.addSetting({ 2525 id: "dohMode", 2526 pref: "network.trr.mode", 2527 setup(emitChange) { 2528 Services.obs.addObserver(emitChange, "network:trr-mode-changed"); 2529 Services.obs.addObserver(emitChange, "network:trr-confirmation"); 2530 return () => { 2531 Services.obs.removeObserver(emitChange, "network:trr-mode-changed"); 2532 Services.obs.removeObserver(emitChange, "network:trr-confirmation"); 2533 }; 2534 }, 2535 }); 2536 2537 Preferences.addSetting({ 2538 id: "dohURL", 2539 pref: "network.trr.uri", 2540 setup(emitChange) { 2541 Services.obs.addObserver(emitChange, "network:trr-uri-changed"); 2542 Services.obs.addObserver(emitChange, "network:trr-confirmation"); 2543 return () => { 2544 Services.obs.removeObserver(emitChange, "network:trr-uri-changed"); 2545 Services.obs.removeObserver(emitChange, "network:trr-confirmation"); 2546 }; 2547 }, 2548 }); 2549 2550 Preferences.addSetting({ 2551 id: "dohDefaultURL", 2552 pref: "network.trr.default_provider_uri", 2553 }); 2554 2555 Preferences.addSetting({ 2556 id: "dohDisableHeuristics", 2557 pref: "doh-rollout.disable-heuristics", 2558 }); 2559 2560 Preferences.addSetting({ 2561 id: "dohModeBoxItem", 2562 deps: ["dohMode"], 2563 getControlConfig: (config, deps) => { 2564 let l10nId = "preferences-doh-overview-off"; 2565 if (deps.dohMode.value == Ci.nsIDNSService.MODE_NATIVEONLY) { 2566 l10nId = "preferences-doh-overview-default"; 2567 } else if ( 2568 deps.dohMode.value == Ci.nsIDNSService.MODE_TRRFIRST || 2569 deps.dohMode.value == Ci.nsIDNSService.MODE_TRRONLY 2570 ) { 2571 l10nId = "preferences-doh-overview-custom"; 2572 } 2573 return { 2574 ...config, 2575 l10nId, 2576 }; 2577 }, 2578 }); 2579 2580 Preferences.addSetting({ 2581 id: "dohStatusBox", 2582 deps: ["dohMode", "dohURL"], 2583 getControlConfig: config => { 2584 let l10nId = "preferences-doh-status-item-off"; 2585 let l10nArgs = {}; 2586 let supportPage = ""; 2587 let controlAttrs = { type: "info" }; 2588 2589 let trrURI = Services.dns.currentTrrURI; 2590 let hostname = URL.parse(trrURI)?.hostname; 2591 2592 let name = hostname || trrURI; 2593 let nameFound = false; 2594 let steering = false; 2595 for (let resolver of DoHConfigController.currentConfig.providerList) { 2596 if (resolver.uri == trrURI) { 2597 name = resolver.UIName || name; 2598 nameFound = true; 2599 break; 2600 } 2601 } 2602 if (!nameFound) { 2603 for (let resolver of DoHConfigController.currentConfig.providerSteering 2604 .providerList) { 2605 if (resolver.uri == trrURI) { 2606 steering = true; 2607 name = resolver.UIName || name; 2608 break; 2609 } 2610 } 2611 } 2612 2613 let mode = Services.dns.currentTrrMode; 2614 if ( 2615 (mode == Ci.nsIDNSService.MODE_TRRFIRST || 2616 mode == Ci.nsIDNSService.MODE_TRRONLY) && 2617 lazy.gParentalControlsService?.parentalControlsEnabled 2618 ) { 2619 l10nId = "preferences-doh-status-item-not-active"; 2620 supportPage = "doh-status"; 2621 l10nArgs = { 2622 reason: Services.dns.getTRRSkipReasonName( 2623 Ci.nsITRRSkipReason.TRR_PARENTAL_CONTROL 2624 ), 2625 name, 2626 }; 2627 } else { 2628 let confirmationState = Services.dns.currentTrrConfirmationState; 2629 if ( 2630 mode != Ci.nsIDNSService.MODE_TRRFIRST && 2631 mode != Ci.nsIDNSService.MODE_TRRONLY 2632 ) { 2633 l10nId = "preferences-doh-status-item-off"; 2634 } else if ( 2635 confirmationState == Ci.nsIDNSService.CONFIRM_TRYING_OK || 2636 confirmationState == Ci.nsIDNSService.CONFIRM_OK || 2637 confirmationState == Ci.nsIDNSService.CONFIRM_DISABLED 2638 ) { 2639 if (steering) { 2640 l10nId = "preferences-doh-status-item-active-local"; 2641 controlAttrs = { type: "success" }; 2642 } else { 2643 l10nId = "preferences-doh-status-item-active"; 2644 controlAttrs = { type: "success" }; 2645 } 2646 } else if (steering) { 2647 l10nId = "preferences-doh-status-item-not-active-local"; 2648 supportPage = "doh-status"; 2649 controlAttrs = { type: "warning" }; 2650 } else { 2651 l10nId = "preferences-doh-status-item-not-active"; 2652 supportPage = "doh-status"; 2653 controlAttrs = { type: "warning" }; 2654 } 2655 2656 let confirmationStatus = Services.dns.lastConfirmationStatus; 2657 if (confirmationStatus != Cr.NS_OK) { 2658 l10nArgs = { 2659 reason: ChromeUtils.getXPCOMErrorName(confirmationStatus), 2660 name, 2661 }; 2662 } else { 2663 l10nArgs = { 2664 reason: Services.dns.getTRRSkipReasonName( 2665 Services.dns.lastConfirmationSkipReason 2666 ), 2667 name, 2668 }; 2669 if ( 2670 Services.dns.lastConfirmationSkipReason == 2671 Ci.nsITRRSkipReason.TRR_BAD_URL || 2672 !name 2673 ) { 2674 l10nId = "preferences-doh-status-item-not-active-bad-url"; 2675 supportPage = "doh-status"; 2676 controlAttrs = { type: "warning" }; 2677 } 2678 } 2679 } 2680 2681 return { 2682 ...config, 2683 l10nId, 2684 l10nArgs, 2685 supportPage, 2686 controlAttrs, 2687 }; 2688 }, 2689 }); 2690 2691 Preferences.addSetting({ 2692 id: "dohRadioGroup", 2693 // These deps are complicated: 2694 // this radio group, along with dohFallbackIfCustom controls the mode and URL. 2695 // Therefore, we set dohMode and dohURL as deps here. This is a smell, but needed 2696 // for the mismatch of control-to-pref. 2697 deps: ["dohFallbackIfCustom", "dohMode", "dohURL"], 2698 onUserChange: (val, deps) => { 2699 let value = null; 2700 if (val == "default") { 2701 value = "dohDefaultRadio"; 2702 } else if (val == "off") { 2703 value = "dohOffRadio"; 2704 } else if (val == "custom" && deps.dohFallbackIfCustom.value) { 2705 value = "dohEnabledRadio"; 2706 } else if (val == "custom" && !deps.dohFallbackIfCustom.value) { 2707 value = "dohStrictRadio"; 2708 } 2709 if (value) { 2710 Glean.securityDohSettings.modeChangedButton.record({ 2711 value, 2712 }); 2713 } 2714 }, 2715 get: (_val, deps) => { 2716 switch (deps.dohMode.value) { 2717 case Ci.nsIDNSService.MODE_NATIVEONLY: 2718 return "default"; 2719 case Ci.nsIDNSService.MODE_TRRFIRST: 2720 case Ci.nsIDNSService.MODE_TRRONLY: 2721 return "custom"; 2722 case Ci.nsIDNSService.MODE_TRROFF: 2723 case Ci.nsIDNSService.MODE_RESERVED1: 2724 case Ci.nsIDNSService.MODE_RESERVED4: 2725 default: 2726 return "off"; 2727 } 2728 }, 2729 set: (val, deps) => { 2730 if (val == "custom") { 2731 if (deps.dohFallbackIfCustom.value) { 2732 deps.dohMode.value = Ci.nsIDNSService.MODE_TRRFIRST; 2733 } else { 2734 deps.dohMode.value = Ci.nsIDNSService.MODE_TRRONLY; 2735 } 2736 } else if (val == "off") { 2737 deps.dohMode.value = Ci.nsIDNSService.MODE_TRROFF; 2738 } else { 2739 deps.dohMode.value = Ci.nsIDNSService.MODE_NATIVEONLY; 2740 } 2741 2742 // When the mode is set to 0 we need to clear the URI so 2743 // doh-rollout can kick in. 2744 if (deps.dohMode.value == Ci.nsIDNSService.MODE_NATIVEONLY) { 2745 deps.dohURL.pref.value = undefined; 2746 Services.prefs.clearUserPref("doh-rollout.disable-heuristics"); 2747 } 2748 2749 // Bug 1861285 2750 // When the mode is set to 2 or 3, we need to check if network.trr.uri is a empty string. 2751 // In this case, we need to update network.trr.uri to default to fallbackProviderURI. 2752 // This occurs when the mode is previously set to 0 (Default Protection). 2753 if ( 2754 deps.dohMode.value == Ci.nsIDNSService.MODE_TRRFIRST || 2755 deps.dohMode.value == Ci.nsIDNSService.MODE_TRRONLY 2756 ) { 2757 if (!deps.dohURL.value) { 2758 deps.dohURL.value = 2759 DoHConfigController.currentConfig.fallbackProviderURI; 2760 } 2761 } 2762 2763 // Bug 1900672 2764 // When the mode is set to 5, clear the pref to ensure that 2765 // network.trr.uri is set to fallbackProviderURIwhen the mode is set to 2 or 3 afterwards 2766 if (deps.dohMode.value == Ci.nsIDNSService.MODE_TRROFF) { 2767 deps.dohURL.pref.value = undefined; 2768 } 2769 }, 2770 }); 2771 2772 Preferences.addSetting({ 2773 id: "dohFallbackIfCustom", 2774 pref: "network.trr_ui.fallback_was_checked", 2775 // These deps are complicated: 2776 // this checkbox, along with dohRadioGroup controls the mode and URL. 2777 // Therefore, we set dohMode as a dep here. This is a smell, but needed 2778 // for the mismatch of control-to-pref. 2779 deps: ["dohMode"], 2780 onUserChange: val => { 2781 if (val) { 2782 Glean.securityDohSettings.modeChangedButton.record({ 2783 value: "dohEnabledRadio", 2784 }); 2785 } else { 2786 Glean.securityDohSettings.modeChangedButton.record({ 2787 value: "dohStrictRadio", 2788 }); 2789 } 2790 }, 2791 get: (val, deps) => { 2792 // If we are in a custom mode, we need to get the value from the Setting 2793 if (deps.dohMode.value == Ci.nsIDNSService.MODE_TRRFIRST) { 2794 return true; 2795 } 2796 if (deps.dohMode.value == Ci.nsIDNSService.MODE_TRRONLY) { 2797 return false; 2798 } 2799 2800 // Propagate the preference otherwise 2801 return val; 2802 }, 2803 set: (val, deps) => { 2804 // Toggle the preference that controls the setting if are in a custom mode 2805 // This should be the only case where the checkbox is enabled, but we can be 2806 // careful and test. 2807 if (deps.dohMode.value == Ci.nsIDNSService.MODE_TRRFIRST && !val) { 2808 deps.dohMode.value = Ci.nsIDNSService.MODE_TRRONLY; 2809 } else if (deps.dohMode.value == Ci.nsIDNSService.MODE_TRRONLY && val) { 2810 deps.dohMode.value = Ci.nsIDNSService.MODE_TRRFIRST; 2811 } 2812 // Propagate to the real preference 2813 return val; 2814 }, 2815 }); 2816 2817 Preferences.addSetting({ 2818 id: "dohCustomProvider", 2819 deps: ["dohProviderSelect", "dohURL"], 2820 _value: null, 2821 visible: deps => { 2822 return deps.dohProviderSelect.value == "custom"; 2823 }, 2824 get(_val, deps) { 2825 if (this._value === null) { 2826 return deps.dohURL.value; 2827 } 2828 return this._value; 2829 }, 2830 set(val, deps) { 2831 this._value = val; 2832 if (val == "") { 2833 val = " "; 2834 } 2835 deps.dohURL.value = val; 2836 }, 2837 }); 2838 2839 Preferences.addSetting({ 2840 id: "dohProviderSelect", 2841 deps: ["dohURL", "dohDefaultURL"], 2842 _custom: false, 2843 onUserChange: value => { 2844 Glean.securityDohSettings.providerChoiceValue.record({ 2845 value, 2846 }); 2847 }, 2848 getControlConfig(config, deps) { 2849 let options = []; 2850 2851 let resolvers = DoHConfigController.currentConfig.providerList; 2852 // if there's no default, we'll hold its position with an empty string 2853 let defaultURI = DoHConfigController.currentConfig.fallbackProviderURI; 2854 let defaultFound = resolvers.some(p => p.uri == defaultURI); 2855 if (!defaultFound && defaultURI) { 2856 // the default value for the pref isn't included in the resolvers list 2857 // so we'll make a stub for it. Without an id, we'll have to use the url as the label 2858 resolvers.unshift({ uri: defaultURI }); 2859 } 2860 let currentURI = deps.dohURL.value; 2861 if (currentURI && !resolvers.some(p => p.uri == currentURI)) { 2862 this._custom = true; 2863 } 2864 2865 options = resolvers.map(resolver => { 2866 let option = { 2867 value: resolver.uri, 2868 l10nArgs: { 2869 name: resolver.UIName || resolver.uri, 2870 }, 2871 }; 2872 if (resolver.uri == defaultURI) { 2873 option.l10nId = "connection-dns-over-https-url-item-default"; 2874 } else { 2875 option.l10nId = "connection-dns-over-https-url-item"; 2876 } 2877 return option; 2878 }); 2879 options.push({ 2880 value: "custom", 2881 l10nId: "connection-dns-over-https-url-custom", 2882 }); 2883 2884 return { 2885 options, 2886 ...config, 2887 }; 2888 }, 2889 get(_val, deps) { 2890 if (this._custom) { 2891 return "custom"; 2892 } 2893 let currentURI = deps.dohURL.value; 2894 if (!currentURI) { 2895 currentURI = deps.dohDefaultURL.value; 2896 } 2897 return currentURI; 2898 }, 2899 set(val, deps, setting) { 2900 if (val != "custom") { 2901 this._custom = false; 2902 deps.dohURL.value = val; 2903 } else { 2904 this._custom = true; 2905 } 2906 setting.emit("change"); 2907 return val; 2908 }, 2909 }); 2910 2911 function shouldDisableETPCategoryControls() { 2912 let policy = Services.policies.getActivePolicies(); 2913 return policy?.EnableTrackingProtection?.Locked || policy?.Cookies?.Locked; 2914 } 2915 2916 Preferences.addSetting({ 2917 id: "contentBlockingCategory", 2918 pref: "browser.contentblocking.category", 2919 }); 2920 2921 // We need a separate setting for the radio group for custom disable behavior. 2922 // Setter and getter simply write to the pref. 2923 Preferences.addSetting({ 2924 id: "contentBlockingCategoryRadioGroup", 2925 deps: ["contentBlockingCategory"], 2926 get(_, { contentBlockingCategory }) { 2927 return contentBlockingCategory.value; 2928 }, 2929 set(value, { contentBlockingCategory }) { 2930 contentBlockingCategory.value = value; 2931 }, 2932 getControlConfig(config, _, setting) { 2933 if (!shouldDisableETPCategoryControls()) { 2934 return config; 2935 } 2936 2937 let { options } = config; 2938 2939 // If ETP level is set to custom keep the radio button enabled so the "customize" button works even when the category selection itself is locked. 2940 for (let option of options) { 2941 option.disabled = 2942 option.id != "etpLevelCustom" || setting.value != "custom"; 2943 } 2944 2945 return config; 2946 }, 2947 }); 2948 2949 Preferences.addSetting({ 2950 id: "etpStatusBoxGroup", 2951 }); 2952 2953 Preferences.addSetting({ 2954 id: "etpStatusItem", 2955 deps: ["contentBlockingCategory"], 2956 getControlConfig(config, { contentBlockingCategory }) { 2957 // Display a different description and label depending on the content blocking category (= ETP level). 2958 let categoryToL10nId = { 2959 standard: "preferences-etp-level-standard", 2960 strict: "preferences-etp-level-strict", 2961 custom: "preferences-etp-level-custom", 2962 }; 2963 2964 return { 2965 ...config, 2966 l10nId: 2967 categoryToL10nId[contentBlockingCategory.value] ?? 2968 "preferences-etp-level-standard", 2969 }; 2970 }, 2971 }); 2972 2973 Preferences.addSetting({ 2974 id: "etpStatusAdvancedButton", 2975 onUserClick(e) { 2976 e.preventDefault(); 2977 gotoPref("etp"); 2978 }, 2979 }); 2980 2981 Preferences.addSetting({ 2982 id: "protectionsDashboardLink", 2983 }); 2984 2985 Preferences.addSetting({ 2986 id: "etpBannerEl", 2987 }); 2988 2989 Preferences.addSetting({ 2990 id: "etpAllowListBaselineEnabled", 2991 pref: "privacy.trackingprotection.allow_list.baseline.enabled", 2992 deps: ["contentBlockingCategory"], 2993 visible({ contentBlockingCategory }) { 2994 return contentBlockingCategory.value == "strict"; 2995 }, 2996 onUserChange(value, _deps, setting) { 2997 gPrivacyPane.onBaselineAllowListSettingChange(value, setting); 2998 }, 2999 }); 3000 3001 Preferences.addSetting({ 3002 id: "etpAllowListConvenienceEnabled", 3003 pref: "privacy.trackingprotection.allow_list.convenience.enabled", 3004 onUserChange() { 3005 gPrivacyPane.maybeNotifyUserToReload(); 3006 }, 3007 }); 3008 3009 Preferences.addSetting({ 3010 id: "etpCustomizeButton", 3011 onUserClick(e) { 3012 e.preventDefault(); 3013 gotoPref("etpCustomize"); 3014 }, 3015 }); 3016 3017 Preferences.addSetting({ 3018 id: "reloadTabsHint", 3019 _showHint: false, 3020 set(value, _, setting) { 3021 this._showHint = value; 3022 setting.emit("change"); 3023 }, 3024 get() { 3025 return this._showHint; 3026 }, 3027 visible(_, setting) { 3028 return setting.value; 3029 }, 3030 onUserClick() { 3031 gPrivacyPane.reloadAllOtherTabs(); 3032 }, 3033 }); 3034 3035 Preferences.addSetting({ 3036 id: "resistFingerprinting", 3037 pref: "privacy.resistFingerprinting", 3038 }); 3039 3040 Preferences.addSetting({ 3041 id: "resistFingerprintingPBM", 3042 pref: "privacy.resistFingerprinting.pbmode", 3043 }); 3044 3045 Preferences.addSetting({ 3046 id: "rfpWarning", 3047 deps: ["resistFingerprinting", "resistFingerprintingPBM"], 3048 visible({ resistFingerprinting, resistFingerprintingPBM }) { 3049 return resistFingerprinting.value || resistFingerprintingPBM.value; 3050 }, 3051 }); 3052 3053 Preferences.addSetting({ 3054 id: "etpLevelWarning", 3055 deps: ["contentBlockingCategory"], 3056 visible({ contentBlockingCategory }) { 3057 return contentBlockingCategory.value != "standard"; 3058 }, 3059 }); 3060 3061 Preferences.addSetting({ 3062 id: "etpManageExceptionsButton", 3063 onUserClick() { 3064 let params = { 3065 permissionType: "trackingprotection", 3066 disableETPVisible: true, 3067 prefilledHost: "", 3068 hideStatusColumn: true, 3069 }; 3070 gSubDialog.open( 3071 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 3072 undefined, 3073 params 3074 ); 3075 }, 3076 }); 3077 3078 Preferences.addSetting({ 3079 id: "etpResetButtonGroup", 3080 }); 3081 3082 Preferences.addSetting({ 3083 id: "etpResetStandardButton", 3084 deps: ["contentBlockingCategory"], 3085 onUserClick(_, { contentBlockingCategory }) { 3086 contentBlockingCategory.value = "standard"; 3087 }, 3088 disabled({ contentBlockingCategory }) { 3089 return ( 3090 contentBlockingCategory.value == "standard" || 3091 shouldDisableETPCategoryControls() 3092 ); 3093 }, 3094 }); 3095 3096 Preferences.addSetting({ 3097 id: "etpResetStrictButton", 3098 deps: ["contentBlockingCategory"], 3099 onUserClick(_, { contentBlockingCategory }) { 3100 contentBlockingCategory.value = "strict"; 3101 }, 3102 disabled({ contentBlockingCategory }) { 3103 return ( 3104 contentBlockingCategory.value == "strict" || 3105 shouldDisableETPCategoryControls() 3106 ); 3107 }, 3108 }); 3109 3110 Preferences.addSetting({ 3111 id: "etpAllowListBaselineEnabledCustom", 3112 pref: "privacy.trackingprotection.allow_list.baseline.enabled", 3113 onUserChange(value, _deps, setting) { 3114 gPrivacyPane.onBaselineAllowListSettingChange(value, setting); 3115 }, 3116 }); 3117 3118 Preferences.addSetting({ 3119 id: "etpAllowListConvenienceEnabledCustom", 3120 pref: "privacy.trackingprotection.allow_list.convenience.enabled", 3121 onUserChange() { 3122 gPrivacyPane.maybeNotifyUserToReload(); 3123 }, 3124 }); 3125 3126 Preferences.addSetting({ 3127 id: "etpCustomCookiesEnabled", 3128 deps: ["cookieBehavior"], 3129 disabled: ({ cookieBehavior }) => { 3130 return cookieBehavior.locked; 3131 }, 3132 get(_, { cookieBehavior }) { 3133 return cookieBehavior.value != Ci.nsICookieService.BEHAVIOR_ACCEPT; 3134 }, 3135 set(value, { cookieBehavior }) { 3136 if (!value) { 3137 cookieBehavior.value = Ci.nsICookieService.BEHAVIOR_ACCEPT; 3138 } else { 3139 // When the user enabled cookie blocking, set the cookie behavior to the default. 3140 cookieBehavior.value = cookieBehavior.pref.defaultValue; 3141 } 3142 }, 3143 }); 3144 3145 Preferences.addSetting({ 3146 id: "trackingProtectionEnabled", 3147 pref: "privacy.trackingprotection.enabled", 3148 }); 3149 3150 Preferences.addSetting({ 3151 id: "trackingProtectionEnabledPBM", 3152 pref: "privacy.trackingprotection.pbmode.enabled", 3153 }); 3154 3155 Preferences.addSetting({ 3156 id: "etpCustomTrackingProtectionEnabledContext", 3157 deps: ["trackingProtectionEnabled", "trackingProtectionEnabledPBM"], 3158 get(_, { trackingProtectionEnabled, trackingProtectionEnabledPBM }) { 3159 if (trackingProtectionEnabled.value && trackingProtectionEnabledPBM.value) { 3160 return "all"; 3161 } else if (trackingProtectionEnabledPBM) { 3162 return "pbmOnly"; 3163 } 3164 return null; 3165 }, 3166 set(value, { trackingProtectionEnabled, trackingProtectionEnabledPBM }) { 3167 if (value == "all") { 3168 trackingProtectionEnabled.value = true; 3169 trackingProtectionEnabledPBM.value = true; 3170 } else if (value == "pbmOnly") { 3171 trackingProtectionEnabled.value = false; 3172 trackingProtectionEnabledPBM.value = true; 3173 } 3174 }, 3175 }); 3176 3177 Preferences.addSetting({ 3178 id: "etpCustomTrackingProtectionEnabled", 3179 deps: ["trackingProtectionEnabled", "trackingProtectionEnabledPBM"], 3180 disabled: ({ trackingProtectionEnabled, trackingProtectionEnabledPBM }) => { 3181 return ( 3182 trackingProtectionEnabled.locked || trackingProtectionEnabledPBM.locked 3183 ); 3184 }, 3185 get(_, { trackingProtectionEnabled, trackingProtectionEnabledPBM }) { 3186 return ( 3187 trackingProtectionEnabled.value || trackingProtectionEnabledPBM.value 3188 ); 3189 }, 3190 set(value, { trackingProtectionEnabled, trackingProtectionEnabledPBM }) { 3191 if (value) { 3192 trackingProtectionEnabled.value = false; 3193 trackingProtectionEnabledPBM.value = true; 3194 } else { 3195 trackingProtectionEnabled.value = false; 3196 trackingProtectionEnabledPBM.value = false; 3197 } 3198 }, 3199 }); 3200 3201 Preferences.addSetting({ 3202 id: "etpCustomCryptominingProtectionEnabled", 3203 pref: "privacy.trackingprotection.cryptomining.enabled", 3204 }); 3205 3206 Preferences.addSetting({ 3207 id: "etpCustomKnownFingerprintingProtectionEnabled", 3208 pref: "privacy.trackingprotection.fingerprinting.enabled", 3209 }); 3210 3211 Preferences.addSetting({ 3212 id: "etpCustomFingerprintingProtectionEnabled", 3213 pref: "privacy.fingerprintingProtection", 3214 }); 3215 3216 Preferences.addSetting({ 3217 id: "etpCustomFingerprintingProtectionEnabledPBM", 3218 pref: "privacy.fingerprintingProtection.pbmode", 3219 }); 3220 3221 Preferences.addSetting({ 3222 id: "etpCustomSuspectFingerprintingProtectionEnabled", 3223 deps: [ 3224 "etpCustomFingerprintingProtectionEnabled", 3225 "etpCustomFingerprintingProtectionEnabledPBM", 3226 ], 3227 disabled({ 3228 etpCustomFingerprintingProtectionEnabled, 3229 etpCustomFingerprintingProtectionEnabledPBM, 3230 }) { 3231 return ( 3232 etpCustomFingerprintingProtectionEnabled.locked || 3233 etpCustomFingerprintingProtectionEnabledPBM.locked 3234 ); 3235 }, 3236 get( 3237 _, 3238 { 3239 etpCustomFingerprintingProtectionEnabled, 3240 etpCustomFingerprintingProtectionEnabledPBM, 3241 } 3242 ) { 3243 return ( 3244 etpCustomFingerprintingProtectionEnabled.value || 3245 etpCustomFingerprintingProtectionEnabledPBM.value 3246 ); 3247 }, 3248 set( 3249 value, 3250 { 3251 etpCustomFingerprintingProtectionEnabled, 3252 etpCustomFingerprintingProtectionEnabledPBM, 3253 } 3254 ) { 3255 if (value) { 3256 etpCustomFingerprintingProtectionEnabled.value = false; 3257 etpCustomFingerprintingProtectionEnabledPBM.value = true; 3258 } else { 3259 etpCustomFingerprintingProtectionEnabled.value = false; 3260 etpCustomFingerprintingProtectionEnabledPBM.value = false; 3261 } 3262 }, 3263 }); 3264 3265 Preferences.addSetting({ 3266 id: "etpCustomSuspectFingerprintingProtectionEnabledContext", 3267 deps: [ 3268 "etpCustomFingerprintingProtectionEnabled", 3269 "etpCustomFingerprintingProtectionEnabledPBM", 3270 ], 3271 get( 3272 _, 3273 { 3274 etpCustomFingerprintingProtectionEnabled, 3275 etpCustomFingerprintingProtectionEnabledPBM, 3276 } 3277 ) { 3278 if ( 3279 etpCustomFingerprintingProtectionEnabled.value && 3280 etpCustomFingerprintingProtectionEnabledPBM.value 3281 ) { 3282 return "all"; 3283 } else if (etpCustomFingerprintingProtectionEnabledPBM) { 3284 return "pbmOnly"; 3285 } 3286 return null; 3287 }, 3288 set( 3289 value, 3290 { 3291 etpCustomFingerprintingProtectionEnabled, 3292 etpCustomFingerprintingProtectionEnabledPBM, 3293 } 3294 ) { 3295 if (value == "all") { 3296 etpCustomFingerprintingProtectionEnabled.value = true; 3297 etpCustomFingerprintingProtectionEnabledPBM.value = true; 3298 } else if (value == "pbmOnly") { 3299 etpCustomFingerprintingProtectionEnabled.value = false; 3300 etpCustomFingerprintingProtectionEnabledPBM.value = true; 3301 } 3302 }, 3303 }); 3304 3305 function setEventListener(aId, aEventType, aCallback) { 3306 document 3307 .getElementById(aId) 3308 .addEventListener(aEventType, aCallback.bind(gPrivacyPane)); 3309 } 3310 3311 function setSyncFromPrefListener(aId, aCallback) { 3312 Preferences.addSyncFromPrefListener(document.getElementById(aId), aCallback); 3313 } 3314 3315 function setSyncToPrefListener(aId, aCallback) { 3316 Preferences.addSyncToPrefListener(document.getElementById(aId), aCallback); 3317 } 3318 3319 function dataCollectionCheckboxHandler({ 3320 checkbox, 3321 pref, 3322 matchPref = () => true, 3323 isDisabled = () => false, 3324 }) { 3325 function updateCheckbox() { 3326 let collectionEnabled = Services.prefs.getBoolPref( 3327 PREF_UPLOAD_ENABLED, 3328 false 3329 ); 3330 3331 if (collectionEnabled && matchPref()) { 3332 checkbox.toggleAttribute( 3333 "checked", 3334 Services.prefs.getBoolPref(pref, false) 3335 ); 3336 checkbox.setAttribute("preference", pref); 3337 } else { 3338 checkbox.removeAttribute("preference"); 3339 checkbox.removeAttribute("checked"); 3340 } 3341 3342 checkbox.disabled = 3343 !collectionEnabled || Services.prefs.prefIsLocked(pref) || isDisabled(); 3344 } 3345 3346 Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateCheckbox); 3347 updateCheckbox(); 3348 } 3349 3350 // Sets the "Learn how" SUMO link in the Strict/Custom options of Content Blocking. 3351 function setUpContentBlockingWarnings() { 3352 document.getElementById("fpiIncompatibilityWarning").hidden = 3353 !gIsFirstPartyIsolated; 3354 3355 document.getElementById("rfpIncompatibilityWarning").hidden = 3356 !Preferences.get("privacy.resistFingerprinting").value && 3357 !Preferences.get("privacy.resistFingerprinting.pbmode").value; 3358 } 3359 3360 function initTCPStandardSection() { 3361 let cookieBehaviorPref = Preferences.get("network.cookie.cookieBehavior"); 3362 let updateTCPSectionVisibilityState = () => { 3363 document.getElementById("etpStandardTCPBox").hidden = 3364 cookieBehaviorPref.value != 3365 Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; 3366 }; 3367 3368 cookieBehaviorPref.on("change", updateTCPSectionVisibilityState); 3369 3370 updateTCPSectionVisibilityState(); 3371 } 3372 3373 var gPrivacyPane = { 3374 _pane: null, 3375 3376 /** 3377 * Show the Security Level UI 3378 */ 3379 _initSecurityLevel() { 3380 SecurityLevelPreferences.init(); 3381 window.addEventListener("unload", () => SecurityLevelPreferences.uninit(), { 3382 once: true, 3383 }); 3384 }, 3385 3386 /** 3387 * Whether the prompt to restart Firefox should appear when changing the autostart pref. 3388 */ 3389 _shouldPromptForRestart: true, 3390 3391 /** 3392 * Update the tracking protection UI to deal with extension control. 3393 */ 3394 _updateTrackingProtectionUI() { 3395 let cBPrefisLocked = CONTENT_BLOCKING_PREFS.some(pref => 3396 Services.prefs.prefIsLocked(pref) 3397 ); 3398 let tPPrefisLocked = TRACKING_PROTECTION_PREFS.some(pref => 3399 Services.prefs.prefIsLocked(pref) 3400 ); 3401 3402 function setInputsDisabledState(isControlled) { 3403 let tpDisabled = tPPrefisLocked || isControlled; 3404 let disabled = cBPrefisLocked || isControlled; 3405 let tpCheckbox = document.getElementById( 3406 "contentBlockingTrackingProtectionCheckbox" 3407 ); 3408 // Only enable the TP menu if Detect All Trackers is enabled. 3409 document.getElementById("trackingProtectionMenu").disabled = 3410 tpDisabled || !tpCheckbox.checked; 3411 tpCheckbox.disabled = tpDisabled; 3412 3413 document.getElementById("standardRadio").disabled = disabled; 3414 document.getElementById("strictRadio").disabled = disabled; 3415 document 3416 .getElementById("contentBlockingOptionStrict") 3417 .classList.toggle("disabled", disabled); 3418 document 3419 .getElementById("contentBlockingOptionStandard") 3420 .classList.toggle("disabled", disabled); 3421 let arrowButtons = document.querySelectorAll("button.arrowhead"); 3422 for (let button of arrowButtons) { 3423 button.disabled = disabled; 3424 } 3425 3426 // Notify observers that the TP UI has been updated. 3427 // This is needed since our tests need to be notified about the 3428 // trackingProtectionMenu element getting disabled/enabled at the right time. 3429 Services.obs.notifyObservers(window, "privacy-pane-tp-ui-updated"); 3430 } 3431 3432 if (shouldDisableETPCategoryControls()) { 3433 setInputsDisabledState(true); 3434 } 3435 if (tPPrefisLocked) { 3436 // An extension can't control this setting if either pref is locked. 3437 hideControllingExtension(TRACKING_PROTECTION_KEY); 3438 setInputsDisabledState(false); 3439 } else { 3440 handleControllingExtension( 3441 PREF_SETTING_TYPE, 3442 TRACKING_PROTECTION_KEY 3443 ).then(setInputsDisabledState); 3444 } 3445 }, 3446 3447 /** 3448 * Set up handlers for showing and hiding controlling extension info 3449 * for tracking protection. 3450 */ 3451 _initTrackingProtectionExtensionControl() { 3452 setEventListener( 3453 "contentBlockingDisableTrackingProtectionExtension", 3454 "command", 3455 makeDisableControllingExtension( 3456 PREF_SETTING_TYPE, 3457 TRACKING_PROTECTION_KEY 3458 ) 3459 ); 3460 3461 let trackingProtectionObserver = { 3462 observe() { 3463 gPrivacyPane._updateTrackingProtectionUI(); 3464 }, 3465 }; 3466 3467 for (let pref of TRACKING_PROTECTION_PREFS) { 3468 Services.prefs.addObserver(pref, trackingProtectionObserver); 3469 } 3470 window.addEventListener("unload", () => { 3471 for (let pref of TRACKING_PROTECTION_PREFS) { 3472 Services.prefs.removeObserver(pref, trackingProtectionObserver); 3473 } 3474 }); 3475 }, 3476 3477 /** 3478 * Ensure the tracking protection exception list is migrated before the privacy 3479 * preferences UI is shown. 3480 * If the migration has already been run, this is a no-op. 3481 */ 3482 _ensureTrackingProtectionExceptionListMigration() { 3483 // Let's check the migration pref here as well to avoid the extra xpcom call 3484 // for the common case where we've already migrated. 3485 if ( 3486 Services.prefs.getBoolPref( 3487 "privacy.trackingprotection.allow_list.hasMigratedCategoryPrefs", 3488 false 3489 ) 3490 ) { 3491 return; 3492 } 3493 3494 let exceptionListService = Cc[ 3495 "@mozilla.org/url-classifier/exception-list-service;1" 3496 ].getService(Ci.nsIUrlClassifierExceptionListService); 3497 3498 exceptionListService.maybeMigrateCategoryPrefs(); 3499 }, 3500 3501 get dnsOverHttpsResolvers() { 3502 let providers = DoHConfigController.currentConfig.providerList; 3503 // if there's no default, we'll hold its position with an empty string 3504 let defaultURI = DoHConfigController.currentConfig.fallbackProviderURI; 3505 let defaultIndex = providers.findIndex(p => p.uri == defaultURI); 3506 if (defaultIndex == -1 && defaultURI) { 3507 // the default value for the pref isn't included in the resolvers list 3508 // so we'll make a stub for it. Without an id, we'll have to use the url as the label 3509 providers.unshift({ uri: defaultURI }); 3510 } 3511 return providers; 3512 }, 3513 3514 updateDoHResolverList(mode) { 3515 let resolvers = this.dnsOverHttpsResolvers; 3516 let currentURI = Preferences.get("network.trr.uri").value; 3517 if (!currentURI) { 3518 currentURI = Preferences.get("network.trr.default_provider_uri").value; 3519 } 3520 let menu = document.getElementById(`${mode}ResolverChoices`); 3521 3522 let selectedIndex = currentURI 3523 ? resolvers.findIndex(r => r.uri == currentURI) 3524 : 0; 3525 if (selectedIndex == -1) { 3526 // select the last "Custom" item 3527 selectedIndex = menu.itemCount - 1; 3528 } 3529 menu.selectedIndex = selectedIndex; 3530 3531 let customInput = document.getElementById(`${mode}InputField`); 3532 customInput.hidden = menu.value != "custom"; 3533 }, 3534 3535 populateDoHResolverList(mode) { 3536 let resolvers = this.dnsOverHttpsResolvers; 3537 let defaultURI = DoHConfigController.currentConfig.fallbackProviderURI; 3538 let menu = document.getElementById(`${mode}ResolverChoices`); 3539 3540 // populate the DNS-Over-HTTPS resolver list 3541 menu.removeAllItems(); 3542 for (let resolver of resolvers) { 3543 let item = menu.appendItem(undefined, resolver.uri); 3544 if (resolver.uri == defaultURI) { 3545 document.l10n.setAttributes( 3546 item, 3547 "connection-dns-over-https-url-item-default", 3548 { 3549 name: resolver.UIName || resolver.uri, 3550 } 3551 ); 3552 } else { 3553 item.label = resolver.UIName || resolver.uri; 3554 } 3555 } 3556 let lastItem = menu.appendItem(undefined, "custom"); 3557 document.l10n.setAttributes( 3558 lastItem, 3559 "connection-dns-over-https-url-custom" 3560 ); 3561 3562 // set initial selection in the resolver provider picker 3563 this.updateDoHResolverList(mode); 3564 3565 let customInput = document.getElementById(`${mode}InputField`); 3566 3567 function updateURIPref() { 3568 if (customInput.value == "") { 3569 // Setting the pref to empty string will make it have the default 3570 // pref value which makes us fallback to using the default TRR 3571 // resolver in network.trr.default_provider_uri. 3572 // If the input is empty we set it to "(space)" which is essentially 3573 // the same. 3574 Services.prefs.setStringPref("network.trr.uri", " "); 3575 } else { 3576 Services.prefs.setStringPref("network.trr.uri", customInput.value); 3577 } 3578 } 3579 3580 menu.addEventListener("command", () => { 3581 if (menu.value == "custom") { 3582 customInput.hidden = false; 3583 updateURIPref(); 3584 } else { 3585 customInput.hidden = true; 3586 Services.prefs.setStringPref("network.trr.uri", menu.value); 3587 } 3588 Glean.securityDohSettings.providerChoiceValue.record({ 3589 value: menu.value, 3590 }); 3591 3592 // Update other menu too. 3593 let otherMode = mode == "dohEnabled" ? "dohStrict" : "dohEnabled"; 3594 let otherMenu = document.getElementById(`${otherMode}ResolverChoices`); 3595 let otherInput = document.getElementById(`${otherMode}InputField`); 3596 otherMenu.value = menu.value; 3597 otherInput.hidden = otherMenu.value != "custom"; 3598 }); 3599 3600 // Change the URL when you press ENTER in the input field it or loses focus 3601 customInput.addEventListener("change", () => { 3602 updateURIPref(); 3603 }); 3604 }, 3605 3606 async updateDoHStatus() { 3607 let trrURI = Services.dns.currentTrrURI; 3608 let hostname = URL.parse(trrURI)?.hostname; 3609 if (!hostname) { 3610 hostname = await document.l10n.formatValue("preferences-doh-bad-url"); 3611 } 3612 3613 let steering = document.getElementById("dohSteeringStatus"); 3614 steering.hidden = true; 3615 3616 let dohResolver = document.getElementById("dohResolver"); 3617 dohResolver.hidden = true; 3618 3619 let status = document.getElementById("dohStatus"); 3620 3621 async function setStatus(localizedStringName, options) { 3622 let opts = options || {}; 3623 let statusString = await document.l10n.formatValue( 3624 localizedStringName, 3625 opts 3626 ); 3627 document.l10n.setAttributes(status, "preferences-doh-status", { 3628 status: statusString, 3629 }); 3630 } 3631 3632 function computeStatus() { 3633 let mode = Services.dns.currentTrrMode; 3634 if ( 3635 mode == Ci.nsIDNSService.MODE_TRRFIRST || 3636 mode == Ci.nsIDNSService.MODE_TRRONLY 3637 ) { 3638 if (lazy.gParentalControlsService?.parentalControlsEnabled) { 3639 return "preferences-doh-status-not-active"; 3640 } 3641 let confirmationState = Services.dns.currentTrrConfirmationState; 3642 switch (confirmationState) { 3643 case Ci.nsIDNSService.CONFIRM_TRYING_OK: 3644 case Ci.nsIDNSService.CONFIRM_OK: 3645 case Ci.nsIDNSService.CONFIRM_DISABLED: 3646 return "preferences-doh-status-active"; 3647 default: 3648 return "preferences-doh-status-not-active"; 3649 } 3650 } 3651 3652 return "preferences-doh-status-disabled"; 3653 } 3654 3655 let errReason = ""; 3656 let confirmationStatus = Services.dns.lastConfirmationStatus; 3657 let mode = Services.dns.currentTrrMode; 3658 if ( 3659 (mode == Ci.nsIDNSService.MODE_TRRFIRST || 3660 mode == Ci.nsIDNSService.MODE_TRRONLY) && 3661 lazy.gParentalControlsService?.parentalControlsEnabled 3662 ) { 3663 errReason = Services.dns.getTRRSkipReasonName( 3664 Ci.nsITRRSkipReason.TRR_PARENTAL_CONTROL 3665 ); 3666 } else if (confirmationStatus != Cr.NS_OK) { 3667 errReason = ChromeUtils.getXPCOMErrorName(confirmationStatus); 3668 } else { 3669 errReason = Services.dns.getTRRSkipReasonName( 3670 Services.dns.lastConfirmationSkipReason 3671 ); 3672 } 3673 let statusLabel = computeStatus(); 3674 // setStatus will format and set the statusLabel asynchronously. 3675 setStatus(statusLabel, { reason: errReason }); 3676 dohResolver.hidden = statusLabel == "preferences-doh-status-disabled"; 3677 3678 let statusLearnMore = document.getElementById("dohStatusLearnMore"); 3679 statusLearnMore.hidden = statusLabel != "preferences-doh-status-not-active"; 3680 3681 // No need to set the resolver name since we're not going to show it. 3682 if (statusLabel == "preferences-doh-status-disabled") { 3683 return; 3684 } 3685 3686 function nameOrDomain() { 3687 for (let resolver of DoHConfigController.currentConfig.providerList) { 3688 if (resolver.uri == trrURI) { 3689 return resolver.UIName || hostname || trrURI; 3690 } 3691 } 3692 3693 // Also check if this is a steering provider. 3694 for (let resolver of DoHConfigController.currentConfig.providerSteering 3695 .providerList) { 3696 if (resolver.uri == trrURI) { 3697 steering.hidden = false; 3698 return resolver.UIName || hostname || trrURI; 3699 } 3700 } 3701 3702 return hostname; 3703 } 3704 3705 let resolverNameOrDomain = nameOrDomain(); 3706 document.l10n.setAttributes(dohResolver, "preferences-doh-resolver", { 3707 name: resolverNameOrDomain, 3708 }); 3709 }, 3710 3711 highlightDoHCategoryAndUpdateStatus() { 3712 let value = Preferences.get("network.trr.mode").value; 3713 let defaultOption = document.getElementById("dohOptionDefault"); 3714 let enabledOption = document.getElementById("dohOptionEnabled"); 3715 let strictOption = document.getElementById("dohOptionStrict"); 3716 let offOption = document.getElementById("dohOptionOff"); 3717 defaultOption.classList.remove("selected"); 3718 enabledOption.classList.remove("selected"); 3719 strictOption.classList.remove("selected"); 3720 offOption.classList.remove("selected"); 3721 3722 switch (value) { 3723 case Ci.nsIDNSService.MODE_NATIVEONLY: 3724 defaultOption.classList.add("selected"); 3725 break; 3726 case Ci.nsIDNSService.MODE_TRRFIRST: 3727 enabledOption.classList.add("selected"); 3728 break; 3729 case Ci.nsIDNSService.MODE_TRRONLY: 3730 strictOption.classList.add("selected"); 3731 break; 3732 case Ci.nsIDNSService.MODE_TRROFF: 3733 offOption.classList.add("selected"); 3734 break; 3735 default: 3736 // The pref is set to a random value. 3737 // This shouldn't happen, but let's make sure off is selected. 3738 offOption.classList.add("selected"); 3739 document.getElementById("dohCategoryRadioGroup").selectedIndex = 3; 3740 break; 3741 } 3742 3743 // When the mode is set to 0 we need to clear the URI so 3744 // doh-rollout can kick in. 3745 if (value == Ci.nsIDNSService.MODE_NATIVEONLY) { 3746 Services.prefs.clearUserPref("network.trr.uri"); 3747 Services.prefs.clearUserPref("doh-rollout.disable-heuristics"); 3748 } 3749 3750 // Bug 1861285 3751 // When the mode is set to 2 or 3, we need to check if network.trr.uri is a empty string. 3752 // In this case, we need to update network.trr.uri to default to fallbackProviderURI. 3753 // This occurs when the mode is previously set to 0 (Default Protection). 3754 if ( 3755 value == Ci.nsIDNSService.MODE_TRRFIRST || 3756 value == Ci.nsIDNSService.MODE_TRRONLY 3757 ) { 3758 if (!Services.prefs.getStringPref("network.trr.uri")) { 3759 Services.prefs.setStringPref( 3760 "network.trr.uri", 3761 DoHConfigController.currentConfig.fallbackProviderURI 3762 ); 3763 } 3764 } 3765 3766 // Bug 1900672 3767 // When the mode is set to 5, clear the pref to ensure that 3768 // network.trr.uri is set to fallbackProviderURIwhen the mode is set to 2 or 3 afterwards 3769 if (value == Ci.nsIDNSService.MODE_TRROFF) { 3770 Services.prefs.clearUserPref("network.trr.uri"); 3771 } 3772 3773 gPrivacyPane.updateDoHStatus(); 3774 }, 3775 3776 /** 3777 * Init DoH corresponding prefs 3778 */ 3779 initDoH() { 3780 setEventListener("dohDefaultArrow", "command", this.toggleExpansion); 3781 setEventListener("dohEnabledArrow", "command", this.toggleExpansion); 3782 setEventListener("dohStrictArrow", "command", this.toggleExpansion); 3783 3784 function modeButtonPressed(e) { 3785 // Clicking the active mode again should not generate another event 3786 if ( 3787 parseInt(e.target.value) == Preferences.get("network.trr.mode").value 3788 ) { 3789 return; 3790 } 3791 Glean.securityDohSettings.modeChangedButton.record({ 3792 value: e.target.id, 3793 }); 3794 } 3795 3796 setEventListener("dohDefaultRadio", "command", modeButtonPressed); 3797 setEventListener("dohEnabledRadio", "command", modeButtonPressed); 3798 setEventListener("dohStrictRadio", "command", modeButtonPressed); 3799 setEventListener("dohOffRadio", "command", modeButtonPressed); 3800 3801 this.populateDoHResolverList("dohEnabled"); 3802 this.populateDoHResolverList("dohStrict"); 3803 3804 Preferences.get("network.trr.uri").on("change", () => { 3805 gPrivacyPane.updateDoHResolverList("dohEnabled"); 3806 gPrivacyPane.updateDoHResolverList("dohStrict"); 3807 gPrivacyPane.updateDoHStatus(); 3808 }); 3809 3810 // Update status box and hightlightling when the pref changes 3811 Preferences.get("network.trr.mode").on( 3812 "change", 3813 gPrivacyPane.highlightDoHCategoryAndUpdateStatus 3814 ); 3815 this.highlightDoHCategoryAndUpdateStatus(); 3816 3817 Services.obs.addObserver(this, "network:trr-uri-changed"); 3818 Services.obs.addObserver(this, "network:trr-mode-changed"); 3819 Services.obs.addObserver(this, "network:trr-confirmation"); 3820 let unload = () => { 3821 Services.obs.removeObserver(this, "network:trr-uri-changed"); 3822 Services.obs.removeObserver(this, "network:trr-mode-changed"); 3823 Services.obs.removeObserver(this, "network:trr-confirmation"); 3824 }; 3825 window.addEventListener("unload", unload, { once: true }); 3826 3827 let uriPref = Services.prefs.getStringPref("network.trr.uri"); 3828 // If the value isn't one of the providers, we need to update the 3829 // custom_uri pref to make sure the input box contains the correct URL. 3830 if (uriPref && !this.dnsOverHttpsResolvers.some(e => e.uri == uriPref)) { 3831 Services.prefs.setStringPref( 3832 "network.trr.custom_uri", 3833 Services.prefs.getStringPref("network.trr.uri") 3834 ); 3835 } 3836 3837 if (Services.prefs.prefIsLocked("network.trr.mode")) { 3838 document.getElementById("dohCategoryRadioGroup").disabled = true; 3839 Services.prefs.setStringPref("network.trr.custom_uri", uriPref); 3840 } 3841 }, 3842 3843 initWebAuthn() { 3844 document.getElementById("openWindowsPasskeySettings").hidden = 3845 !Services.prefs.getBoolPref( 3846 "security.webauthn.show_ms_settings_link", 3847 true 3848 ); 3849 }, 3850 3851 /** 3852 * Sets up the UI for the number of days of history to keep, and updates the 3853 * label of the "Clear Now..." button. 3854 */ 3855 init() { 3856 initSettingGroup("nonTechnicalPrivacy"); 3857 initSettingGroup("nonTechnicalPrivacy2"); 3858 initSettingGroup("securityPrivacyStatus"); 3859 initSettingGroup("securityPrivacyWarnings"); 3860 initSettingGroup("httpsOnly"); 3861 initSettingGroup("browsingProtection"); 3862 initSettingGroup("cookiesAndSiteData"); 3863 initSettingGroup("cookiesAndSiteData2"); 3864 initSettingGroup("certificates"); 3865 initSettingGroup("ipprotection"); 3866 initSettingGroup("history"); 3867 initSettingGroup("history2"); 3868 initSettingGroup("permissions"); 3869 initSettingGroup("dnsOverHttps"); 3870 initSettingGroup("dnsOverHttpsAdvanced"); 3871 initSettingGroup("etpStatus"); 3872 initSettingGroup("etpBanner"); 3873 initSettingGroup("etpAdvanced"); 3874 initSettingGroup("etpReset"); 3875 initSettingGroup("etpCustomize"); 3876 3877 /* Initialize Content Blocking */ 3878 this.initContentBlocking(); 3879 3880 this.trackingProtectionReadPrefs(); 3881 this.fingerprintingProtectionReadPrefs(); 3882 this.networkCookieBehaviorReadPrefs(); 3883 this._initTrackingProtectionExtensionControl(); 3884 this._ensureTrackingProtectionExceptionListMigration(); 3885 this._initProfilesInfo(); 3886 OnionServicesAuthPreferences.init(); 3887 this._initSecurityLevel(); 3888 3889 Preferences.get("privacy.trackingprotection.enabled").on( 3890 "change", 3891 gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane) 3892 ); 3893 Preferences.get("privacy.trackingprotection.pbmode.enabled").on( 3894 "change", 3895 gPrivacyPane.trackingProtectionReadPrefs.bind(gPrivacyPane) 3896 ); 3897 3898 // Watch all of the prefs that the new Cookies & Site Data UI depends on 3899 Preferences.get("network.cookie.cookieBehavior").on( 3900 "change", 3901 gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane) 3902 ); 3903 Preferences.get("browser.privatebrowsing.autostart").on( 3904 "change", 3905 gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane) 3906 ); 3907 Preferences.get("privacy.firstparty.isolate").on( 3908 "change", 3909 gPrivacyPane.networkCookieBehaviorReadPrefs.bind(gPrivacyPane) 3910 ); 3911 3912 Preferences.get("privacy.fingerprintingProtection").on( 3913 "change", 3914 gPrivacyPane.fingerprintingProtectionReadPrefs.bind(gPrivacyPane) 3915 ); 3916 Preferences.get("privacy.fingerprintingProtection.pbmode").on( 3917 "change", 3918 gPrivacyPane.fingerprintingProtectionReadPrefs.bind(gPrivacyPane) 3919 ); 3920 3921 setEventListener( 3922 "trackingProtectionExceptions", 3923 "command", 3924 gPrivacyPane.showTrackingProtectionExceptions 3925 ); 3926 3927 setEventListener( 3928 "dohExceptionsButton", 3929 "command", 3930 gPrivacyPane.showDoHExceptions 3931 ); 3932 setEventListener( 3933 "passwordExceptions", 3934 "command", 3935 gPrivacyPane.showPasswordExceptions 3936 ); 3937 setEventListener( 3938 "useMasterPassword", 3939 "command", 3940 gPrivacyPane.updateMasterPasswordButton 3941 ); 3942 setEventListener( 3943 "changeMasterPassword", 3944 "command", 3945 gPrivacyPane.changeMasterPassword 3946 ); 3947 setEventListener("showPasswords", "command", gPrivacyPane.showPasswords); 3948 3949 this._pane = document.getElementById("panePrivacy"); 3950 3951 this._initPasswordGenerationUI(); 3952 this._initRelayIntegrationUI(); 3953 this._initMasterPasswordUI(); 3954 this._initOSAuthentication(); 3955 3956 // Init passwords settings group 3957 initSettingGroup("passwords"); 3958 3959 this.initListenersForExtensionControllingPasswordManager(); 3960 3961 setSyncFromPrefListener("contentBlockingBlockCookiesCheckbox", () => 3962 this.readBlockCookies() 3963 ); 3964 setSyncToPrefListener("contentBlockingBlockCookiesCheckbox", () => 3965 this.writeBlockCookies() 3966 ); 3967 setSyncFromPrefListener("blockCookiesMenu", () => 3968 this.readBlockCookiesFrom() 3969 ); 3970 setSyncToPrefListener("blockCookiesMenu", () => 3971 this.writeBlockCookiesFrom() 3972 ); 3973 3974 setSyncFromPrefListener("savePasswords", () => this.readSavePasswords()); 3975 3976 this.initSiteDataControls(); 3977 3978 this.initCookieBannerHandling(); 3979 3980 this.initDataCollection(); 3981 3982 if (AppConstants.MOZ_DATA_REPORTING) { 3983 this.updateSubmitHealthReportFromPref(); 3984 Preferences.get(PREF_UPLOAD_ENABLED).on( 3985 "change", 3986 gPrivacyPane.updateSubmitHealthReportFromPref 3987 ); 3988 setEventListener( 3989 "submitHealthReportBox", 3990 "command", 3991 gPrivacyPane.updateSubmitHealthReportToPref 3992 ); 3993 if (AppConstants.MOZ_NORMANDY) { 3994 this.initOptOutStudyCheckbox(); 3995 } 3996 this.initAddonRecommendationsCheckbox(); 3997 } 3998 3999 let signonBundle = document.getElementById("signonBundle"); 4000 appendSearchKeywords("showPasswords", [ 4001 signonBundle.getString("loginsDescriptionAll2"), 4002 ]); 4003 4004 setEventListener( 4005 "contentBlockingBaselineExceptionsStrict", 4006 "change", 4007 gPrivacyPane.onBaselineCheckboxChange 4008 ); 4009 4010 setEventListener( 4011 "contentBlockingBaselineExceptionsCustom", 4012 "change", 4013 gPrivacyPane.onBaselineCheckboxChange 4014 ); 4015 4016 setEventListener( 4017 "contentBlockingConvenienceExceptionsStrict", 4018 "change", 4019 gPrivacyPane.maybeNotifyUserToReload 4020 ); 4021 4022 setEventListener( 4023 "contentBlockingConvenienceExceptionsCustom", 4024 "change", 4025 gPrivacyPane.maybeNotifyUserToReload 4026 ); 4027 4028 this.initDoH(); 4029 4030 this.initWebAuthn(); 4031 4032 // Notify observers that the UI is now ready 4033 Services.obs.notifyObservers(window, "privacy-pane-loaded"); 4034 }, 4035 4036 initSiteDataControls() { 4037 SiteDataManager.updateSites(); 4038 }, 4039 4040 // CONTENT BLOCKING 4041 4042 /** 4043 * Initializes the content blocking section. 4044 */ 4045 initContentBlocking() { 4046 setEventListener( 4047 "contentBlockingTrackingProtectionCheckbox", 4048 "command", 4049 this.trackingProtectionWritePrefs 4050 ); 4051 setEventListener( 4052 "contentBlockingTrackingProtectionCheckbox", 4053 "command", 4054 this._updateTrackingProtectionUI 4055 ); 4056 setEventListener( 4057 "contentBlockingCryptominersCheckbox", 4058 "command", 4059 this.updateCryptominingLists 4060 ); 4061 setEventListener( 4062 "contentBlockingFingerprintersCheckbox", 4063 "command", 4064 this.updateFingerprintingLists 4065 ); 4066 setEventListener( 4067 "trackingProtectionMenu", 4068 "command", 4069 this.trackingProtectionWritePrefs 4070 ); 4071 setEventListener( 4072 "contentBlockingFingerprintingProtectionCheckbox", 4073 "command", 4074 e => { 4075 const extra = { checked: e.target.checked }; 4076 Glean.privacyUiFppClick.checkbox.record(extra); 4077 this.fingerprintingProtectionWritePrefs(); 4078 } 4079 ); 4080 setEventListener("fingerprintingProtectionMenu", "command", e => { 4081 const extra = { value: e.target.value }; 4082 Glean.privacyUiFppClick.menu.record(extra); 4083 this.fingerprintingProtectionWritePrefs(); 4084 }); 4085 setEventListener("standardArrow", "command", this.toggleExpansion); 4086 setEventListener("strictArrow", "command", this.toggleExpansion); 4087 setEventListener("customArrow", "command", this.toggleExpansion); 4088 4089 Preferences.get("network.cookie.cookieBehavior").on( 4090 "change", 4091 gPrivacyPane.readBlockCookies.bind(gPrivacyPane) 4092 ); 4093 Preferences.get("browser.contentblocking.category").on( 4094 "change", 4095 gPrivacyPane.highlightCBCategory 4096 ); 4097 4098 // If any relevant content blocking pref changes, show a warning that the changes will 4099 // not be implemented until they refresh their tabs. 4100 for (let pref of CONTENT_BLOCKING_PREFS) { 4101 // Skip registering change listeners for baseline and convenience allow list prefs. 4102 // Their UI is handled in gPrivacyPane.onBaselineCheckboxChange to prevent redundant reload 4103 // warnings when user toggles the checkboxes. 4104 if ( 4105 pref == "privacy.trackingprotection.allow_list.baseline.enabled" || 4106 pref == "privacy.trackingprotection.allow_list.convenience.enabled" 4107 ) { 4108 continue; 4109 } 4110 Preferences.get(pref).on("change", gPrivacyPane.maybeNotifyUserToReload); 4111 // If the value changes, run populateCategoryContents, since that change might have been 4112 // triggered by a default value changing in the standard category. 4113 Preferences.get(pref).on("change", gPrivacyPane.populateCategoryContents); 4114 } 4115 Preferences.get("urlclassifier.trackingTable").on( 4116 "change", 4117 gPrivacyPane.maybeNotifyUserToReload 4118 ); 4119 for (let button of document.querySelectorAll(".reload-tabs-button")) { 4120 button.addEventListener("command", gPrivacyPane.reloadAllOtherTabs); 4121 } 4122 4123 let cryptoMinersOption = document.getElementById( 4124 "contentBlockingCryptominersOption" 4125 ); 4126 let fingerprintersOption = document.getElementById( 4127 "contentBlockingFingerprintersOption" 4128 ); 4129 let trackingAndIsolateOption = document.querySelector( 4130 "#blockCookiesMenu menuitem[value='trackers-plus-isolate']" 4131 ); 4132 cryptoMinersOption.hidden = !Services.prefs.getBoolPref( 4133 "browser.contentblocking.cryptomining.preferences.ui.enabled" 4134 ); 4135 fingerprintersOption.hidden = !Services.prefs.getBoolPref( 4136 "browser.contentblocking.fingerprinting.preferences.ui.enabled" 4137 ); 4138 let updateTrackingAndIsolateOption = () => { 4139 trackingAndIsolateOption.hidden = 4140 !Services.prefs.getBoolPref( 4141 "browser.contentblocking.reject-and-isolate-cookies.preferences.ui.enabled", 4142 false 4143 ) || gIsFirstPartyIsolated; 4144 }; 4145 Preferences.get("privacy.firstparty.isolate").on( 4146 "change", 4147 updateTrackingAndIsolateOption 4148 ); 4149 updateTrackingAndIsolateOption(); 4150 4151 Preferences.get("browser.contentblocking.features.strict").on( 4152 "change", 4153 this.populateCategoryContents 4154 ); 4155 this.populateCategoryContents(); 4156 this.highlightCBCategory(); 4157 this.readBlockCookies(); 4158 4159 // Toggles the text "Cross-site and social media trackers" based on the 4160 // social tracking pref. If the pref is false, the text reads 4161 // "Cross-site trackers". 4162 const STP_COOKIES_PREF = "privacy.socialtracking.block_cookies.enabled"; 4163 if (Services.prefs.getBoolPref(STP_COOKIES_PREF)) { 4164 let contentBlockOptionSocialMedia = document.getElementById( 4165 "blockCookiesSocialMedia" 4166 ); 4167 4168 document.l10n.setAttributes( 4169 contentBlockOptionSocialMedia, 4170 "sitedata-option-block-cross-site-tracking-cookies" 4171 ); 4172 } 4173 4174 Preferences.get("privacy.resistFingerprinting").on( 4175 "change", 4176 setUpContentBlockingWarnings 4177 ); 4178 Preferences.get("privacy.resistFingerprinting.pbmode").on( 4179 "change", 4180 setUpContentBlockingWarnings 4181 ); 4182 4183 setUpContentBlockingWarnings(); 4184 4185 initTCPStandardSection(); 4186 }, 4187 4188 populateCategoryContents() { 4189 for (let type of ["strict", "standard"]) { 4190 let rulesArray = []; 4191 let selector; 4192 if (type == "strict") { 4193 selector = "#contentBlockingOptionStrict"; 4194 rulesArray = Services.prefs 4195 .getStringPref("browser.contentblocking.features.strict") 4196 .split(","); 4197 if (gIsFirstPartyIsolated) { 4198 let idx = rulesArray.indexOf("cookieBehavior5"); 4199 if (idx != -1) { 4200 rulesArray[idx] = "cookieBehavior4"; 4201 } 4202 } 4203 } else { 4204 selector = "#contentBlockingOptionStandard"; 4205 // In standard show/hide UI items based on the default values of the relevant prefs. 4206 let defaults = Services.prefs.getDefaultBranch(""); 4207 4208 let cookieBehavior = defaults.getIntPref( 4209 "network.cookie.cookieBehavior" 4210 ); 4211 switch (cookieBehavior) { 4212 case Ci.nsICookieService.BEHAVIOR_ACCEPT: 4213 rulesArray.push("cookieBehavior0"); 4214 break; 4215 case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: 4216 rulesArray.push("cookieBehavior1"); 4217 break; 4218 case Ci.nsICookieService.BEHAVIOR_REJECT: 4219 rulesArray.push("cookieBehavior2"); 4220 break; 4221 case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: 4222 rulesArray.push("cookieBehavior3"); 4223 break; 4224 case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: 4225 rulesArray.push("cookieBehavior4"); 4226 break; 4227 case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: 4228 rulesArray.push( 4229 gIsFirstPartyIsolated ? "cookieBehavior4" : "cookieBehavior5" 4230 ); 4231 break; 4232 } 4233 let cookieBehaviorPBM = defaults.getIntPref( 4234 "network.cookie.cookieBehavior.pbmode" 4235 ); 4236 switch (cookieBehaviorPBM) { 4237 case Ci.nsICookieService.BEHAVIOR_ACCEPT: 4238 rulesArray.push("cookieBehaviorPBM0"); 4239 break; 4240 case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: 4241 rulesArray.push("cookieBehaviorPBM1"); 4242 break; 4243 case Ci.nsICookieService.BEHAVIOR_REJECT: 4244 rulesArray.push("cookieBehaviorPBM2"); 4245 break; 4246 case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: 4247 rulesArray.push("cookieBehaviorPBM3"); 4248 break; 4249 case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: 4250 rulesArray.push("cookieBehaviorPBM4"); 4251 break; 4252 case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: 4253 rulesArray.push( 4254 gIsFirstPartyIsolated 4255 ? "cookieBehaviorPBM4" 4256 : "cookieBehaviorPBM5" 4257 ); 4258 break; 4259 } 4260 rulesArray.push( 4261 defaults.getBoolPref( 4262 "privacy.trackingprotection.cryptomining.enabled" 4263 ) 4264 ? "cryptoTP" 4265 : "-cryptoTP" 4266 ); 4267 rulesArray.push( 4268 defaults.getBoolPref( 4269 "privacy.trackingprotection.fingerprinting.enabled" 4270 ) 4271 ? "fp" 4272 : "-fp" 4273 ); 4274 rulesArray.push( 4275 Services.prefs.getBoolPref( 4276 "privacy.socialtracking.block_cookies.enabled" 4277 ) 4278 ? "stp" 4279 : "-stp" 4280 ); 4281 rulesArray.push( 4282 defaults.getBoolPref("privacy.trackingprotection.enabled") 4283 ? "tp" 4284 : "-tp" 4285 ); 4286 rulesArray.push( 4287 defaults.getBoolPref("privacy.trackingprotection.pbmode.enabled") 4288 ? "tpPrivate" 4289 : "-tpPrivate" 4290 ); 4291 } 4292 4293 // Hide all cookie options first, until we learn which one should be showing. 4294 document.querySelector(selector + " .all-cookies-option").hidden = true; 4295 document.querySelector(selector + " .unvisited-cookies-option").hidden = 4296 true; 4297 document.querySelector(selector + " .cross-site-cookies-option").hidden = 4298 true; 4299 document.querySelector( 4300 selector + " .third-party-tracking-cookies-option" 4301 ).hidden = true; 4302 document.querySelector( 4303 selector + " .all-third-party-cookies-private-windows-option" 4304 ).hidden = true; 4305 document.querySelector( 4306 selector + " .all-third-party-cookies-option" 4307 ).hidden = true; 4308 document.querySelector(selector + " .social-media-option").hidden = true; 4309 4310 for (let item of rulesArray) { 4311 // Note "cookieBehavior0", will result in no UI changes, so is not listed here. 4312 switch (item) { 4313 case "tp": 4314 document.querySelector(selector + " .trackers-option").hidden = 4315 false; 4316 break; 4317 case "-tp": 4318 document.querySelector(selector + " .trackers-option").hidden = 4319 true; 4320 break; 4321 case "tpPrivate": 4322 document.querySelector(selector + " .pb-trackers-option").hidden = 4323 false; 4324 break; 4325 case "-tpPrivate": 4326 document.querySelector(selector + " .pb-trackers-option").hidden = 4327 true; 4328 break; 4329 case "fp": 4330 document.querySelector( 4331 selector + " .fingerprinters-option" 4332 ).hidden = false; 4333 break; 4334 case "-fp": 4335 document.querySelector( 4336 selector + " .fingerprinters-option" 4337 ).hidden = true; 4338 break; 4339 case "cryptoTP": 4340 document.querySelector(selector + " .cryptominers-option").hidden = 4341 false; 4342 break; 4343 case "-cryptoTP": 4344 document.querySelector(selector + " .cryptominers-option").hidden = 4345 true; 4346 break; 4347 case "stp": { 4348 // Store social tracking cookies pref 4349 const STP_COOKIES_PREF = 4350 "privacy.socialtracking.block_cookies.enabled"; 4351 4352 if (Services.prefs.getBoolPref(STP_COOKIES_PREF)) { 4353 document.querySelector( 4354 selector + " .social-media-option" 4355 ).hidden = false; 4356 } 4357 break; 4358 } 4359 case "-stp": 4360 // Store social tracking cookies pref 4361 document.querySelector(selector + " .social-media-option").hidden = 4362 true; 4363 break; 4364 case "cookieBehavior1": 4365 document.querySelector( 4366 selector + " .all-third-party-cookies-option" 4367 ).hidden = false; 4368 break; 4369 case "cookieBehavior2": 4370 document.querySelector(selector + " .all-cookies-option").hidden = 4371 false; 4372 break; 4373 case "cookieBehavior3": 4374 document.querySelector( 4375 selector + " .unvisited-cookies-option" 4376 ).hidden = false; 4377 break; 4378 case "cookieBehavior4": 4379 document.querySelector( 4380 selector + " .third-party-tracking-cookies-option" 4381 ).hidden = false; 4382 break; 4383 case "cookieBehavior5": 4384 document.querySelector( 4385 selector + " .cross-site-cookies-option" 4386 ).hidden = false; 4387 break; 4388 case "cookieBehaviorPBM5": 4389 // We only need to show the cookie option for private windows if the 4390 // cookieBehaviors are different between regular windows and private 4391 // windows. 4392 if (!rulesArray.includes("cookieBehavior5")) { 4393 document.querySelector( 4394 selector + " .all-third-party-cookies-private-windows-option" 4395 ).hidden = false; 4396 } 4397 break; 4398 } 4399 } 4400 // Hide the "tracking protection in private browsing" list item 4401 // if the "tracking protection enabled in all windows" list item is showing. 4402 if (!document.querySelector(selector + " .trackers-option").hidden) { 4403 document.querySelector(selector + " .pb-trackers-option").hidden = true; 4404 } 4405 } 4406 }, 4407 4408 highlightCBCategory() { 4409 let value = Preferences.get("browser.contentblocking.category").value; 4410 let standardEl = document.getElementById("contentBlockingOptionStandard"); 4411 let strictEl = document.getElementById("contentBlockingOptionStrict"); 4412 let customEl = document.getElementById("contentBlockingOptionCustom"); 4413 standardEl.classList.remove("selected"); 4414 strictEl.classList.remove("selected"); 4415 customEl.classList.remove("selected"); 4416 4417 switch (value) { 4418 case "strict": 4419 strictEl.classList.add("selected"); 4420 break; 4421 case "custom": 4422 customEl.classList.add("selected"); 4423 break; 4424 case "standard": 4425 /* fall through */ 4426 default: 4427 standardEl.classList.add("selected"); 4428 break; 4429 } 4430 }, 4431 4432 updateCryptominingLists() { 4433 let listPrefs = [ 4434 "urlclassifier.features.cryptomining.blacklistTables", 4435 "urlclassifier.features.cryptomining.whitelistTables", 4436 ]; 4437 4438 let listValue = listPrefs 4439 .map(l => Services.prefs.getStringPref(l)) 4440 .join(","); 4441 listManager.forceUpdates(listValue); 4442 }, 4443 4444 updateFingerprintingLists() { 4445 let listPrefs = [ 4446 "urlclassifier.features.fingerprinting.blacklistTables", 4447 "urlclassifier.features.fingerprinting.whitelistTables", 4448 ]; 4449 4450 let listValue = listPrefs 4451 .map(l => Services.prefs.getStringPref(l)) 4452 .join(","); 4453 listManager.forceUpdates(listValue); 4454 }, 4455 4456 // TRACKING PROTECTION MODE 4457 4458 /** 4459 * Selects the right item of the Tracking Protection menulist and checkbox. 4460 */ 4461 trackingProtectionReadPrefs() { 4462 let enabledPref = Preferences.get("privacy.trackingprotection.enabled"); 4463 let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled"); 4464 let tpMenu = document.getElementById("trackingProtectionMenu"); 4465 let tpCheckbox = document.getElementById( 4466 "contentBlockingTrackingProtectionCheckbox" 4467 ); 4468 4469 this._updateTrackingProtectionUI(); 4470 4471 // Global enable takes precedence over enabled in Private Browsing. 4472 if (enabledPref.value) { 4473 tpMenu.value = "always"; 4474 tpCheckbox.checked = true; 4475 } else if (pbmPref.value) { 4476 tpMenu.value = "private"; 4477 tpCheckbox.checked = true; 4478 } else { 4479 tpMenu.value = "never"; 4480 tpCheckbox.checked = false; 4481 } 4482 }, 4483 4484 /** 4485 * Selects the right item of the Fingerprinting Protection menulist and 4486 * checkbox. 4487 */ 4488 fingerprintingProtectionReadPrefs() { 4489 let enabledPref = Preferences.get("privacy.fingerprintingProtection"); 4490 let pbmPref = Preferences.get("privacy.fingerprintingProtection.pbmode"); 4491 let fppMenu = document.getElementById("fingerprintingProtectionMenu"); 4492 let fppCheckbox = document.getElementById( 4493 "contentBlockingFingerprintingProtectionCheckbox" 4494 ); 4495 4496 // Global enable takes precedence over enabled in Private Browsing. 4497 if (enabledPref.value) { 4498 fppMenu.value = "always"; 4499 fppCheckbox.checked = true; 4500 } else if (pbmPref.value) { 4501 fppMenu.value = "private"; 4502 fppCheckbox.checked = true; 4503 } else { 4504 fppMenu.value = "never"; 4505 fppCheckbox.checked = false; 4506 } 4507 fppMenu.disabled = !fppCheckbox.checked || enabledPref.locked; 4508 fppCheckbox.disabled = enabledPref.locked; 4509 }, 4510 4511 /** 4512 * Selects the right items of the new Cookies & Site Data UI. 4513 */ 4514 networkCookieBehaviorReadPrefs() { 4515 let behavior = Services.cookies.getCookieBehavior(false); 4516 let blockCookiesMenu = document.getElementById("blockCookiesMenu"); 4517 let blockCookies = behavior != Ci.nsICookieService.BEHAVIOR_ACCEPT; 4518 let cookieBehaviorLocked = Services.prefs.prefIsLocked( 4519 "network.cookie.cookieBehavior" 4520 ); 4521 let blockCookiesControlsDisabled = !blockCookies || cookieBehaviorLocked; 4522 blockCookiesMenu.disabled = blockCookiesControlsDisabled; 4523 4524 switch (behavior) { 4525 case Ci.nsICookieService.BEHAVIOR_ACCEPT: 4526 break; 4527 case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: 4528 blockCookiesMenu.value = "all-third-parties"; 4529 break; 4530 case Ci.nsICookieService.BEHAVIOR_REJECT: 4531 blockCookiesMenu.value = "always"; 4532 break; 4533 case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: 4534 blockCookiesMenu.value = "unvisited"; 4535 break; 4536 case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: 4537 blockCookiesMenu.value = "trackers"; 4538 break; 4539 case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: 4540 blockCookiesMenu.value = "trackers-plus-isolate"; 4541 break; 4542 } 4543 }, 4544 4545 /** 4546 * Sets the pref values based on the selected item of the radiogroup. 4547 */ 4548 trackingProtectionWritePrefs() { 4549 let enabledPref = Preferences.get("privacy.trackingprotection.enabled"); 4550 let pbmPref = Preferences.get("privacy.trackingprotection.pbmode.enabled"); 4551 let stpPref = Preferences.get( 4552 "privacy.trackingprotection.socialtracking.enabled" 4553 ); 4554 let stpCookiePref = Preferences.get( 4555 "privacy.socialtracking.block_cookies.enabled" 4556 ); 4557 // Currently, we don't expose the email tracking protection setting on our 4558 // privacy UI. Instead, we use the existing tracking protection checkbox to 4559 // control the email tracking protection. 4560 let emailTPPref = Preferences.get( 4561 "privacy.trackingprotection.emailtracking.enabled" 4562 ); 4563 let emailTPPBMPref = Preferences.get( 4564 "privacy.trackingprotection.emailtracking.pbmode.enabled" 4565 ); 4566 let tpMenu = document.getElementById("trackingProtectionMenu"); 4567 let tpCheckbox = document.getElementById( 4568 "contentBlockingTrackingProtectionCheckbox" 4569 ); 4570 4571 let value; 4572 if (tpCheckbox.checked) { 4573 if (tpMenu.value == "never") { 4574 tpMenu.value = "private"; 4575 } 4576 value = tpMenu.value; 4577 } else { 4578 tpMenu.value = "never"; 4579 value = "never"; 4580 } 4581 4582 switch (value) { 4583 case "always": 4584 enabledPref.value = true; 4585 pbmPref.value = true; 4586 emailTPPref.value = true; 4587 emailTPPBMPref.value = true; 4588 if (stpCookiePref.value) { 4589 stpPref.value = true; 4590 } 4591 break; 4592 case "private": 4593 enabledPref.value = false; 4594 pbmPref.value = true; 4595 emailTPPref.value = false; 4596 emailTPPBMPref.value = true; 4597 if (stpCookiePref.value) { 4598 stpPref.value = false; 4599 } 4600 break; 4601 case "never": 4602 enabledPref.value = false; 4603 pbmPref.value = false; 4604 emailTPPref.value = false; 4605 emailTPPBMPref.value = false; 4606 if (stpCookiePref.value) { 4607 stpPref.value = false; 4608 } 4609 break; 4610 } 4611 }, 4612 4613 fingerprintingProtectionWritePrefs() { 4614 let enabledPref = Preferences.get("privacy.fingerprintingProtection"); 4615 let pbmPref = Preferences.get("privacy.fingerprintingProtection.pbmode"); 4616 let fppMenu = document.getElementById("fingerprintingProtectionMenu"); 4617 let fppCheckbox = document.getElementById( 4618 "contentBlockingFingerprintingProtectionCheckbox" 4619 ); 4620 4621 let value; 4622 if (fppCheckbox.checked) { 4623 if (fppMenu.value == "never") { 4624 fppMenu.value = "private"; 4625 } 4626 value = fppMenu.value; 4627 } else { 4628 fppMenu.value = "never"; 4629 value = "never"; 4630 } 4631 4632 fppMenu.disabled = !fppCheckbox.checked; 4633 4634 switch (value) { 4635 case "always": 4636 enabledPref.value = true; 4637 pbmPref.value = true; 4638 break; 4639 case "private": 4640 enabledPref.value = false; 4641 pbmPref.value = true; 4642 break; 4643 case "never": 4644 enabledPref.value = false; 4645 pbmPref.value = false; 4646 break; 4647 } 4648 }, 4649 4650 toggleExpansion(e) { 4651 let carat = e.target; 4652 carat.classList.toggle("up"); 4653 carat.closest(".privacy-detailedoption").classList.toggle("expanded"); 4654 carat.setAttribute( 4655 "aria-expanded", 4656 carat.getAttribute("aria-expanded") === "false" 4657 ); 4658 }, 4659 4660 // CLEAR PRIVATE DATA 4661 4662 /* 4663 * Preferences: 4664 * 4665 * privacy.sanitize.sanitizeOnShutdown 4666 * - true if the user's private data is cleared on startup according to the 4667 * Clear Private Data settings, false otherwise 4668 */ 4669 4670 /** 4671 * Displays the Clear Private Data settings dialog. 4672 */ 4673 showClearPrivateDataSettings() { 4674 let dialogFile = useOldClearHistoryDialog 4675 ? "chrome://browser/content/preferences/dialogs/sanitize.xhtml" 4676 : "chrome://browser/content/sanitize_v2.xhtml"; 4677 4678 gSubDialog.open( 4679 dialogFile, 4680 { 4681 features: "resizable=no", 4682 }, 4683 { 4684 mode: "clearOnShutdown", 4685 } 4686 ); 4687 }, 4688 4689 /** 4690 * Displays a dialog from which individual parts of private data may be 4691 * cleared. 4692 */ 4693 clearPrivateDataNow(aClearEverything) { 4694 var ts = Preferences.get("privacy.sanitize.timeSpan"); 4695 var timeSpanOrig = ts.value; 4696 4697 if (aClearEverything) { 4698 ts.value = 0; 4699 } 4700 4701 // Bug 1856418 We intend to remove the old dialog box 4702 let dialogFile = useOldClearHistoryDialog 4703 ? "chrome://browser/content/sanitize.xhtml" 4704 : "chrome://browser/content/sanitize_v2.xhtml"; 4705 4706 gSubDialog.open(dialogFile, { 4707 features: "resizable=no", 4708 closingCallback: () => { 4709 // reset the timeSpan pref 4710 if (aClearEverything) { 4711 ts.value = timeSpanOrig; 4712 } 4713 4714 Services.obs.notifyObservers(null, "clear-private-data"); 4715 }, 4716 }); 4717 }, 4718 4719 /* 4720 Checks if the user set cleaning prefs that do not belong to DeleteOnClose 4721 */ 4722 _isCustomCleaningPrefPresent() { 4723 let sanitizeOnShutdownPrefsArray = useOldClearHistoryDialog 4724 ? SANITIZE_ON_SHUTDOWN_PREFS_ONLY 4725 : SANITIZE_ON_SHUTDOWN_PREFS_ONLY_V2; 4726 4727 return sanitizeOnShutdownPrefsArray.some( 4728 pref => Preferences.get(pref).value 4729 ); 4730 }, 4731 4732 /** 4733 * Displays fine-grained, per-site preferences for tracking protection. 4734 */ 4735 showTrackingProtectionExceptions() { 4736 let params = { 4737 permissionType: "trackingprotection", 4738 disableETPVisible: true, 4739 prefilledHost: "", 4740 hideStatusColumn: true, 4741 }; 4742 gSubDialog.open( 4743 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 4744 undefined, 4745 params 4746 ); 4747 }, 4748 4749 // COOKIES AND SITE DATA 4750 4751 /* 4752 * Preferences: 4753 * 4754 * network.cookie.cookieBehavior 4755 * - determines how the browser should handle cookies: 4756 * 0 means enable all cookies 4757 * 1 means reject all third party cookies 4758 * 2 means disable all cookies 4759 * 3 means reject third party cookies unless at least one is already set for the eTLD 4760 * 4 means reject all trackers 4761 * 5 means reject all trackers and partition third-party cookies 4762 * see netwerk/cookie/src/CookieService.cpp for details 4763 */ 4764 4765 /** 4766 * Reads the network.cookie.cookieBehavior preference value and 4767 * enables/disables the "blockCookiesMenu" menulist accordingly. 4768 */ 4769 readBlockCookies() { 4770 let bcControl = document.getElementById("blockCookiesMenu"); 4771 bcControl.disabled = 4772 Services.cookies.getCookieBehavior(false) == 4773 Ci.nsICookieService.BEHAVIOR_ACCEPT; 4774 }, 4775 4776 /** 4777 * Updates the "accept third party cookies" menu based on whether the 4778 * "contentBlockingBlockCookiesCheckbox" checkbox is checked. 4779 */ 4780 writeBlockCookies() { 4781 let block = document.getElementById("contentBlockingBlockCookiesCheckbox"); 4782 let blockCookiesMenu = document.getElementById("blockCookiesMenu"); 4783 4784 if (block.checked) { 4785 // Automatically select 'third-party trackers' as the default. 4786 blockCookiesMenu.selectedIndex = 0; 4787 return this.writeBlockCookiesFrom(); 4788 } 4789 return Ci.nsICookieService.BEHAVIOR_ACCEPT; 4790 }, 4791 4792 readBlockCookiesFrom() { 4793 switch (Services.cookies.getCookieBehavior(false)) { 4794 case Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN: 4795 return "all-third-parties"; 4796 case Ci.nsICookieService.BEHAVIOR_REJECT: 4797 return "always"; 4798 case Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN: 4799 return "unvisited"; 4800 case Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER: 4801 return "trackers"; 4802 case BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN: 4803 return "trackers-plus-isolate"; 4804 default: 4805 return undefined; 4806 } 4807 }, 4808 4809 writeBlockCookiesFrom() { 4810 let block = document.getElementById("blockCookiesMenu").selectedItem; 4811 switch (block.value) { 4812 case "trackers": 4813 return Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER; 4814 case "unvisited": 4815 return Ci.nsICookieService.BEHAVIOR_LIMIT_FOREIGN; 4816 case "always": 4817 return Ci.nsICookieService.BEHAVIOR_REJECT; 4818 case "all-third-parties": 4819 return Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN; 4820 case "trackers-plus-isolate": 4821 return Ci.nsICookieService 4822 .BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN; 4823 default: 4824 return undefined; 4825 } 4826 }, 4827 4828 /** 4829 * Discard the browsers of all tabs in all windows. Pinned tabs, as 4830 * well as tabs for which discarding doesn't succeed (e.g. selected 4831 * tabs, tabs with beforeunload listeners), are reloaded. 4832 */ 4833 reloadAllOtherTabs() { 4834 let ourTab = window.browsingContext.topChromeWindow.gBrowser.selectedTab; 4835 BrowserWindowTracker.orderedWindows.forEach(win => { 4836 let otherGBrowser = win.gBrowser; 4837 for (let tab of otherGBrowser.tabs) { 4838 if (tab == ourTab) { 4839 // Don't reload our preferences tab. 4840 continue; 4841 } 4842 4843 if (tab.pinned || tab.selected) { 4844 otherGBrowser.reloadTab(tab); 4845 } else { 4846 otherGBrowser.discardBrowser(tab); 4847 } 4848 } 4849 }); 4850 4851 for (let notification of document.querySelectorAll(".reload-tabs")) { 4852 notification.hidden = true; 4853 } 4854 4855 Preferences.getSetting("reloadTabsHint").value = false; 4856 }, 4857 4858 /** 4859 * If there are more tabs than just the preferences tab, show a warning to the user that 4860 * they need to reload their tabs to apply the setting. 4861 */ 4862 maybeNotifyUserToReload() { 4863 let shouldShow = false; 4864 if (window.BrowserWindowTracker.orderedWindows.length > 1) { 4865 shouldShow = true; 4866 } else { 4867 let tabbrowser = window.browsingContext.topChromeWindow.gBrowser; 4868 if (tabbrowser.tabs.length > 1) { 4869 shouldShow = true; 4870 } 4871 } 4872 if (shouldShow) { 4873 for (let notification of document.querySelectorAll(".reload-tabs")) { 4874 notification.hidden = false; 4875 } 4876 } 4877 4878 Preferences.getSetting("reloadTabsHint").value = true; 4879 }, 4880 4881 /** 4882 * Displays per-site preferences for HTTPS-Only Mode exceptions. 4883 */ 4884 showHttpsOnlyModeExceptions() { 4885 var params = { 4886 blockVisible: false, 4887 sessionVisible: true, 4888 allowVisible: false, 4889 prefilledHost: "", 4890 permissionType: "https-only-load-insecure", 4891 forcedHTTP: true, 4892 }; 4893 gSubDialog.open( 4894 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 4895 undefined, 4896 params 4897 ); 4898 }, 4899 4900 showDoHExceptions() { 4901 gSubDialog.open( 4902 "chrome://browser/content/preferences/dialogs/dohExceptions.xhtml", 4903 undefined 4904 ); 4905 }, 4906 4907 /** 4908 * Initializes the cookie banner handling subgroup on the privacy pane. 4909 * 4910 * This UI is shown if the "cookiebanners.ui.desktop.enabled" pref is true. 4911 * 4912 * The cookie banner handling checkbox reflects the cookie banner feature 4913 * state. It is enabled when the service enabled via the 4914 * cookiebanners.service.mode pref. If detection-only mode is enabled the 4915 * checkbox is unchecked, since in this mode no banners are handled. It is 4916 * only used for detection for banners which means we may prompt the user to 4917 * enable the feature via other UI surfaces such as the onboarding doorhanger. 4918 * 4919 * If the user checks the checkbox, the pref value is set to 4920 * nsICookieBannerService.MODE_REJECT_OR_ACCEPT. 4921 * 4922 * If the user unchecks the checkbox, the mode pref value is set to 4923 * nsICookieBannerService.MODE_DISABLED. 4924 * 4925 * Advanced users can choose other int-valued modes via about:config. 4926 */ 4927 initCookieBannerHandling() { 4928 setSyncFromPrefListener("handleCookieBanners", () => 4929 this.readCookieBannerMode() 4930 ); 4931 setSyncToPrefListener("handleCookieBanners", () => 4932 this.writeCookieBannerMode() 4933 ); 4934 4935 let preference = Preferences.get("cookiebanners.ui.desktop.enabled"); 4936 preference.on("change", () => this.updateCookieBannerHandlingVisibility()); 4937 4938 this.updateCookieBannerHandlingVisibility(); 4939 }, 4940 4941 /** 4942 * Reads the cookiebanners.service.mode.privateBrowsing pref, 4943 * interpreting the multiple modes as a true/false value 4944 */ 4945 readCookieBannerMode() { 4946 return ( 4947 Preferences.get("cookiebanners.service.mode.privateBrowsing").value != 4948 Ci.nsICookieBannerService.MODE_DISABLED 4949 ); 4950 }, 4951 4952 /** 4953 * Translates user clicks on the cookie banner handling checkbox to the 4954 * corresponding integer-valued cookie banner mode preference. 4955 */ 4956 writeCookieBannerMode() { 4957 let checkbox = document.getElementById("handleCookieBanners"); 4958 if (!checkbox.checked) { 4959 /* because we removed UI control for the non-PBM pref, disabling it here 4960 provides an off-ramp for profiles where it had previously been enabled from the UI */ 4961 Services.prefs.setIntPref( 4962 "cookiebanners.service.mode", 4963 Ci.nsICookieBannerService.MODE_DISABLED 4964 ); 4965 return Ci.nsICookieBannerService.MODE_DISABLED; 4966 } 4967 return Ci.nsICookieBannerService.MODE_REJECT; 4968 }, 4969 4970 /** 4971 * Shows or hides the cookie banner handling section based on the value of 4972 * the "cookiebanners.ui.desktop.enabled" pref. 4973 */ 4974 updateCookieBannerHandlingVisibility() { 4975 let groupbox = document.getElementById("cookieBannerHandlingGroup"); 4976 let isEnabled = Preferences.get("cookiebanners.ui.desktop.enabled").value; 4977 4978 // Because the top-level pane showing code unsets the hidden attribute, we 4979 // manually hide the section when cookie banner handling is preffed off. 4980 if (isEnabled) { 4981 groupbox.removeAttribute("style"); 4982 } else { 4983 groupbox.setAttribute("style", "display: none !important"); 4984 } 4985 }, 4986 4987 // GEOLOCATION 4988 4989 /** 4990 * Displays the location exceptions dialog where specific site location 4991 * preferences can be set. 4992 */ 4993 showLocationExceptions() { 4994 let params = { permissionType: "geo" }; 4995 4996 gSubDialog.open( 4997 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 4998 { features: "resizable=yes" }, 4999 params 5000 ); 5001 }, 5002 5003 // LOCALHOST 5004 5005 /** 5006 * Displays the localhost exceptions dialog where specific site localhost 5007 * preferences can be set. 5008 */ 5009 showLocalHostExceptions() { 5010 let params = { permissionType: "localhost" }; 5011 5012 gSubDialog.open( 5013 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5014 { features: "resizable=yes" }, 5015 params 5016 ); 5017 }, 5018 5019 // LOCAL-NETWORK 5020 5021 /** 5022 * Displays the local network exceptions dialog where specific site local network 5023 * preferences can be set. 5024 */ 5025 showLocalNetworkExceptions() { 5026 let params = { permissionType: "local-network" }; 5027 5028 gSubDialog.open( 5029 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5030 { features: "resizable=yes" }, 5031 params 5032 ); 5033 }, 5034 5035 // XR 5036 5037 /** 5038 * Displays the XR exceptions dialog where specific site XR 5039 * preferences can be set. 5040 */ 5041 showXRExceptions() { 5042 let params = { permissionType: "xr" }; 5043 5044 gSubDialog.open( 5045 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5046 { features: "resizable=yes" }, 5047 params 5048 ); 5049 }, 5050 5051 // CAMERA 5052 5053 /** 5054 * Displays the camera exceptions dialog where specific site camera 5055 * preferences can be set. 5056 */ 5057 showCameraExceptions() { 5058 let params = { permissionType: "camera" }; 5059 5060 gSubDialog.open( 5061 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5062 { features: "resizable=yes" }, 5063 params 5064 ); 5065 }, 5066 5067 // MICROPHONE 5068 5069 /** 5070 * Displays the microphone exceptions dialog where specific site microphone 5071 * preferences can be set. 5072 */ 5073 showMicrophoneExceptions() { 5074 let params = { permissionType: "microphone" }; 5075 5076 gSubDialog.open( 5077 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5078 { features: "resizable=yes" }, 5079 params 5080 ); 5081 }, 5082 5083 // SPEAKER 5084 5085 /** 5086 * Displays the speaker exceptions dialog where specific site speaker 5087 * preferences can be set. 5088 */ 5089 showSpeakerExceptions() { 5090 let params = { permissionType: "speaker" }; 5091 5092 gSubDialog.open( 5093 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5094 { features: "resizable=yes" }, 5095 params 5096 ); 5097 }, 5098 5099 // NOTIFICATIONS 5100 5101 /** 5102 * Displays the notifications exceptions dialog where specific site notification 5103 * preferences can be set. 5104 */ 5105 showNotificationExceptions() { 5106 let params = { permissionType: "desktop-notification" }; 5107 5108 gSubDialog.open( 5109 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5110 { features: "resizable=yes" }, 5111 params 5112 ); 5113 }, 5114 5115 // MEDIA 5116 5117 showAutoplayMediaExceptions() { 5118 var params = { permissionType: "autoplay-media" }; 5119 5120 gSubDialog.open( 5121 "chrome://browser/content/preferences/dialogs/sitePermissions.xhtml", 5122 { features: "resizable=yes" }, 5123 params 5124 ); 5125 }, 5126 5127 // POP-UPS 5128 5129 /** 5130 * Displays the popup exceptions dialog where specific site popup preferences 5131 * can be set. 5132 */ 5133 showPopupExceptions() { 5134 var params = { 5135 blockVisible: false, 5136 sessionVisible: false, 5137 allowVisible: true, 5138 prefilledHost: "", 5139 permissionType: "popup", 5140 }; 5141 5142 gSubDialog.open( 5143 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 5144 { features: "resizable=yes" }, 5145 params 5146 ); 5147 }, 5148 5149 // UTILITY FUNCTIONS 5150 5151 /** 5152 * Utility function to enable/disable the button specified by aButtonID based 5153 * on the value of the Boolean preference specified by aPreferenceID. 5154 */ 5155 updateButtons(aButtonID, aPreferenceID) { 5156 var button = document.getElementById(aButtonID); 5157 var preference = Preferences.get(aPreferenceID); 5158 button.disabled = !preference.value || preference.locked; 5159 return undefined; 5160 }, 5161 5162 // BEGIN UI CODE 5163 5164 /* 5165 * Preferences: 5166 * 5167 * dom.disable_open_during_load 5168 * - true if popups are blocked by default, false otherwise 5169 */ 5170 5171 // POP-UPS 5172 5173 /** 5174 * Displays a dialog in which the user can view and modify the list of sites 5175 * where passwords are never saved. 5176 */ 5177 showPasswordExceptions() { 5178 var params = { 5179 blockVisible: true, 5180 sessionVisible: false, 5181 allowVisible: false, 5182 hideStatusColumn: true, 5183 prefilledHost: "", 5184 permissionType: "login-saving", 5185 }; 5186 5187 gSubDialog.open( 5188 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 5189 undefined, 5190 params 5191 ); 5192 }, 5193 5194 /** 5195 * Initializes master password UI: the "use master password" checkbox, selects 5196 * the master password button to show, and enables/disables it as necessary. 5197 * The master password is controlled by various bits of NSS functionality, so 5198 * the UI for it can't be controlled by the normal preference bindings. 5199 */ 5200 _initMasterPasswordUI() { 5201 var noMP = !LoginHelper.isPrimaryPasswordSet(); 5202 5203 var button = document.getElementById("changeMasterPassword"); 5204 button.disabled = noMP; 5205 5206 var checkbox = document.getElementById("useMasterPassword"); 5207 checkbox.checked = !noMP; 5208 checkbox.disabled = 5209 (noMP && !Services.policies.isAllowed("createMasterPassword")) || 5210 (!noMP && !Services.policies.isAllowed("removeMasterPassword")); 5211 }, 5212 5213 /** 5214 * Enables/disables the master password button depending on the state of the 5215 * "use master password" checkbox, and prompts for master password removal if 5216 * one is set. 5217 */ 5218 async updateMasterPasswordButton() { 5219 var checkbox = document.getElementById("useMasterPassword"); 5220 var button = document.getElementById("changeMasterPassword"); 5221 button.disabled = !checkbox.checked; 5222 5223 // unchecking the checkbox should try to immediately remove the master 5224 // password, because it's impossible to non-destructively remove the master 5225 // password used to encrypt all the passwords without providing it (by 5226 // design), and it would be extremely odd to pop up that dialog when the 5227 // user closes the prefwindow and saves his settings 5228 if (!checkbox.checked) { 5229 await this._removeMasterPassword(); 5230 } else { 5231 await this.changeMasterPassword(); 5232 } 5233 5234 this._initMasterPasswordUI(); 5235 }, 5236 5237 /** 5238 * Displays the "remove master password" dialog to allow the user to remove 5239 * the current master password. When the dialog is dismissed, master password 5240 * UI is automatically updated. 5241 */ 5242 async _removeMasterPassword() { 5243 var secmodDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService( 5244 Ci.nsIPKCS11ModuleDB 5245 ); 5246 if (secmodDB.isFIPSEnabled) { 5247 let title = document.getElementById("fips-title").textContent; 5248 let desc = document.getElementById("fips-desc").textContent; 5249 Services.prompt.alert(window, title, desc); 5250 this._initMasterPasswordUI(); 5251 } else { 5252 gSubDialog.open("chrome://mozapps/content/preferences/removemp.xhtml", { 5253 closingCallback: () => { 5254 Services.obs.notifyObservers(null, "passwordmgr-primary-pw-changed"); 5255 this._initMasterPasswordUI(); 5256 }, 5257 }); 5258 } 5259 }, 5260 5261 /** 5262 * Displays a dialog in which the primary password may be changed. 5263 */ 5264 async changeMasterPassword() { 5265 // Require OS authentication before the user can set a Primary Password. 5266 // OS reauthenticate functionality is not available on Linux yet (bug 1527745) 5267 if (!LoginHelper.isPrimaryPasswordSet() && LoginHelper.getOSAuthEnabled()) { 5268 // Uses primary-password-os-auth-dialog-message-win and 5269 // primary-password-os-auth-dialog-message-macosx via concatenation: 5270 let messageId = 5271 "primary-password-os-auth-dialog-message-" + AppConstants.platform; 5272 let [messageText, captionText] = await document.l10n.formatMessages([ 5273 { 5274 id: messageId, 5275 }, 5276 { 5277 id: "master-password-os-auth-dialog-caption", 5278 }, 5279 ]); 5280 let win = Services.wm.getMostRecentBrowserWindow(); 5281 5282 // Note on Glean collection: because OSKeyStore.ensureLoggedIn() is not wrapped in 5283 // verifyOSAuth(), it will be documenting "success" for unsupported platforms 5284 // and won't record "fail_error", only "fail_user_canceled" 5285 let loggedIn = await OSKeyStore.ensureLoggedIn( 5286 messageText.value, 5287 captionText.value, 5288 win, 5289 false 5290 ); 5291 5292 const result = loggedIn.authenticated ? "success" : "fail_user_canceled"; 5293 Glean.pwmgr.promptShownOsReauth.record({ 5294 trigger: "toggle_pref_primary_password", 5295 result, 5296 }); 5297 5298 if (!loggedIn.authenticated) { 5299 return; 5300 } 5301 } 5302 5303 gSubDialog.open("chrome://mozapps/content/preferences/changemp.xhtml", { 5304 features: "resizable=no", 5305 closingCallback: () => { 5306 Services.obs.notifyObservers(null, "passwordmgr-primary-pw-changed"); 5307 this._initMasterPasswordUI(); 5308 }, 5309 }); 5310 }, 5311 5312 /** 5313 * Set up the initial state for the password generation UI. 5314 * It will be hidden unless the .available pref is true 5315 */ 5316 _initPasswordGenerationUI() { 5317 // we don't watch the .available pref for runtime changes 5318 let prefValue = Services.prefs.getBoolPref( 5319 PREF_PASSWORD_GENERATION_AVAILABLE, 5320 false 5321 ); 5322 document.getElementById("generatePasswordsBox").hidden = !prefValue; 5323 }, 5324 5325 toggleRelayIntegration() { 5326 const checkbox = document.getElementById("relayIntegration"); 5327 if (checkbox.checked) { 5328 FirefoxRelay.markAsAvailable(); 5329 Glean.relayIntegration.enabledPrefChange.record(); 5330 } else { 5331 FirefoxRelay.markAsDisabled(); 5332 Glean.relayIntegration.disabledPrefChange.record(); 5333 } 5334 }, 5335 5336 _updateRelayIntegrationUI() { 5337 // In Base Browser, we always hide the integration checkbox since 5338 // FirefoxRelay should remain disabled. 5339 // See tor-browser#43109 and tor-browser#42814. 5340 // NOTE: FirefoxRelay.isAvailable will be true whenever 5341 // FirefoxRelay.isDisabled is true. 5342 document.getElementById("relayIntegrationBox").hidden = true; 5343 document.getElementById("relayIntegration").checked = 5344 FirefoxRelay.isAvailable && !FirefoxRelay.isDisabled; 5345 }, 5346 5347 _initRelayIntegrationUI() { 5348 document 5349 .getElementById("relayIntegrationLearnMoreLink") 5350 .setAttribute("href", FirefoxRelay.learnMoreUrl); 5351 5352 setEventListener( 5353 "relayIntegration", 5354 "command", 5355 gPrivacyPane.toggleRelayIntegration.bind(gPrivacyPane) 5356 ); 5357 Preferences.get("signon.firefoxRelay.feature").on( 5358 "change", 5359 gPrivacyPane._updateRelayIntegrationUI.bind(gPrivacyPane) 5360 ); 5361 5362 this._updateRelayIntegrationUI(); 5363 }, 5364 5365 async _toggleOSAuth() { 5366 let osReauthCheckbox = document.getElementById("osReauthCheckbox"); 5367 5368 const messageText = await lazy.AboutLoginsL10n.formatValue( 5369 "about-logins-os-auth-dialog-message" 5370 ); 5371 const captionText = await lazy.AboutLoginsL10n.formatValue( 5372 "about-logins-os-auth-dialog-caption" 5373 ); 5374 let win = 5375 osReauthCheckbox.ownerGlobal.docShell.chromeEventHandler.ownerGlobal; 5376 5377 // Calling OSKeyStore.ensureLoggedIn() instead of LoginHelper.verifyOSAuth() 5378 // since we want to authenticate user each time this setting is changed. 5379 5380 // Note on Glean collection: because OSKeyStore.ensureLoggedIn() is not wrapped in 5381 // verifyOSAuth(), it will be documenting "success" for unsupported platforms 5382 // and won't record "fail_error", only "fail_user_canceled" 5383 let isAuthorized = ( 5384 await OSKeyStore.ensureLoggedIn(messageText, captionText, win, false) 5385 ).authenticated; 5386 5387 Glean.pwmgr.promptShownOsReauth.record({ 5388 trigger: "toggle_pref_os_auth", 5389 result: isAuthorized ? "success" : "fail_user_canceled", 5390 }); 5391 5392 if (!isAuthorized) { 5393 osReauthCheckbox.checked = !osReauthCheckbox.checked; 5394 return; 5395 } 5396 5397 // If osReauthCheckbox is checked enable osauth. 5398 LoginHelper.setOSAuthEnabled(osReauthCheckbox.checked); 5399 5400 Glean.pwmgr.requireOsReauthToggle.record({ 5401 toggle_state: osReauthCheckbox.checked, 5402 }); 5403 }, 5404 5405 _initOSAuthentication() { 5406 let osReauthCheckbox = document.getElementById("osReauthCheckbox"); 5407 if ( 5408 !OSKeyStore.canReauth() || 5409 Services.prefs.getBoolPref("security.nocertdb", false) 5410 ) { 5411 osReauthCheckbox.hidden = true; 5412 return; 5413 } 5414 5415 osReauthCheckbox.toggleAttribute("checked", LoginHelper.getOSAuthEnabled()); 5416 5417 setEventListener( 5418 "osReauthCheckbox", 5419 "command", 5420 gPrivacyPane._toggleOSAuth.bind(gPrivacyPane) 5421 ); 5422 }, 5423 5424 /** 5425 * Shows the sites where the user has saved passwords and the associated login 5426 * information. 5427 */ 5428 showPasswords() { 5429 let loginManager = window.windowGlobalChild.getActor("LoginManager"); 5430 loginManager.sendAsyncMessage("PasswordManager:OpenPreferences", { 5431 entryPoint: "Preferences", 5432 }); 5433 }, 5434 5435 /** 5436 * Enables/disables dependent controls related to password saving 5437 * When password saving is not enabled, we need to also disable the password generation checkbox 5438 * The Exceptions button is used to configure sites where passwords are never saved. 5439 */ 5440 readSavePasswords() { 5441 var prefValue = Preferences.get("signon.rememberSignons").value; 5442 document.getElementById("passwordExceptions").disabled = !prefValue; 5443 document.getElementById("generatePasswords").disabled = !prefValue; 5444 document.getElementById("passwordAutofillCheckbox").disabled = !prefValue; 5445 document.getElementById("relayIntegration").disabled = 5446 !prefValue || Services.prefs.prefIsLocked("signon.firefoxRelay.feature"); 5447 // don't override pref value in UI 5448 return undefined; 5449 }, 5450 5451 /** 5452 * Initalizes pref listeners for the password manager. 5453 * 5454 * This ensures that the user is always notified if an extension is controlling the password manager. 5455 */ 5456 initListenersForExtensionControllingPasswordManager() { 5457 this._passwordManagerCheckbox = document.getElementById("savePasswords"); 5458 this._disableExtensionButton = document.getElementById( 5459 "disablePasswordManagerExtension" 5460 ); 5461 5462 this._disableExtensionButton.addEventListener( 5463 "command", 5464 makeDisableControllingExtension( 5465 PREF_SETTING_TYPE, 5466 PASSWORD_MANAGER_PREF_ID 5467 ) 5468 ); 5469 5470 initListenersForPrefChange( 5471 PREF_SETTING_TYPE, 5472 PASSWORD_MANAGER_PREF_ID, 5473 this._passwordManagerCheckbox 5474 ); 5475 }, 5476 5477 /** 5478 * Displays the exceptions lists for add-on installation warnings. 5479 */ 5480 showAddonExceptions() { 5481 var params = this._addonParams; 5482 5483 gSubDialog.open( 5484 "chrome://browser/content/preferences/dialogs/permissions.xhtml", 5485 undefined, 5486 params 5487 ); 5488 }, 5489 5490 /** 5491 * Parameters for the add-on install permissions dialog. 5492 */ 5493 _addonParams: { 5494 blockVisible: false, 5495 sessionVisible: false, 5496 allowVisible: true, 5497 prefilledHost: "", 5498 permissionType: "install", 5499 }, 5500 5501 /** 5502 * Displays the user's certificates and associated options. 5503 */ 5504 showCertificates() { 5505 gSubDialog.open("chrome://pippki/content/certManager.xhtml"); 5506 }, 5507 5508 /** 5509 * Displays a dialog from which the user can manage his security devices. 5510 */ 5511 showSecurityDevices() { 5512 gSubDialog.open("chrome://pippki/content/device_manager.xhtml"); 5513 }, 5514 5515 initDataCollection() { 5516 if ( 5517 !AppConstants.MOZ_DATA_REPORTING && 5518 !Services.prefs.getBoolPref( 5519 "browser.privacySegmentation.preferences.show" 5520 ) 5521 ) { 5522 // Nothing to control in the data collection section, remove it. 5523 document.getElementById("dataCollectionCategory").remove(); 5524 document.getElementById("dataCollectionGroup").remove(); 5525 return; 5526 } 5527 5528 this._setupLearnMoreLink( 5529 "toolkit.datacollection.infoURL", 5530 "dataCollectionPrivacyNotice" 5531 ); 5532 this.initPrivacySegmentation(); 5533 }, 5534 5535 initPrivacySegmentation() { 5536 // Section visibility 5537 let section = document.getElementById("privacySegmentationSection"); 5538 let updatePrivacySegmentationSectionVisibilityState = () => { 5539 section.hidden = !Services.prefs.getBoolPref( 5540 "browser.privacySegmentation.preferences.show" 5541 ); 5542 }; 5543 5544 Services.prefs.addObserver( 5545 "browser.privacySegmentation.preferences.show", 5546 updatePrivacySegmentationSectionVisibilityState 5547 ); 5548 5549 window.addEventListener("unload", () => { 5550 Services.prefs.removeObserver( 5551 "browser.privacySegmentation.preferences.show", 5552 updatePrivacySegmentationSectionVisibilityState 5553 ); 5554 }); 5555 5556 updatePrivacySegmentationSectionVisibilityState(); 5557 }, 5558 5559 /** 5560 * Set up or hide the Learn More links for various data collection options 5561 */ 5562 _setupLearnMoreLink(pref, element) { 5563 // set up the Learn More link with the correct URL 5564 let url = Services.urlFormatter.formatURLPref(pref); 5565 let el = document.getElementById(element); 5566 5567 if (url) { 5568 el.setAttribute("href", url); 5569 } else { 5570 el.hidden = true; 5571 } 5572 }, 5573 5574 /** 5575 * Update the health report service checkbox from preference. 5576 */ 5577 updateSubmitHealthReportFromPref() { 5578 let checkbox = document.getElementById("submitHealthReportBox"); 5579 let telemetryContainer = document.getElementById("telemetry-container"); 5580 5581 // Telemetry is only sending data if MOZ_TELEMETRY_REPORTING is defined. 5582 // We still want to display the preferences panel if that's not the case, but 5583 // we want it to be disabled and unchecked. 5584 if ( 5585 Services.prefs.prefIsLocked(PREF_UPLOAD_ENABLED) || 5586 !AppConstants.MOZ_TELEMETRY_REPORTING 5587 ) { 5588 checkbox.setAttribute("disabled", "true"); 5589 return; 5590 } 5591 5592 checkbox.checked = 5593 Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED) && 5594 AppConstants.MOZ_TELEMETRY_REPORTING; 5595 telemetryContainer.hidden = checkbox.checked; 5596 }, 5597 5598 /** 5599 * Update the health report preference with state from checkbox. 5600 */ 5601 updateSubmitHealthReportToPref() { 5602 let checkbox = document.getElementById("submitHealthReportBox"); 5603 let telemetryContainer = document.getElementById("telemetry-container"); 5604 5605 Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked); 5606 telemetryContainer.hidden = checkbox.checked; 5607 }, 5608 5609 /** 5610 * Initialize the opt-out-study preference checkbox into about:preferences and 5611 * handles events coming from the UI for it. 5612 */ 5613 initOptOutStudyCheckbox() { 5614 // The checkbox should be disabled if any of the below are true. This 5615 // prevents the user from changing the value in the box. 5616 // 5617 // * the policy forbids shield 5618 // * Normandy is disabled 5619 // 5620 // The checkbox should match the value of the preference only if all of 5621 // these are true. Otherwise, the checkbox should remain unchecked. This 5622 // is because in these situations, Shield studies are always disabled, and 5623 // so showing a checkbox would be confusing. 5624 // 5625 // * the policy allows Shield 5626 // * Normandy is enabled 5627 5628 const allowedByPolicy = Services.policies.isAllowed("Shield"); 5629 const checkbox = document.getElementById("optOutStudiesEnabled"); 5630 5631 function updateCheckbox() { 5632 if ( 5633 allowedByPolicy && 5634 Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED, false) && 5635 Services.prefs.getBoolPref(PREF_NORMANDY_ENABLED, false) 5636 ) { 5637 checkbox.toggleAttribute( 5638 "checked", 5639 Services.prefs.getBoolPref(PREF_OPT_OUT_STUDIES_ENABLED, false) 5640 ); 5641 checkbox.setAttribute("preference", PREF_OPT_OUT_STUDIES_ENABLED); 5642 checkbox.removeAttribute("disabled"); 5643 } else { 5644 checkbox.removeAttribute("preference"); 5645 checkbox.removeAttribute("checked"); 5646 checkbox.setAttribute("disabled", "true"); 5647 } 5648 } 5649 Preferences.get(PREF_UPLOAD_ENABLED).on("change", updateCheckbox); 5650 updateCheckbox(); 5651 }, 5652 5653 initAddonRecommendationsCheckbox() { 5654 // Setup the checkbox. 5655 dataCollectionCheckboxHandler({ 5656 checkbox: document.getElementById("addonRecommendationEnabled"), 5657 pref: PREF_ADDON_RECOMMENDATIONS_ENABLED, 5658 }); 5659 }, 5660 5661 observe(aSubject, aTopic) { 5662 switch (aTopic) { 5663 case "network:trr-uri-changed": 5664 case "network:trr-mode-changed": 5665 case "network:trr-confirmation": 5666 gPrivacyPane.updateDoHStatus(); 5667 break; 5668 } 5669 }, 5670 5671 _initProfilesInfo() { 5672 setEventListener( 5673 "dataCollectionViewProfiles", 5674 "click", 5675 gMainPane.manageProfiles 5676 ); 5677 5678 let listener = () => gPrivacyPane.updateProfilesPrivacyInfo(); 5679 SelectableProfileService.on("enableChanged", listener); 5680 window.addEventListener("unload", () => 5681 SelectableProfileService.off("enableChanged", listener) 5682 ); 5683 this.updateProfilesPrivacyInfo(); 5684 }, 5685 5686 updateProfilesPrivacyInfo() { 5687 let profilesInfo = document.getElementById("preferences-privacy-profiles"); 5688 profilesInfo.hidden = !SelectableProfileService.isEnabled; 5689 }, 5690 5691 /** 5692 * Handles change events on baseline and convenience exception checkboxes for content blocking preferences. 5693 * 5694 * - For baseline checkboxes: If the user attempts to uncheck, shows a confirmation dialog. 5695 * If confirmed, disables the baseline allow list preference. 5696 * - For other cases: Toggles the checkbox and updates the corresponding preference. 5697 * 5698 * @param {Event} event - The change event triggered by the checkbox. 5699 */ 5700 async onBaselineCheckboxChange(event) { 5701 // Ignore events from nested checkboxes 5702 if (event.target.slot === "nested") { 5703 return; 5704 } 5705 5706 // If the user is checking the checkbox, don't show a confirmation prompt. 5707 if (event.target.checked) { 5708 this.maybeNotifyUserToReload(); 5709 return; 5710 } 5711 5712 const confirmed = await this._confirmBaselineAllowListDisable(); 5713 5714 if (confirmed) { 5715 // User confirmed, set the checkbox to false. 5716 event.target.checked = false; 5717 this.maybeNotifyUserToReload(); 5718 } else { 5719 // User cancelled, set the checkbox and the baseline pref to true. 5720 event.target.checked = true; 5721 Services.prefs.setBoolPref( 5722 "privacy.trackingprotection.allow_list.baseline.enabled", 5723 true 5724 ); 5725 } 5726 }, 5727 5728 async onBaselineAllowListSettingChange(value, setting) { 5729 if (value) { 5730 this.maybeNotifyUserToReload(); 5731 return; 5732 } 5733 5734 const confirmed = await this._confirmBaselineAllowListDisable(); 5735 if (confirmed) { 5736 this.maybeNotifyUserToReload(); 5737 return; 5738 } 5739 5740 setting.value = true; 5741 }, 5742 5743 async _confirmBaselineAllowListDisable() { 5744 let [title, body, okButtonText, cancelButtonText] = 5745 await document.l10n.formatValues([ 5746 { id: "content-blocking-baseline-uncheck-warning-dialog-title" }, 5747 { id: "content-blocking-baseline-uncheck-warning-dialog-body" }, 5748 { id: "content-blocking-baseline-uncheck-warning-dialog-ok-button" }, 5749 { 5750 id: "content-blocking-baseline-uncheck-warning-dialog-cancel-button", 5751 }, 5752 ]); 5753 5754 let flags = 5755 Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_1 + 5756 Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0 + 5757 Services.prompt.BUTTON_POS_0_DEFAULT; 5758 5759 const result = await Services.prompt.asyncConfirmEx( 5760 window.browsingContext, 5761 Services.prompt.MODAL_TYPE_CONTENT, 5762 title, 5763 body, 5764 flags, 5765 cancelButtonText, 5766 okButtonText, 5767 null, 5768 null, 5769 false, 5770 { 5771 useTitle: true, 5772 } 5773 ); 5774 5775 const propertyBag = result.QueryInterface(Ci.nsIPropertyBag2); 5776 return propertyBag.get("buttonNumClicked") == 1; 5777 }, 5778 };