mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			312 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			312 lines
		
	
	
	
		
			9.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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,
 | 
						|
  originAttributes = { userContextId: 0 }
 | 
						|
) {
 | 
						|
  // Get all matching tabs for this origin
 | 
						|
  let tabs = originToTabs[originFirstParty];
 | 
						|
  if (!tabs) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Filter tabs by userContextId if specified
 | 
						|
  let relevantTabs = tabs;
 | 
						|
  if (originAttributes.userContextId !== undefined) {
 | 
						|
    relevantTabs = tabs.filter(
 | 
						|
      tab => tab.userContextId === originAttributes.userContextId
 | 
						|
    );
 | 
						|
  }
 | 
						|
 | 
						|
  for (let tab of relevantTabs) {
 | 
						|
    // 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;
 | 
						|
    }
 | 
						|
    if (tab.userContextId) {
 | 
						|
      msg += ` (userContextId: ${tab.userContextId})`;
 | 
						|
    }
 | 
						|
 | 
						|
    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],
 | 
						|
    [ORIGIN_A, ORIGIN_B, 1],
 | 
						|
  ].map(async ([firstParty, thirdParty, userContextId]) => {
 | 
						|
    info(
 | 
						|
      `Creating new tab for ${firstParty}, userContextId: ${userContextId ?? 0}`
 | 
						|
    );
 | 
						|
    let tab = BrowserTestUtils.addTab(gBrowser, firstParty, { userContextId });
 | 
						|
    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_deleteDataFromSite() {
 | 
						|
  await addTestTabs();
 | 
						|
 | 
						|
  info("Clearing sessionStorage for base domain A " + BASE_DOMAIN_A);
 | 
						|
  await new Promise(resolve => {
 | 
						|
    Services.clearData.deleteDataFromSite(
 | 
						|
      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, false, null, { userContextId: 1 });
 | 
						|
  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, false, ORIGIN_B, { userContextId: 1 });
 | 
						|
  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, false, ORIGIN_A, { userContextId: 1 });
 | 
						|
  await testHasEntry(ORIGIN_B_SUB, false, ORIGIN_A_SUB);
 | 
						|
  await testHasEntry(ORIGIN_B_HTTP, false, ORIGIN_A_HTTP);
 | 
						|
 | 
						|
  cleanup();
 | 
						|
});
 | 
						|
 | 
						|
add_task(async function test_deleteDataFromSiteAndPattern() {
 | 
						|
  await addTestTabs();
 | 
						|
 | 
						|
  let pattern = {
 | 
						|
    userContextId: 1,
 | 
						|
  };
 | 
						|
 | 
						|
  info(
 | 
						|
    `Clearing sessionStorage for base domain A ${BASE_DOMAIN_A} with pattern ${JSON.stringify(pattern)}.`
 | 
						|
  );
 | 
						|
  await new Promise(resolve => {
 | 
						|
    Services.clearData.deleteDataFromSite(
 | 
						|
      BASE_DOMAIN_A,
 | 
						|
      pattern,
 | 
						|
      false,
 | 
						|
      Ci.nsIClearDataService.CLEAR_DOM_QUOTA,
 | 
						|
      resolve
 | 
						|
    );
 | 
						|
  });
 | 
						|
 | 
						|
  info("Only entries for A that match the pattern should have been cleared.");
 | 
						|
  await testHasEntry(ORIGIN_A, true);
 | 
						|
  await testHasEntry(ORIGIN_A, false, null, { userContextId: 1 });
 | 
						|
  await testHasEntry(ORIGIN_A_SUB, true);
 | 
						|
  await testHasEntry(ORIGIN_A_HTTP, true);
 | 
						|
 | 
						|
  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  which match the pattern should have been cleared."
 | 
						|
  );
 | 
						|
  await testHasEntry(ORIGIN_A, true, ORIGIN_B);
 | 
						|
  await testHasEntry(ORIGIN_A, false, ORIGIN_B, { userContextId: 1 });
 | 
						|
  await testHasEntry(ORIGIN_A_SUB, true, ORIGIN_B_SUB);
 | 
						|
  await testHasEntry(ORIGIN_A_HTTP, true, ORIGIN_B_HTTP);
 | 
						|
 | 
						|
  info(
 | 
						|
    "All partitioned entries of A under B which match the pattern should have been cleared."
 | 
						|
  );
 | 
						|
  await testHasEntry(ORIGIN_B, true, ORIGIN_A);
 | 
						|
  await testHasEntry(ORIGIN_B, false, ORIGIN_A, { userContextId: 1 });
 | 
						|
  await testHasEntry(ORIGIN_B_SUB, true, ORIGIN_A_SUB);
 | 
						|
  await testHasEntry(ORIGIN_B_HTTP, true, 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();
 | 
						|
});
 |