/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ /* Basic UI tests for the protections panel */ "use strict"; const TRACKING_PAGE = // eslint-disable-next-line @microsoft/sdl/no-insecure-url "http://tracking.example.org/browser/browser/base/content/test/protectionsUI/trackingPage.html"; ChromeUtils.defineESModuleGetters(this, { ContentBlockingAllowList: "resource://gre/modules/ContentBlockingAllowList.sys.mjs", }); const { CustomizableUITestUtils } = ChromeUtils.importESModule( "resource://testing-common/CustomizableUITestUtils.sys.mjs" ); add_setup(async function () { await SpecialPowers.pushPrefEnv({ set: [ // Set the auto hide timing to 100ms for blocking the test less. ["browser.protections_panel.toast.timeout", 100], // Hide protections cards so as not to trigger more async messaging // when landing on the page. ["browser.contentblocking.report.monitor.enabled", false], ["browser.contentblocking.report.lockwise.enabled", false], ["browser.contentblocking.report.proxy.enabled", false], ["privacy.trackingprotection.enabled", true], ], }); let oldCanRecord = Services.telemetry.canRecordExtended; Services.telemetry.canRecordExtended = true; Services.telemetry.clearEvents(); registerCleanupFunction(() => { Services.telemetry.canRecordExtended = oldCanRecord; Services.telemetry.clearEvents(); }); }); add_task(async function testToggleSwitch() { let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, TRACKING_PAGE ); await openProtectionsPanel(); await TestUtils.waitForCondition(() => { return gProtectionsHandler._protectionsPopup.hasAttribute("blocking"); }); let events = Services.telemetry.snapshotEvents( Ci.nsITelemetry.DATASET_PRERELEASE_CHANNELS ).parent; let buttonEvents = events.filter( e => e[1] == "security.ui.protectionspopup" && e[2] == "open" && e[3] == "protections_popup" ); is(buttonEvents.length, 1, "recorded telemetry for opening the popup"); // Check the visibility of the "Site not working?" link. ok( BrowserTestUtils.is_visible( gProtectionsHandler._protectionsPopupTPSwitchBreakageLink ), "The 'Site not working?' link should be visible." ); // The 'Site Fixed?' link should be hidden. ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink ), "The 'Site Fixed?' link should be hidden." ); // Navigate through the 'Site Not Working?' flow and back to the main view, // checking for telemetry on the way. let siteNotWorkingView = document.getElementById( "protections-popup-siteNotWorkingView" ); let viewShown = BrowserTestUtils.waitForEvent( siteNotWorkingView, "ViewShown" ); gProtectionsHandler._protectionsPopupTPSwitchBreakageLink.click(); await viewShown; checkClickTelemetry("sitenotworking_link"); let sendReportButton = document.getElementById( "protections-popup-siteNotWorkingView-sendReport" ); let sendReportView = document.getElementById( "protections-popup-sendReportView" ); viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); sendReportButton.click(); await viewShown; checkClickTelemetry("send_report_link"); viewShown = BrowserTestUtils.waitForEvent(siteNotWorkingView, "ViewShown"); sendReportView.querySelector(".subviewbutton-back").click(); await viewShown; let mainView = document.getElementById("protections-popup-mainView"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); siteNotWorkingView.querySelector(".subviewbutton-back").click(); await viewShown; ok( gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("enabled"), "TP Switch should be enabled" ); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); gProtectionsHandler._protectionsPopupTPSwitch.click(); // The 'Site not working?' link should be hidden after clicking the TP switch. ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageLink ), "The 'Site not working?' link should be hidden after TP switch turns to off." ); // Same for the 'Site Fixed?' link ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink ), "The 'Site Fixed?' link should be hidden." ); await popuphiddenPromise; checkClickTelemetry("etp_toggle_off"); // We need to wait toast's popup shown and popup hidden events. It won't fire // the popup shown event if we open the protections panel while the toast is // opening. let popupShownPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popupshown" ); popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); await browserLoadedPromise; // Wait until the toast is shown and hidden. await popupShownPromise; await popuphiddenPromise; await openProtectionsPanel(); ok( !gProtectionsHandler._protectionsPopupTPSwitch.hasAttribute("enabled"), "TP Switch should be disabled" ); // The 'Site not working?' link should be hidden if the TP is off. ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageLink ), "The 'Site not working?' link should be hidden if TP is off." ); // The 'Site Fixed?' link should be shown if TP is off. ok( BrowserTestUtils.is_visible( gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink ), "The 'Site Fixed?' link should be visible." ); // Check telemetry for 'Site Fixed?' link. viewShown = BrowserTestUtils.waitForEvent(sendReportView, "ViewShown"); gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink.click(); await viewShown; checkClickTelemetry("sitenotworking_link", "sitefixed"); viewShown = BrowserTestUtils.waitForEvent(mainView, "ViewShown"); sendReportView.querySelector(".subviewbutton-back").click(); await viewShown; // Click the TP switch again and check the visibility of the 'Site not // Working?'. It should be hidden after toggling the TP switch. browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); gProtectionsHandler._protectionsPopupTPSwitch.click(); ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageLink ), `The 'Site not working?' link should be still hidden after toggling TP switch to on from off.` ); ok( BrowserTestUtils.is_hidden( gProtectionsHandler._protectionsPopupTPSwitchBreakageFixedLink ), "The 'Site Fixed?' link should be hidden." ); await browserLoadedPromise; checkClickTelemetry("etp_toggle_on"); ContentBlockingAllowList.remove(tab.linkedBrowser); BrowserTestUtils.removeTab(tab); }); /** * A test for the protection settings button. */ add_task(async function testSettingsButton() { // Open a tab and its protection panel. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await openProtectionsPanel(); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); let newTabPromise = BrowserTestUtils.waitForNewTab( gBrowser, "about:preferences#privacy" ); gProtectionsHandler._protectionsPopupSettingsButton.click(); // The protection popup should be hidden after clicking settings button. await popuphiddenPromise; // Wait until the about:preferences has been opened correctly. let newTab = await newTabPromise; ok(true, "about:preferences has been opened successfully"); checkClickTelemetry("settings"); BrowserTestUtils.removeTab(newTab); BrowserTestUtils.removeTab(tab); }); /** * A test for ensuring Tracking Protection label is shown correctly */ add_task(async function testTrackingProtectionLabel() { // Open a tab. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await openProtectionsPanel(); let trackingProtectionLabel = document.getElementById( "protections-popup-footer-protection-type-label" ); is( trackingProtectionLabel.textContent, "Custom", "The label is correctly set to Custom." ); await closeProtectionsPanel(); Services.prefs.setStringPref("browser.contentblocking.category", "standard"); await openProtectionsPanel(); is( trackingProtectionLabel.textContent, "Standard", "The label is correctly set to Standard." ); await closeProtectionsPanel(); Services.prefs.setStringPref("browser.contentblocking.category", "strict"); await openProtectionsPanel(); is( trackingProtectionLabel.textContent, "Strict", "The label is correctly set to Strict." ); await closeProtectionsPanel(); Services.prefs.setStringPref("browser.contentblocking.category", "custom"); BrowserTestUtils.removeTab(tab); }); /** * A test for the 'Show Full Report' button in the footer section. */ add_task(async function testShowFullReportButton() { // Open a tab and its protection panel. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await openProtectionsPanel(); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); let newTabPromise = waitForAboutProtectionsTab(); let showFullReportButton = document.getElementById( "protections-popup-show-report-button" ); showFullReportButton.click(); // The protection popup should be hidden after clicking the link. await popuphiddenPromise; // Wait until the 'about:protections' has been opened correctly. let newTab = await newTabPromise; ok(true, "about:protections has been opened successfully"); checkClickTelemetry("full_report"); BrowserTestUtils.removeTab(newTab); BrowserTestUtils.removeTab(tab); }); /** * A test for ensuring the mini panel is working correctly */ add_task(async function testMiniPanel() { // Open a tab. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); // Open the mini panel. await openProtectionsPanel(true); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); // Check that only the header is displayed. let mainView = document.getElementById("protections-popup-mainView"); for (let item of mainView.childNodes) { if (item.id !== "protections-popup-mainView-panel-header-section") { ok( !BrowserTestUtils.is_visible(item), `The section '${item.id}' is hidden in the toast.` ); } else { ok( BrowserTestUtils.is_visible(item), "The panel header is displayed as the content of the toast." ); } } // Wait until the auto hide is happening. await popuphiddenPromise; ok(true, "The mini panel hides automatically."); BrowserTestUtils.removeTab(tab); }); /** * A test for the toggle switch flow */ add_task(async function testToggleSwitchFlow() { // Open a tab. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await openProtectionsPanel(); let popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); let popupShownPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popupshown" ); let browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); // Click the TP switch, from On -> Off. gProtectionsHandler._protectionsPopupTPSwitch.click(); // Check that the icon state has been changed. ok( gProtectionsHandler.iconBox.hasAttribute("hasException"), "The tracking protection icon state has been changed to disabled." ); // The panel should be closed and the mini panel will show up after refresh. await popuphiddenPromise; await browserLoadedPromise; await popupShownPromise; ok( gProtectionsHandler._protectionsPopup.hasAttribute("toast"), "The protections popup should have the 'toast' attribute." ); // Click on the mini panel and making sure the protection popup shows up. popupShownPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popupshown" ); popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); document.getElementById("protections-popup-mainView-panel-header").click(); await popuphiddenPromise; await popupShownPromise; ok( !gProtectionsHandler._protectionsPopup.hasAttribute("toast"), "The 'toast' attribute should be cleared on the protections popup." ); // Click the TP switch again, from Off -> On. popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); popupShownPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popupshown" ); browserLoadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser); gProtectionsHandler._protectionsPopupTPSwitch.click(); // Check that the icon state has been changed. ok( !gProtectionsHandler.iconBox.hasAttribute("hasException"), "The tracking protection icon state has been changed to enabled." ); // Protections popup hidden -> Page refresh -> Mini panel shows up. await popuphiddenPromise; popuphiddenPromise = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopup, "popuphidden" ); await browserLoadedPromise; await popupShownPromise; ok( gProtectionsHandler._protectionsPopup.hasAttribute("toast"), "The protections popup should have the 'toast' attribute." ); // Wait until the auto hide is happening. await popuphiddenPromise; // Clean up the TP state. ContentBlockingAllowList.remove(tab.linkedBrowser); BrowserTestUtils.removeTab(tab); }); /** * A test for ensuring the tracking protection icon will show a correct * icon according to the TP enabling state. */ add_task(async function testTrackingProtectionIcon() { // Open a tab and its protection panel. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); let TPIcon = document.getElementById("tracking-protection-icon"); // Check the icon url. It will show a shield icon if TP is enabled. is( gBrowser.ownerGlobal .getComputedStyle(TPIcon) .getPropertyValue("list-style-image"), `url("chrome://browser/skin/tracking-protection.svg")`, "The tracking protection icon shows a shield icon." ); // Disable the tracking protection. let browserLoadedPromise = BrowserTestUtils.browserLoaded( tab.linkedBrowser, false, "https://example.com/" ); gProtectionsHandler.disableForCurrentPage(); await browserLoadedPromise; // Check that the tracking protection icon should show a strike-through shield // icon after page is reloaded. is( gBrowser.ownerGlobal .getComputedStyle(TPIcon) .getPropertyValue("list-style-image"), `url("chrome://browser/skin/tracking-protection-disabled.svg")`, "The tracking protection icon shows a strike through shield icon." ); // Clean up the TP state. ContentBlockingAllowList.remove(tab.linkedBrowser); BrowserTestUtils.removeTab(tab); }); /** * A test for ensuring the number of blocked trackers is displayed properly. */ add_task(async function testNumberOfBlockedTrackers() { // First, clear the tracking database. await TrackingDBService.clearAll(); // Open a tab. let tab = await BrowserTestUtils.openNewForegroundTab( gBrowser, "https://example.com" ); await openProtectionsPanel(); let trackerCounterBox = document.getElementById( "protections-popup-trackers-blocked-counter-box" ); let trackerCounterDesc = document.getElementById( "protections-popup-trackers-blocked-counter-description" ); // Check that whether the counter is not shown if the number of blocked // trackers is zero. ok( BrowserTestUtils.is_hidden(trackerCounterBox), "The blocked tracker counter is hidden if there is no blocked tracker." ); await closeProtectionsPanel(); // Add one tracker into the database and check that the tracker counter is // properly shown. await addTrackerDataIntoDB(1); // A promise for waiting the `showing` attributes has been set to the counter // box. This means the database access is finished. let counterShownPromise = BrowserTestUtils.waitForAttribute( "showing", trackerCounterBox ); await openProtectionsPanel(); await counterShownPromise; // Check that the number of blocked trackers is shown. ok( BrowserTestUtils.is_visible(trackerCounterBox), "The blocked tracker counter is shown if there is one blocked tracker." ); is( trackerCounterDesc.textContent, "1 Blocked", "The blocked tracker counter is correct." ); await closeProtectionsPanel(); await TrackingDBService.clearAll(); // Add trackers into the database and check that the tracker counter is // properly shown as well as whether the pre-fetch is triggered by the // keyboard navigation. await addTrackerDataIntoDB(10); // We cannot wait for the change of "showing" attribute here since this // attribute will only be set if the previous counter is zero. Instead, we // wait for the change of the text content of the counter. let updateCounterPromise = new Promise(resolve => { let mut = new MutationObserver(mutations => { resolve(); mut.disconnect(); }); mut.observe(trackerCounterDesc, { childList: true, }); }); await openProtectionsPanelWithKeyNav(); await updateCounterPromise; // Check that the number of blocked trackers is shown. ok( BrowserTestUtils.is_visible(trackerCounterBox), "The blocked tracker counter is shown if there are more than one blocked tracker." ); is( trackerCounterDesc.textContent, "10 Blocked", "The blocked tracker counter is correct." ); await closeProtectionsPanel(); await TrackingDBService.clearAll(); BrowserTestUtils.removeTab(tab); }); add_task(async function testSubViewTelemetry() { let items = [ ["protections-popup-category-trackers", "trackers"], ["protections-popup-category-socialblock", "social"], ["protections-popup-category-cookies", "cookies"], ["protections-popup-category-cryptominers", "cryptominers"], ["protections-popup-category-fingerprinters", "fingerprinters"], ].map(item => [document.getElementById(item[0]), item[1]]); for (let [item, telemetryId] of items) { // eslint-disable-next-line @microsoft/sdl/no-insecure-url await BrowserTestUtils.withNewTab("http://www.example.com", async () => { await openProtectionsPanel(); item.classList.remove("notFound"); // Force visible for test gProtectionsHandler._categoryItemOrderInvalidated = true; gProtectionsHandler.reorderCategoryItems(); let viewShownEvent = BrowserTestUtils.waitForEvent( gProtectionsHandler._protectionsPopupMultiView, "ViewShown" ); item.click(); let panelView = (await viewShownEvent).originalTarget; checkClickTelemetry(telemetryId); let prefsTabPromise = BrowserTestUtils.waitForNewTab( gBrowser, "about:preferences#privacy" ); panelView.querySelector(".panel-subview-footer-button").click(); let prefsTab = await prefsTabPromise; BrowserTestUtils.removeTab(prefsTab); checkClickTelemetry("subview_settings", telemetryId); }); } }); /** * A test to make sure the TP state won't apply incorrectly if we quickly switch * tab after toggling the TP switch. */ add_task(async function testQuickSwitchTabAfterTogglingTPSwitch() { const FIRST_TEST_SITE = "https://example.com/"; const SECOND_TEST_SITE = "https://example.org/"; // First, clear the tracking database. await TrackingDBService.clearAll(); // Open two tabs with different origins. let tabOne = await BrowserTestUtils.openNewForegroundTab( gBrowser, FIRST_TEST_SITE ); let tabTwo = await BrowserTestUtils.openNewForegroundTab( gBrowser, SECOND_TEST_SITE ); // Open the protection panel of the second tab. await openProtectionsPanel(); // A promise to check the reload happens on the second tab. let browserLoadedPromise = BrowserTestUtils.browserLoaded( tabTwo.linkedBrowser, false, SECOND_TEST_SITE ); // Toggle the TP state and switch tab without waiting it to be finished. gProtectionsHandler._protectionsPopupTPSwitch.click(); gBrowser.selectedTab = tabOne; // Wait for the second tab to be reloaded. await browserLoadedPromise; // Check that the first tab is still with ETP enabled. ok( !ContentBlockingAllowList.includes(gBrowser.selectedBrowser), "The ETP state of the first tab is still enabled." ); // Check the ETP is disabled on the second origin. ok( ContentBlockingAllowList.includes(tabTwo.linkedBrowser), "The ETP state of the second tab has been changed to disabled." ); // Clean up the state of the allow list for the second tab. ContentBlockingAllowList.remove(tabTwo.linkedBrowser); BrowserTestUtils.removeTab(tabOne); BrowserTestUtils.removeTab(tabTwo); // Finally, clear the tracking database. await TrackingDBService.clearAll(); });