зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1685355 - Use storage principal when clearing data via clear-site-data header. r=johannh
Differential Revision: https://phabricator.services.mozilla.com/D101192
This commit is contained in:
Родитель
34292507e2
Коммит
55b95d81de
|
@ -167,3 +167,6 @@ support-files =
|
|||
file_saveAsVideo.sjs
|
||||
file_saveAsPageInfo.html
|
||||
file_video.ogv
|
||||
[browser_partitionedClearSiteDataHeader.js]
|
||||
support-files =
|
||||
clearSiteData.sjs
|
||||
|
|
|
@ -0,0 +1,575 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
"use-strict";
|
||||
|
||||
/**
|
||||
* Tests that when receiving the "clear-site-data" header - with dFPI enabled -
|
||||
* we clear storage under the correct partition.
|
||||
*/
|
||||
|
||||
const { SiteDataTestUtils } = ChromeUtils.import(
|
||||
"resource://testing-common/SiteDataTestUtils.jsm"
|
||||
);
|
||||
|
||||
const HOST_A = "example.com";
|
||||
const HOST_B = "example.org";
|
||||
const ORIGIN_A = `https://${HOST_A}`;
|
||||
const ORIGIN_B = `https://${HOST_B}`;
|
||||
const CLEAR_SITE_DATA_PATH = `/${TEST_PATH}clearSiteData.sjs`;
|
||||
const CLEAR_SITE_DATA_URL_ORIGIN_B = ORIGIN_B + CLEAR_SITE_DATA_PATH;
|
||||
const CLEAR_SITE_DATA_URL_ORIGIN_A = ORIGIN_A + CLEAR_SITE_DATA_PATH;
|
||||
const THIRD_PARTY_FRAME_ID_ORIGIN_B = "thirdPartyFrame";
|
||||
const THIRD_PARTY_FRAME_ID_ORIGIN_A = "thirdPartyFrame2";
|
||||
const STORAGE_KEY = "testKey";
|
||||
|
||||
/**
|
||||
* Creates an iframe in the passed browser and waits for it to load.
|
||||
* @param {Browser} browser - Browser to create the frame in.
|
||||
* @param {String} src - Frame source url.
|
||||
* @param {String} id - Frame id.
|
||||
* @param {boolean} sandbox - Whether the frame should be sandboxed.
|
||||
* @returns {Promise} - Resolves once the frame has loaded.
|
||||
*/
|
||||
function createFrame(browser, src, id, sandbox) {
|
||||
return SpecialPowers.spawn(
|
||||
browser,
|
||||
[{ page: src, frameId: id, sandbox }],
|
||||
async function(obj) {
|
||||
await new content.Promise(resolve => {
|
||||
let frame = content.document.createElement("iframe");
|
||||
if (obj.sandbox) {
|
||||
frame.setAttribute("sandbox", "allow-scripts");
|
||||
}
|
||||
frame.src = obj.page;
|
||||
frame.id = obj.frameId;
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
content.document.body.appendChild(frame);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab, loads a url and creates an iframe.
|
||||
* Callers need to clean up the tab before the test ends.
|
||||
* @param {String} firstPartyUrl - Url to load in tab.
|
||||
* @param {String} thirdPartyUrl - Url to load in frame.
|
||||
* @param {String} frameId - Id of iframe element.
|
||||
* @param {boolean} sandbox - Whether the frame should be sandboxed.
|
||||
* @returns {Promise} - Resolves with the tab and the frame BrowsingContext once
|
||||
* the tab and the frame have loaded.
|
||||
*/
|
||||
async function createTabWithFrame(
|
||||
firstPartyUrl,
|
||||
thirdPartyUrl,
|
||||
frameId,
|
||||
sandbox
|
||||
) {
|
||||
// Create tab and wait for it to be loaded.
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, firstPartyUrl);
|
||||
await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
|
||||
// Create cross origin iframe.
|
||||
await createFrame(tab.linkedBrowser, thirdPartyUrl, frameId, sandbox);
|
||||
|
||||
// Return BrowsingContext of created iframe.
|
||||
return { tab, frameBC: tab.linkedBrowser.browsingContext.children[0] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Test wrapper for the ClearSiteData tests.
|
||||
* Loads ORIGIN_A and ORIGIN_B in two tabs and inserts a cross origin pointing
|
||||
* to the other iframe each.
|
||||
* Both frames ORIGIN_B under ORIGIN_A and ORIGIN_A under ORIGIN_B will be
|
||||
* storage partitioned.
|
||||
* Depending on the clearDataContext variable we then either navigate ORIGIN_A
|
||||
* (as top level) or ORIGIN_B (as third party frame) to the clear-site-data
|
||||
* endpoint.
|
||||
* @param {function} cbPreClear - Called after initial setup, once top levels
|
||||
* and frames have been loaded.
|
||||
* @param {function} cbPostClear - Called after data has been cleared via the
|
||||
* "Clear-Site-Data" header.
|
||||
* @param {("firstParty"|"thirdPartyPartitioned")} clearDataContext - Whether to
|
||||
* navigate to the path that sets the "Clear-Site-Data" header with the first or
|
||||
* third party.
|
||||
* @param {boolean} [sandboxFrame] - Whether the frames should be sandboxed. No
|
||||
* sandbox by default.
|
||||
*/
|
||||
async function runClearSiteDataTest(
|
||||
cbPreClear,
|
||||
cbPostClear,
|
||||
clearDataContext,
|
||||
sandboxFrame = false
|
||||
) {
|
||||
// Create a tabs for origin A and B with cross origins frames B and A
|
||||
let [
|
||||
{ frameBC: frameContextB, tab: tabA },
|
||||
{ frameBC: frameContextA, tab: tabB },
|
||||
] = await Promise.all([
|
||||
createTabWithFrame(
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
THIRD_PARTY_FRAME_ID_ORIGIN_B,
|
||||
sandboxFrame
|
||||
),
|
||||
createTabWithFrame(
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
THIRD_PARTY_FRAME_ID_ORIGIN_A,
|
||||
sandboxFrame
|
||||
),
|
||||
]);
|
||||
|
||||
let browserA = tabA.linkedBrowser;
|
||||
let contextA = browserA.browsingContext;
|
||||
let browserB = tabB.linkedBrowser;
|
||||
let contextB = browserB.browsingContext;
|
||||
|
||||
// Run test callback before clear-site-data
|
||||
if (cbPreClear) {
|
||||
await cbPreClear(contextA, contextB, frameContextB, frameContextA);
|
||||
}
|
||||
|
||||
// Navigate to path with clear-site-data header
|
||||
// Depending on the clearDataContext variable we either do this with the
|
||||
// top browser or the third party storage partitioned frame (B embedded in A).
|
||||
info(`Opening path with clear-site-data-header for ${clearDataContext}`);
|
||||
if (clearDataContext == "firstParty") {
|
||||
// Open in new tab so we keep our current test tab intact. The
|
||||
// post-clear-callback might need it.
|
||||
await BrowserTestUtils.withNewTab(
|
||||
(browserA, CLEAR_SITE_DATA_URL_ORIGIN_A),
|
||||
() => {}
|
||||
);
|
||||
} else if (clearDataContext == "thirdPartyPartitioned") {
|
||||
// Navigate frame to path with clear-site-data header
|
||||
await SpecialPowers.spawn(
|
||||
browserA,
|
||||
[
|
||||
{
|
||||
page: CLEAR_SITE_DATA_URL_ORIGIN_B,
|
||||
frameId: THIRD_PARTY_FRAME_ID_ORIGIN_B,
|
||||
},
|
||||
],
|
||||
async function(obj) {
|
||||
await new content.Promise(resolve => {
|
||||
let frame = content.document.getElementById(obj.frameId);
|
||||
frame.addEventListener("load", resolve, { once: true });
|
||||
frame.src = obj.page;
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
ok(false, "Invalid context requested for clear-site-data");
|
||||
}
|
||||
|
||||
if (cbPostClear) {
|
||||
await cbPostClear(contextA, contextB, frameContextB, frameContextA);
|
||||
}
|
||||
|
||||
info("Cleaning up.");
|
||||
BrowserTestUtils.removeTab(tabA);
|
||||
BrowserTestUtils.removeTab(tabB);
|
||||
await new Promise(resolve => {
|
||||
Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an origin with partitionKey.
|
||||
* @param {String} originNoSuffix - Origin without origin attributes.
|
||||
* @param {String} [firstParty] - First party to create partitionKey.
|
||||
* @returns {String} Origin with suffix. If not passed this will return the
|
||||
* umodified origin.
|
||||
*/
|
||||
function getOrigin(originNoSuffix, firstParty) {
|
||||
let origin = originNoSuffix;
|
||||
if (firstParty) {
|
||||
let [scheme, host] = firstParty.split("://");
|
||||
origin += `^partitionKey=(${scheme},${host})`;
|
||||
}
|
||||
return origin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a storage item for an origin.
|
||||
* @param {("cookie"|"localStorage")} storageType - Which storage type to use.
|
||||
* @param {String} originNoSuffix - Context to set storage item in.
|
||||
* @param {String} [firstParty] - Optional first party domain to partition
|
||||
* under.
|
||||
* @param {String} key - Key of the entry.
|
||||
* @param {String} value - Value of the entry.
|
||||
*/
|
||||
function setStorageEntry(storageType, originNoSuffix, firstParty, key, value) {
|
||||
if (storageType != "cookie" && storageType != "localStorage") {
|
||||
ok(false, "Invalid storageType passed");
|
||||
return;
|
||||
}
|
||||
|
||||
let origin = getOrigin(originNoSuffix, firstParty);
|
||||
|
||||
if (storageType == "cookie") {
|
||||
SiteDataTestUtils.addToCookies(origin, key, value);
|
||||
return;
|
||||
}
|
||||
// localStorage
|
||||
SiteDataTestUtils.addToLocalStorage(origin, key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a host sets a cookie.
|
||||
* For the purpose of this test we assume that there is either one or no cookie
|
||||
* set.
|
||||
* This performs cookie lookups directly via the cookie service.
|
||||
* @param {boolean} hasCookie - Whether we expect to see a cookie.
|
||||
* @param {String} originNoSuffix - Origin the cookie is stored for.
|
||||
* @param {String|null} firstParty - Whether to test for a partitioned cookie.
|
||||
* If set this will be used to construct the partitionKey.
|
||||
* @param {String} [key] - Expected key / name of the cookie.
|
||||
* @param {String} [value] - Expected value of the cookie.
|
||||
*/
|
||||
function testHasCookie(hasCookie, originNoSuffix, firstParty, key, value) {
|
||||
let origin = getOrigin(originNoSuffix, firstParty);
|
||||
|
||||
let label = `${originNoSuffix}${
|
||||
firstParty ? ` (partitioned under ${firstParty})` : ""
|
||||
}`;
|
||||
|
||||
if (!hasCookie) {
|
||||
ok(
|
||||
!SiteDataTestUtils.hasCookies(origin),
|
||||
`Cookie for ${label} is not set for key ${key}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ok(
|
||||
SiteDataTestUtils.hasCookies(origin, [{ key, value }]),
|
||||
`Cookie for ${label} is set ${key}=${value}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a context has a localStorage entry.
|
||||
* @param {boolean} hasEntry - Whether we expect to see an entry.
|
||||
* @param {String} originNoSuffix - Origin to test localStorage for.
|
||||
* @param {String} [firstParty] - First party context to test under.
|
||||
* @param {String} key - key of the localStorage item.
|
||||
* @param {String} [expectedValue] - Expected value of the item.
|
||||
*/
|
||||
function testHasLocalStorageEntry(
|
||||
hasEntry,
|
||||
originNoSuffix,
|
||||
firstParty,
|
||||
key,
|
||||
expectedValue
|
||||
) {
|
||||
if (key == null) {
|
||||
ok(false, "localStorage key is mandatory");
|
||||
return;
|
||||
}
|
||||
let label = `${originNoSuffix}${
|
||||
firstParty ? ` (partitioned under ${firstParty})` : ""
|
||||
}`;
|
||||
let origin = getOrigin(originNoSuffix, firstParty);
|
||||
if (hasEntry) {
|
||||
let hasEntry = SiteDataTestUtils.hasLocalStorage(origin, [
|
||||
{ key, value: expectedValue },
|
||||
]);
|
||||
ok(
|
||||
hasEntry,
|
||||
`localStorage for ${label} has expected value ${key}=${expectedValue}`
|
||||
);
|
||||
} else {
|
||||
let hasEntry = SiteDataTestUtils.hasLocalStorage(origin);
|
||||
ok(!hasEntry, `localStorage for ${label} is not set for key ${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial storage entries used by the storage tests in this file.
|
||||
* 1. first party ( A )
|
||||
* 2. first party ( B )
|
||||
* 3. third party partitioned ( B under A)
|
||||
* 4. third party partitioned ( A under B)
|
||||
* The entry values reflect which context they are set for.
|
||||
* @param {("cookie"|"localStorage")} storageType - Storage type to initialize.
|
||||
*/
|
||||
async function setupInitialStorageState(storageType) {
|
||||
if (storageType != "cookie" && storageType != "localStorage") {
|
||||
ok(false, "Invalid storageType passed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Set a first party entry
|
||||
setStorageEntry(storageType, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
|
||||
// Set a storage entry in the storage partitioned third party frame
|
||||
setStorageEntry(
|
||||
storageType,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
|
||||
// Set a storage entry in the non storage partitioned third party page
|
||||
setStorageEntry(storageType, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
|
||||
// Set a storage entry in the second storage partitioned third party frame.
|
||||
setStorageEntry(
|
||||
storageType,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
|
||||
info("Test that storage entries are set for all contexts");
|
||||
|
||||
if (storageType == "cookie") {
|
||||
testHasCookie(true, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
testHasCookie(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
testHasLocalStorageEntry(true, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
testHasLocalStorageEntry(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
testHasLocalStorageEntry(
|
||||
true,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
testHasLocalStorageEntry(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
}
|
||||
|
||||
add_task(async function setup() {
|
||||
info("Starting ClearSiteData test");
|
||||
|
||||
await SpecialPowers.flushPrefEnv();
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["dom.storage_access.enabled", true],
|
||||
[
|
||||
"network.cookie.cookieBehavior",
|
||||
Ci.nsICookieService.BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN,
|
||||
],
|
||||
["privacy.trackingprotection.enabled", false],
|
||||
["privacy.trackingprotection.pbmode.enabled", false],
|
||||
// Needed for SiteDataTestUtils#hasLocalStorage
|
||||
["dom.storage.client_validation", false],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test clearing partitioned cookies via clear-site-data header
|
||||
* (Cleared via the cookie service).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that when a storage partitioned third party frame loads a site with
|
||||
* "Clear-Site-Data", the cookies are cleared for only that partitioned frame.
|
||||
*/
|
||||
add_task(async function cookieClearThirdParty() {
|
||||
await runClearSiteDataTest(
|
||||
// Pre Clear-Site-Data
|
||||
() => setupInitialStorageState("cookie"),
|
||||
// Post Clear-Site-Data
|
||||
() => {
|
||||
info("Testing: First party cookie has not changed");
|
||||
testHasCookie(true, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
info("Testing: Unpartitioned cookie has not changed");
|
||||
testHasCookie(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
info("Testing: Partitioned cookie for HOST_B (HOST_A) has been cleared");
|
||||
testHasCookie(false, ORIGIN_B, ORIGIN_A);
|
||||
info("Testing: Partitioned cookie for HOST_A (HOST_B) has not changed");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
},
|
||||
// Send clear-site-data header in partitioned third party context.
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that when a sandboxed storage partitioned third party frame loads a
|
||||
* site with "Clear-Site-Data", no cookies are cleared and we don't crash.
|
||||
* Crash details in Bug 1686938.
|
||||
*/
|
||||
add_task(async function cookieClearThirdPartySandbox() {
|
||||
await runClearSiteDataTest(
|
||||
// Pre Clear-Site-Data
|
||||
() => setupInitialStorageState("cookie"),
|
||||
// Post Clear-Site-Data
|
||||
() => {
|
||||
info("Testing: First party cookie has not changed");
|
||||
testHasCookie(true, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
info("Testing: Unpartitioned cookie has not changed");
|
||||
testHasCookie(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
info("Testing: Partitioned cookie for HOST_B (HOST_A) has not changed");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
info("Testing: Partitioned cookie for HOST_A (HOST_B) has not changed");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
},
|
||||
// Send clear-site-data header in partitioned third party context.
|
||||
"thirdPartyPartitioned",
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that when the we load a path with "Clear-Site-Data" at top level, the
|
||||
* cookies are cleared only in the first party context.
|
||||
*/
|
||||
add_task(async function cookieClearFirstParty() {
|
||||
await runClearSiteDataTest(
|
||||
// Pre Clear-Site-Data
|
||||
() => setupInitialStorageState("cookie"),
|
||||
// Post Clear-Site-Data
|
||||
() => {
|
||||
info("Testing: First party cookie has been cleared");
|
||||
testHasCookie(false, ORIGIN_A, null);
|
||||
info("Testing: Unpartitioned cookie has not changed");
|
||||
testHasCookie(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
info("Testing: Partitioned cookie for HOST_B (HOST_A) has not changed");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
info("Testing: Partitioned cookie for HOST_A (HOST_B) has not changed");
|
||||
testHasCookie(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
},
|
||||
// Send clear-site-data header in first party context.
|
||||
"firstParty"
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Test clearing partitioned localStorage via clear-site-data header
|
||||
* (Cleared via the quota manager).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tests that when a storage partitioned third party frame loads a site with
|
||||
* "Clear-Site-Data", localStorage is cleared for only that partitioned frame.
|
||||
*/
|
||||
add_task(async function localStorageClearThirdParty() {
|
||||
await runClearSiteDataTest(
|
||||
// Pre Clear-Site-Data
|
||||
() => setupInitialStorageState("localStorage"),
|
||||
// Post Clear-Site-Data
|
||||
async () => {
|
||||
info("Testing: First party localStorage has not changed");
|
||||
testHasLocalStorageEntry(true, ORIGIN_A, null, STORAGE_KEY, "firstParty");
|
||||
info("Testing: Unpartitioned localStorage has not changed");
|
||||
testHasLocalStorageEntry(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
info(
|
||||
"Testing: Partitioned localStorage for HOST_B (HOST_A) has been cleared"
|
||||
);
|
||||
testHasLocalStorageEntry(false, ORIGIN_B, ORIGIN_A, STORAGE_KEY);
|
||||
info(
|
||||
"Testing: Partitioned localStorage for HOST_A (HOST_B) has not changed"
|
||||
);
|
||||
testHasLocalStorageEntry(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
},
|
||||
// Send clear-site-data header in partitioned third party context.
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests that when the we load a path with "Clear-Site-Data" at top level,
|
||||
* localStorage is cleared only in the first party context.
|
||||
*/
|
||||
add_task(async function localStorageClearFirstParty() {
|
||||
await runClearSiteDataTest(
|
||||
// Pre Clear-Site-Data
|
||||
() => setupInitialStorageState("localStorage"),
|
||||
// Post Clear-Site-Data
|
||||
() => {
|
||||
info("Testing: First party localStorage has been cleared");
|
||||
testHasLocalStorageEntry(false, ORIGIN_A, null, STORAGE_KEY);
|
||||
info("Testing: Unpartitioned thirdParty localStorage has not changed");
|
||||
testHasLocalStorageEntry(true, ORIGIN_B, null, STORAGE_KEY, "thirdParty");
|
||||
info(
|
||||
"Testing: Partitioned localStorage for HOST_B (HOST_A) has not changed"
|
||||
);
|
||||
testHasLocalStorageEntry(
|
||||
true,
|
||||
ORIGIN_B,
|
||||
ORIGIN_A,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned"
|
||||
);
|
||||
info(
|
||||
"Testing: Partitioned localStorage for HOST_A (HOST_B) has not changed"
|
||||
);
|
||||
testHasLocalStorageEntry(
|
||||
true,
|
||||
ORIGIN_A,
|
||||
ORIGIN_B,
|
||||
STORAGE_KEY,
|
||||
"thirdPartyPartitioned2"
|
||||
);
|
||||
},
|
||||
// Send clear-site-data header in first party context.
|
||||
"firstParty"
|
||||
);
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
function handleRequest(aRequest, aResponse) {
|
||||
aResponse.setStatusLine(aRequest.httpVersion, 200);
|
||||
aResponse.setHeader("Clear-Site-Data", '"*"');
|
||||
aResponse.setHeader("Content-Type", "text/plain");
|
||||
aResponse.write("Clear-Site-Data");
|
||||
}
|
|
@ -109,8 +109,8 @@ var SiteDataTestUtils = {
|
|||
* Adds a new localStorage entry for the specified origin, with the specified contents.
|
||||
*
|
||||
* @param {String} origin - the origin of the site to add test data for
|
||||
* @param {String} key [optional] - the localStorage key
|
||||
* @param {String} value [optional] - the localStorage value
|
||||
* @param {String} [key] - the localStorage key
|
||||
* @param {String} [value] - the localStorage value
|
||||
*/
|
||||
addToLocalStorage(origin, key = "foo", value = "bar") {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
|
@ -122,17 +122,19 @@ var SiteDataTestUtils = {
|
|||
principal,
|
||||
""
|
||||
);
|
||||
storage.setItem("key", "value");
|
||||
storage.setItem(key, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks whether the given origin is storing data in localStorage
|
||||
*
|
||||
* @param {String} origin - the origin of the site to check
|
||||
* @param {{key: String, value: String}[]} [testEntries] - An array of entries
|
||||
* to test for.
|
||||
*
|
||||
* @returns {Boolean} whether the origin has localStorage data
|
||||
*/
|
||||
hasLocalStorage(origin) {
|
||||
hasLocalStorage(origin, testEntries) {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
origin
|
||||
);
|
||||
|
@ -142,7 +144,16 @@ var SiteDataTestUtils = {
|
|||
principal,
|
||||
""
|
||||
);
|
||||
return !!storage.length;
|
||||
if (!storage.length) {
|
||||
return false;
|
||||
}
|
||||
if (!testEntries) {
|
||||
return true;
|
||||
}
|
||||
return (
|
||||
storage.length >= testEntries.length &&
|
||||
testEntries.every(({ key, value }) => storage.getItem(key) == value)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -180,22 +191,37 @@ var SiteDataTestUtils = {
|
|||
});
|
||||
},
|
||||
|
||||
hasCookies(origin) {
|
||||
hasCookies(origin, testEntries) {
|
||||
let principal = Services.scriptSecurityManager.createContentPrincipalFromOrigin(
|
||||
origin
|
||||
);
|
||||
for (let cookie of Services.cookies.cookies) {
|
||||
if (
|
||||
|
||||
let filterFn = cookie => {
|
||||
return (
|
||||
ChromeUtils.isOriginAttributesEqual(
|
||||
principal.originAttributes,
|
||||
cookie.originAttributes
|
||||
) &&
|
||||
cookie.host.includes(principal.host)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
) && cookie.host.includes(principal.host)
|
||||
);
|
||||
};
|
||||
|
||||
// Return on first cookie found for principal.
|
||||
if (!testEntries) {
|
||||
return Services.cookies.cookies.some(filterFn);
|
||||
}
|
||||
return false;
|
||||
|
||||
// Collect all cookies that match the principal
|
||||
let cookies = Services.cookies.cookies.filter(filterFn);
|
||||
|
||||
if (cookies.length < testEntries.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// This code isn't very efficient. It should only be used for testing
|
||||
// a small amount of cookies.
|
||||
return testEntries.every(({ key, value }) =>
|
||||
cookies.some(cookie => cookie.name == key && cookie.value == value)
|
||||
);
|
||||
},
|
||||
|
||||
hasIndexedDB(origin) {
|
||||
|
|
|
@ -158,8 +158,9 @@ void ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel) {
|
|||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
rv = ssm->GetChannelResultStoragePrincipal(aChannel,
|
||||
getter_AddRefs(principal));
|
||||
if (NS_WARN_IF(NS_FAILED(rv) || !principal)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче