tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 3cad1db09ce659efac80269e3f7d86e92659e4d5
parent dafe5b51f3cf0265b5fdf186ce632a66775f58ac
Author: Emma Zuehlcke <emz@mozilla.com>
Date:   Thu, 18 Dec 2025 17:00:38 +0000

Bug 2003471 - Implement tests for the ETP advanced and customize preferences sections. r=hjones,bvandersloot

Differential Revision: https://phabricator.services.mozilla.com/D276696

Diffstat:
Mbrowser/components/preferences/tests/browser.toml | 10+++++++---
Abrowser/components/preferences/tests/browser_etp_advanced.js | 365+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abrowser/components/preferences/tests/browser_etp_customize.js | 388+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mbrowser/components/preferences/widgets/setting-control/setting-control.mjs | 2+-
4 files changed, 761 insertions(+), 4 deletions(-)

diff --git a/browser/components/preferences/tests/browser.toml b/browser/components/preferences/tests/browser.toml @@ -107,6 +107,10 @@ skip-if = [ ["browser_ensure_prefs_bindings_initted.js"] +["browser_etp_advanced.js"] + +["browser_etp_customize.js"] + ["browser_etp_exceptions_dialog.js"] ["browser_etp_status.js"] @@ -243,12 +247,12 @@ skip-if = [ ["browser_privacypane_2.js"] skip-if = [ - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && opt && a11y_checks", + "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && a11y_checks && opt", ] ["browser_privacypane_3.js"] skip-if = [ - "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && opt && a11y_checks", + "os == 'linux' && os_version == '24.04' && arch == 'x86_64' && display == 'x11' && a11y_checks && opt", ] ["browser_proxy_backup.js"] @@ -310,7 +314,7 @@ skip-if = [ ["browser_search_within_preferences_1.js"] skip-if = [ - "os == 'win' && os_version == '11.26100' && arch == 'x86_64' && ccov" + "os == 'win' && os_version == '11.26100' && arch == 'x86_64' && ccov", ] ["browser_search_within_preferences_2.js"] diff --git a/browser/components/preferences/tests/browser_etp_advanced.js b/browser/components/preferences/tests/browser_etp_advanced.js @@ -0,0 +1,365 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Tests for the ETP advanced settings view. + +const CAT_PREF = "browser.contentblocking.category"; +const BASELINE_PREF = "privacy.trackingprotection.allow_list.baseline.enabled"; +const CONVENIENCE_PREF = + "privacy.trackingprotection.allow_list.convenience.enabled"; +const PERMISSIONS_DIALOG_URL = + "chrome://browser/content/preferences/dialogs/permissions.xhtml"; + +function getControl(doc, id) { + let control = doc.getElementById(id); + ok(control, `Control ${id} exists`); + return control; +} + +function synthesizeClick(el) { + let target = el.buttonEl ?? el.inputEl ?? el; + target.scrollIntoView({ block: "center" }); + EventUtils.synthesizeMouseAtCenter(target, {}, target.ownerGlobal); +} + +function getControlWrapper(doc, id) { + return getControl(doc, id).closest("setting-control"); +} + +async function clickBaselineCheckboxWithConfirm( + doc, + controlId, + prefName, + expectedValue, + buttonNumClick +) { + let checkbox = getControl(doc, controlId); + + let promptPromise = PromptTestUtils.handleNextPrompt( + gBrowser.selectedBrowser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT }, + { buttonNumClick } + ); + + let prefChangePromise = null; + if (buttonNumClick === 1) { + prefChangePromise = waitForAndAssertPrefState( + prefName, + expectedValue, + `${prefName} updated` + ); + } + + synthesizeClick(checkbox); + + await promptPromise; + + if (prefChangePromise) { + await prefChangePromise; + } + + is( + checkbox.checked, + expectedValue, + `Checkbox ${controlId} should be ${expectedValue}` + ); + + return checkbox; +} + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["browser.settings-redesign.enabled", true]], + }); +}); + +async function openEtpPage() { + await openPreferencesViaOpenPreferencesAPI("etp", { leaveOpen: true }); + let doc = gBrowser.contentDocument; + await BrowserTestUtils.waitForCondition( + () => doc.getElementById("contentBlockingCategoryRadioGroup"), + "Wait for the ETP advanced radio group to render" + ); + return { + win: gBrowser.contentWindow, + doc, + tab: gBrowser.selectedTab, + }; +} + +// Verifies category radios reflect pref changes and the customize entry point navigates correctly. +add_task(async function test_etp_category_radios_and_customize_navigation() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "standard"], + [BASELINE_PREF, true], + [CONVENIENCE_PREF, true], + ], + }); + + let { win, doc, tab } = await openEtpPage(); + + let standardRadio = getControl(doc, "etpLevelStandard"); + let strictRadio = getControl(doc, "etpLevelStrict"); + let customRadio = getControl(doc, "etpLevelCustom"); + let customizeButton = getControl(doc, "etpCustomizeButton"); + let levelWarning = getControl(doc, "etpLevelWarning"); + + ok(standardRadio.checked, "Standard ETP level is initially selected"); + ok( + customizeButton.parentDisabled, + "Customize button disabled until custom level selected" + ); + ok( + BrowserTestUtils.isHidden(levelWarning), + "ETP level warning hidden while standard selected" + ); + + info("Switch to strict and wait for the pref to change"); + let prefChange = waitForAndAssertPrefState( + CAT_PREF, + "strict", + "ETP category pref set to strict" + ); + synthesizeClick(strictRadio); + await prefChange; + ok(strictRadio.checked, "Strict radio button selected"); + + let strictBaselineWrapper = getControlWrapper( + doc, + "etpAllowListBaselineEnabled" + ); + ok( + BrowserTestUtils.isVisible(strictBaselineWrapper), + "Baseline checkbox for strict is visible when strict selected" + ); + + let levelWarningVisible = () => BrowserTestUtils.isVisible(levelWarning); + if (!levelWarningVisible()) { + await BrowserTestUtils.waitForMutationCondition( + levelWarning, + { attributes: true, attributeFilter: ["hidden"] }, + levelWarningVisible + ); + } + ok(levelWarningVisible(), "ETP level warning visible for strict level"); + + info("Switch to custom and ensure the pref updates"); + prefChange = waitForAndAssertPrefState( + CAT_PREF, + "custom", + "ETP category pref set to custom" + ); + synthesizeClick(customRadio); + await prefChange; + ok(customRadio.checked, "Custom radio button selected"); + + let customizeEnabled = () => !customizeButton.parentDisabled; + if (!customizeEnabled()) { + await BrowserTestUtils.waitForMutationCondition( + customizeButton, + { attributes: true, attributeFilter: ["parentdisabled"] }, + customizeEnabled + ); + } + ok(customizeEnabled(), "Customize button enabled when custom level selected"); + ok(levelWarningVisible(), "ETP level warning remains visible for custom"); + + info("Click customize and wait for the custom pane to load"); + let paneShown = waitForPaneChange("etpCustomize"); + synthesizeClick(customizeButton); + await paneShown; + is( + win.history.state, + "paneEtpCustomize", + "Customize button navigated to the ETP custom pane" + ); + + BrowserTestUtils.removeTab(tab); +}); + +// Ensures strict baseline checkbox flows prompt for confirmation and gate the convenience checkbox. +add_task(async function test_strict_baseline_checkbox_requires_confirmation() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "strict"], + [BASELINE_PREF, true], + [CONVENIENCE_PREF, true], + ], + }); + + let { doc, tab } = await openEtpPage(); + + let baselineCheckbox = getControl(doc, "etpAllowListBaselineEnabled"); + let convenienceCheckbox = getControl(doc, "etpAllowListConvenienceEnabled"); + + ok(baselineCheckbox.checked, "Baseline checkbox starts checked"); + + info("Cancel the confirmation dialog and ensure checkbox stays checked"); + await clickBaselineCheckboxWithConfirm( + doc, + "etpAllowListBaselineEnabled", + BASELINE_PREF, + true, + 0 + ); + ok( + baselineCheckbox.checked, + "Baseline checkbox remains checked after cancelling dialog" + ); + + info("Confirm the dialog to disable the baseline allow list"); + await clickBaselineCheckboxWithConfirm( + doc, + "etpAllowListBaselineEnabled", + BASELINE_PREF, + false, + 1 + ); + ok( + !Services.prefs.getBoolPref(BASELINE_PREF), + "Baseline pref disabled after confirming dialog" + ); + ok( + convenienceCheckbox.parentDisabled, + "Convenience checkbox disabled when baseline unchecked" + ); + + info("Re-enable baseline and ensure convenience becomes active again"); + let prefChange = waitForAndAssertPrefState( + BASELINE_PREF, + true, + "Baseline pref restored" + ); + synthesizeClick(baselineCheckbox); + await prefChange; + + let convenienceEnabled = () => !convenienceCheckbox.parentDisabled; + if (!convenienceEnabled()) { + await BrowserTestUtils.waitForMutationCondition( + convenienceCheckbox, + { attributes: true, attributeFilter: ["parentdisabled"] }, + convenienceEnabled + ); + } + ok( + convenienceEnabled(), + "Convenience checkbox enabled again after baseline re-enabled" + ); + + BrowserTestUtils.removeTab(tab); +}); + +// Ensures the RFP warning visibility follows the resistFingerprinting pref. +add_task(async function test_rfp_warning_visibility() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "strict"], + ["privacy.resistFingerprinting.pbmode", false], + ["privacy.resistFingerprinting", false], + ], + }); + + let { doc, tab } = await openEtpPage(); + + let rfpWarning = getControl(doc, "rfpWarning"); + ok( + BrowserTestUtils.isHidden(rfpWarning), + "RFP warning hidden while pref disabled" + ); + + let mutationTarget = doc.documentElement; + let warningVisibleCondition = () => { + let el = doc.getElementById("rfpWarning"); + return el && BrowserTestUtils.isVisible(el); + }; + + info("Enable normal RFP and wait for the warning to show"); + let waitForVisible = BrowserTestUtils.waitForMutationCondition( + mutationTarget, + { attributes: true, childList: true, subtree: true }, + warningVisibleCondition + ); + Services.prefs.setBoolPref("privacy.resistFingerprinting", true); + await waitForVisible; + rfpWarning = getControl(doc, "rfpWarning"); + ok( + BrowserTestUtils.isVisible(rfpWarning), + "RFP warning visible when pref enabled" + ); + + let warningHiddenCondition = () => { + let el = doc.getElementById("rfpWarning"); + return el && BrowserTestUtils.isHidden(el); + }; + + info("Disable normal RFP and wait for the warning to hide"); + let waitForHidden = BrowserTestUtils.waitForMutationCondition( + mutationTarget, + { attributes: true, childList: true, subtree: true }, + warningHiddenCondition + ); + Services.prefs.setBoolPref("privacy.resistFingerprinting", false); + await waitForHidden; + rfpWarning = getControl(doc, "rfpWarning"); + ok( + BrowserTestUtils.isHidden(rfpWarning), + "RFP warning hidden when pref disabled again" + ); + + info("Enable PBM RFP and wait for the warning to show"); + waitForVisible = BrowserTestUtils.waitForMutationCondition( + mutationTarget, + { attributes: true, childList: true, subtree: true }, + warningVisibleCondition + ); + Services.prefs.setBoolPref("privacy.resistFingerprinting.pbmode", true); + await waitForVisible; + rfpWarning = getControl(doc, "rfpWarning"); + ok( + BrowserTestUtils.isVisible(rfpWarning), + "RFP warning visible when PBM pref enabled" + ); + + info("Disable PBM RFP and wait for the warning to hide"); + waitForHidden = BrowserTestUtils.waitForMutationCondition( + mutationTarget, + { attributes: true, childList: true, subtree: true }, + warningHiddenCondition + ); + Services.prefs.setBoolPref("privacy.resistFingerprinting.pbmode", false); + await waitForHidden; + rfpWarning = getControl(doc, "rfpWarning"); + ok( + BrowserTestUtils.isHidden(rfpWarning), + "RFP warning hidden when PBM pref disabled again" + ); + + BrowserTestUtils.removeTab(tab); +}); + +// Ensures the manage exceptions button opens the permissions dialog. +add_task(async function test_manage_exceptions_button_opens_dialog() { + await SpecialPowers.pushPrefEnv({ + set: [[CAT_PREF, "standard"]], + }); + + let { doc, tab } = await openEtpPage(); + + let manageButton = getControl(doc, "etpManageExceptionsButton"); + let dialogPromise = promiseLoadSubDialog(PERMISSIONS_DIALOG_URL); + synthesizeClick(manageButton); + let dialogWin = await dialogPromise; + await dialogWin.document.mozSubdialogReady; + ok( + dialogWin.document.getElementById("permissionsBox"), + "Permissions dialog rendered for manage exceptions" + ); + let dialogEl = dialogWin.document.querySelector("dialog"); + dialogEl.getButton("cancel").click(); + await BrowserTestUtils.waitForEvent(dialogWin, "unload"); + + BrowserTestUtils.removeTab(tab); +}); diff --git a/browser/components/preferences/tests/browser_etp_customize.js b/browser/components/preferences/tests/browser_etp_customize.js @@ -0,0 +1,388 @@ +/* Any copyright is dedicated to the Public Domain. + * https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const CAT_PREF = "browser.contentblocking.category"; +const BASELINE_PREF = "privacy.trackingprotection.allow_list.baseline.enabled"; +const CONVENIENCE_PREF = + "privacy.trackingprotection.allow_list.convenience.enabled"; +const COOKIE_BEHAVIOR_PREF = "network.cookie.cookieBehavior"; +const TP_PREF = "privacy.trackingprotection.enabled"; +const TP_PBM_PREF = "privacy.trackingprotection.pbmode.enabled"; +const CRYPTOMINING_PREF = "privacy.trackingprotection.cryptomining.enabled"; +const FINGERPRINTING_PREF = "privacy.trackingprotection.fingerprinting.enabled"; +const SUSPECT_FP_PREF = "privacy.fingerprintingProtection"; +const SUSPECT_FP_PBM_PREF = "privacy.fingerprintingProtection.pbmode"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [["browser.settings-redesign.enabled", true]], + }); +}); + +function getControl(doc, id) { + let control = doc.getElementById(id); + ok(control, `Control ${id} exists`); + return control; +} + +function synthesizeClick(el) { + let target = el.buttonEl ?? el.inputEl ?? el; + target.scrollIntoView({ block: "center" }); + EventUtils.synthesizeMouseAtCenter(target, {}, target.ownerGlobal); +} + +async function openEtpCustomizePage() { + await openPreferencesViaOpenPreferencesAPI("etpCustomize", { + leaveOpen: true, + }); + let doc = gBrowser.contentDocument; + await BrowserTestUtils.waitForCondition( + () => doc.getElementById("etpAllowListBaselineEnabledCustom"), + "Wait for the ETP customize controls to render" + ); + return { + win: gBrowser.contentWindow, + doc, + }; +} + +async function changeMozSelectValue(selectEl, value) { + let control = selectEl.control; + let changePromise = waitForSettingControlChange(control); + selectEl.value = value; + selectEl.dispatchEvent(new Event("change", { bubbles: true })); + await changePromise; +} + +async function clickBaselineCheckboxWithConfirm( + doc, + controlId, + prefName, + expectedValue, + buttonNumClick +) { + let checkbox = getControl(doc, controlId); + + let promptPromise = PromptTestUtils.handleNextPrompt( + gBrowser.selectedBrowser, + { modalType: Services.prompt.MODAL_TYPE_CONTENT }, + { buttonNumClick } + ); + + let prefChangePromise = null; + if (buttonNumClick === 1) { + prefChangePromise = waitForAndAssertPrefState( + prefName, + expectedValue, + `${prefName} updated` + ); + } + + synthesizeClick(checkbox); + + await promptPromise; + if (prefChangePromise) { + await prefChangePromise; + } + + is( + checkbox.checked, + expectedValue, + `Checkbox ${controlId} should be ${expectedValue}` + ); + + return checkbox; +} + +// Confirms reset buttons drive the category pref and enable/disable appropriately. +add_task(async function test_etp_reset_buttons_update_category() { + await SpecialPowers.pushPrefEnv({ + set: [[CAT_PREF, "standard"]], + }); + + let { doc } = await openEtpCustomizePage(); + let standardButton = getControl(doc, "etpResetStandardButton"); + let strictButton = getControl(doc, "etpResetStrictButton"); + + ok(standardButton.disabled, "Standard reset button disabled on standard"); + ok(!strictButton.disabled, "Strict reset button enabled"); + + let prefChange = waitForAndAssertPrefState( + CAT_PREF, + "strict", + "ETP category pref switched to strict" + ); + synthesizeClick(strictButton); + await prefChange; + + ok(strictButton.disabled, "Strict reset button disabled after use"); + ok( + !standardButton.disabled, + "Standard reset button enabled after switching to strict" + ); + + prefChange = waitForAndAssertPrefState( + CAT_PREF, + "standard", + "ETP category pref switched back to standard" + ); + synthesizeClick(standardButton); + await prefChange; + + gBrowser.removeCurrentTab(); +}); + +// Mirrors legacy allow list checkbox behavior and confirmation prompts in the custom pane. +add_task(async function test_custom_allow_list_controls_match_old_behavior() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "custom"], + [BASELINE_PREF, true], + [CONVENIENCE_PREF, true], + ], + }); + + let { doc } = await openEtpCustomizePage(); + let baselineCheckbox = getControl(doc, "etpAllowListBaselineEnabledCustom"); + let convenienceCheckbox = getControl( + doc, + "etpAllowListConvenienceEnabledCustom" + ); + + ok(baselineCheckbox.checked, "Custom baseline checkbox starts checked"); + + await clickBaselineCheckboxWithConfirm( + doc, + "etpAllowListBaselineEnabledCustom", + BASELINE_PREF, + false, + 1 + ); + + ok( + !Services.prefs.getBoolPref(BASELINE_PREF), + "Baseline pref disabled from custom controls" + ); + ok( + convenienceCheckbox.parentDisabled, + "Custom convenience checkbox disabled when baseline unchecked" + ); + + let baselinePrefChange = waitForAndAssertPrefState( + BASELINE_PREF, + true, + "Baseline pref restored" + ); + synthesizeClick(baselineCheckbox); + await baselinePrefChange; + await BrowserTestUtils.waitForCondition( + () => !convenienceCheckbox.parentDisabled, + "Custom convenience checkbox enabled once baseline rechecked" + ); + + gBrowser.removeCurrentTab(); +}); + +// Validates the cookie toggle/select wiring for custom mode. +add_task(async function test_custom_cookie_controls() { + let defaults = Services.prefs.getDefaultBranch(""); + let defaultCookieBehavior = defaults.getIntPref(COOKIE_BEHAVIOR_PREF); + + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "custom"], + [COOKIE_BEHAVIOR_PREF, Ci.nsICookieService.BEHAVIOR_ACCEPT], + ], + }); + + let { doc } = await openEtpCustomizePage(); + let cookieToggle = getControl(doc, "etpCustomCookiesEnabled"); + let cookieSelect = getControl(doc, "cookieBehavior"); + + ok( + !cookieToggle.pressed, + "Cookie toggle starts disabled when behavior is accept" + ); + + let prefChange = waitForAndAssertPrefState( + COOKIE_BEHAVIOR_PREF, + defaultCookieBehavior, + "Enabling cookie toggle restores default behavior" + ); + synthesizeClick(cookieToggle.buttonEl); + await prefChange; + + ok(cookieToggle.pressed, "Cookie toggle is pressed when enabled"); + + info("Select a stricter cookie behavior through the dropdown"); + let newBehavior = Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN.toString(); + await changeMozSelectValue(cookieSelect, newBehavior); + is( + Services.prefs.getIntPref(COOKIE_BEHAVIOR_PREF), + Ci.nsICookieService.BEHAVIOR_REJECT_FOREIGN, + "Cookie behavior pref updated from moz-select" + ); + + prefChange = waitForAndAssertPrefState( + COOKIE_BEHAVIOR_PREF, + Ci.nsICookieService.BEHAVIOR_ACCEPT, + "Disabling cookie toggle accepts all cookies" + ); + synthesizeClick(cookieToggle.buttonEl); + await prefChange; + + ok(!cookieToggle.pressed, "Cookie toggle reflects disabled state"); + + gBrowser.removeCurrentTab(); +}); + +// Checks tracking protection toggle and scope dropdown interactions. +add_task(async function test_custom_tracking_protection_controls() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "custom"], + [TP_PREF, false], + [TP_PBM_PREF, true], + ], + }); + + let { doc } = await openEtpCustomizePage(); + let tpToggle = getControl(doc, "etpCustomTrackingProtectionEnabled"); + let tpContext = getControl(doc, "etpCustomTrackingProtectionEnabledContext"); + + ok(tpToggle.pressed, "Tracking protection toggle starts enabled"); + + let prefChange = TestUtils.waitForPrefChange( + TP_PBM_PREF, + value => value === false + ); + synthesizeClick(tpToggle.buttonEl); + await prefChange; + + ok(!tpToggle.pressed, "Tracking protection toggle reflects disabled state"); + ok( + !Services.prefs.getBoolPref(TP_PREF), + "All-windows tracking protection pref remains false" + ); + + prefChange = TestUtils.waitForPrefChange( + TP_PBM_PREF, + value => value === true + ); + synthesizeClick(tpToggle.buttonEl); + await prefChange; + ok(tpToggle.pressed, "Tracking protection toggle enabled again"); + ok( + !Services.prefs.getBoolPref(TP_PREF), + "All-windows tracking protection pref still false after re-enabling toggle" + ); + + info("Switch context to protect all windows"); + await changeMozSelectValue(tpContext, "all"); + ok( + Services.prefs.getBoolPref(TP_PREF), + "Tracking protection pref enabled for all windows" + ); + ok( + Services.prefs.getBoolPref(TP_PBM_PREF), + "Tracking protection PBM pref stays enabled" + ); + + info("Switch back to private windows only"); + await changeMozSelectValue(tpContext, "pbmOnly"); + ok( + !Services.prefs.getBoolPref(TP_PREF), + "All windows pref disabled when choosing private only" + ); + ok( + Services.prefs.getBoolPref(TP_PBM_PREF), + "Private windows pref stays enabled" + ); + + gBrowser.removeCurrentTab(); +}); + +// Covers cryptomining/fingerprinting toggles and suspect protection context behavior. +add_task(async function test_custom_fingerprinting_controls() { + await SpecialPowers.pushPrefEnv({ + set: [ + [CAT_PREF, "custom"], + [CRYPTOMINING_PREF, false], + [FINGERPRINTING_PREF, false], + [SUSPECT_FP_PREF, false], + [SUSPECT_FP_PBM_PREF, false], + ], + }); + + let { doc } = await openEtpCustomizePage(); + let cryptoToggle = getControl(doc, "etpCustomCryptominingProtectionEnabled"); + let knownFpToggle = getControl( + doc, + "etpCustomKnownFingerprintingProtectionEnabled" + ); + let suspectFpToggle = getControl( + doc, + "etpCustomSuspectFingerprintingProtectionEnabled" + ); + let suspectContext = getControl( + doc, + "etpCustomSuspectFingerprintingProtectionEnabledContext" + ); + + info("Enable cryptomining protection"); + let prefChange = waitForAndAssertPrefState( + CRYPTOMINING_PREF, + true, + "Cryptomining pref enabled" + ); + synthesizeClick(cryptoToggle.buttonEl); + await prefChange; + + info("Enable known fingerprinting protection"); + prefChange = waitForAndAssertPrefState( + FINGERPRINTING_PREF, + true, + "Fingerprinting pref enabled" + ); + synthesizeClick(knownFpToggle.buttonEl); + await prefChange; + + info("Enable suspect fingerprinting protection"); + prefChange = TestUtils.waitForPrefChange( + SUSPECT_FP_PBM_PREF, + value => value === true + ); + synthesizeClick(suspectFpToggle.buttonEl); + await prefChange; + ok( + !Services.prefs.getBoolPref(SUSPECT_FP_PREF), + "All-windows suspect fingerprinting pref remains false after toggle" + ); + + info("Switch suspect protection context to all windows"); + await changeMozSelectValue(suspectContext, "all"); + ok( + Services.prefs.getBoolPref(SUSPECT_FP_PREF), + "All-windows suspect fingerprinting pref enabled" + ); + ok( + Services.prefs.getBoolPref(SUSPECT_FP_PBM_PREF), + "PBM suspect fingerprinting pref remains enabled" + ); + + info("Disable suspect protection through the toggle"); + prefChange = TestUtils.waitForPrefChange( + SUSPECT_FP_PBM_PREF, + value => value === false + ); + synthesizeClick(suspectFpToggle.buttonEl); + await prefChange; + ok( + !Services.prefs.getBoolPref(SUSPECT_FP_PREF), + "All-window suspect pref disabled after toggle off" + ); + + gBrowser.removeCurrentTab(); +}); diff --git a/browser/components/preferences/widgets/setting-control/setting-control.mjs b/browser/components/preferences/widgets/setting-control/setting-control.mjs @@ -214,7 +214,7 @@ export class SettingControl extends SettingElement { control.value = this.value; } - control.requestUpdate(); + control.requestUpdate?.(); } /**