summaryrefslogtreecommitdiffstats
path: root/toolkit/components/cleardata/tests/browser
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/cleardata/tests/browser
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/cleardata/tests/browser')
-rw-r--r--toolkit/components/cleardata/tests/browser/browser.toml29
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_auth_tokens.js108
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_css_cache.js129
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_image_cache.js219
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_preflight_cache.js166
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_quota.js318
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_serviceworkers.js287
-rw-r--r--toolkit/components/cleardata/tests/browser/browser_sessionStorage.js235
-rw-r--r--toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs38
-rw-r--r--toolkit/components/cleardata/tests/browser/file_css_cache.css3
-rw-r--r--toolkit/components/cleardata/tests/browser/file_css_cache.html6
-rw-r--r--toolkit/components/cleardata/tests/browser/file_image_cache.html7
-rw-r--r--toolkit/components/cleardata/tests/browser/file_image_cache.jpgbin0 -> 361 bytes
-rw-r--r--toolkit/components/cleardata/tests/browser/worker.js1
14 files changed, 1546 insertions, 0 deletions
diff --git a/toolkit/components/cleardata/tests/browser/browser.toml b/toolkit/components/cleardata/tests/browser/browser.toml
new file mode 100644
index 0000000000..16ecffce02
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser.toml
@@ -0,0 +1,29 @@
+[DEFAULT]
+
+["browser_auth_tokens.js"]
+
+["browser_css_cache.js"]
+https_first_disabled = true
+support-files = [
+ "file_css_cache.css",
+ "file_css_cache.html"
+]
+
+["browser_image_cache.js"]
+https_first_disabled = true
+support-files = [
+ "file_image_cache.html",
+ "file_image_cache.jpg"
+]
+
+["browser_preflight_cache.js"]
+https_first_disabled = true
+support-files = ["file_cors_preflight.sjs"]
+
+["browser_quota.js"]
+support-files = ["worker.js"]
+
+["browser_serviceworkers.js"]
+
+["browser_sessionStorage.js"]
+https_first_disabled = true
diff --git a/toolkit/components/cleardata/tests/browser/browser_auth_tokens.js b/toolkit/components/cleardata/tests/browser/browser_auth_tokens.js
new file mode 100644
index 0000000000..e62c8b4ac4
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_auth_tokens.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests for AuthTokensCleaner.
+ */
+
+const TEST_SECRET = "secret";
+const TEST_PRINCIPAL =
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://example.com"
+ );
+const TEST_CLEAR_DATA_FLAGS = Services.clearData.CLEAR_AUTH_TOKENS;
+
+const pk11db = Cc["@mozilla.org/security/pk11tokendb;1"].getService(
+ Ci.nsIPK11TokenDB
+);
+
+const { LoginTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/LoginTestUtils.sys.mjs"
+);
+
+function testLoggedIn(isLoggedIn) {
+ Assert.equal(
+ pk11db.getInternalKeyToken().isLoggedIn(),
+ isLoggedIn,
+ `Should ${isLoggedIn ? "" : "not "}be logged in`
+ );
+ pk11db.getInternalKeyToken().isLoggedIn();
+}
+
+function clearData({ deleteBy = "all", hasUserInput = false } = {}) {
+ return new Promise(resolve => {
+ if (deleteBy == "principal") {
+ Services.clearData.deleteDataFromPrincipal(
+ TEST_PRINCIPAL,
+ hasUserInput,
+ TEST_CLEAR_DATA_FLAGS,
+ value => {
+ Assert.equal(value, 0);
+ resolve();
+ }
+ );
+ } else if (deleteBy == "baseDomain") {
+ Services.clearData.deleteDataFromBaseDomain(
+ TEST_PRINCIPAL.baseDomain,
+ hasUserInput,
+ TEST_CLEAR_DATA_FLAGS,
+ value => {
+ Assert.equal(value, 0);
+ resolve();
+ }
+ );
+ } else {
+ Services.clearData.deleteData(TEST_CLEAR_DATA_FLAGS, value => {
+ Assert.equal(value, 0);
+ resolve();
+ });
+ }
+ });
+}
+
+function runTest({ deleteBy, hasUserInput }) {
+ testLoggedIn(false);
+
+ info("Setup primary password and login");
+ LoginTestUtils.primaryPassword.enable(true);
+ testLoggedIn(true);
+
+ info(
+ `Clear AuthTokensCleaner data for ${deleteBy}, hasUserInput: ${hasUserInput}`
+ );
+ clearData({ deleteBy, hasUserInput });
+
+ // The auth tokens cleaner cannot delete by principal or baseDomain
+ // (yet). If this method is called, it will check whether the used requested
+ // the clearing. If the user requested clearing, it will over-clear (clear
+ // all data). If the request didn't come from the user, for example from the
+ // PurgeTrackerService, it will not clear anything to avoid clearing storage
+ // unrelated to the baseDomain or principal.
+ let isCleared = deleteBy == "all" || hasUserInput;
+ testLoggedIn(!isCleared);
+
+ // Cleanup
+ let sdr = Cc["@mozilla.org/security/sdr;1"].getService(
+ Ci.nsISecretDecoderRing
+ );
+ sdr.logoutAndTeardown();
+ LoginTestUtils.primaryPassword.disable();
+}
+
+add_task(async function test_deleteAll() {
+ runTest({ deleteBy: "all" });
+});
+
+add_task(async function test_deleteByPrincipal() {
+ for (let hasUserInput of [false, true]) {
+ runTest({ deleteBy: "principal", hasUserInput });
+ }
+});
+
+add_task(async function test_deleteByBaseDomain() {
+ for (let hasUserInput of [false, true]) {
+ runTest({ deleteBy: "baseDomain", hasUserInput });
+ }
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_css_cache.js b/toolkit/components/cleardata/tests/browser/browser_css_cache.js
new file mode 100644
index 0000000000..47088e5011
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_css_cache.js
@@ -0,0 +1,129 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const BASE_DOMAIN_A = "example.com";
+const ORIGIN_A = `https://${BASE_DOMAIN_A}`;
+const ORIGIN_A_HTTP = `http://${BASE_DOMAIN_A}`;
+const ORIGIN_A_SUB = `https://test1.${BASE_DOMAIN_A}`;
+
+const BASE_DOMAIN_B = "example.org";
+const ORIGIN_B = `https://${BASE_DOMAIN_B}`;
+const ORIGIN_B_HTTP = `http://${BASE_DOMAIN_B}`;
+const ORIGIN_B_SUB = `https://test1.${BASE_DOMAIN_B}`;
+
+const TEST_ROOT_DIR = getRootDirectory(gTestPath);
+
+// Stylesheets are cached per process, so we need to keep tabs open for the
+// duration of a test.
+let tabs = {};
+
+function getTestURLForOrigin(origin) {
+ return (
+ TEST_ROOT_DIR.replace("chrome://mochitests/content", origin) +
+ "file_css_cache.html"
+ );
+}
+
+async function testCached(origin, isCached) {
+ let url = getTestURLForOrigin(origin);
+
+ let numParsed;
+
+ let tab = tabs[origin];
+ let loadedPromise;
+ if (!tab) {
+ info("Creating new tab for " + url);
+ tab = BrowserTestUtils.addTab(gBrowser, url);
+ loadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ tabs[origin] = tab;
+ } else {
+ loadedPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ tab.linkedBrowser.reload();
+ }
+ await loadedPromise;
+
+ numParsed = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ return SpecialPowers.getDOMWindowUtils(content).parsedStyleSheets;
+ });
+
+ // Stylesheets is cached if numParsed is 0.
+ is(!numParsed, isCached, `${origin} is${isCached ? " " : " not "}cached`);
+}
+
+async function addTestTabs() {
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, false);
+ await testCached(ORIGIN_A_HTTP, false);
+ await testCached(ORIGIN_B, false);
+ await testCached(ORIGIN_B_SUB, false);
+ await testCached(ORIGIN_B_HTTP, false);
+ // Test that the cache has been populated.
+ await testCached(ORIGIN_A, true);
+ await testCached(ORIGIN_A_SUB, true);
+ await testCached(ORIGIN_A_HTTP, true);
+ await testCached(ORIGIN_B, true);
+ await testCached(ORIGIN_B_SUB, true);
+ await testCached(ORIGIN_B_HTTP, true);
+}
+
+async function cleanupTestTabs() {
+ Object.values(tabs).forEach(BrowserTestUtils.removeTab);
+ tabs = {};
+}
+
+add_task(async function test_deleteByPrincipal() {
+ await addTestTabs();
+
+ // Clear data for content principal of A
+ info("Clearing cache for principal " + ORIGIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(ORIGIN_A),
+ false,
+ Ci.nsIClearDataService.CLEAR_CSS_CACHE,
+ resolve
+ );
+ });
+
+ // Only the cache entry for ORIGIN_A should have been cleared.
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, true);
+ await testCached(ORIGIN_A_HTTP, true);
+ await testCached(ORIGIN_B, true);
+ await testCached(ORIGIN_B_SUB, true);
+ await testCached(ORIGIN_B_HTTP, true);
+
+ // Cleanup
+ cleanupTestTabs();
+ ChromeUtils.clearStyleSheetCache();
+});
+
+add_task(async function test_deleteByBaseDomain() {
+ await addTestTabs();
+
+ // Clear data for base domain of A.
+ info("Clearing cache for base domain " + BASE_DOMAIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ BASE_DOMAIN_A,
+ false,
+ Ci.nsIClearDataService.CLEAR_CSS_CACHE,
+ resolve
+ );
+ });
+
+ // All entries for A should have been cleared.
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, false);
+ await testCached(ORIGIN_A_HTTP, false);
+ // Entries for B should still exist.
+ await testCached(ORIGIN_B, true);
+ await testCached(ORIGIN_B_SUB, true);
+ await testCached(ORIGIN_B_HTTP, true);
+
+ // Cleanup
+ cleanupTestTabs();
+ ChromeUtils.clearStyleSheetCache();
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_image_cache.js b/toolkit/components/cleardata/tests/browser/browser_image_cache.js
new file mode 100644
index 0000000000..d7116d2502
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_image_cache.js
@@ -0,0 +1,219 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const BASE_DOMAIN_A = "example.com";
+const ORIGIN_A = `https://${BASE_DOMAIN_A}`;
+const ORIGIN_A_HTTP = `http://${BASE_DOMAIN_A}`;
+const ORIGIN_A_SUB = `https://test1.${BASE_DOMAIN_A}`;
+
+const BASE_DOMAIN_B = "example.org";
+const ORIGIN_B = `https://${BASE_DOMAIN_B}`;
+const ORIGIN_B_HTTP = `http://${BASE_DOMAIN_B}`;
+const ORIGIN_B_SUB = `https://test1.${BASE_DOMAIN_B}`;
+
+const TEST_ROOT_DIR = getRootDirectory(gTestPath);
+
+// Images are cached per process, so we need to keep tabs open for the
+// duration of a test.
+let originToTabs = {};
+
+function getTestURLForOrigin(origin) {
+ return TEST_ROOT_DIR.replace("chrome://mochitests/content", origin);
+}
+
+async function testCached(origin, isCached, testPartitioned = false) {
+ let tabs = originToTabs[origin];
+
+ for (let tab of tabs) {
+ // For the partition test we inspect the cache state of the iframe.
+ let browsingContext = testPartitioned
+ ? tab.linkedBrowser.browsingContext.children[0]
+ : tab.linkedBrowser.browsingContext;
+ let actualCached = await SpecialPowers.spawn(browsingContext, [], () => {
+ let imgUrl = content.document.querySelector("img").src;
+ let imageCache = SpecialPowers.Cc[
+ "@mozilla.org/image/tools;1"
+ ].getService(Ci.imgITools);
+ let uri = SpecialPowers.Services.io.newURI(imgUrl);
+ let properties = imageCache
+ .getImgCacheForDocument(content.document)
+ .findEntryProperties(uri, content.document);
+ return !!properties;
+ });
+
+ let msg = `${origin}${isCached ? " " : " not "}cached`;
+ if (testPartitioned) {
+ msg = "Partitioned under " + msg;
+ }
+
+ is(actualCached, isCached, msg);
+ }
+}
+
+/**
+ * Creates tabs and loads images in first party and third party context.
+ * @returns {Promise} - Promise which resolves once all tabs are initialized,
+ * {@link originToTabs} is populated and (sub-)resources have loaded.
+ */
+function addTestTabs() {
+ // Adding two tabs for ORIGIN_A so we can test clearing for a principal
+ // cross-process (non-fission).
+ let promises = [
+ [ORIGIN_A, ORIGIN_B],
+ [ORIGIN_A, ORIGIN_B],
+ [ORIGIN_A_SUB, ORIGIN_B_SUB],
+ [ORIGIN_A_HTTP, ORIGIN_B_HTTP],
+ [ORIGIN_B, ORIGIN_A],
+ [ORIGIN_B_SUB, ORIGIN_A_SUB],
+ [ORIGIN_B_HTTP, ORIGIN_A_HTTP],
+ ].map(async ([firstParty, thirdParty]) => {
+ let urlFirstParty =
+ getTestURLForOrigin(firstParty) + "file_image_cache.html";
+ let urlThirdParty =
+ getTestURLForOrigin(thirdParty) + "file_image_cache.html";
+
+ info("Creating new tab for " + urlFirstParty);
+ let tab = BrowserTestUtils.addTab(gBrowser, urlFirstParty);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("Creating iframe for " + urlThirdParty);
+ await SpecialPowers.spawn(tab.linkedBrowser, [urlThirdParty], async url => {
+ let iframe = content.document.createElement("iframe");
+ iframe.src = url;
+
+ let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load", false);
+ content.document.body.appendChild(iframe);
+ await loadPromise;
+ });
+
+ let tabs = originToTabs[firstParty];
+ if (!tabs) {
+ tabs = [];
+ originToTabs[firstParty] = tabs;
+ }
+ tabs.push(tab);
+ });
+
+ return Promise.all(promises);
+}
+
+function cleanup() {
+ Object.values(originToTabs).flat().forEach(BrowserTestUtils.removeTab);
+ originToTabs = {};
+ let imageCache = Cc["@mozilla.org/image/tools;1"]
+ .getService(Ci.imgITools)
+ .getImgCacheForDocument(null);
+ imageCache.clearCache(false);
+ imageCache.clearCache(true);
+}
+
+add_setup(function () {
+ cleanup();
+});
+
+add_task(async function test_deleteByPrincipal() {
+ await addTestTabs();
+
+ // Clear data for content principal of A
+ info("Clearing cache for principal " + ORIGIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(ORIGIN_A),
+ false,
+ Ci.nsIClearDataService.CLEAR_IMAGE_CACHE,
+ resolve
+ );
+ });
+
+ // Only the cache entry for ORIGIN_A should have been cleared.
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, true);
+ await testCached(ORIGIN_A_HTTP, true);
+ await testCached(ORIGIN_B, true);
+ await testCached(ORIGIN_B_SUB, true);
+ await testCached(ORIGIN_B_HTTP, true);
+
+ // No partitioned cache should have been cleared.
+ await testCached(ORIGIN_A, true, true);
+ await testCached(ORIGIN_A_SUB, true, true);
+ await testCached(ORIGIN_A_HTTP, true, true);
+ // TODO: ImageCacheCleaner deleteByPrincipal does not look at the cache key's
+ // isolationKey and thus over-clears. In this case it clears cache for A
+ // partitioned under B, even though the test principal does not set a
+ // partitionKey.
+ // See Bug 1713088.
+ await testCached(ORIGIN_B, false, true);
+ await testCached(ORIGIN_B_SUB, true, true);
+ await testCached(ORIGIN_B_HTTP, true, true);
+
+ cleanup();
+});
+
+add_task(async function test_deleteByBaseDomain() {
+ await addTestTabs();
+
+ // Clear data for base domain of A.
+ info("Clearing cache for base domain " + BASE_DOMAIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ BASE_DOMAIN_A,
+ false,
+ Ci.nsIClearDataService.CLEAR_IMAGE_CACHE,
+ resolve
+ );
+ });
+
+ // All entries for A should have been cleared.
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, false);
+ await testCached(ORIGIN_A_HTTP, false);
+ // Entries for B should still exist.
+ await testCached(ORIGIN_B, true);
+ await testCached(ORIGIN_B_SUB, true);
+ await testCached(ORIGIN_B_HTTP, true);
+
+ // All partitioned entries for B under A should have been cleared.
+ await testCached(ORIGIN_A, false, true);
+ await testCached(ORIGIN_A_SUB, false, true);
+ await testCached(ORIGIN_A_HTTP, false, true);
+
+ // All partitioned entries of A under B should have been cleared.
+ await testCached(ORIGIN_B, false, true);
+ await testCached(ORIGIN_B_SUB, false, true);
+ await testCached(ORIGIN_B_HTTP, false, true);
+
+ cleanup();
+});
+
+add_task(async function test_deleteAll() {
+ await addTestTabs();
+
+ // Clear the whole image cache across processes.
+ info("Clearing cache for base domain " + BASE_DOMAIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_IMAGE_CACHE,
+ resolve
+ );
+ });
+
+ // All entries should have been cleared.
+ await testCached(ORIGIN_A, false);
+ await testCached(ORIGIN_A_SUB, false);
+ await testCached(ORIGIN_A_HTTP, false);
+ await testCached(ORIGIN_B, false);
+ await testCached(ORIGIN_B_SUB, false);
+ await testCached(ORIGIN_B_HTTP, false);
+
+ // All partitioned entries should have been cleared.
+ await testCached(ORIGIN_A, false, true);
+ await testCached(ORIGIN_A_SUB, false, true);
+ await testCached(ORIGIN_A_HTTP, false, true);
+ await testCached(ORIGIN_B, false, true);
+ await testCached(ORIGIN_B_SUB, false, true);
+ await testCached(ORIGIN_B_HTTP, false, true);
+
+ cleanup();
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js b/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js
new file mode 100644
index 0000000000..d3eabb9e38
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_preflight_cache.js
@@ -0,0 +1,166 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { SiteDataTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/SiteDataTestUtils.sys.mjs"
+);
+
+const uuidGenerator = Services.uuid;
+
+const ORIGIN_A = "http://example.net";
+const ORIGIN_B = "http://example.org";
+
+const PREFLIGHT_URL_PATH =
+ "/browser/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs";
+
+const PREFLIGHT_URL_A = ORIGIN_A + PREFLIGHT_URL_PATH;
+const PREFLIGHT_URL_B = ORIGIN_B + PREFLIGHT_URL_PATH;
+
+function testPreflightCached(browser, url, token, isCached) {
+ return SpecialPowers.spawn(
+ browser,
+ [url, token, isCached],
+ async (url, token, isCached) => {
+ let response = await content.fetch(
+ new content.Request(`${url}?token=${token}`, {
+ mode: "cors",
+ method: "GET",
+ headers: [["x-test-header", "check"]],
+ })
+ );
+
+ let expected = isCached ? "0" : "1";
+ is(
+ await response.text(),
+ expected,
+ `Preflight cache for ${url} ${isCached ? "HIT" : "MISS"}.`
+ );
+ }
+ );
+}
+
+async function testDeleteAll(
+ clearDataFlag,
+ { deleteBy = "all", hasUserInput = false } = {}
+) {
+ await BrowserTestUtils.withNewTab("http://example.com", async browser => {
+ let token = uuidGenerator.generateUUID().toString();
+
+ // Populate the preflight cache.
+ await testPreflightCached(browser, PREFLIGHT_URL_A, token, false);
+ await testPreflightCached(browser, PREFLIGHT_URL_B, token, false);
+ // Cache should be populated.
+ await testPreflightCached(browser, PREFLIGHT_URL_A, token, true);
+ await testPreflightCached(browser, PREFLIGHT_URL_B, token, true);
+
+ await new Promise(resolve => {
+ if (deleteBy == "principal") {
+ Services.clearData.deleteDataFromPrincipal(
+ browser.contentPrincipal,
+ hasUserInput,
+ clearDataFlag,
+ value => {
+ Assert.equal(value, 0);
+ resolve();
+ }
+ );
+ } else if (deleteBy == "baseDomain") {
+ Services.clearData.deleteDataFromBaseDomain(
+ browser.contentPrincipal.baseDomain,
+ hasUserInput,
+ clearDataFlag,
+ value => {
+ Assert.equal(value, 0);
+ resolve();
+ }
+ );
+ } else {
+ Services.clearData.deleteData(clearDataFlag, value => {
+ Assert.equal(value, 0);
+ resolve();
+ });
+ }
+ });
+
+ // The preflight cache cleaner cannot delete by principal or baseDomain
+ // (Bug 1727141). If this method is called, it will check whether the used
+ // requested the clearing. If the user requested clearing, it will
+ // over-clear (clear all data). If the request didn't come from the user,
+ // for example from the PurgeTrackerService, it will not clear anything to
+ // avoid clearing storage unrelated to the baseDomain or principal.
+ let clearedAll = deleteBy == "all" || hasUserInput;
+
+ // Cache should be cleared.
+ await testPreflightCached(browser, PREFLIGHT_URL_A, token, !clearedAll);
+ await testPreflightCached(browser, PREFLIGHT_URL_B, token, !clearedAll);
+ });
+
+ SiteDataTestUtils.clear();
+}
+
+add_task(async function test_deleteAll() {
+ // The cleaner should be called when we target all cleaners, all cache
+ // cleaners, or just the preflight cache.
+ let { CLEAR_ALL, CLEAR_ALL_CACHES, CLEAR_PREFLIGHT_CACHE } =
+ Ci.nsIClearDataService;
+
+ for (let flag of [CLEAR_ALL, CLEAR_ALL_CACHES, CLEAR_PREFLIGHT_CACHE]) {
+ await testDeleteAll(flag);
+ }
+});
+
+add_task(async function test_deleteByPrincipal() {
+ // The cleaner should be called when we target all cleaners, all cache
+ // cleaners, or just the preflight cache.
+ let { CLEAR_ALL, CLEAR_ALL_CACHES, CLEAR_PREFLIGHT_CACHE } =
+ Ci.nsIClearDataService;
+
+ for (let flag of [CLEAR_ALL, CLEAR_ALL_CACHES, CLEAR_PREFLIGHT_CACHE]) {
+ for (let hasUserInput of [true, false]) {
+ await testDeleteAll(flag, { deleteBy: "principal", hasUserInput });
+ }
+ }
+});
+
+add_task(async function test_deletePrivateBrowsingCache() {
+ async function deletePrivateBrowsingCache(token) {
+ const browser = await BrowserTestUtils.openNewBrowserWindow({
+ private: true,
+ });
+
+ const tab = (browser.gBrowser.selectedTab = BrowserTestUtils.addTab(
+ browser.gBrowser,
+ "http://example.com"
+ ));
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ // Populate the preflight cache and make sure it isn't populated right now
+ await testPreflightCached(tab.linkedBrowser, PREFLIGHT_URL_A, token, false);
+ await testPreflightCached(tab.linkedBrowser, PREFLIGHT_URL_B, token, false);
+ // Cache should be populated.
+ await testPreflightCached(tab.linkedBrowser, PREFLIGHT_URL_A, token, true);
+ await testPreflightCached(tab.linkedBrowser, PREFLIGHT_URL_B, token, true);
+
+ await browser.close();
+ }
+
+ // Disable https_first mode to not upgrade the connection of the main page
+ // and get "Blocked loading mixed active content" for the CORS request
+ // making this test case fail. Another solution would be to change all URLs
+ // to https.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.security.https_first_pbm", false]],
+ });
+
+ let token = uuidGenerator.generateUUID().toString();
+
+ // Make sure the CORS preflight cache is cleared between two private
+ // browsing sessions. Calling this function twice to see if the cache isn't
+ // populated anymore after the first call.
+ await deletePrivateBrowsingCache(token);
+ await deletePrivateBrowsingCache(token);
+
+ await SpecialPowers.clearUserPref("dom.security.https_first_pbm");
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_quota.js b/toolkit/components/cleardata/tests/browser/browser_quota.js
new file mode 100644
index 0000000000..94ff223773
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_quota.js
@@ -0,0 +1,318 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// This function adds the quota storage by simpleDB (one of quota clients
+// managed by the QuotaManager). In this function, a directory
+// ${profile}/storage/default/${origin}/sdb/ and a file inside are expected to
+// be added.
+async function addQuotaStorage(principal) {
+ let connection = Cc["@mozilla.org/dom/sdb-connection;1"].createInstance(
+ Ci.nsISDBConnection
+ );
+
+ connection.init(principal);
+
+ await new Promise((aResolve, aReject) => {
+ let request = connection.open("db");
+ request.callback = request => {
+ if (request.resultCode == Cr.NS_OK) {
+ aResolve();
+ } else {
+ aReject(request.resultCode);
+ }
+ };
+ });
+
+ await new Promise((aResolve, aReject) => {
+ let request = connection.write(new ArrayBuffer(1));
+ request.callback = request => {
+ if (request.resultCode == Cr.NS_OK) {
+ aResolve();
+ } else {
+ aReject(request.resultCode);
+ }
+ };
+ });
+}
+
+function getPrincipal(url, attr = {}) {
+ let uri = Services.io.newURI(url);
+ let ssm = Services.scriptSecurityManager;
+
+ return ssm.createContentPrincipal(uri, attr);
+}
+
+function getProfileDir() {
+ let directoryService = Services.dirsvc;
+
+ return directoryService.get("ProfD", Ci.nsIFile);
+}
+
+function getRelativeFile(relativePath) {
+ let profileDir = getProfileDir();
+
+ let file = profileDir.clone();
+ relativePath.split("/").forEach(function (component) {
+ file.append(component);
+ });
+
+ return file;
+}
+
+function getPath(origin) {
+ // Sanitizing
+ let regex = /[:\/]/g;
+ return "storage/default/" + origin.replace(regex, "+");
+}
+
+// This function checks if the origin has the quota storage by checking whether
+// the origin directory of that origin exists or not.
+function hasQuotaStorage(origin, attr) {
+ let path = getPath(origin);
+ if (attr) {
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI(origin),
+ attr
+ );
+ path += principal.originSuffix;
+ }
+
+ let file = getRelativeFile(path);
+ return file.exists();
+}
+
+async function runTest(sites, deleteDataFunc) {
+ info(`Adding quota storage`);
+ for (let site of sites) {
+ const principal = getPrincipal(site.origin, site.originAttributes);
+ await addQuotaStorage(principal);
+ }
+
+ info(`Verifying ${deleteDataFunc.name}`);
+ let site;
+ while ((site = sites.shift())) {
+ await new Promise(aResolve => {
+ deleteDataFunc(...site.args, value => {
+ Assert.equal(value, 0);
+ aResolve();
+ });
+ });
+
+ ok(
+ !hasQuotaStorage(site.origin, site.originAttributes),
+ `${site.origin} has no quota storage`
+ );
+ sites.forEach(remainSite =>
+ ok(
+ hasQuotaStorage(remainSite.origin, remainSite.originAttributes),
+ `${remainSite.origin} has quota storage`
+ )
+ );
+ }
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.quotaManager.testing", true],
+ ["dom.simpleDB.enabled", true],
+ ],
+ });
+});
+
+const ORG_DOMAIN = "example.org";
+const ORG_DOMAIN_SUB = `test.${ORG_DOMAIN}`;
+const ORG_ORIGIN = `https://${ORG_DOMAIN}`;
+const ORG_ORIGIN_SUB = `https://${ORG_DOMAIN_SUB}`;
+const COM_DOMAIN = "example.com";
+const COM_ORIGIN = `https://${COM_DOMAIN}`;
+const LH_DOMAIN = "localhost";
+const FOO_DOMAIN = "foo.com";
+
+add_task(async function test_deleteFromHost() {
+ const sites = [
+ {
+ args: [ORG_DOMAIN, true, Ci.nsIClearDataService.CLEAR_DOM_QUOTA],
+ origin: ORG_ORIGIN,
+ },
+ {
+ args: [COM_DOMAIN, true, Ci.nsIClearDataService.CLEAR_DOM_QUOTA],
+ origin: COM_ORIGIN,
+ },
+ {
+ args: [LH_DOMAIN, true, Ci.nsIClearDataService.CLEAR_DOM_QUOTA],
+ origin: `http://${LH_DOMAIN}:8000`,
+ },
+ {
+ args: [FOO_DOMAIN, true, Ci.nsIClearDataService.CLEAR_DOM_QUOTA],
+ origin: `http://${FOO_DOMAIN}`,
+ originAttributes: { userContextId: 1 },
+ },
+ ];
+
+ await runTest(sites, Services.clearData.deleteDataFromHost);
+});
+
+add_task(async function test_deleteFromPrincipal() {
+ const sites = [
+ {
+ args: [
+ getPrincipal(ORG_ORIGIN),
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ ],
+ origin: ORG_ORIGIN,
+ },
+ {
+ args: [
+ getPrincipal(COM_ORIGIN),
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ ],
+ origin: COM_ORIGIN,
+ },
+ ];
+
+ await runTest(sites, Services.clearData.deleteDataFromPrincipal);
+});
+
+add_task(async function test_deleteFromOriginAttributes() {
+ const ORG_OA = { userContextId: 1 };
+ const COM_OA = { userContextId: 2 };
+ const sites = [
+ {
+ args: [ORG_OA],
+ origin: ORG_ORIGIN,
+ originAttributes: ORG_OA,
+ },
+ {
+ args: [COM_OA],
+ origin: COM_ORIGIN,
+ originAttributes: COM_OA,
+ },
+ ];
+
+ await runTest(
+ sites,
+ Services.clearData.deleteDataFromOriginAttributesPattern
+ );
+});
+
+add_task(async function test_deleteAll() {
+ info(`Adding quota storage`);
+ await addQuotaStorage(getPrincipal(ORG_ORIGIN));
+ await addQuotaStorage(getPrincipal(COM_ORIGIN));
+
+ info(`Verifying deleteData`);
+ await new Promise(aResolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ ok(!hasQuotaStorage(ORG_ORIGIN), `${ORG_ORIGIN} has no quota storage`);
+ ok(!hasQuotaStorage(COM_ORIGIN), `${COM_ORIGIN} has no quota storage`);
+});
+
+add_task(async function test_deleteSubdomain() {
+ const ANOTHER_ORIGIN = `https://wwww.${ORG_DOMAIN}`;
+ info(`Adding quota storage`);
+ await addQuotaStorage(getPrincipal(ORG_ORIGIN));
+ await addQuotaStorage(getPrincipal(ANOTHER_ORIGIN));
+
+ info(`Verifying deleteDataFromHost for subdomain`);
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromHost(
+ ORG_DOMAIN,
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ ok(!hasQuotaStorage(ORG_ORIGIN), `${ORG_ORIGIN} has no quota storage`);
+ ok(!hasQuotaStorage(COM_ORIGIN), `${ANOTHER_ORIGIN} has no quota storage`);
+});
+
+function getOAWithPartitionKey(topLevelBaseDomain, originAttributes = {}) {
+ if (!topLevelBaseDomain) {
+ return originAttributes;
+ }
+ return {
+ ...originAttributes,
+ partitionKey: `(https,${topLevelBaseDomain})`,
+ };
+}
+
+add_task(async function test_deleteBaseDomain() {
+ info("Adding quota storage");
+ await addQuotaStorage(getPrincipal(ORG_ORIGIN));
+ await addQuotaStorage(getPrincipal(ORG_ORIGIN_SUB));
+ await addQuotaStorage(getPrincipal(COM_ORIGIN));
+
+ info("Adding partitioned quota storage");
+ // Partitioned
+ await addQuotaStorage(
+ getPrincipal(COM_ORIGIN, getOAWithPartitionKey(ORG_DOMAIN))
+ );
+ await addQuotaStorage(
+ getPrincipal(COM_ORIGIN, getOAWithPartitionKey(FOO_DOMAIN))
+ );
+ await addQuotaStorage(
+ getPrincipal(ORG_ORIGIN, getOAWithPartitionKey(COM_DOMAIN))
+ );
+
+ info(`Verifying deleteDataFromBaseDomain`);
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ ORG_DOMAIN,
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+
+ ok(!hasQuotaStorage(ORG_ORIGIN), `${ORG_ORIGIN} has no quota storage`);
+ ok(
+ !hasQuotaStorage(ORG_ORIGIN_SUB),
+ `${ORG_ORIGIN_SUB} has no quota storage`
+ );
+ ok(hasQuotaStorage(COM_ORIGIN), `${COM_ORIGIN} has quota storage`);
+
+ // Partitioned
+ ok(
+ !hasQuotaStorage(COM_ORIGIN, getOAWithPartitionKey(ORG_DOMAIN)),
+ `${COM_ORIGIN} under ${ORG_DOMAIN} has no quota storage`
+ );
+ ok(
+ hasQuotaStorage(COM_ORIGIN, getOAWithPartitionKey(FOO_DOMAIN)),
+ `${COM_ORIGIN} under ${FOO_DOMAIN} has quota storage`
+ );
+ ok(
+ !hasQuotaStorage(ORG_ORIGIN, getOAWithPartitionKey(COM_DOMAIN)),
+ `${ORG_ORIGIN} under ${COM_DOMAIN} has no quota storage`
+ );
+
+ // Cleanup
+ await new Promise(aResolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_serviceworkers.js b/toolkit/components/cleardata/tests/browser/browser_serviceworkers.js
new file mode 100644
index 0000000000..ad501aed70
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_serviceworkers.js
@@ -0,0 +1,287 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const { SiteDataTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/SiteDataTestUtils.sys.mjs"
+);
+
+async function addServiceWorker(origin) {
+ let swURL =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ "worker.js";
+
+ let registered = SiteDataTestUtils.promiseServiceWorkerRegistered(swURL);
+ await SiteDataTestUtils.addServiceWorker(swURL);
+ await registered;
+
+ ok(true, `${origin} has a service worker`);
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test_deleteFromHost() {
+ await addServiceWorker("https://example.com");
+ await addServiceWorker("https://example.org");
+ await addServiceWorker("https://test1.example.org");
+
+ let unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://example.com"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromHost(
+ "example.com",
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+ "example.com has no service worker"
+ );
+ ok(
+ SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+ "example.org has a service worker"
+ );
+ ok(
+ SiteDataTestUtils.hasServiceWorkers("https://test1.example.org"),
+ "test1.example.org has a service worker"
+ );
+
+ // Clearing the subdomain should not clear the base domain.
+ unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test1.example.org"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromHost(
+ "test1.example.org",
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.org"),
+ "test1.example.org has no service worker"
+ );
+ ok(
+ SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+ "example.org has a service worker"
+ );
+
+ unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://example.org"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromHost(
+ "example.org",
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+ "example.org has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+ "example.com has no service worker"
+ );
+});
+
+add_task(async function test_deleteFromBaseDomain() {
+ await addServiceWorker("https://example.com");
+ await addServiceWorker("https://test1.example.com");
+ await addServiceWorker("https://test2.example.com");
+ await addServiceWorker("https://example.org");
+
+ let unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://example.com"
+ );
+ let unregisteredSub1 = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test1.example.com"
+ );
+ let unregisteredSub2 = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test1.example.com"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ "example.com",
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await Promise.all([unregistered, unregisteredSub1, unregisteredSub2]);
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+ "example.com has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.com"),
+ "test1.example.com has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test2.example.com"),
+ "test2.example.com has no service worker"
+ );
+ ok(
+ SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+ "example.org has a service worker"
+ );
+
+ unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://example.org"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ "example.org",
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.org"),
+ "example.org has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://example.com"),
+ "example.com has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.com"),
+ "test1.example.com has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test2.example.com"),
+ "test2.example.com has no service worker"
+ );
+});
+
+add_task(async function test_deleteFromPrincipal() {
+ await addServiceWorker("https://test1.example.com");
+ await addServiceWorker("https://test1.example.org");
+
+ let unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test1.example.com"
+ );
+ let principal =
+ Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://test1.example.com/"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ principal,
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.com"),
+ "test1.example.com has no service worker"
+ );
+ ok(
+ SiteDataTestUtils.hasServiceWorkers("https://test1.example.org"),
+ "test1.example.org has a service worker"
+ );
+
+ unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test1.example.org"
+ );
+ principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
+ "https://test1.example.org/"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ principal,
+ true,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.org"),
+ "test1.example.org has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test1.example.com"),
+ "test1.example.com has no service worker"
+ );
+});
+
+add_task(async function test_deleteAll() {
+ await addServiceWorker("https://test2.example.com");
+ await addServiceWorker("https://test2.example.org");
+
+ let unregistered = SiteDataTestUtils.promiseServiceWorkerUnregistered(
+ "https://test2.example.com"
+ );
+ await new Promise(aResolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ value => {
+ Assert.equal(value, 0);
+ aResolve();
+ }
+ );
+ });
+ await unregistered;
+
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test2.example.com"),
+ "test2.example.com has no service worker"
+ );
+ ok(
+ !SiteDataTestUtils.hasServiceWorkers("https://test2.example.org"),
+ "test2.example.org has no service worker"
+ );
+
+ await SiteDataTestUtils.clear();
+});
diff --git a/toolkit/components/cleardata/tests/browser/browser_sessionStorage.js b/toolkit/components/cleardata/tests/browser/browser_sessionStorage.js
new file mode 100644
index 0000000000..013ae0fa92
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/browser_sessionStorage.js
@@ -0,0 +1,235 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const BASE_DOMAIN_A = "example.com";
+const ORIGIN_A = `https://${BASE_DOMAIN_A}`;
+const ORIGIN_A_HTTP = `http://${BASE_DOMAIN_A}`;
+const ORIGIN_A_SUB = `https://test1.${BASE_DOMAIN_A}`;
+
+const BASE_DOMAIN_B = "example.org";
+const ORIGIN_B = `https://${BASE_DOMAIN_B}`;
+const ORIGIN_B_HTTP = `http://${BASE_DOMAIN_B}`;
+const ORIGIN_B_SUB = `https://test1.${BASE_DOMAIN_B}`;
+
+const TEST_ROOT_DIR = getRootDirectory(gTestPath);
+
+// Session storage is only valid for the lifetime of a tab, so we need to keep
+// tabs open for the duration of a test.
+let originToTabs = {};
+
+function getTestURLForOrigin(origin) {
+ return TEST_ROOT_DIR.replace("chrome://mochitests/content", origin);
+}
+
+function getTestEntryName(origin, partitioned) {
+ return `${origin}${partitioned ? "_partitioned" : ""}`;
+}
+
+async function testHasEntry(originFirstParty, isSet, originThirdParty) {
+ let tabs = originToTabs[originFirstParty];
+
+ for (let tab of tabs) {
+ // For the partition test we inspect the sessionStorage of the iframe.
+ let browsingContext = originThirdParty
+ ? tab.linkedBrowser.browsingContext.children[0]
+ : tab.linkedBrowser.browsingContext;
+ let actualSet = await SpecialPowers.spawn(
+ browsingContext,
+ [
+ getTestEntryName(
+ originThirdParty || originFirstParty,
+ !!originThirdParty
+ ),
+ ],
+ key => {
+ return !!content.sessionStorage.getItem(key);
+ }
+ );
+
+ let msg = `${originFirstParty}${isSet ? " " : " not "}set`;
+ if (originThirdParty) {
+ msg = "Partitioned under " + msg;
+ }
+
+ is(actualSet, isSet, msg);
+ }
+}
+
+/**
+ * Creates tabs and sets sessionStorage entries in first party and third party
+ * context.
+ * @returns {Promise} - Promise which resolves once all tabs are initialized,
+ * {@link originToTabs} is populated and (sub-)resources have loaded.
+ */
+function addTestTabs() {
+ let promises = [
+ [ORIGIN_A, ORIGIN_B],
+ [ORIGIN_A_SUB, ORIGIN_B_SUB],
+ [ORIGIN_A_HTTP, ORIGIN_B_HTTP],
+ [ORIGIN_B, ORIGIN_A],
+ [ORIGIN_B_SUB, ORIGIN_A_SUB],
+ [ORIGIN_B_HTTP, ORIGIN_A_HTTP],
+ ].map(async ([firstParty, thirdParty]) => {
+ info("Creating new tab for " + firstParty);
+ let tab = BrowserTestUtils.addTab(gBrowser, firstParty);
+ await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+
+ info("Creating iframe for " + thirdParty);
+ await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [getTestEntryName(firstParty, false), thirdParty],
+ async (storageKey, url) => {
+ // Set unpartitioned sessionStorage for firstParty.
+ content.sessionStorage.setItem(storageKey, "foo");
+
+ let iframe = content.document.createElement("iframe");
+ iframe.src = url;
+
+ let loadPromise = ContentTaskUtils.waitForEvent(iframe, "load", false);
+ content.document.body.appendChild(iframe);
+ await loadPromise;
+ }
+ );
+
+ await SpecialPowers.spawn(
+ tab.linkedBrowser.browsingContext.children[0],
+ [getTestEntryName(thirdParty, true)],
+ async storageKey => {
+ // Set sessionStorage in partitioned third-party iframe.
+ content.sessionStorage.setItem(storageKey, "foo");
+ }
+ );
+
+ let tabs = originToTabs[firstParty];
+ if (!tabs) {
+ tabs = [];
+ originToTabs[firstParty] = tabs;
+ }
+ tabs.push(tab);
+ });
+
+ return Promise.all(promises);
+}
+
+function cleanup() {
+ Object.values(originToTabs).flat().forEach(BrowserTestUtils.removeTab);
+ originToTabs = {};
+ Services.obs.notifyObservers(null, "browser:purge-sessionStorage");
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior", 5]],
+ });
+ cleanup();
+});
+
+add_task(async function test_deleteByBaseDomain() {
+ await addTestTabs();
+
+ info("Clearing sessionStorage for base domain A " + BASE_DOMAIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromBaseDomain(
+ BASE_DOMAIN_A,
+ false,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ resolve
+ );
+ });
+
+ info("All entries for A should have been cleared.");
+ await testHasEntry(ORIGIN_A, false);
+ await testHasEntry(ORIGIN_A_SUB, false);
+ await testHasEntry(ORIGIN_A_HTTP, false);
+
+ info("Entries for B should still exist.");
+ await testHasEntry(ORIGIN_B, true);
+ await testHasEntry(ORIGIN_B_SUB, true);
+ await testHasEntry(ORIGIN_B_HTTP, true);
+
+ info("All partitioned entries for B under A should have been cleared.");
+ await testHasEntry(ORIGIN_A, false, ORIGIN_B);
+ await testHasEntry(ORIGIN_A_SUB, false, ORIGIN_B_SUB);
+ await testHasEntry(ORIGIN_A_HTTP, false, ORIGIN_B_HTTP);
+
+ info("All partitioned entries of A under B should have been cleared.");
+ await testHasEntry(ORIGIN_B, false, ORIGIN_A);
+ await testHasEntry(ORIGIN_B_SUB, false, ORIGIN_A_SUB);
+ await testHasEntry(ORIGIN_B_HTTP, false, ORIGIN_A_HTTP);
+
+ cleanup();
+});
+
+add_task(async function test_deleteAll() {
+ await addTestTabs();
+
+ info("Clearing sessionStorage for base domain A " + BASE_DOMAIN_A);
+ await new Promise(resolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ resolve
+ );
+ });
+
+ info("All entries should have been cleared.");
+ await testHasEntry(ORIGIN_A, false);
+ await testHasEntry(ORIGIN_A_SUB, false);
+ await testHasEntry(ORIGIN_A_HTTP, false);
+ await testHasEntry(ORIGIN_B, false);
+ await testHasEntry(ORIGIN_B_SUB, false);
+ await testHasEntry(ORIGIN_B_HTTP, false);
+
+ info("All partitioned entries should have been cleared.");
+ await testHasEntry(ORIGIN_A, false, ORIGIN_B);
+ await testHasEntry(ORIGIN_A_SUB, false, ORIGIN_B_SUB);
+ await testHasEntry(ORIGIN_A_HTTP, false, ORIGIN_B_HTTP);
+ await testHasEntry(ORIGIN_B, false, ORIGIN_A);
+ await testHasEntry(ORIGIN_B_SUB, false, ORIGIN_A_SUB);
+ await testHasEntry(ORIGIN_B_HTTP, false, ORIGIN_A_HTTP);
+
+ cleanup();
+});
+
+add_task(async function test_deleteFromPrincipal() {
+ await addTestTabs();
+
+ info("Clearing sessionStorage for partitioned principal A " + BASE_DOMAIN_A);
+
+ let principalA = Services.scriptSecurityManager.createContentPrincipal(
+ Services.io.newURI(ORIGIN_A),
+ { partitionKey: `(https,${BASE_DOMAIN_B})` }
+ );
+
+ info("principal: " + principalA.origin);
+ info("principal partitionKey " + principalA.originAttributes.partitionKey);
+ await new Promise(resolve => {
+ Services.clearData.deleteDataFromPrincipal(
+ principalA,
+ false,
+ Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
+ resolve
+ );
+ });
+
+ info("Unpartitioned entries should still exist.");
+ await testHasEntry(ORIGIN_A, true);
+ await testHasEntry(ORIGIN_A_SUB, true);
+ await testHasEntry(ORIGIN_A_HTTP, true);
+ await testHasEntry(ORIGIN_B, true);
+ await testHasEntry(ORIGIN_B_SUB, true);
+ await testHasEntry(ORIGIN_B_HTTP, true);
+
+ info("Only entries of principal should have been cleared.");
+ await testHasEntry(ORIGIN_A, true, ORIGIN_B);
+ await testHasEntry(ORIGIN_A_SUB, true, ORIGIN_B_SUB);
+ await testHasEntry(ORIGIN_A_HTTP, true, ORIGIN_B_HTTP);
+
+ await testHasEntry(ORIGIN_B, false, ORIGIN_A);
+
+ await testHasEntry(ORIGIN_B_SUB, true, ORIGIN_A_SUB);
+ await testHasEntry(ORIGIN_B_HTTP, true, ORIGIN_A_HTTP);
+
+ cleanup();
+});
diff --git a/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs b/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs
new file mode 100644
index 0000000000..22ce6aad20
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/file_cors_preflight.sjs
@@ -0,0 +1,38 @@
+/* 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";
+
+function handleRequest(request, response) {
+ let query = new URLSearchParams(request.queryString);
+ let token = query.get("token");
+
+ response.setHeader("Content-Type", "text/plain", false);
+ response.setHeader("Access-Control-Allow-Origin", "*", false);
+ response.setHeader("Access-Control-Allow-Headers", "x-test-header", false);
+
+ if (request.method == "OPTIONS") {
+ response.setHeader(
+ "Access-Control-Allow-Methods",
+ request.getHeader("Access-Control-Request-Method"),
+ false
+ );
+ response.setHeader("Access-Control-Max-Age", "20", false);
+
+ setState(token, token);
+ } else {
+ let test_op = request.getHeader("x-test-header");
+
+ if (test_op == "check") {
+ let value = getState(token);
+
+ if (value) {
+ response.write("1");
+ setState(token, "");
+ } else {
+ response.write("0");
+ }
+ }
+ }
+}
diff --git a/toolkit/components/cleardata/tests/browser/file_css_cache.css b/toolkit/components/cleardata/tests/browser/file_css_cache.css
new file mode 100644
index 0000000000..2ceb1b7e0b
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/file_css_cache.css
@@ -0,0 +1,3 @@
+:root {
+ background-color: lime;
+}
diff --git a/toolkit/components/cleardata/tests/browser/file_css_cache.html b/toolkit/components/cleardata/tests/browser/file_css_cache.html
new file mode 100644
index 0000000000..b382bc1887
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/file_css_cache.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset="utf-8">
+<head>
+ <link rel="stylesheet" href="file_css_cache.css">
+</head>
+<body></body>
diff --git a/toolkit/components/cleardata/tests/browser/file_image_cache.html b/toolkit/components/cleardata/tests/browser/file_image_cache.html
new file mode 100644
index 0000000000..37439a8fa7
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/file_image_cache.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<meta charset="utf-8">
+<head>
+</head>
+<body>
+ <img src="file_image_cache.jpg">
+</body>
diff --git a/toolkit/components/cleardata/tests/browser/file_image_cache.jpg b/toolkit/components/cleardata/tests/browser/file_image_cache.jpg
new file mode 100644
index 0000000000..48c454d27c
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/file_image_cache.jpg
Binary files differ
diff --git a/toolkit/components/cleardata/tests/browser/worker.js b/toolkit/components/cleardata/tests/browser/worker.js
new file mode 100644
index 0000000000..aa8a83a4ce
--- /dev/null
+++ b/toolkit/components/cleardata/tests/browser/worker.js
@@ -0,0 +1 @@
+// Empty script for testing service workers