/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /** * Tests the cookie banner handling section in the protections panel. */ const { TelemetryTestUtils } = ChromeUtils.importESModule( "resource://testing-common/TelemetryTestUtils.sys.mjs" ); const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); const { MODE_DISABLED, MODE_REJECT, MODE_REJECT_OR_ACCEPT, MODE_UNSET } = Ci.nsICookieBannerService; const exampleRules = JSON.stringify([ { id: "4b18afb0-76db-4f9e-a818-ed9a783fae6a", cookies: {}, click: { optIn: "#foo", presence: "#bar", }, domains: ["example.com"], }, ]); /** * Determines whether the cookie banner section in the protections panel should * be visible with the given configuration. * @param {*} options - Configuration to test. * @param {Number} options.featureMode - nsICookieBannerService::Modes value for * normal browsing. * @param {Number} options.featureModePBM - nsICookieBannerService::Modes value * for private browsing. * @param {boolean} options.visibilityPref - State of the cookie banner UI * visibility pref. * @param {boolean} options.testPBM - Whether the window is in private browsing * mode (true) or not (false). * @returns {boolean} Whether the section should be visible for the given * config. */ function cookieBannerSectionIsVisible({ featureMode, featureModePBM, detectOnly, visibilityPref, testPBM, }) { if (!visibilityPref) { return false; } if (detectOnly) { return false; } return ( (testPBM && featureModePBM != MODE_DISABLED) || (!testPBM && featureMode != MODE_DISABLED) ); } /** * Runs a visibility test of the cookie banner section in the protections panel. * @param {*} options - Test options. * @param {Window} options.win - Browser window to use for testing. It's * browsing mode should match the testPBM variable. * @param {Number} options.featureMode - nsICookieBannerService::Modes value for * normal browsing. * @param {Number} options.featureModePBM - nsICookieBannerService::Modes value * for private browsing. * @param {boolean} options.visibilityPref - State of the cookie banner UI * visibility pref. * @param {boolean} options.testPBM - Whether the window is in private browsing * mode (true) or not (false). * @returns {Promise} Resolves once the test is complete. */ async function testSectionVisibility({ win, featureMode, featureModePBM, visibilityPref, testPBM, }) { info( "testSectionVisibility " + JSON.stringify({ featureMode, featureModePBM, visibilityPref, testPBM }) ); // initialize the pref environment await SpecialPowers.pushPrefEnv({ set: [ ["cookiebanners.service.mode", featureMode], ["cookiebanners.service.mode.privateBrowsing", featureModePBM], ["cookiebanners.ui.desktop.enabled", visibilityPref], ], }); // Open a tab with example.com so the protections panel can be opened. await BrowserTestUtils.withNewTab( { gBrowser: win.gBrowser, url: "https://example.com" }, async () => { await openProtectionsPanel(null, win); // Get panel elements to test let el = { section: win.document.getElementById( "protections-popup-cookie-banner-section" ), sectionSeparator: win.document.getElementById( "protections-popup-cookie-banner-section-separator" ), switch: win.document.getElementById( "protections-popup-cookie-banner-switch" ), }; let expectVisible = cookieBannerSectionIsVisible({ featureMode, featureModePBM, visibilityPref, testPBM, }); is( BrowserTestUtils.is_visible(el.section), expectVisible, `Cookie banner section should be ${ expectVisible ? "visible" : "not visible" }.` ); is( BrowserTestUtils.is_visible(el.sectionSeparator), expectVisible, `Cookie banner section separator should be ${ expectVisible ? "visible" : "not visible" }.` ); is( BrowserTestUtils.is_visible(el.switch), expectVisible, `Cookie banner switch should be ${ expectVisible ? "visible" : "not visible" }.` ); } ); await SpecialPowers.popPrefEnv(); } /** * Tests cookie banner section visibility state in different configurations. */ add_task(async function test_section_visibility() { // Test all combinations of cookie banner service modes and normal and // private browsing. for (let testPBM of [false, true]) { let win = window; // Open a new private window to test the panel in for testing PBM, otherwise // reuse the existing window. if (testPBM) { win = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); win.focus(); } for (let featureMode of [ MODE_DISABLED, MODE_REJECT, MODE_REJECT_OR_ACCEPT, ]) { for (let featureModePBM of [ MODE_DISABLED, MODE_REJECT, MODE_REJECT_OR_ACCEPT, ]) { for (let detectOnly of [false, true]) { // Testing detect only mode for normal browsing is sufficient. if (detectOnly && featureModePBM != MODE_DISABLED) { continue; } await testSectionVisibility({ win, featureMode, featureModePBM, detectOnly, testPBM, visibilityPref: true, }); } } } if (testPBM) { await BrowserTestUtils.closeWindow(win); } } }); /** * Tests that the cookie banner section is only visible if enabled by UI pref. */ add_task(async function test_section_visibility_pref() { for (let visibilityPref of [false, true]) { await testSectionVisibility({ win: window, featureMode: MODE_REJECT, featureModePBM: MODE_DISABLED, testPBM: false, visibilityPref, }); } }); /** * Test the state of the per-site exception switch in the cookie banner section * and whether a matching per-site exception is set. * @param {*} options * @param {Window} options.win - Chrome window to test exception for (selected * tab). * @param {boolean} options.isPBM - Whether the given window is in private * browsing mode. * @param {string} options.expectedSwitchState - Whether the switch is expected to be * "on" (CBH enabled), "off" (user added exception), or "unsupported" (no rules for site). */ function assertSwitchAndPrefState({ win, isPBM, expectedSwitchState }) { let el = { section: win.document.getElementById( "protections-popup-cookie-banner-section" ), switch: win.document.getElementById( "protections-popup-cookie-banner-switch" ), labelON: win.document.querySelector( "#protections-popup-cookie-banner-detected" ), labelOFF: win.document.querySelector( "#protections-popup-cookie-banner-site-disabled" ), labelUNDETECTED: win.document.querySelector( "#protections-popup-cookie-banner-undetected" ), }; let currentURI = win.gBrowser.currentURI; let pref = Services.cookieBanners.getDomainPref(currentURI, isPBM); if (expectedSwitchState == "on") { ok(el.section.dataset.state == "detected", "CBH switch is set to ON"); ok(BrowserTestUtils.is_visible(el.labelON), "ON label should be visible"); ok( !BrowserTestUtils.is_visible(el.labelOFF), "OFF label should not be visible" ); ok( !BrowserTestUtils.is_visible(el.labelUNDETECTED), "UNDETECTED label should not be visible" ); is( pref, MODE_UNSET, `There should be no per-site exception for ${currentURI.spec}.` ); } else if (expectedSwitchState === "off") { ok(el.section.dataset.state == "site-disabled", "CBH switch is set to OFF"); ok( !BrowserTestUtils.is_visible(el.labelON), "ON label should not be visible" ); ok(BrowserTestUtils.is_visible(el.labelOFF), "OFF label should be visible"); ok( !BrowserTestUtils.is_visible(el.labelUNDETECTED), "UNDETECTED label should not be visible" ); is( pref, MODE_DISABLED, `There should be a per-site exception for ${currentURI.spec}.` ); } else { ok(el.section.dataset.state == "undetected", "CBH not supported for site"); ok( !BrowserTestUtils.is_visible(el.labelON), "ON label should not be visible" ); ok( !BrowserTestUtils.is_visible(el.labelOFF), "OFF label should not be visible" ); ok( BrowserTestUtils.is_visible(el.labelUNDETECTED), "UNDETECTED label should be visible" ); } } /** * Test the telemetry associated with the cookie banner toggle. To be called * after interacting with the toggle. * @param {*} options * @param {boolean|null} - Expected telemetry state matching the button state. * button on = true = cookieb_toggle_on event. Pass null to expect no event * recorded. */ function assertTelemetryState({ expectEnabled = null } = {}) { info("Test telemetry state."); let events = []; const CATEGORY = "security.ui.protectionspopup"; const METHOD = "click"; if (expectEnabled != null) { events.push({ category: CATEGORY, method: METHOD, object: expectEnabled ? "cookieb_toggle_on" : "cookieb_toggle_off", }); } // Assert event state and clear event list. TelemetryTestUtils.assertEvents(events, { category: CATEGORY, method: METHOD, }); } /** * Test the cookie banner enable / disable by clicking the switch, then * clicking the on/off button in the cookie banner subview. Assumes the * protections panel is already open. * * @param {boolean} enable - Whether we want to enable or disable. * @param {Window} win - Current chrome window under test. */ async function toggleCookieBannerHandling(enable, win) { let switchEl = win.document.getElementById( "protections-popup-cookie-banner-switch" ); let enableButton = win.document.getElementById( "protections-popup-cookieBannerView-enable-button" ); let disableButton = win.document.getElementById( "protections-popup-cookieBannerView-disable-button" ); let subView = win.document.getElementById( "protections-popup-cookieBannerView" ); let subViewShownPromise = BrowserTestUtils.waitForEvent(subView, "ViewShown"); switchEl.click(); await subViewShownPromise; if (enable) { ok(BrowserTestUtils.is_visible(enableButton), "Enable button is visible"); enableButton.click(); } else { ok(BrowserTestUtils.is_visible(disableButton), "Disable button is visible"); disableButton.click(); } } function waitForProtectionsPopupHide(win = window) { return BrowserTestUtils.waitForEvent( win.document.getElementById("protections-popup"), "popuphidden" ); } /** * Tests the cookie banner section per-site preference toggle. */ add_task(async function test_section_toggle() { // initialize the pref environment await SpecialPowers.pushPrefEnv({ set: [ ["cookiebanners.service.mode", MODE_REJECT_OR_ACCEPT], ["cookiebanners.service.mode.privateBrowsing", MODE_REJECT_OR_ACCEPT], ["cookiebanners.ui.desktop.enabled", true], ["cookiebanners.listService.testRules", exampleRules], ["cookiebanners.listService.testSkipRemoteSettings", true], ], }); Services.cookieBanners.resetRules(false); await BrowserTestUtils.waitForCondition( () => !!Services.cookieBanners.rules.length, "waiting for Services.cookieBanners.rules.length to be greater than 0" ); // Test both normal and private browsing windows. For normal windows we reuse // the existing one, for private windows we need to open a new window. for (let testPBM of [false, true]) { let win = window; if (testPBM) { win = await BrowserTestUtils.openNewBrowserWindow({ private: true, }); } await BrowserTestUtils.withNewTab( { gBrowser: win.gBrowser, url: "https://example.com" }, async () => { let clearSiteDataSpy = sinon.spy(window.SiteDataManager, "remove"); await openProtectionsPanel(null, win); let switchEl = win.document.getElementById( "protections-popup-cookie-banner-switch" ); info("Testing initial switch ON state."); assertSwitchAndPrefState({ win, isPBM: testPBM, switchEl, expectedSwitchState: "on", }); assertTelemetryState(); info("Testing switch state after toggle OFF"); let closePromise = waitForProtectionsPopupHide(win); await toggleCookieBannerHandling(false, win); await closePromise; if (testPBM) { Assert.ok( clearSiteDataSpy.notCalled, "clearSiteData should not be called in private browsing mode" ); } else { Assert.ok( clearSiteDataSpy.calledOnce, "clearSiteData should be called in regular browsing mode" ); } clearSiteDataSpy.restore(); await openProtectionsPanel(null, win); assertSwitchAndPrefState({ win, isPBM: testPBM, switchEl, expectedSwitchState: "off", }); assertTelemetryState({ expectEnabled: false }); info("Testing switch state after toggle ON."); closePromise = waitForProtectionsPopupHide(win); await toggleCookieBannerHandling(true, win); await closePromise; await openProtectionsPanel(null, win); assertSwitchAndPrefState({ win, isPBM: testPBM, switchEl, expectedSwitchState: "on", }); assertTelemetryState({ expectEnabled: true }); } ); if (testPBM) { await BrowserTestUtils.closeWindow(win); } } await SpecialPowers.popPrefEnv(); });