diff options
Diffstat (limited to 'toolkit/components/cleardata/tests/browser')
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 Binary files differnew file mode 100644 index 0000000000..48c454d27c --- /dev/null +++ b/toolkit/components/cleardata/tests/browser/file_image_cache.jpg 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 |