browser_protectionsUI_cookie_banner.js (14194B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 /** 7 * Tests the cookie banner handling section in the protections panel. 8 */ 9 10 const { TelemetryTestUtils } = ChromeUtils.importESModule( 11 "resource://testing-common/TelemetryTestUtils.sys.mjs" 12 ); 13 const { sinon } = ChromeUtils.importESModule( 14 "resource://testing-common/Sinon.sys.mjs" 15 ); 16 17 const { MODE_DISABLED, MODE_REJECT, MODE_REJECT_OR_ACCEPT, MODE_UNSET } = 18 Ci.nsICookieBannerService; 19 20 const exampleRules = JSON.stringify([ 21 { 22 id: "4b18afb0-76db-4f9e-a818-ed9a783fae6a", 23 cookies: {}, 24 click: { 25 optIn: "#foo", 26 presence: "#bar", 27 }, 28 domains: ["example.com"], 29 }, 30 ]); 31 32 /** 33 * Determines whether the cookie banner section in the protections panel should 34 * be visible with the given configuration. 35 * 36 * @param {*} options - Configuration to test. 37 * @param {number} options.featureMode - nsICookieBannerService::Modes value for 38 * normal browsing. 39 * @param {number} options.featureModePBM - nsICookieBannerService::Modes value 40 * for private browsing. 41 * @param {boolean} options.visibilityPref - State of the cookie banner UI 42 * visibility pref. 43 * @param {boolean} options.testPBM - Whether the window is in private browsing 44 * mode (true) or not (false). 45 * @returns {boolean} Whether the section should be visible for the given 46 * config. 47 */ 48 function cookieBannerSectionIsVisible({ 49 featureMode, 50 featureModePBM, 51 detectOnly, 52 visibilityPref, 53 testPBM, 54 }) { 55 if (!visibilityPref) { 56 return false; 57 } 58 if (detectOnly) { 59 return false; 60 } 61 62 return ( 63 (testPBM && featureModePBM != MODE_DISABLED) || 64 (!testPBM && featureMode != MODE_DISABLED) 65 ); 66 } 67 68 /** 69 * Runs a visibility test of the cookie banner section in the protections panel. 70 * 71 * @param {*} options - Test options. 72 * @param {Window} options.win - Browser window to use for testing. It's 73 * browsing mode should match the testPBM variable. 74 * @param {number} options.featureMode - nsICookieBannerService::Modes value for 75 * normal browsing. 76 * @param {number} options.featureModePBM - nsICookieBannerService::Modes value 77 * for private browsing. 78 * @param {boolean} options.visibilityPref - State of the cookie banner UI 79 * visibility pref. 80 * @param {boolean} options.testPBM - Whether the window is in private browsing 81 * mode (true) or not (false). 82 * @returns {Promise} Resolves once the test is complete. 83 */ 84 async function testSectionVisibility({ 85 win, 86 featureMode, 87 featureModePBM, 88 visibilityPref, 89 testPBM, 90 }) { 91 info( 92 "testSectionVisibility " + 93 JSON.stringify({ featureMode, featureModePBM, visibilityPref, testPBM }) 94 ); 95 // initialize the pref environment 96 await SpecialPowers.pushPrefEnv({ 97 set: [ 98 ["cookiebanners.service.mode", featureMode], 99 ["cookiebanners.service.mode.privateBrowsing", featureModePBM], 100 ["cookiebanners.ui.desktop.enabled", visibilityPref], 101 ], 102 }); 103 104 // Open a tab with example.com so the protections panel can be opened. 105 await BrowserTestUtils.withNewTab( 106 { gBrowser: win.gBrowser, url: "https://example.com" }, 107 async () => { 108 await openProtectionsPanel(null, win); 109 110 // Get panel elements to test 111 let el = { 112 section: win.document.getElementById( 113 "protections-popup-cookie-banner-section" 114 ), 115 sectionSeparator: win.document.getElementById( 116 "protections-popup-cookie-banner-section-separator" 117 ), 118 switch: win.document.getElementById( 119 "protections-popup-cookie-banner-switch" 120 ), 121 }; 122 123 let expectVisible = cookieBannerSectionIsVisible({ 124 featureMode, 125 featureModePBM, 126 visibilityPref, 127 testPBM, 128 }); 129 is( 130 BrowserTestUtils.isVisible(el.section), 131 expectVisible, 132 `Cookie banner section should be ${ 133 expectVisible ? "visible" : "not visible" 134 }.` 135 ); 136 is( 137 BrowserTestUtils.isVisible(el.sectionSeparator), 138 expectVisible, 139 `Cookie banner section separator should be ${ 140 expectVisible ? "visible" : "not visible" 141 }.` 142 ); 143 is( 144 BrowserTestUtils.isVisible(el.switch), 145 expectVisible, 146 `Cookie banner switch should be ${ 147 expectVisible ? "visible" : "not visible" 148 }.` 149 ); 150 } 151 ); 152 153 await SpecialPowers.popPrefEnv(); 154 } 155 156 /** 157 * Tests cookie banner section visibility state in different configurations. 158 */ 159 add_task(async function test_section_visibility() { 160 // Test all combinations of cookie banner service modes and normal and 161 // private browsing. 162 163 for (let testPBM of [false, true]) { 164 let win = window; 165 // Open a new private window to test the panel in for testing PBM, otherwise 166 // reuse the existing window. 167 if (testPBM) { 168 win = await BrowserTestUtils.openNewBrowserWindow({ 169 private: true, 170 }); 171 win.focus(); 172 } 173 174 for (let featureMode of [ 175 MODE_DISABLED, 176 MODE_REJECT, 177 MODE_REJECT_OR_ACCEPT, 178 ]) { 179 for (let featureModePBM of [ 180 MODE_DISABLED, 181 MODE_REJECT, 182 MODE_REJECT_OR_ACCEPT, 183 ]) { 184 for (let detectOnly of [false, true]) { 185 // Testing detect only mode for normal browsing is sufficient. 186 if (detectOnly && featureModePBM != MODE_DISABLED) { 187 continue; 188 } 189 await testSectionVisibility({ 190 win, 191 featureMode, 192 featureModePBM, 193 detectOnly, 194 testPBM, 195 visibilityPref: true, 196 }); 197 } 198 } 199 } 200 201 if (testPBM) { 202 await BrowserTestUtils.closeWindow(win); 203 } 204 } 205 }); 206 207 /** 208 * Tests that the cookie banner section is only visible if enabled by UI pref. 209 */ 210 add_task(async function test_section_visibility_pref() { 211 for (let visibilityPref of [false, true]) { 212 await testSectionVisibility({ 213 win: window, 214 featureMode: MODE_REJECT, 215 featureModePBM: MODE_DISABLED, 216 testPBM: false, 217 visibilityPref, 218 }); 219 } 220 }); 221 222 /** 223 * Test the state of the per-site exception switch in the cookie banner section 224 * and whether a matching per-site exception is set. 225 * 226 * @param {*} options 227 * @param {Window} options.win - Chrome window to test exception for (selected 228 * tab). 229 * @param {boolean} options.isPBM - Whether the given window is in private 230 * browsing mode. 231 * @param {string} options.expectedSwitchState - Whether the switch is expected to be 232 * "on" (CBH enabled), "off" (user added exception), or "unsupported" (no rules for site). 233 */ 234 function assertSwitchAndPrefState({ win, isPBM, expectedSwitchState }) { 235 let el = { 236 section: win.document.getElementById( 237 "protections-popup-cookie-banner-section" 238 ), 239 switch: win.document.getElementById( 240 "protections-popup-cookie-banner-switch" 241 ), 242 labelON: win.document.querySelector( 243 "#protections-popup-cookie-banner-detected" 244 ), 245 labelOFF: win.document.querySelector( 246 "#protections-popup-cookie-banner-site-disabled" 247 ), 248 labelUNDETECTED: win.document.querySelector( 249 "#protections-popup-cookie-banner-undetected" 250 ), 251 }; 252 253 let currentURI = win.gBrowser.currentURI; 254 let pref = Services.cookieBanners.getDomainPref(currentURI, isPBM); 255 if (expectedSwitchState == "on") { 256 Assert.equal( 257 el.section.dataset.state, 258 "detected", 259 "CBH switch is set to ON" 260 ); 261 262 ok(BrowserTestUtils.isVisible(el.labelON), "ON label should be visible"); 263 ok( 264 !BrowserTestUtils.isVisible(el.labelOFF), 265 "OFF label should not be visible" 266 ); 267 ok( 268 !BrowserTestUtils.isVisible(el.labelUNDETECTED), 269 "UNDETECTED label should not be visible" 270 ); 271 272 is( 273 pref, 274 MODE_UNSET, 275 `There should be no per-site exception for ${currentURI.spec}.` 276 ); 277 } else if (expectedSwitchState === "off") { 278 Assert.equal( 279 el.section.dataset.state, 280 "site-disabled", 281 "CBH switch is set to OFF" 282 ); 283 284 ok( 285 !BrowserTestUtils.isVisible(el.labelON), 286 "ON label should not be visible" 287 ); 288 ok(BrowserTestUtils.isVisible(el.labelOFF), "OFF label should be visible"); 289 ok( 290 !BrowserTestUtils.isVisible(el.labelUNDETECTED), 291 "UNDETECTED label should not be visible" 292 ); 293 294 is( 295 pref, 296 MODE_DISABLED, 297 `There should be a per-site exception for ${currentURI.spec}.` 298 ); 299 } else { 300 Assert.equal( 301 el.section.dataset.state, 302 "undetected", 303 "CBH not supported for site" 304 ); 305 306 ok( 307 !BrowserTestUtils.isVisible(el.labelON), 308 "ON label should not be visible" 309 ); 310 ok( 311 !BrowserTestUtils.isVisible(el.labelOFF), 312 "OFF label should not be visible" 313 ); 314 ok( 315 BrowserTestUtils.isVisible(el.labelUNDETECTED), 316 "UNDETECTED label should be visible" 317 ); 318 } 319 } 320 321 /** 322 * Test the telemetry associated with the cookie banner toggle. To be called 323 * after interacting with the toggle. 324 * 325 * @param {*} options 326 * @param {boolean|null} - Expected telemetry state matching the button state. 327 * button on = true = cookieb_toggle_on event. Pass null to expect no event 328 * recorded. 329 */ 330 function assertTelemetryState({ expectEnabled = null } = {}) { 331 info("Test telemetry state."); 332 333 let events = []; 334 const CATEGORY = "security.ui.protectionspopup"; 335 const METHOD = "click"; 336 337 if (expectEnabled != null) { 338 events.push({ 339 category: CATEGORY, 340 method: METHOD, 341 object: expectEnabled ? "cookieb_toggle_on" : "cookieb_toggle_off", 342 }); 343 } 344 345 // Assert event state and clear event list. 346 TelemetryTestUtils.assertEvents(events, { 347 category: CATEGORY, 348 method: METHOD, 349 }); 350 } 351 352 /** 353 * Test the cookie banner enable / disable by clicking the switch, then 354 * clicking the on/off button in the cookie banner subview. Assumes the 355 * protections panel is already open. 356 * 357 * @param {boolean} enable - Whether we want to enable or disable. 358 * @param {Window} win - Current chrome window under test. 359 */ 360 async function toggleCookieBannerHandling(enable, win) { 361 let switchEl = win.document.getElementById( 362 "protections-popup-cookie-banner-switch" 363 ); 364 let enableButton = win.document.getElementById( 365 "protections-popup-cookieBannerView-enable-button" 366 ); 367 let disableButton = win.document.getElementById( 368 "protections-popup-cookieBannerView-disable-button" 369 ); 370 let subView = win.document.getElementById( 371 "protections-popup-cookieBannerView" 372 ); 373 374 let subViewShownPromise = BrowserTestUtils.waitForEvent(subView, "ViewShown"); 375 switchEl.click(); 376 await subViewShownPromise; 377 378 if (enable) { 379 ok(BrowserTestUtils.isVisible(enableButton), "Enable button is visible"); 380 enableButton.click(); 381 } else { 382 ok(BrowserTestUtils.isVisible(disableButton), "Disable button is visible"); 383 disableButton.click(); 384 } 385 } 386 387 function waitForProtectionsPopupHide(win = window) { 388 return BrowserTestUtils.waitForEvent( 389 win.document.getElementById("protections-popup"), 390 "popuphidden" 391 ); 392 } 393 394 /** 395 * Tests the cookie banner section per-site preference toggle. 396 */ 397 add_task(async function test_section_toggle() { 398 requestLongerTimeout(3); 399 400 // initialize the pref environment 401 await SpecialPowers.pushPrefEnv({ 402 set: [ 403 ["cookiebanners.service.mode", MODE_REJECT_OR_ACCEPT], 404 ["cookiebanners.service.mode.privateBrowsing", MODE_REJECT_OR_ACCEPT], 405 ["cookiebanners.ui.desktop.enabled", true], 406 ["cookiebanners.listService.testRules", exampleRules], 407 ["cookiebanners.listService.testSkipRemoteSettings", true], 408 ], 409 }); 410 411 Services.cookieBanners.resetRules(false); 412 await BrowserTestUtils.waitForCondition( 413 () => !!Services.cookieBanners.rules.length, 414 "waiting for Services.cookieBanners.rules.length to be greater than 0" 415 ); 416 417 // Test both normal and private browsing windows. For normal windows we reuse 418 // the existing one, for private windows we need to open a new window. 419 for (let testPBM of [false, true]) { 420 let win = window; 421 if (testPBM) { 422 win = await BrowserTestUtils.openNewBrowserWindow({ 423 private: true, 424 }); 425 } 426 427 await BrowserTestUtils.withNewTab( 428 { gBrowser: win.gBrowser, url: "https://example.com" }, 429 async () => { 430 let clearSiteDataSpy = sinon.spy(window.SiteDataManager, "remove"); 431 432 await openProtectionsPanel(null, win); 433 let switchEl = win.document.getElementById( 434 "protections-popup-cookie-banner-switch" 435 ); 436 info("Testing initial switch ON state."); 437 assertSwitchAndPrefState({ 438 win, 439 isPBM: testPBM, 440 switchEl, 441 expectedSwitchState: "on", 442 }); 443 assertTelemetryState(); 444 445 info("Testing switch state after toggle OFF"); 446 let closePromise = waitForProtectionsPopupHide(win); 447 await toggleCookieBannerHandling(false, win); 448 await closePromise; 449 if (testPBM) { 450 Assert.ok( 451 clearSiteDataSpy.notCalled, 452 "clearSiteData should not be called in private browsing mode" 453 ); 454 } else { 455 Assert.ok( 456 clearSiteDataSpy.calledOnce, 457 "clearSiteData should be called in regular browsing mode" 458 ); 459 } 460 clearSiteDataSpy.restore(); 461 462 await openProtectionsPanel(null, win); 463 assertSwitchAndPrefState({ 464 win, 465 isPBM: testPBM, 466 switchEl, 467 expectedSwitchState: "off", 468 }); 469 assertTelemetryState({ expectEnabled: false }); 470 471 info("Testing switch state after toggle ON."); 472 closePromise = waitForProtectionsPopupHide(win); 473 await toggleCookieBannerHandling(true, win); 474 await closePromise; 475 476 await openProtectionsPanel(null, win); 477 assertSwitchAndPrefState({ 478 win, 479 isPBM: testPBM, 480 switchEl, 481 expectedSwitchState: "on", 482 }); 483 assertTelemetryState({ expectEnabled: true }); 484 } 485 ); 486 487 if (testPBM) { 488 await BrowserTestUtils.closeWindow(win); 489 } 490 } 491 492 await SpecialPowers.popPrefEnv(); 493 });