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 --- .../originattributes/test/browser/head.js | 463 +++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 browser/components/originattributes/test/browser/head.js (limited to 'browser/components/originattributes/test/browser/head.js') diff --git a/browser/components/originattributes/test/browser/head.js b/browser/components/originattributes/test/browser/head.js new file mode 100644 index 0000000000..bd4307fd1c --- /dev/null +++ b/browser/components/originattributes/test/browser/head.js @@ -0,0 +1,463 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const TEST_URL_PATH = + "/browser/browser/components/originattributes/test/browser/"; + +// The flags of test modes. +const TEST_MODE_FIRSTPARTY = 0; +const TEST_MODE_NO_ISOLATION = 1; +const TEST_MODE_CONTAINERS = 2; + +// The name of each mode. +const TEST_MODE_NAMES = ["first party isolation", "no isolation", "containers"]; + +// The frame types. +const TEST_TYPE_FRAME = 1; +const TEST_TYPE_IFRAME = 2; + +// The default frame setting. +const DEFAULT_FRAME_SETTING = [TEST_TYPE_IFRAME]; + +let gFirstPartyBasicPage = TEST_URL_PATH + "file_firstPartyBasic.html"; + +/** + * Add a tab for the given url with the specific user context id. + * + * @param aURL + * The url of the page. + * @param aUserContextId + * The user context id for this tab. + * + * @return tab - The tab object of this tab. + * browser - The browser object of this tab. + */ +async function openTabInUserContext(aURL, aUserContextId) { + info(`Start to open tab in specific userContextID: ${aUserContextId}.`); + let originAttributes = { + userContextId: aUserContextId, + }; + info("Create triggeringPrincipal."); + let triggeringPrincipal = + Services.scriptSecurityManager.createContentPrincipal( + makeURI(aURL), + originAttributes + ); + // Open the tab in the correct userContextId. + info("Open the tab and wait for it to be loaded."); + let tab = BrowserTestUtils.addTab(gBrowser, aURL, { + userContextId: aUserContextId, + triggeringPrincipal, + }); + + // Select tab and make sure its browser is focused. + gBrowser.selectedTab = tab; + tab.ownerGlobal.focus(); + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + info("Finished tab opening."); + + return { tab, browser }; +} + +/** + * Add a tab for a page with the given first party domain. This page will have + * an iframe which is loaded with the given url by default or you could specify + * a frame setting to create nested frames. And this function will also modify + * the 'content' in the ContentTask to the target frame's window object. + * + * @param aURL + * The url of the iframe. + * @param aFirstPartyDomain + * The first party domain. + * @param aFrameSetting + * This setting controls how frames are organized within the page. The + * setting is an array of frame types, the first item indicates the + * frame type (iframe or frame) of the first layer of the frame structure, + * and the second item indicates the second layer, and so on. The aURL will + * be loaded at the deepest layer. This is optional. + * + * @return tab - The tab object of this tab. + * browser - The browser object of this tab. + */ +async function openTabInFirstParty( + aURL, + aFirstPartyDomain, + aFrameSetting = DEFAULT_FRAME_SETTING +) { + info(`Start to open tab under first party domain "${aFirstPartyDomain}".`); + // If the first party domain ends with '/', we remove it. + if (aFirstPartyDomain.endsWith("/")) { + aFirstPartyDomain = aFirstPartyDomain.slice(0, -1); + } + + let basicPageURL = aFirstPartyDomain + gFirstPartyBasicPage; + + // Open the tab for the basic first party page. + info("Open the tab and then wait for it to be loaded."); + let tab = BrowserTestUtils.addTab(gBrowser, basicPageURL); + + // Select tab and make sure its browser is focused. + gBrowser.selectedTab = tab; + tab.ownerGlobal.focus(); + + let browser = gBrowser.getBrowserForTab(tab); + await BrowserTestUtils.browserLoaded(browser); + + // Clone the frame setting here since we will modify it later. + let frameSetting = aFrameSetting.slice(0); + let frameType; + let targetBrowsingContext; + + // Create the frame structure. + info("Create the frame structure."); + while ((frameType = frameSetting.shift())) { + if (!targetBrowsingContext) { + targetBrowsingContext = browser; + } + + let frameURL = !frameSetting.length ? aURL : basicPageURL; + + if (frameType == TEST_TYPE_FRAME) { + info("Add the frameset."); + targetBrowsingContext = await SpecialPowers.spawn( + targetBrowsingContext, + [frameURL], + async function (aFrameURL) { + // Add a frameset which carries the frame element. + let frameSet = content.document.createElement("frameset"); + frameSet.cols = "50%,50%"; + + let frame = content.document.createElement("frame"); + let dummyFrame = content.document.createElement("frame"); + + frameSet.appendChild(frame); + frameSet.appendChild(dummyFrame); + + content.document.body.appendChild(frameSet); + + // Wait for the frame to be loaded. + await new Promise(done => { + frame.addEventListener( + "load", + function () { + done(); + }, + { capture: true, once: true } + ); + + frame.setAttribute("src", aFrameURL); + }); + + return frame.browsingContext; + } + ); + } else if (frameType == TEST_TYPE_IFRAME) { + info("Add the iframe."); + targetBrowsingContext = await SpecialPowers.spawn( + targetBrowsingContext, + [frameURL], + async function (aFrameURL) { + // Add an iframe. + let frame = content.document.createElement("iframe"); + content.document.body.appendChild(frame); + + // Wait for the frame to be loaded. + await new Promise(done => { + frame.addEventListener( + "load", + function () { + done(); + }, + { capture: true, once: true } + ); + + frame.setAttribute("src", aFrameURL); + }); + + return frame.browsingContext; + } + ); + } else { + ok(false, "Invalid frame type."); + break; + } + info("Successfully added a frame"); + } + info("Finished the frame structure"); + + return { tab, browser: targetBrowsingContext }; +} + +this.IsolationTestTools = { + /** + * Adds isolation tests for first party isolation, no isolation + * and containers respectively. + * + * @param aTask + * The testing task which will be run in different settings. + */ + _add_task(aTask) { + let testSettings = [ + { + mode: TEST_MODE_FIRSTPARTY, + skip: false, + prefs: [["privacy.firstparty.isolate", true]], + }, + { + mode: TEST_MODE_NO_ISOLATION, + skip: false, + prefs: [["privacy.firstparty.isolate", false]], + }, + { + mode: TEST_MODE_CONTAINERS, + skip: false, + prefs: [["privacy.userContext.enabled", true]], + }, + ]; + + // Add test tasks. + for (let testSetting of testSettings) { + IsolationTestTools._addTaskForMode( + testSetting.mode, + testSetting.prefs, + testSetting.skip, + aTask + ); + } + }, + + _addTaskForMode(aMode, aPref, aSkip, aTask) { + if (aSkip) { + return; + } + + add_task(async function () { + info(`Starting the test for ${TEST_MODE_NAMES[aMode]}.`); + + // Before run this task, reset the preferences first. + await SpecialPowers.flushPrefEnv(); + + // Make sure preferences are set properly. + await SpecialPowers.pushPrefEnv({ set: aPref }); + + await SpecialPowers.pushPrefEnv({ set: [["dom.ipc.processCount", 1]] }); + await SpecialPowers.pushPrefEnv({ + set: [ + [ + "network.auth.non-web-content-triggered-resources-http-auth-allow", + true, + ], + ], + }); + + await aTask(aMode); + }); + }, + + /** + * Add a tab with the given tab setting, this will open different types of + * tabs according to the given test mode. A tab setting means a isolation + * target in different test mode; a tab setting indicates a first party + * domain when testing the first party isolation, it is a user context + * id when testing containers. + * + * @param aMode + * The test mode which decides what type of tabs will be opened. + * @param aURL + * The url which is going to open. + * @param aTabSettingObj + * The tab setting object includes 'firstPartyDomain' for the first party + * domain and 'userContextId' for Containers. + * @param aFrameSetting + * This setting controls how frames are organized within the page. The + * setting is an array of frame types, the first item indicates the + * frame type (iframe or frame) of the first layer of the frame structure, + * and the second item indicates the second layer, and so on. The aURL + * will be loaded at the deepest layer. This is optional. + * + * @return tab - The tab object of this tab. + * browser - The browser object of this tab. + */ + _addTab(aMode, aURL, aTabSettingObj, aFrameSetting) { + if (aMode === TEST_MODE_CONTAINERS) { + return openTabInUserContext(aURL, aTabSettingObj.userContextId); + } + + return openTabInFirstParty( + aURL, + aTabSettingObj.firstPartyDomain, + aFrameSetting + ); + }, + + /** + * Run isolation tests. The framework will run tests with standard combinations + * of prefs and tab settings, and checks whether the isolation is working. + * + * @param aURL + * The URL of the page that will be tested or an object contains 'url', + * the tested page, 'firstFrameSetting' for the frame setting of the first + * tab, and 'secondFrameSetting' for the second tab. + * @param aGetResultFuncs + * An array of functions or a single function which are responsible for + * returning the isolation result back to the framework for further checking. + * Each of these functions will be provided the browser object of the tab, + * that allows modifying or fetchings results from the page content. + * @param aCompareResultFunc + * An optional function which allows modifying the way how does framework + * check results. This function will be provided a boolean to indicate + * the isolation is no or off and two results. This function should return + * a boolean to tell that whether isolation is working. If this function + * is not given, the framework will take case checking by itself. + * @param aBeforeFunc + * An optional function which is called before any tabs are created so + * that the test case can set up/reset local state. + * @param aGetResultImmediately + * An optional boolean to ensure we get results before the next tab is opened. + */ + runTests( + aURL, + aGetResultFuncs, + aCompareResultFunc, + aBeforeFunc, + aGetResultImmediately, + aUseHttps + ) { + let pageURL; + let firstFrameSetting; + let secondFrameSetting; + + // Request a longer timeout since the test will run a test for three times + // with different settings. Thus, one test here represents three tests. + // For this reason, we triple the timeout. + requestLongerTimeout(3); + + if (typeof aURL === "string") { + pageURL = aURL; + } else if (typeof aURL === "object") { + pageURL = aURL.url; + firstFrameSetting = aURL.firstFrameSetting; + secondFrameSetting = aURL.secondFrameSetting; + } + + if (!Array.isArray(aGetResultFuncs)) { + aGetResultFuncs = [aGetResultFuncs]; + } + + let tabSettings = aUseHttps + ? [ + { firstPartyDomain: "https://example.com", userContextId: 1 }, + { firstPartyDomain: "https://example.org", userContextId: 2 }, + ] + : [ + { firstPartyDomain: "http://example.com", userContextId: 1 }, + { firstPartyDomain: "http://example.org", userContextId: 2 }, + ]; + + this._add_task(async function (aMode) { + let tabSettingA = 0; + + for (let tabSettingB of [0, 1]) { + // Give the test a chance to set up before each case is run. + if (aBeforeFunc) { + try { + await aBeforeFunc(aMode); + } catch (e) { + ok(false, `Caught error while doing testing setup: ${e}.`); + } + } + // Create Tabs. + info(`Create tab A for ${TEST_MODE_NAMES[aMode]} test.`); + let tabInfoA = await IsolationTestTools._addTab( + aMode, + pageURL, + tabSettings[tabSettingA], + firstFrameSetting + ); + info(`Finished Create tab A for ${TEST_MODE_NAMES[aMode]} test.`); + let resultsA = []; + if (aGetResultImmediately) { + try { + info( + `Immediately get result from tab A for ${TEST_MODE_NAMES[aMode]} test` + ); + for (let getResultFunc of aGetResultFuncs) { + resultsA.push(await getResultFunc(tabInfoA.browser)); + } + } catch (e) { + ok(false, `Caught error while getting result from Tab A: ${e}.`); + } + } + info(`Create tab B for ${TEST_MODE_NAMES[aMode]}.`); + let tabInfoB = await IsolationTestTools._addTab( + aMode, + pageURL, + tabSettings[tabSettingB], + secondFrameSetting + ); + info(`Finished Create tab B for ${TEST_MODE_NAMES[aMode]} test.`); + let i = 0; + for (let getResultFunc of aGetResultFuncs) { + // Fetch results from tabs. + info(`Fetching result from tab A for ${TEST_MODE_NAMES[aMode]}.`); + let resultA; + try { + resultA = aGetResultImmediately + ? resultsA[i++] + : await getResultFunc(tabInfoA.browser); + } catch (e) { + ok(false, `Caught error while getting result from Tab A: ${e}.`); + } + info(`Fetching result from tab B for ${TEST_MODE_NAMES[aMode]}.`); + let resultB; + try { + resultB = await getResultFunc(tabInfoB.browser); + } catch (e) { + ok(false, `Caught error while getting result from Tab B: ${e}.`); + } + // Compare results. + let result = false; + let shouldIsolate = + aMode !== TEST_MODE_NO_ISOLATION && tabSettingA !== tabSettingB; + if (aCompareResultFunc) { + result = await aCompareResultFunc(shouldIsolate, resultA, resultB); + } else { + result = shouldIsolate ? resultA !== resultB : resultA === resultB; + } + + let msg = + `Result of Testing ${TEST_MODE_NAMES[aMode]} for ` + + `isolation ${shouldIsolate ? "on" : "off"} with TabSettingA ` + + `${tabSettingA} and tabSettingB ${tabSettingB}` + + `, resultA = ${resultA}, resultB = ${resultB}`; + + ok(result, msg); + } + + // Close Tabs. + BrowserTestUtils.removeTab(tabInfoA.tab); + BrowserTestUtils.removeTab(tabInfoB.tab); + + // A workaround for avoiding a timing issue in Fission. This workaround + // makes sure that the shutdown process between parent and content + // is finished before the next round of testing. + if (SpecialPowers.useRemoteSubframes) { + await new Promise(resolve => { + let observer = (subject, topic, data) => { + if (topic === "ipc:content-shutdown") { + Services.obs.removeObserver(observer, "ipc:content-shutdown"); + resolve(); + } + }; + Services.obs.addObserver(observer, "ipc:content-shutdown"); + }); + } + } + }); + }, +}; -- cgit v1.2.3