/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Test that warning messages can be grouped, per navigation and category, and that // interacting with these groups works as expected. "use strict"; requestLongerTimeout(2); const TEST_FILE = "browser/devtools/client/webconsole/test/browser/test-warning-groups.html"; const TEST_URI = "https://example.org/" + TEST_FILE; const TRACKER_URL = "https://tracking.example.com/"; const FILE_PATH = "browser/devtools/client/webconsole/test/browser/test-image.png"; const CONTENT_BLOCKED_URL = TRACKER_URL + FILE_PATH; const STORAGE_BLOCKED_URL = "https://example.com/" + FILE_PATH; const COOKIE_BEHAVIOR_PREF = "network.cookie.cookieBehavior"; const COOKIE_BEHAVIORS_REJECT_FOREIGN = 1; const CONTENT_BLOCKED_GROUP_LABEL = "The resource at “” was blocked because content blocking is enabled."; const STORAGE_BLOCKED_GROUP_LABEL = "Request to access cookie or storage on “” " + "was blocked because we are blocking all third-party storage access requests and " + "content blocking is enabled."; const { UrlClassifierTestUtils } = ChromeUtils.importESModule( "resource://testing-common/UrlClassifierTestUtils.sys.mjs" ); UrlClassifierTestUtils.addTestTrackers(); registerCleanupFunction(function () { UrlClassifierTestUtils.cleanupTestTrackers(); }); pushPref("privacy.trackingprotection.enabled", true); pushPref("devtools.webconsole.groupWarningMessages", true); add_task(async function testContentBlockingMessage() { await pushPref(COOKIE_BEHAVIOR_PREF, COOKIE_BEHAVIORS_REJECT_FOREIGN); await pushPref("devtools.webconsole.persistlog", true); const hud = await openNewTabAndConsole(TEST_URI); // Bug 1763367 - Filter out message like: // Cookie “name=value” has been rejected as third-party. // that appear in a random order. await setFilterState(hud, { text: "-has been rejected" }); info( "Log a tracking protection message to check a single message isn't grouped" ); let onContentBlockedMessage = waitForMessageByType( hud, CONTENT_BLOCKED_URL, ".warn" ); emitContentBlockingMessage(hud); let { node } = await onContentBlockedMessage; is( node.querySelector(".warning-indent"), null, "The message has the expected style" ); is( node.getAttribute("data-indent"), "0", "The message has the expected indent" ); info("Log a simple message"); await logString(hud, "simple message 1"); info( "Log a second tracking protection message to check that it causes the grouping" ); let onContentBlockedWarningGroupMessage = waitForMessageByType( hud, CONTENT_BLOCKED_GROUP_LABEL, ".warn" ); emitContentBlockingMessage(hud); const { node: contentBlockedWarningGroupNode } = await onContentBlockedWarningGroupMessage; is( contentBlockedWarningGroupNode.querySelector(".warning-group-badge") .textContent, "2", "The badge has the expected text" ); await checkConsoleOutputForWarningGroup(hud, [ `▶︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `simple message 1`, ]); let onStorageBlockedWarningGroupMessage = waitForMessageByType( hud, STORAGE_BLOCKED_URL, ".warn" ); emitStorageAccessBlockedMessage(hud); ({ node } = await onStorageBlockedWarningGroupMessage); is( node.querySelector(".warning-indent"), null, "The message has the expected style" ); is( node.getAttribute("data-indent"), "0", "The message has the expected indent" ); info("Log a second simple message"); await logString(hud, "simple message 2"); await checkConsoleOutputForWarningGroup(hud, [ `▶︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `simple message 1`, `${STORAGE_BLOCKED_URL}`, `simple message 2`, ]); info( "Log a second storage blocked message to check that it creates another group" ); onStorageBlockedWarningGroupMessage = waitForMessageByType( hud, STORAGE_BLOCKED_GROUP_LABEL, ".warn" ); emitStorageAccessBlockedMessage(hud); const { node: storageBlockedWarningGroupNode } = await onStorageBlockedWarningGroupMessage; is( storageBlockedWarningGroupNode.querySelector(".warning-group-badge") .textContent, "2", "The badge has the expected text" ); info("Expand the second warning group"); storageBlockedWarningGroupNode.querySelector(".arrow").click(); await waitFor(() => findWarningMessage(hud, STORAGE_BLOCKED_URL)); await checkConsoleOutputForWarningGroup(hud, [ `▶︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `simple message 1`, `▼︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `| ${STORAGE_BLOCKED_URL}?3`, `| ${STORAGE_BLOCKED_URL}?4`, `simple message 2`, ]); info( "Add another storage blocked message to check it does go into the opened group" ); let onStorageBlockedMessage = waitForMessageByType( hud, STORAGE_BLOCKED_URL, ".warn" ); emitStorageAccessBlockedMessage(hud); await onStorageBlockedMessage; await checkConsoleOutputForWarningGroup(hud, [ `▶︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `simple message 1`, `▼︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `| ${STORAGE_BLOCKED_URL}?3`, `| ${STORAGE_BLOCKED_URL}?4`, `| ${STORAGE_BLOCKED_URL}?5`, `simple message 2`, ]); info( "Add a content blocked message to check the first group badge is updated" ); emitContentBlockingMessage(); await waitForBadgeNumber(contentBlockedWarningGroupNode, 3); info("Expand the first warning group"); contentBlockedWarningGroupNode.querySelector(".arrow").click(); await waitFor(() => findWarningMessage(hud, CONTENT_BLOCKED_URL)); await checkConsoleOutputForWarningGroup(hud, [ `▼︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `| ${CONTENT_BLOCKED_URL}?1`, `| ${CONTENT_BLOCKED_URL}?2`, `| ${CONTENT_BLOCKED_URL}?6`, `simple message 1`, `▼︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `| ${STORAGE_BLOCKED_URL}?3`, `| ${STORAGE_BLOCKED_URL}?4`, `| ${STORAGE_BLOCKED_URL}?5`, `simple message 2`, ]); info("Reload the page and wait for it to be ready"); await reloadPage(); // Also wait for the navigation message to be displayed. await waitFor(() => findMessageByType(hud, "Navigated to", ".navigationMarker") ); info("Add a storage blocked message and a content blocked one"); onStorageBlockedMessage = waitForMessageByType( hud, STORAGE_BLOCKED_URL, ".warn" ); emitStorageAccessBlockedMessage(hud); await onStorageBlockedMessage; onContentBlockedMessage = waitForMessageByType( hud, CONTENT_BLOCKED_URL, ".warn" ); emitContentBlockingMessage(hud); await onContentBlockedMessage; await checkConsoleOutputForWarningGroup(hud, [ `▼︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `| ${CONTENT_BLOCKED_URL}?1`, `| ${CONTENT_BLOCKED_URL}?2`, `| ${CONTENT_BLOCKED_URL}?6`, `simple message 1`, `▼︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `| ${STORAGE_BLOCKED_URL}?3`, `| ${STORAGE_BLOCKED_URL}?4`, `| ${STORAGE_BLOCKED_URL}?5`, `simple message 2`, `Navigated to`, `${STORAGE_BLOCKED_URL}?7`, `${CONTENT_BLOCKED_URL}?8`, ]); info( "Add a storage blocked message and a content blocked one to create warningGroups" ); onStorageBlockedWarningGroupMessage = waitForMessageByType( hud, STORAGE_BLOCKED_GROUP_LABEL, ".warn" ); emitStorageAccessBlockedMessage(); await onStorageBlockedWarningGroupMessage; onContentBlockedWarningGroupMessage = waitForMessageByType( hud, CONTENT_BLOCKED_GROUP_LABEL, ".warn" ); emitContentBlockingMessage(); await onContentBlockedWarningGroupMessage; await checkConsoleOutputForWarningGroup(hud, [ `▼︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, `| ${CONTENT_BLOCKED_URL}?1`, `| ${CONTENT_BLOCKED_URL}?2`, `| ${CONTENT_BLOCKED_URL}?6`, `simple message 1`, `▼︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `| ${STORAGE_BLOCKED_URL}?3`, `| ${STORAGE_BLOCKED_URL}?4`, `| ${STORAGE_BLOCKED_URL}?5`, `simple message 2`, `Navigated to`, `▶︎⚠ ${STORAGE_BLOCKED_GROUP_LABEL}`, `▶︎⚠ ${CONTENT_BLOCKED_GROUP_LABEL}`, ]); }); let cpt = 0; const now = Date.now(); /** * Emit a Content Blocking message. This is done by loading an image from an origin * tagged as tracker. The image is loaded with a incremented counter query parameter * each time so we can get the warning message. */ function emitContentBlockingMessage() { const url = `${CONTENT_BLOCKED_URL}?${++cpt}-${now}`; SpecialPowers.spawn(gBrowser.selectedBrowser, [url], function (innerURL) { content.wrappedJSObject.loadImage(innerURL); }); } /** * Emit a Storage blocked message. This is done by loading an image from a different * origin, with the cookier permission rejecting foreign origin cookie access. */ function emitStorageAccessBlockedMessage() { const url = `${STORAGE_BLOCKED_URL}?${++cpt}-${now}`; SpecialPowers.spawn(gBrowser.selectedBrowser, [url], function (innerURL) { content.wrappedJSObject.loadImage(innerURL); }); } /** * Log a string from the content page. * * @param {WebConsole} hud * @param {String} str */ function logString(hud, str) { const onMessage = waitForMessageByType(hud, str, ".console-api"); SpecialPowers.spawn(gBrowser.selectedBrowser, [str], function (arg) { content.console.log(arg); }); return onMessage; } function waitForBadgeNumber(messageNode, expectedNumber) { return waitFor( () => messageNode.querySelector(".warning-group-badge").textContent == expectedNumber ); }