browser_protectionsUI.js (20240B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 /* Basic UI tests for the protections panel */ 5 6 "use strict"; 7 8 const TRACKING_PAGE = 9 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 10 "http://tracking.example.org/browser/browser/base/content/test/protectionsUI/trackingPage.html"; 11 12 ChromeUtils.defineESModuleGetters(this, { 13 ContentBlockingAllowList: 14 "resource://gre/modules/ContentBlockingAllowList.sys.mjs", 15 }); 16 17 const { CustomizableUITestUtils } = ChromeUtils.importESModule( 18 "resource://testing-common/CustomizableUITestUtils.sys.mjs" 19 ); 20 21 requestLongerTimeout(3); 22 23 add_setup(async function () { 24 await SpecialPowers.pushPrefEnv({ 25 set: [ 26 // Set the auto hide timing to 100ms for blocking the test less. 27 ["browser.protections_panel.toast.timeout", 100], 28 // Hide protections cards so as not to trigger more async messaging 29 // when landing on the page. 30 ["browser.contentblocking.report.monitor.enabled", false], 31 ["browser.contentblocking.report.lockwise.enabled", false], 32 ["browser.contentblocking.report.proxy.enabled", false], 33 ["privacy.trackingprotection.enabled", true], 34 ["browser.urlbar.scotchBonnet.enableOverride", true], 35 ], 36 }); 37 38 let oldCanRecord = Services.telemetry.canRecordExtended; 39 Services.telemetry.canRecordExtended = true; 40 Services.telemetry.clearEvents(); 41 42 registerCleanupFunction(() => { 43 Services.telemetry.canRecordExtended = oldCanRecord; 44 Services.telemetry.clearEvents(); 45 }); 46 47 Services.fog.testResetFOG(); 48 }); 49 50 async function clickToggle(toggle) { 51 let changed = BrowserTestUtils.waitForEvent(toggle, "toggle"); 52 await EventUtils.synthesizeMouseAtCenter(toggle.buttonEl, {}); 53 await changed; 54 } 55 56 add_task(async function testToggleSwitch() { 57 let tab = await BrowserTestUtils.openNewForegroundTab( 58 gBrowser, 59 TRACKING_PAGE 60 ); 61 62 await openProtectionsPanel(); 63 64 await TestUtils.waitForCondition(() => { 65 return gProtectionsHandler._protectionsPopup.hasAttribute("blocking"); 66 }); 67 68 let buttonEvents = 69 Glean.securityUiProtectionspopup.openProtectionsPopup.testGetValue(); 70 71 is(buttonEvents.length, 1, "recorded telemetry for opening the popup"); 72 73 let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); 74 75 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 76 gProtectionsHandler._protectionsPopup, 77 "popuphidden" 78 ); 79 80 await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); 81 await popuphiddenPromise; 82 83 checkClickTelemetry("etp_toggle_off"); 84 85 // We need to wait toast's popup shown and popup hidden events. It won't fire 86 // the popup shown event if we open the protections panel while the toast is 87 // opening. 88 let toastShown = waitForProtectionsPanelToast(); 89 90 await browserLoadedPromise; 91 92 // Wait until the ETP state confirmation toast is shown and hides itself. 93 await toastShown; 94 95 // Re-open the protections panel and confirm that the toggle is off, then toggle it back on. 96 await openProtectionsPanel(); 97 ok( 98 !gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("pressed"), 99 "TP Switch should be off" 100 ); 101 102 browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); 103 104 popuphiddenPromise = BrowserTestUtils.waitForEvent( 105 gProtectionsHandler._protectionsPopup, 106 "popuphidden" 107 ); 108 109 await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); 110 111 // Wait for the protections panel to be hidden as the result of the ETP toggle 112 // on action. 113 await popuphiddenPromise; 114 115 toastShown = waitForProtectionsPanelToast(); 116 117 await browserLoadedPromise; 118 119 // Wait until the ETP state confirmation toast is shown and hides itself. 120 await toastShown; 121 122 checkClickTelemetry("etp_toggle_on"); 123 124 ContentBlockingAllowList.remove(tab.linkedBrowser); 125 BrowserTestUtils.removeTab(tab); 126 }); 127 128 /** 129 * A test for the protection settings button. 130 */ 131 add_task(async function testSettingsButton() { 132 // Open a tab and its protection panel. 133 let tab = await BrowserTestUtils.openNewForegroundTab( 134 gBrowser, 135 "https://example.com" 136 ); 137 await openProtectionsPanel(); 138 139 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 140 gProtectionsHandler._protectionsPopup, 141 "popuphidden" 142 ); 143 let newTabPromise = BrowserTestUtils.waitForNewTab( 144 gBrowser, 145 "about:preferences#privacy" 146 ); 147 gProtectionsHandler._protectionsPopupSettingsButton.click(); 148 149 // The protection popup should be hidden after clicking settings button. 150 await popuphiddenPromise; 151 // Wait until the about:preferences has been opened correctly. 152 let newTab = await newTabPromise; 153 154 ok(true, "about:preferences has been opened successfully"); 155 checkClickTelemetry("settings"); 156 157 BrowserTestUtils.removeTab(newTab); 158 BrowserTestUtils.removeTab(tab); 159 }); 160 161 /** 162 * A test for ensuring Tracking Protection label is shown correctly 163 */ 164 add_task(async function testTrackingProtectionLabel() { 165 // Open a tab. 166 let tab = await BrowserTestUtils.openNewForegroundTab( 167 gBrowser, 168 "https://example.com" 169 ); 170 await openProtectionsPanel(); 171 172 let trackingProtectionLabel = document.getElementById( 173 "protections-popup-footer-protection-type-label" 174 ); 175 176 is( 177 trackingProtectionLabel.textContent, 178 "Custom", 179 "The label is correctly set to Custom." 180 ); 181 await closeProtectionsPanel(); 182 183 Services.prefs.setStringPref("browser.contentblocking.category", "standard"); 184 await openProtectionsPanel(); 185 186 is( 187 trackingProtectionLabel.textContent, 188 "Standard", 189 "The label is correctly set to Standard." 190 ); 191 await closeProtectionsPanel(); 192 193 Services.prefs.setStringPref("browser.contentblocking.category", "strict"); 194 await openProtectionsPanel(); 195 196 is( 197 trackingProtectionLabel.textContent, 198 "Strict", 199 "The label is correctly set to Strict." 200 ); 201 202 await closeProtectionsPanel(); 203 Services.prefs.setStringPref("browser.contentblocking.category", "custom"); 204 BrowserTestUtils.removeTab(tab); 205 }); 206 207 /** 208 * A test for the 'Show Full Report' button in the footer section. 209 */ 210 add_task(async function testShowFullReportButton() { 211 // Open a tab and its protection panel. 212 let tab = await BrowserTestUtils.openNewForegroundTab( 213 gBrowser, 214 "https://example.com" 215 ); 216 await openProtectionsPanel(); 217 218 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 219 gProtectionsHandler._protectionsPopup, 220 "popuphidden" 221 ); 222 let newTabPromise = waitForAboutProtectionsTab(); 223 let showFullReportButton = document.getElementById( 224 "protections-popup-show-report-button" 225 ); 226 227 showFullReportButton.click(); 228 229 // The protection popup should be hidden after clicking the link. 230 await popuphiddenPromise; 231 // Wait until the 'about:protections' has been opened correctly. 232 let newTab = await newTabPromise; 233 234 ok(true, "about:protections has been opened successfully"); 235 236 checkClickTelemetry("full_report"); 237 238 BrowserTestUtils.removeTab(newTab); 239 BrowserTestUtils.removeTab(tab); 240 }); 241 242 function checkMiniPanel() { 243 // Check that only the header is displayed. 244 let mainView = document.getElementById("protections-popup-mainView"); 245 for (let item of mainView.childNodes) { 246 if (item.id !== "protections-popup-mainView-panel-header-section") { 247 ok( 248 !BrowserTestUtils.isVisible(item), 249 `The section '${item.id}' is hidden in the toast.` 250 ); 251 } else { 252 ok( 253 BrowserTestUtils.isVisible(item), 254 "The panel header is displayed as the content of the toast." 255 ); 256 } 257 } 258 } 259 260 /** 261 * A test for ensuring the mini panel closes automatically 262 */ 263 add_task(async function testMiniPanel() { 264 // Open a tab. 265 let tab = await BrowserTestUtils.openNewForegroundTab( 266 gBrowser, 267 "https://example.com" 268 ); 269 270 // Open the mini panel. 271 await openProtectionsPanel(true); 272 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 273 gProtectionsHandler._protectionsPopup, 274 "popuphidden" 275 ); 276 277 checkMiniPanel(); 278 279 // Wait until the auto hide is happening. 280 await popuphiddenPromise; 281 282 ok(true, "The mini panel hides automatically."); 283 284 BrowserTestUtils.removeTab(tab); 285 }); 286 287 /** 288 * A test for ensuring that clicking the mini panel opens the big panel 289 */ 290 add_task(async function testMiniPanelClick() { 291 // Open a tab. 292 let tab = await BrowserTestUtils.openNewForegroundTab( 293 gBrowser, 294 "https://example.com" 295 ); 296 297 // Open the mini panel. 298 await openProtectionsPanel(true); 299 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 300 gProtectionsHandler._protectionsPopup, 301 "popuphidden" 302 ); 303 304 checkMiniPanel(); 305 306 let popupShownPromise = BrowserTestUtils.waitForEvent( 307 window, 308 "popupshown", 309 true, 310 e => e.target.id == "protections-popup" 311 ); 312 313 // Simulate clicking on the mini panel text 314 let buttonEl = document.getElementById( 315 "protections-popup-toast-panel-tp-on-desc" 316 ); 317 await EventUtils.synthesizeMouseAtCenter(buttonEl, {}); 318 319 info("Waiting for mini panel to close"); 320 await popuphiddenPromise; 321 322 info("Waiting for big popup to be shown"); 323 await popupShownPromise; 324 325 let header = document.getElementById( 326 "protections-popup-mainView-panel-header-section" 327 ); 328 ok(BrowserTestUtils.isVisible(header), "Header is visible"); 329 330 let body = document.getElementById("protections-popup-main-body"); 331 ok(BrowserTestUtils.isVisible(body), "Main body is visible"); 332 333 let footer = document.getElementById("protections-popup-footer"); 334 ok(BrowserTestUtils.isVisible(footer), "Footer is visible"); 335 336 BrowserTestUtils.removeTab(tab); 337 }); 338 339 /** 340 * A test for the toggle switch flow 341 */ 342 add_task(async function testToggleSwitchFlow() { 343 // Open a tab. 344 let tab = await BrowserTestUtils.openNewForegroundTab( 345 gBrowser, 346 "https://example.com" 347 ); 348 await openProtectionsPanel(); 349 350 let popuphiddenPromise = BrowserTestUtils.waitForEvent( 351 gProtectionsHandler._protectionsPopup, 352 "popuphidden" 353 ); 354 let popupShownPromise = BrowserTestUtils.waitForEvent( 355 gProtectionsHandler._protectionsPopup, 356 "popupshown" 357 ); 358 let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); 359 360 // Click the TP switch, from On -> Off. 361 await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); 362 363 // Check that the icon state has been changed. 364 ok( 365 gProtectionsHandler.iconBox.hasAttribute("hasException"), 366 "The tracking protection icon state has been changed to disabled." 367 ); 368 369 // The panel should be closed and the mini panel will show up after refresh. 370 await popuphiddenPromise; 371 await browserLoadedPromise; 372 await popupShownPromise; 373 374 ok( 375 gProtectionsHandler._protectionsPopup.hasAttribute("toast"), 376 "The protections popup should have the 'toast' attribute." 377 ); 378 379 // Click on the mini panel and making sure the protection popup shows up. 380 popupShownPromise = BrowserTestUtils.waitForEvent( 381 gProtectionsHandler._protectionsPopup, 382 "popupshown" 383 ); 384 popuphiddenPromise = BrowserTestUtils.waitForEvent( 385 gProtectionsHandler._protectionsPopup, 386 "popuphidden" 387 ); 388 389 // Simulate clicking on the mini panel text 390 let buttonEl = document.getElementById( 391 "protections-popup-toast-panel-tp-off-desc" 392 ); 393 await EventUtils.synthesizeMouseAtCenter(buttonEl, {}); 394 await popuphiddenPromise; 395 await popupShownPromise; 396 397 ok( 398 !gProtectionsHandler._protectionsPopup.hasAttribute("toast"), 399 "The 'toast' attribute should be cleared on the protections popup." 400 ); 401 402 // Click the TP switch again, from Off -> On. 403 popuphiddenPromise = BrowserTestUtils.waitForEvent( 404 gProtectionsHandler._protectionsPopup, 405 "popuphidden" 406 ); 407 popupShownPromise = BrowserTestUtils.waitForEvent( 408 gProtectionsHandler._protectionsPopup, 409 "popupshown" 410 ); 411 browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); 412 await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); 413 414 // Check that the icon state has been changed. 415 ok( 416 !gProtectionsHandler.iconBox.hasAttribute("hasException"), 417 "The tracking protection icon state has been changed to enabled." 418 ); 419 420 // Protections popup hidden -> Page refresh -> Mini panel shows up. 421 await popuphiddenPromise; 422 popuphiddenPromise = BrowserTestUtils.waitForEvent( 423 gProtectionsHandler._protectionsPopup, 424 "popuphidden" 425 ); 426 await browserLoadedPromise; 427 await popupShownPromise; 428 429 ok( 430 gProtectionsHandler._protectionsPopup.hasAttribute("toast"), 431 "The protections popup should have the 'toast' attribute." 432 ); 433 434 // Wait until the auto hide is happening. 435 await popuphiddenPromise; 436 437 // Clean up the TP state. 438 ContentBlockingAllowList.remove(tab.linkedBrowser); 439 BrowserTestUtils.removeTab(tab); 440 }); 441 442 /** 443 * A test for ensuring the tracking protection icon will show a correct 444 * icon according to the TP enabling state. 445 */ 446 add_task(async function testTrackingProtectionIcon() { 447 // Open a tab and its protection panel. 448 let tab = await BrowserTestUtils.openNewForegroundTab( 449 gBrowser, 450 "https://example.com" 451 ); 452 453 let TPIcon = document.getElementById("tracking-protection-icon"); 454 // Check the icon url. It will show a shield icon if TP is enabled. 455 is( 456 gBrowser.ownerGlobal 457 .getComputedStyle(TPIcon) 458 .getPropertyValue("list-style-image"), 459 `url("chrome://browser/skin/tracking-protection.svg")`, 460 "The tracking protection icon shows a shield icon." 461 ); 462 463 // Disable the tracking protection. 464 let browserLoadedPromise = BrowserTestUtils.browserLoaded( 465 tab.linkedBrowser, 466 false, 467 "https://example.com/" 468 ); 469 gProtectionsHandler.disableForCurrentPage(); 470 await browserLoadedPromise; 471 472 // Check that the tracking protection icon should show a strike-through shield 473 // icon after page is reloaded. 474 is( 475 gBrowser.ownerGlobal 476 .getComputedStyle(TPIcon) 477 .getPropertyValue("list-style-image"), 478 `url("chrome://browser/skin/tracking-protection-disabled.svg")`, 479 "The tracking protection icon shows a strike through shield icon." 480 ); 481 482 // Clean up the TP state. 483 ContentBlockingAllowList.remove(tab.linkedBrowser); 484 BrowserTestUtils.removeTab(tab); 485 }); 486 487 /** 488 * A test for ensuring the number of blocked trackers is displayed properly. 489 */ 490 add_task(async function testNumberOfBlockedTrackers() { 491 // First, clear the tracking database. 492 await TrackingDBService.clearAll(); 493 494 // Open a tab. 495 let tab = await BrowserTestUtils.openNewForegroundTab( 496 gBrowser, 497 "https://example.com" 498 ); 499 await openProtectionsPanel(); 500 501 let trackerCounterBox = document.getElementById( 502 "protections-popup-trackers-blocked-counter-box" 503 ); 504 let trackerCounterDesc = document.getElementById( 505 "protections-popup-trackers-blocked-counter-description" 506 ); 507 508 // Check that whether the counter is not shown if the number of blocked 509 // trackers is zero. 510 ok( 511 BrowserTestUtils.isHidden(trackerCounterBox), 512 "The blocked tracker counter is hidden if there is no blocked tracker." 513 ); 514 515 await closeProtectionsPanel(); 516 517 // Add one tracker into the database and check that the tracker counter is 518 // properly shown. 519 await addTrackerDataIntoDB(1); 520 521 // A promise for waiting the `showing` attributes has been set to the counter 522 // box. This means the database access is finished. 523 let counterShownPromise = BrowserTestUtils.waitForAttribute( 524 "showing", 525 trackerCounterBox 526 ); 527 528 await openProtectionsPanel(); 529 await counterShownPromise; 530 531 // Check that the number of blocked trackers is shown. 532 ok( 533 BrowserTestUtils.isVisible(trackerCounterBox), 534 "The blocked tracker counter is shown if there is one blocked tracker." 535 ); 536 is( 537 trackerCounterDesc.textContent, 538 "1 Blocked", 539 "The blocked tracker counter is correct." 540 ); 541 542 await closeProtectionsPanel(); 543 await TrackingDBService.clearAll(); 544 545 // Add trackers into the database and check that the tracker counter is 546 // properly shown as well as whether the pre-fetch is triggered by the 547 // keyboard navigation. 548 await addTrackerDataIntoDB(10); 549 550 // We cannot wait for the change of "showing" attribute here since this 551 // attribute will only be set if the previous counter is zero. Instead, we 552 // wait for the change of the text content of the counter. 553 let updateCounterPromise = new Promise(resolve => { 554 let mut = new MutationObserver(() => { 555 resolve(); 556 mut.disconnect(); 557 }); 558 559 mut.observe(trackerCounterDesc, { 560 childList: true, 561 }); 562 }); 563 564 await openProtectionsPanelWithKeyNav(); 565 await updateCounterPromise; 566 567 // Check that the number of blocked trackers is shown. 568 ok( 569 BrowserTestUtils.isVisible(trackerCounterBox), 570 "The blocked tracker counter is shown if there are more than one blocked tracker." 571 ); 572 is( 573 trackerCounterDesc.textContent, 574 "10 Blocked", 575 "The blocked tracker counter is correct." 576 ); 577 578 await closeProtectionsPanel(); 579 await TrackingDBService.clearAll(); 580 BrowserTestUtils.removeTab(tab); 581 }); 582 583 add_task(async function testSubViewTelemetry() { 584 let items = [ 585 ["protections-popup-category-trackers", "trackers"], 586 ["protections-popup-category-socialblock", "social"], 587 ["protections-popup-category-cookies", "cookies"], 588 ["protections-popup-category-cryptominers", "cryptominers"], 589 ["protections-popup-category-fingerprinters", "fingerprinters"], 590 ].map(item => [document.getElementById(item[0]), item[1]]); 591 592 for (let [item, telemetryId] of items) { 593 // eslint-disable-next-line @microsoft/sdl/no-insecure-url 594 await BrowserTestUtils.withNewTab("http://www.example.com", async () => { 595 await openProtectionsPanel(); 596 597 item.classList.remove("notFound"); // Force visible for test 598 gProtectionsHandler._categoryItemOrderInvalidated = true; 599 gProtectionsHandler.reorderCategoryItems(); 600 601 let viewShownEvent = BrowserTestUtils.waitForEvent( 602 gProtectionsHandler._protectionsPopupMultiView, 603 "ViewShown" 604 ); 605 item.click(); 606 let panelView = (await viewShownEvent).originalTarget; 607 checkClickTelemetry(telemetryId); 608 let prefsTabPromise = BrowserTestUtils.waitForNewTab( 609 gBrowser, 610 "about:preferences#privacy" 611 ); 612 panelView.querySelector(".panel-subview-footer-button").click(); 613 let prefsTab = await prefsTabPromise; 614 BrowserTestUtils.removeTab(prefsTab); 615 checkClickTelemetry("subview_settings", telemetryId); 616 }); 617 } 618 }); 619 620 /** 621 * A test to make sure the TP state won't apply incorrectly if we quickly switch 622 * tab after toggling the TP switch. 623 */ 624 add_task(async function testQuickSwitchTabAfterTogglingTPSwitch() { 625 const FIRST_TEST_SITE = "https://example.com/"; 626 const SECOND_TEST_SITE = "https://example.org/"; 627 628 // First, clear the tracking database. 629 await TrackingDBService.clearAll(); 630 631 // Open two tabs with different origins. 632 let tabOne = await BrowserTestUtils.openNewForegroundTab( 633 gBrowser, 634 FIRST_TEST_SITE 635 ); 636 let tabTwo = await BrowserTestUtils.openNewForegroundTab( 637 gBrowser, 638 SECOND_TEST_SITE 639 ); 640 641 // Open the protection panel of the second tab. 642 await openProtectionsPanel(); 643 644 // A promise to check the reload happens on the second tab. 645 let browserLoadedPromise = BrowserTestUtils.browserLoaded( 646 tabTwo.linkedBrowser, 647 false, 648 SECOND_TEST_SITE 649 ); 650 651 // Toggle the TP state and switch tab without waiting it to be finished. 652 await clickToggle(gProtectionsHandler._protectionsPopupTPSwitch); 653 gBrowser.selectedTab = tabOne; 654 655 // Wait for the second tab to be reloaded. 656 await browserLoadedPromise; 657 658 // Check that the first tab is still with ETP enabled. 659 ok( 660 !ContentBlockingAllowList.includes(gBrowser.selectedBrowser), 661 "The ETP state of the first tab is still enabled." 662 ); 663 664 // Check the ETP is disabled on the second origin. 665 ok( 666 ContentBlockingAllowList.includes(tabTwo.linkedBrowser), 667 "The ETP state of the second tab has been changed to disabled." 668 ); 669 670 // Clean up the state of the allow list for the second tab. 671 ContentBlockingAllowList.remove(tabTwo.linkedBrowser); 672 673 BrowserTestUtils.removeTab(tabOne); 674 BrowserTestUtils.removeTab(tabTwo); 675 676 // Finally, clear the tracking database. 677 await TrackingDBService.clearAll(); 678 });