summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js')
-rw-r--r--browser/base/content/test/protectionsUI/browser_protectionsUI_subview_shim.js403
1 files changed, 403 insertions, 0 deletions
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);
+ }
+ }
+});