From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../browser_protectionsUI_subview_shim.js | 403 +++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js (limited to 'browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js') diff --git a/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js b/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js new file mode 100644 index 0000000000..cf120a33da --- /dev/null +++ b/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js @@ -0,0 +1,403 @@ +/* eslint-disable mozilla/no-arbitrary-setTimeout */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +/** + * Tests the warning and list indicators that are shown in the protections panel + * subview when a tracking channel is allowed via the + * "urlclassifier-before-block-channel" event. + */ + +// Choose origin so that all tracking origins used are third-parties. +const TRACKING_PAGE = + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + "http://example.net/browser/browser/base/content/test/protectionsUI/trackingPage.html"; + +add_setup(async function () { + await SpecialPowers.pushPrefEnv({ + set: [ + ["privacy.trackingprotection.enabled", true], + ["privacy.trackingprotection.annotate_channels", true], + ["privacy.trackingprotection.cryptomining.enabled", true], + ["privacy.trackingprotection.socialtracking.enabled", true], + ["privacy.trackingprotection.fingerprinting.enabled", true], + ["privacy.socialtracking.block_cookies.enabled", true], + // Allowlist trackertest.org loaded by default in trackingPage.html + ["urlclassifier.trackingSkipURLs", "trackertest.org"], + ["urlclassifier.trackingAnnotationSkipURLs", "trackertest.org"], + // Additional denylisted hosts. + [ + "urlclassifier.trackingAnnotationTable.testEntries", + "tracking.example.com", + ], + [ + "urlclassifier.features.cryptomining.blacklistHosts", + "cryptomining.example.com", + ], + [ + "urlclassifier.features.cryptomining.annotate.blacklistHosts", + "cryptomining.example.com", + ], + [ + "urlclassifier.features.fingerprinting.blacklistHosts", + "fingerprinting.example.com", + ], + [ + "urlclassifier.features.fingerprinting.annotate.blacklistHosts", + "fingerprinting.example.com", + ], + ], + }); + + await UrlClassifierTestUtils.addTestTrackers(); + registerCleanupFunction(() => { + UrlClassifierTestUtils.cleanupTestTrackers(); + }); +}); + +async function assertSubViewState(category, expectedState) { + await openProtectionsPanel(); + + // Sort the expected state by origin and transform it into an array. + let expectedStateSorted = Object.keys(expectedState) + .sort() + .reduce((stateArr, key) => { + let obj = expectedState[key]; + obj.origin = key; + stateArr.push(obj); + return stateArr; + }, []); + + if (!expectedStateSorted.length) { + ok( + BrowserTestUtils.is_visible( + document.getElementById( + "protections-popup-no-trackers-found-description" + ) + ), + "No Trackers detected should be shown" + ); + return; + } + + let categoryItem = document.getElementById( + `protections-popup-category-${category}` + ); + + // Explicitly waiting for the category item becoming visible. + await TestUtils.waitForCondition(() => { + return BrowserTestUtils.is_visible(categoryItem); + }); + + ok( + BrowserTestUtils.is_visible(categoryItem), + `${category} category item is visible` + ); + + ok(!categoryItem.disabled, `${category} category item is enabled`); + + let subView = document.getElementById(`protections-popup-${category}View`); + let viewShown = BrowserTestUtils.waitForEvent(subView, "ViewShown"); + categoryItem.click(); + await viewShown; + + ok(true, `${category} subView was shown`); + + info("Testing tracker list"); + + // Get the listed trackers in the UI and sort them by origin. + let items = Array.from( + subView.querySelectorAll( + `#protections-popup-${category}View-list .protections-popup-list-item` + ) + ).sort((a, b) => { + let originA = a.querySelector("label").value; + let originB = b.querySelector("label").value; + return originA.localeCompare(originB); + }); + + is( + items.length, + expectedStateSorted.length, + "List has expected amount of entries" + ); + + for (let i = 0; i < expectedStateSorted.length; i += 1) { + let expected = expectedStateSorted[i]; + let item = items[i]; + + let label = item.querySelector(".protections-popup-list-host-label"); + ok(label, "Item has label."); + is(label.tooltipText, expected.origin, "Label has correct tooltip."); + is(label.value, expected.origin, "Label has correct text."); + + is( + item.classList.contains("allowed"), + !expected.block, + "Item has allowed class if tracker is not blocked" + ); + + let shimAllowIndicator = item.querySelector( + ".protections-popup-list-host-shim-allow-indicator" + ); + + if (expected.shimAllow) { + is(item.childNodes.length, 2, "Item has two childNodes."); + ok(shimAllowIndicator, "Item has shim allow indicator icon."); + ok( + shimAllowIndicator.tooltipText, + "Shim allow indicator icon has tooltip text" + ); + } else { + is(item.childNodes.length, 1, "Item has one childNode."); + ok(!shimAllowIndicator, "Item does not have shim allow indicator icon."); + } + } + + let shimAllowSection = document.getElementById( + `protections-popup-${category}View-shim-allow-hint` + ); + ok(shimAllowSection, `Category ${category} has shim-allow hint.`); + + if (Object.values(expectedState).some(entry => entry.shimAllow)) { + BrowserTestUtils.is_visible( + shimAllowSection, + "Shim allow hint is visible." + ); + } else { + BrowserTestUtils.is_hidden(shimAllowSection, "Shim allow hint is hidden."); + } + + await closeProtectionsPanel(); +} + +async function runTestForCategoryAndState(category, action) { + // Maps the protection categories to the test tracking origins defined in + // ./trackingAPI.js and the UI class identifiers to look for in the + // protections UI. + let categoryToTestData = { + tracking: { + apiMessage: "more-tracking", + origin: "https://itisatracker.org", + elementId: "trackers", + }, + socialtracking: { + origin: "https://social-tracking.example.org", + elementId: "socialblock", + }, + cryptomining: { + // eslint-disable-next-line @microsoft/sdl/no-insecure-url + origin: "http://cryptomining.example.com", + elementId: "cryptominers", + }, + fingerprinting: { + origin: "https://fingerprinting.example.com", + elementId: "fingerprinters", + }, + }; + + let promise = BrowserTestUtils.openNewForegroundTab({ + url: TRACKING_PAGE, + gBrowser, + }); + // Wait for the tab to load and the initial blocking events from the + // classifier. + let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]); + + let { + origin: trackingOrigin, + elementId: categoryElementId, + apiMessage, + } = categoryToTestData[category]; + if (!apiMessage) { + apiMessage = category; + } + + // For allow or replace actions we need to hook into before-block-channel. + // If we don't hook into the event, the tracking channel will be blocked. + let beforeBlockChannelPromise; + if (action != "block") { + beforeBlockChannelPromise = UrlClassifierTestUtils.handleBeforeBlockChannel( + { + filterOrigin: trackingOrigin, + action, + } + ); + } + // Load the test tracker matching the category. + await SpecialPowers.spawn( + tab.linkedBrowser, + [{ apiMessage }], + function (args) { + content.postMessage(args.apiMessage, "*"); + } + ); + await beforeBlockChannelPromise; + + // Next, test if the UI state is correct for the given category and action. + let expectedState = {}; + expectedState[trackingOrigin] = { + block: action == "block", + shimAllow: action == "allow", + }; + + await assertSubViewState(categoryElementId, expectedState); + + BrowserTestUtils.removeTab(tab); +} + +/** + * Test mixed allow/block/replace states for the tracking protection category. + * @param {Object} options - States to test. + * @param {boolean} options.block - Test tracker block state. + * @param {boolean} options.allow - Test tracker allow state. + * @param {boolean} options.replace - Test tracker replace state. + */ +async function runTestMixed({ block, allow, replace }) { + const ORIGIN_BLOCK = "https://trackertest.org"; + const ORIGIN_ALLOW = "https://itisatracker.org"; + const ORIGIN_REPLACE = "https://tracking.example.com"; + + let promise = BrowserTestUtils.openNewForegroundTab({ + url: TRACKING_PAGE, + gBrowser, + }); + + let [tab] = await Promise.all([promise, waitForContentBlockingEvent()]); + + if (block) { + // Temporarily remove trackertest.org from the allowlist. + await SpecialPowers.pushPrefEnv({ + clear: [ + ["urlclassifier.trackingSkipURLs"], + ["urlclassifier.trackingAnnotationSkipURLs"], + ], + }); + let blockEventPromise = waitForContentBlockingEvent(); + await SpecialPowers.spawn(tab.linkedBrowser, [], function () { + content.postMessage("tracking", "*"); + }); + await blockEventPromise; + await SpecialPowers.popPrefEnv(); + } + + if (allow) { + let promiseEvent = waitForContentBlockingEvent(); + let promiseAllow = UrlClassifierTestUtils.handleBeforeBlockChannel({ + filterOrigin: ORIGIN_ALLOW, + action: "allow", + }); + + await SpecialPowers.spawn(tab.linkedBrowser, [], function () { + content.postMessage("more-tracking", "*"); + }); + + await promiseAllow; + await promiseEvent; + } + + if (replace) { + let promiseReplace = UrlClassifierTestUtils.handleBeforeBlockChannel({ + filterOrigin: ORIGIN_REPLACE, + action: "replace", + }); + + await SpecialPowers.spawn(tab.linkedBrowser, [], function () { + content.postMessage("more-tracking-2", "*"); + }); + + await promiseReplace; + } + + let expectedState = {}; + + if (block) { + expectedState[ORIGIN_BLOCK] = { + shimAllow: false, + block: true, + }; + } + + if (replace) { + expectedState[ORIGIN_REPLACE] = { + shimAllow: false, + block: false, + }; + } + + if (allow) { + expectedState[ORIGIN_ALLOW] = { + shimAllow: true, + block: false, + }; + } + + // Check the protection categories subview with the block list. + await assertSubViewState("trackers", expectedState); + + BrowserTestUtils.removeTab(tab); +} + +add_task(async function testNoShim() { + await runTestMixed({ + allow: false, + replace: false, + block: false, + }); + await runTestMixed({ + allow: false, + replace: false, + block: true, + }); +}); + +add_task(async function testShimAllow() { + await runTestMixed({ + allow: true, + replace: false, + block: false, + }); + await runTestMixed({ + allow: true, + replace: false, + block: true, + }); +}); + +add_task(async function testShimReplace() { + await runTestMixed({ + allow: false, + replace: true, + block: false, + }); + await runTestMixed({ + allow: false, + replace: true, + block: true, + }); +}); + +add_task(async function testShimMixed() { + await runTestMixed({ + allow: true, + replace: true, + block: true, + }); +}); + +add_task(async function testShimCategorySubviews() { + let categories = [ + "tracking", + "socialtracking", + "cryptomining", + "fingerprinting", + ]; + for (let category of categories) { + for (let action of ["block", "allow", "replace"]) { + info(`Test category subview. category: ${category}, action: ${action}`); + await runTestForCategoryAndState(category, action); + } + } +}); -- cgit v1.2.3