diff options
Diffstat (limited to 'toolkit/components/antitracking/test/browser/partitionedstorage_head.js')
-rw-r--r-- | toolkit/components/antitracking/test/browser/partitionedstorage_head.js | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/toolkit/components/antitracking/test/browser/partitionedstorage_head.js b/toolkit/components/antitracking/test/browser/partitionedstorage_head.js new file mode 100644 index 0000000000..1e07c348a6 --- /dev/null +++ b/toolkit/components/antitracking/test/browser/partitionedstorage_head.js @@ -0,0 +1,456 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* 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/. */ + +/* import-globals-from head.js */ + +"use strict"; + +/* import-globals-from dynamicfpi_head.js */ +Services.scriptloader.loadSubScript( + "chrome://mochitests/content/browser/toolkit/components/antitracking/test/browser/dynamicfpi_head.js", + this +); + +this.PartitionedStorageHelper = { + runTestInNormalAndPrivateMode(name, callback, cleanupFunction, extraPrefs) { + // Normal mode + this.runTest(name, callback, cleanupFunction, extraPrefs, { + runInPrivateWindow: false, + }); + + // Private mode + this.runTest(name, callback, cleanupFunction, extraPrefs, { + runInPrivateWindow: true, + }); + }, + + runTest( + name, + callback, + cleanupFunction, + extraPrefs, + { runInPrivateWindow = false, runInSecureContext = false } = {} + ) { + DynamicFPIHelper.runTest( + name, + callback, + cleanupFunction, + extraPrefs, + runInPrivateWindow, + { runInSecureContext } + ); + }, + + runPartitioningTestInNormalAndPrivateMode( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + expectUnpartition = false + ) { + // Normal mode + this.runPartitioningTest( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + expectUnpartition, + false + ); + + // Private mode + this.runPartitioningTest( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + expectUnpartition, + true + ); + }, + + runPartitioningTest( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + expectUnpartition, + runInPrivateWindow = false + ) { + for (let variant of ["normal", "initial-aboutblank"]) { + for (let limitForeignContexts of [false, true]) { + this.runPartitioningTestInner( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + variant, + runInPrivateWindow, + limitForeignContexts, + expectUnpartition + ); + } + } + }, + + runPartitioningTestInner( + name, + testCategory, + getDataCallback, + addDataCallback, + cleanupFunction, + variant, + runInPrivateWindow, + limitForeignContexts, + expectUnpartition + ) { + add_task(async _ => { + info( + "Starting test `" + + name + + "' testCategory `" + + testCategory + + "' variant `" + + variant + + "' in a " + + (runInPrivateWindow ? "private" : "normal") + + " window " + + (limitForeignContexts ? "with" : "without") + + " limitForeignContexts to check that 2 tabs are correctly partititioned" + ); + + await SpecialPowers.flushPrefEnv(); + await setCookieBehaviorPref( + BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN, + runInPrivateWindow + ); + await SpecialPowers.pushPrefEnv({ + set: [ + ["dom.storage_access.enabled", true], + [ + "privacy.partition.always_partition_third_party_non_cookie_storage", + true, + ], + ["privacy.dynamic_firstparty.limitForeign", limitForeignContexts], + ["privacy.trackingprotection.enabled", false], + ["privacy.trackingprotection.pbmode.enabled", false], + ["privacy.trackingprotection.annotate_channels", true], + ["dom.security.https_first_pbm", false], + [ + "privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", + "not-tracking.example.com", + ], + ], + }); + + let win = window; + if (runInPrivateWindow) { + win = OpenBrowserWindow({ private: true }); + await TestUtils.topicObserved("browser-delayed-startup-finished"); + } + + info("Creating the first tab"); + let tab1 = BrowserTestUtils.addTab(win.gBrowser, TEST_TOP_PAGE); + win.gBrowser.selectedTab = tab1; + + let browser1 = win.gBrowser.getBrowserForTab(tab1); + await BrowserTestUtils.browserLoaded(browser1); + + info("Creating the second tab"); + let tab2 = BrowserTestUtils.addTab(win.gBrowser, TEST_TOP_PAGE_6); + win.gBrowser.selectedTab = tab2; + + let browser2 = win.gBrowser.getBrowserForTab(tab2); + await BrowserTestUtils.browserLoaded(browser2); + + info("Creating the third tab"); + let tab3 = BrowserTestUtils.addTab( + win.gBrowser, + TEST_4TH_PARTY_PARTITIONED_PAGE + ); + win.gBrowser.selectedTab = tab3; + + let browser3 = win.gBrowser.getBrowserForTab(tab3); + await BrowserTestUtils.browserLoaded(browser3); + + // Use the same URL as first tab to check partitioned data + info("Creating the forth tab"); + let tab4 = BrowserTestUtils.addTab(win.gBrowser, TEST_TOP_PAGE); + win.gBrowser.selectedTab = tab4; + + let browser4 = win.gBrowser.getBrowserForTab(tab4); + await BrowserTestUtils.browserLoaded(browser4); + + async function getDataFromThirdParty(browser, result) { + // Overwrite the special case here since third party cookies are not + // avilable when `limitForeignContexts` is enabled. + if (testCategory === "cookies" && limitForeignContexts) { + info("overwrite result to empty"); + result = ""; + } + + await SpecialPowers.spawn( + browser, + [ + { + page: TEST_4TH_PARTY_PARTITIONED_PAGE + "?variant=" + variant, + getDataCallback: getDataCallback.toString(), + result, + }, + ], + async obj => { + await new content.Promise(resolve => { + let ifr = content.document.createElement("iframe"); + ifr.onload = __ => { + info("Sending code to the 3rd party content"); + ifr.contentWindow.postMessage({ cb: obj.getDataCallback }, "*"); + }; + + content.addEventListener( + "message", + function msg(event) { + is( + event.data, + obj.result, + "Partitioned cookie jar has value: " + obj.result + ); + resolve(); + }, + { once: true } + ); + + content.document.body.appendChild(ifr); + ifr.src = obj.page; + }); + } + ); + } + + async function getDataFromFirstParty(browser, result) { + await SpecialPowers.spawn( + browser, + [ + { + getDataCallback: getDataCallback.toString(), + result, + variant, + }, + ], + async obj => { + let runnableStr = `(() => {return (${obj.getDataCallback});})();`; + let runnable = eval(runnableStr); // eslint-disable-line no-eval + let win = content; + if (obj.variant == "initial-aboutblank") { + let i = win.document.createElement("iframe"); + i.src = "about:blank"; + win.document.body.appendChild(i); + // override win to make it point to the initial about:blank window + win = i.contentWindow; + } + + let result = await runnable.call(content, win); + is( + result, + obj.result, + "Partitioned cookie jar is empty: " + obj.result + ); + } + ); + } + + info("Checking 3rd party has an empty cookie jar in first tab"); + await getDataFromThirdParty(browser1, ""); + + info("Checking 3rd party has an empty cookie jar in second tab"); + await getDataFromThirdParty(browser2, ""); + + info("Checking first party has an empty cookie jar in third tab"); + await getDataFromFirstParty(browser3, ""); + + info("Checking 3rd party has an empty cookie jar in forth tab"); + await getDataFromThirdParty(browser4, ""); + + async function createDataInThirdParty(browser, value) { + await SpecialPowers.spawn( + browser, + [ + { + page: TEST_4TH_PARTY_PARTITIONED_PAGE + "?variant=" + variant, + addDataCallback: addDataCallback.toString(), + value, + }, + ], + async obj => { + await new content.Promise(resolve => { + let ifr = content.document.getElementsByTagName("iframe")[0]; + content.addEventListener( + "message", + function msg(event) { + ok(event.data, "Data created"); + resolve(); + }, + { once: true } + ); + + ifr.contentWindow.postMessage( + { + cb: obj.addDataCallback, + value: obj.value, + }, + "*" + ); + }); + } + ); + } + + async function createDataInFirstParty(browser, value) { + await SpecialPowers.spawn( + browser, + [ + { + addDataCallback: addDataCallback.toString(), + value, + variant, + }, + ], + async obj => { + let runnableStr = `(() => {return (${obj.addDataCallback});})();`; + let runnable = eval(runnableStr); // eslint-disable-line no-eval + let win = content; + if (obj.variant == "initial-aboutblank") { + let i = win.document.createElement("iframe"); + i.src = "about:blank"; + win.document.body.appendChild(i); + // override win to make it point to the initial about:blank window + win = i.contentWindow; + } + + let result = await runnable.call(content, win, obj.value); + ok(result, "Data created"); + } + ); + } + + info("Creating data in the first tab"); + await createDataInThirdParty(browser1, "A"); + + info("Creating data in the second tab"); + await createDataInThirdParty(browser2, "B"); + + // Before writing browser4, check data written by browser1 + info("First tab should still have just 'A'"); + await getDataFromThirdParty(browser1, "A"); + info("Forth tab should still have just 'A'"); + await getDataFromThirdParty(browser4, "A"); + + // Ensure to create data in the forth tab before the third tab, + // otherwise cookie will be written successfully due to prior cookie + // of the base domain exists. + info("Creating data in the forth tab"); + await createDataInThirdParty(browser4, "D"); + + info("Creating data in the third tab"); + await createDataInFirstParty(browser3, "C"); + + // read all tabs + info("First tab should be changed to 'D'"); + await getDataFromThirdParty(browser1, "D"); + + info("Second tab should still have just 'B'"); + await getDataFromThirdParty(browser2, "B"); + + info("Third tab should still have just 'C'"); + await getDataFromFirstParty(browser3, "C"); + + info("Forth tab should still have just 'D'"); + await getDataFromThirdParty(browser4, "D"); + + async function setStorageAccessForThirdParty(browser) { + info(`Setting permission for ${browser.currentURI.spec}`); + let type = "3rdPartyStorage^http://not-tracking.example.com"; + let permission = Services.perms.ALLOW_ACTION; + let expireType = Services.perms.EXPIRE_SESSION; + Services.perms.addFromPrincipal( + browser.contentPrincipal, + type, + permission, + expireType, + 0 + ); + // Wait for permission to be set successfully + let originAttributes = runInPrivateWindow + ? { privateBrowsingId: 1 } + : {}; + await new Promise(resolve => { + let id = setInterval(async _ => { + if ( + await SpecialPowers.testPermission(type, permission, { + url: browser.currentURI.spec, + originAttributes, + }) + ) { + clearInterval(id); + resolve(); + } + }, 0); + }); + } + + if (!expectUnpartition) { + info("Setting Storage access for third parties"); + + await setStorageAccessForThirdParty(browser1); + await setStorageAccessForThirdParty(browser2); + await setStorageAccessForThirdParty(browser3); + await setStorageAccessForThirdParty(browser4); + + info("Done setting Storage access for third parties"); + + // read all tabs + info("First tab should still have just 'D'"); + await getDataFromThirdParty(browser1, "D"); + + info("Second tab should still have just 'B'"); + await getDataFromThirdParty(browser2, "B"); + + info("Third tab should still have just 'C'"); + await getDataFromFirstParty(browser3, "C"); + + info("Forth tab should still have just 'D'"); + await getDataFromThirdParty(browser4, "D"); + } + + info("Done checking departitioned state"); + + info("Removing the tabs"); + BrowserTestUtils.removeTab(tab1); + BrowserTestUtils.removeTab(tab2); + BrowserTestUtils.removeTab(tab3); + BrowserTestUtils.removeTab(tab4); + + if (runInPrivateWindow) { + win.close(); + } + }); + + add_task(async _ => { + info("Cleaning up."); + if (cleanupFunction) { + await cleanupFunction(); + } + + // While running these tests we typically do not have enough idle time to do + // GC reliably, so force it here. + /* import-globals-from antitracking_head.js */ + forceGC(); + }); + }, +}; |