summaryrefslogtreecommitdiffstats
path: root/netwerk/cookie/test/browser
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/cookie/test/browser')
-rw-r--r--netwerk/cookie/test/browser/browser.toml6
-rw-r--r--netwerk/cookie/test/browser/browser_cookie_chips.js539
-rw-r--r--netwerk/cookie/test/browser/browser_cookies_serviceWorker.js540
-rw-r--r--netwerk/cookie/test/browser/chips.sjs28
-rw-r--r--netwerk/cookie/test/browser/cookies.sjs17
-rw-r--r--netwerk/cookie/test/browser/serviceWorker.js21
6 files changed, 1151 insertions, 0 deletions
diff --git a/netwerk/cookie/test/browser/browser.toml b/netwerk/cookie/test/browser/browser.toml
index 05a302ddbd..56807e4bf1 100644
--- a/netwerk/cookie/test/browser/browser.toml
+++ b/netwerk/cookie/test/browser/browser.toml
@@ -8,6 +8,9 @@ support-files = [
["browser_broadcastChannel.js"]
+["browser_cookie_chips.js"]
+support-files = ["chips.sjs"]
+
["browser_cookie_insecure_overwrites_secure.js"]
["browser_cookie_purge_sync.js"]
@@ -17,6 +20,9 @@ support-files = ["server.sjs"]
["browser_cookies_ipv6.js"]
+["browser_cookies_serviceWorker.js"]
+support-files = ["cookies.sjs", "serviceWorker.js"]
+
["browser_domCache.js"]
["browser_indexedDB.js"]
diff --git a/netwerk/cookie/test/browser/browser_cookie_chips.js b/netwerk/cookie/test/browser/browser_cookie_chips.js
new file mode 100644
index 0000000000..ba0c8f3a0e
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookie_chips.js
@@ -0,0 +1,539 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_setup(() => {
+ // These are functional and not integration tests, cookieBehavior accept lets
+ // us have StorageAccess on thirparty.
+ Services.prefs.setIntPref(
+ "network.cookie.cookieBehavior",
+ Ci.nsICookieService.BEHAVIOR_ACCEPT
+ );
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ Services.prefs.setBoolPref(
+ "network.cookie.cookieBehavior.optInPartitioning",
+ true
+ );
+ Services.prefs.setBoolPref("dom.storage_access.enabled", true);
+ Services.prefs.setBoolPref("dom.storage_access.prompt.testing", true);
+ Services.cookies.removeAll();
+ Services.perms.removeAll();
+});
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+ Services.prefs.clearUserPref(
+ "network.cookieJarSettings.unblocked_for_testing"
+ );
+ Services.prefs.clearUserPref(
+ "network.cookie.cookieBehavior.optInPartitioning"
+ );
+ Services.prefs.clearUserPref("dom.storage_access.enabled");
+ Services.prefs.clearUserPref("dom.storage_access.prompt.testing");
+ Services.cookies.removeAll();
+ Services.perms.removeAll();
+});
+
+const COOKIE_PARTITIONED =
+ "cookie=partitioned; Partitioned; Secure; SameSite=None;";
+const COOKIE_UNPARTITIONED = "cookie=unpartitioned; Secure; SameSite=None;";
+
+const PATH = "/browser/netwerk/cookie/test/browser/";
+const PATH_EMPTY = PATH + "file_empty.html";
+const HTTP_COOKIE_SET = PATH + "chips.sjs?set";
+const HTTP_COOKIE_GET = PATH + "chips.sjs?get";
+
+const FIRST_PARTY = "example.com";
+const THIRD_PARTY = "example.org";
+
+const URL_DOCUMENT_FIRSTPARTY = "https://" + FIRST_PARTY + PATH_EMPTY;
+const URL_DOCUMENT_THIRDPARTY = "https://" + THIRD_PARTY + PATH_EMPTY;
+const URL_HTTP_FIRSTPARTY = "https://" + FIRST_PARTY + "/" + HTTP_COOKIE_SET;
+const URL_HTTP_THIRDPARTY = "https://" + THIRD_PARTY + "/" + HTTP_COOKIE_SET;
+
+function createOriginAttributes(partitionKey) {
+ return JSON.stringify({
+ firstPartyDomain: "",
+ geckoViewSessionContextId: "",
+ inIsolatedMozBrowser: false,
+ partitionKey,
+ privateBrowsingId: 0,
+ userContextId: 0,
+ });
+}
+
+function createPartitonKey(url) {
+ let uri = NetUtil.newURI(url);
+ return `(${uri.scheme},${uri.host})`;
+}
+
+// OriginAttributes used to access partitioned and unpartitioned cookie jars
+// in all tests.
+const partitionedOAs = createOriginAttributes(
+ createPartitonKey(URL_DOCUMENT_FIRSTPARTY)
+);
+const unpartitionedOAs = createOriginAttributes("");
+
+// Set partitioned and unpartitioned cookie from first-party document.
+// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
+// This calls CookieServiceChild::SetCookieStringFromDocument() internally.
+// CookieService::SetCookieStringFromDocument() is not explicitly tested since
+// CHIPS are in the common function CookieCommons::CreateCookieFromDocument().
+add_task(
+ async function test_chips_store_partitioned_document_first_party_child() {
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Set partitioned and unpartitioned cookie from document child-side
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED],
+ (partitioned, unpartitioned) => {
+ content.document.cookie = partitioned;
+ content.document.cookie = unpartitioned;
+ }
+ );
+
+ // Get cookies from partitioned jar
+ let partitioned = Services.cookies.getCookiesWithOriginAttributes(
+ partitionedOAs,
+ FIRST_PARTY
+ );
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ FIRST_PARTY
+ );
+
+ // Assert partitioned/unpartitioned cookie were stored in correct jars
+ Assert.equal(partitioned.length, 1);
+ Assert.equal(partitioned[0].value, "partitioned");
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ }
+);
+
+// Set partitioned and unpartitioned cookie from third-party document with storage
+// access. CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
+// This calls CookieServiceChild::SetCookieStringFromDocument() internally.
+// CookieService::SetCookieStringFromDocument() is not explicitly tested since
+// CHIPS are in the common function CookieCommons::CreateCookieFromDocument().
+add_task(
+ async function test_chips_store_partitioned_document_third_party_storage_access_child() {
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Spawn document bc
+ await SpecialPowers.spawn(
+ browser,
+ [URL_DOCUMENT_THIRDPARTY, COOKIE_PARTITIONED, COOKIE_UNPARTITIONED],
+ async (url, partitioned, unpartitioned) => {
+ let ifr = content.document.createElement("iframe");
+ ifr.src = url;
+ content.document.body.appendChild(ifr);
+ await ContentTaskUtils.waitForEvent(ifr, "load");
+
+ // Spawn iframe bc
+ await SpecialPowers.spawn(
+ await ifr.browsingContext,
+ [partitioned, unpartitioned],
+ async (partitioned, unpartitioned) => {
+ ok(
+ await content.document.hasStorageAccess(),
+ "example.org should have storageAccess by CookieBehavior 0 / test setup"
+ );
+
+ content.document.cookie = partitioned;
+ content.document.cookie = unpartitioned;
+ }
+ );
+ }
+ );
+
+ // Get cookies from partitioned jar
+ let partitioned = Services.cookies.getCookiesWithOriginAttributes(
+ partitionedOAs,
+ THIRD_PARTY
+ );
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ THIRD_PARTY
+ );
+
+ // Assert partitioned/unpartitioned cookie were stored in correct jars
+ Assert.equal(partitioned.length, 1);
+ Assert.equal(partitioned[0].value, "partitioned");
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ }
+);
+
+// Set partitioned and unpartitioned cookie from first-party http load.
+// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
+// This calls CookieService::SetCookieStringFromHttp() internally.
+add_task(async function test_chips_store_partitioned_http_first_party_parent() {
+ // Set partitioned and unpartitioned cookie from http parent side through
+ // chips.sjs being loaded.
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_HTTP_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Get cookies from partitioned jar
+ let partitioned = Services.cookies.getCookiesWithOriginAttributes(
+ partitionedOAs,
+ FIRST_PARTY
+ );
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ FIRST_PARTY
+ );
+
+ // Assert partitioned/unpartitioned cookie were stored in correct jars
+ Assert.equal(partitioned.length, 1);
+ Assert.equal(partitioned[0].value, "partitioned");
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
+
+// Set partitioned and unpartitioned cookie from third-party http load.
+// CHIPS "Partitioned" cookie MUST always be stored in partitioned jar.
+// This calls CookieService::SetCookieStringFromHttp() internally.
+add_task(
+ async function test_chips_store_partitioned_http_third_party_storage_access_parent() {
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Spawn document bc
+ await SpecialPowers.spawn(browser, [URL_HTTP_THIRDPARTY], async url => {
+ let ifr = content.document.createElement("iframe");
+ ifr.src = url;
+ content.document.body.appendChild(ifr);
+ // Send http request with "set" query parameter, partitioned and
+ // unpartitioned cookie will be set through http response from chips.sjs.
+ await ContentTaskUtils.waitForEvent(ifr, "load");
+
+ // Spawn iframe bc
+ await SpecialPowers.spawn(await ifr.browsingContext, [], async () => {
+ ok(
+ await content.document.hasStorageAccess(),
+ "example.org should have storageAccess by CookieBehavior 0 / test setup"
+ );
+ });
+ });
+
+ // Get cookies from partitioned jar
+ let partitioned = Services.cookies.getCookiesWithOriginAttributes(
+ partitionedOAs,
+ THIRD_PARTY
+ );
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ THIRD_PARTY
+ );
+
+ // Assert partitioned/unpartitioned cookie were stored in correct jars
+ Assert.equal(partitioned.length, 1);
+ Assert.equal(partitioned[0].value, "partitioned");
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ }
+);
+
+// TODO CHIPS - Tests for CookieServiceChild::SetCookieStringFromHttp() need
+// to be added. Since this is only checkable on onProxyConnectSuccess needs
+// proxy setup test harness. It is also called after onStartRequest() (Http)
+// but cookies are already set by the parents
+// CookieService::SetCookieStringFromHttp() call.
+
+// Get partitioned and unpartitioned cookies from document (child).
+// This calls CookieServiceChild::GetCookieStringFromDocument() internally.
+add_task(
+ async function test_chips_send_partitioned_and_unpartitioned_document_child() {
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Spawn document bc
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED],
+ async (partitioned, unpartitioned) => {
+ content.document.cookie = partitioned;
+ content.document.cookie = unpartitioned;
+
+ // Assert both unpartitioned and partitioned cookie are returned.
+ let cookies = content.document.cookie;
+ ok(
+ cookies.includes("cookie=partitioned"),
+ "Cookie from partitioned jar was sent."
+ );
+ ok(
+ cookies.includes("cookie=unpartitioned"),
+ "Cookie from unpartitioned jar was sent."
+ );
+ }
+ );
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ }
+);
+
+// Get partitioned and unpartitioned cookies from document (child) after
+// storageAccess was granted. This calls CookieServiceChild::TrackCookieLoad()
+// internally to update child's cookies.
+add_task(
+ async function test_chips_send_partitioned_and_unpartitioned_on_storage_access_child() {
+ // Set cookieBehavior to BEHAVIOR_REJECT_TRACKERS_AND_PARTITION_FOREIGN for
+ // requestStorageAccess() based test.
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 5);
+
+ // Set example.org first-party unpartitioned cookie
+ await BrowserTestUtils.withNewTab(
+ URL_DOCUMENT_THIRDPARTY,
+ async browser => {
+ info("Set a first party cookie via `document.cookie`.");
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_UNPARTITIONED],
+ async unpartitioned => {
+ content.document.cookie = unpartitioned;
+ is(
+ content.document.cookie,
+ "cookie=unpartitioned",
+ "Unpartitioned cookie was set."
+ );
+ }
+ );
+ }
+ );
+
+ // Assert cookie was set on parent cookie service for example.org.
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ THIRD_PARTY
+ );
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Load example.com as first-party in tab
+ const tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Set third-party cookie from example.org iframe, get storageAccess and
+ // check cookies.
+ // Spawn document bc
+ await SpecialPowers.spawn(
+ browser,
+ [URL_DOCUMENT_THIRDPARTY, COOKIE_PARTITIONED],
+ async (url, partitioned) => {
+ // Create third-party iframe
+ let ifr = content.document.createElement("iframe");
+ ifr.src = url;
+ content.document.body.appendChild(ifr);
+ await ContentTaskUtils.waitForEvent(ifr, "load");
+
+ // Spawn iframe bc
+ await SpecialPowers.spawn(
+ await ifr.browsingContext,
+ [partitioned],
+ async partitioned => {
+ ok(
+ !(await content.document.hasStorageAccess()),
+ "example.org should not have storageAccess initially."
+ );
+
+ // Set a partitioned third-party cookie and assert its the only.
+ content.document.cookie = partitioned;
+ is(
+ content.document.cookie,
+ "cookie=partitioned",
+ "Partitioned cookie was set."
+ );
+
+ info("Simulate user activation.");
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+
+ info("Request storage access.");
+ await content.document.requestStorageAccess();
+
+ ok(
+ await content.document.hasStorageAccess(),
+ "example.org should now have storageAccess."
+ );
+
+ // Assert both unpartitioned and partitioned cookie are returned.
+ let cookies = content.document.cookie;
+ ok(
+ cookies.includes("cookie=partitioned"),
+ "Cookie from partitioned jar was sent."
+ );
+ ok(
+ cookies.includes("cookie=unpartitioned"),
+ "Cookie from unpartitioned jar was sent."
+ );
+ }
+ );
+ }
+ );
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ Services.perms.removeAll();
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ }
+);
+
+// Set partitioned and unpartitioned cookies for URL_DOCUMENT_FIRSTPARTY, then
+// load URL again, assure cookies are correctly send to content child process.
+// This tests CookieServiceParent::TrackCookieLoad() internally.
+add_task(
+ async function test_chips_send_partitioned_and_unpartitioned_document_parent() {
+ // Set example.com first-party unpartitioned and partitioned cookie, then
+ // close tab.
+ await BrowserTestUtils.withNewTab(
+ URL_DOCUMENT_FIRSTPARTY,
+ async browser => {
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_PARTITIONED, COOKIE_UNPARTITIONED],
+ async (partitioned, unpartitioned) => {
+ content.document.cookie = unpartitioned;
+ content.document.cookie = partitioned;
+ let cookies = content.document.cookie;
+ ok(
+ cookies.includes("cookie=unpartitioned"),
+ "Unpartitioned cookie was set."
+ );
+ ok(
+ cookies.includes("cookie=partitioned"),
+ "Partitioned cookie was set."
+ );
+ }
+ );
+ }
+ );
+
+ // Assert we have one partitioned and one unpartitioned cookie set.
+ // Get cookies from partitioned jar
+ let partitioned = Services.cookies.getCookiesWithOriginAttributes(
+ partitionedOAs,
+ FIRST_PARTY
+ );
+ // Get cookies from unpartitioned jar
+ let unpartitioned = Services.cookies.getCookiesWithOriginAttributes(
+ unpartitionedOAs,
+ FIRST_PARTY
+ );
+ Assert.equal(partitioned.length, 1);
+ Assert.equal(partitioned[0].value, "partitioned");
+ Assert.equal(unpartitioned.length, 1);
+ Assert.equal(unpartitioned[0].value, "unpartitioned");
+
+ // Reload example.com and assert previously set cookies are correctly
+ // send to content child document.
+ await BrowserTestUtils.withNewTab(
+ URL_DOCUMENT_FIRSTPARTY,
+ async browser => {
+ await SpecialPowers.spawn(browser, [], () => {
+ let cookies = content.document.cookie;
+ ok(
+ cookies.includes("cookie=unpartitioned"),
+ "Unpartitioned cookie was sent."
+ );
+ ok(
+ cookies.includes("cookie=partitioned"),
+ "Partitioned cookie was sent."
+ );
+ });
+ }
+ );
+
+ // Cleanup
+ Services.cookies.removeAll();
+ }
+);
+
+// Set partitioned and unpartitioned cookies for URL_DOCUMENT_FIRSTPARTY, then
+// send http request, assure cookies are correctly send in "Cookie" header.
+// This tests CookieService::GetCookieStringFromHttp() internally.
+add_task(
+ async function test_chips_send_partitioned_and_unpartitioned_http_parent() {
+ // Load empty document.
+ let tab = BrowserTestUtils.addTab(gBrowser, URL_DOCUMENT_FIRSTPARTY);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ await SpecialPowers.spawn(
+ browser,
+ [HTTP_COOKIE_SET, HTTP_COOKIE_GET],
+ async (set, get) => {
+ // Send http request with "set" query parameter, partitioned and
+ // unpartitioned cookie will be set through http response.
+ await content.fetch(set);
+
+ // Assert cookies were set to document.
+ let cookies = content.document.cookie;
+ ok(
+ cookies.includes("cookie=unpartitioned"),
+ "Unpartitioned cookie was set to document."
+ );
+ ok(
+ cookies.includes("cookie=partitioned"),
+ "Partitioned cookie was set to document."
+ );
+
+ // Send http request with "get" query parameter, chips.sjs will return
+ // the request "Cookie" header string.
+ await content
+ .fetch(get)
+ .then(response => response.text())
+ .then(requestCookies => {
+ // Assert cookies were sent in http request.
+ ok(
+ requestCookies.includes("cookie=unpartitioned"),
+ "Unpartitioned cookie was sent in http request."
+ );
+ ok(
+ requestCookies.includes("cookie=partitioned"),
+ "Partitioned cookie was sent in http request."
+ );
+ });
+ }
+ );
+
+ // Cleanup
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+ }
+);
diff --git a/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js b/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js
new file mode 100644
index 0000000000..813e1fe7cb
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookies_serviceWorker.js
@@ -0,0 +1,540 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_CROSS_SITE_DOMAIN = "https://example.net/";
+const TEST_CROSS_SITE_PAGE =
+ TEST_CROSS_SITE_DOMAIN + TEST_PATH + "file_empty.html";
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ],
+ });
+
+ Services.cookies.removeAll();
+});
+
+function registerSW(browser) {
+ return SpecialPowers.spawn(browser, [], async _ => {
+ let reg = await content.navigator.serviceWorker.register(
+ "serviceWorker.js"
+ );
+
+ await ContentTaskUtils.waitForCondition(() => {
+ return reg.active && reg.active.state === "activated";
+ }, "The service worker is activated");
+
+ ok(
+ content.navigator.serviceWorker.controller,
+ "The service worker controls the document successfully."
+ );
+ });
+}
+
+function fetchCookiesFromSW(browser) {
+ return SpecialPowers.spawn(browser, [], async _ => {
+ return new content.Promise(resolve => {
+ content.navigator.serviceWorker.addEventListener("message", event => {
+ resolve(event.data.content);
+ });
+
+ content.navigator.serviceWorker.controller.postMessage({
+ action: "fetch",
+ url: `cookies.sjs`,
+ });
+ });
+ });
+}
+
+function setCookiesFromSW(browser, cookies) {
+ let setCookieQuery = "";
+
+ for (let cookie of cookies) {
+ setCookieQuery += `Set-Cookie=${cookie}&`;
+ }
+
+ return SpecialPowers.spawn(browser, [setCookieQuery], async query => {
+ return new content.Promise(resolve => {
+ content.navigator.serviceWorker.addEventListener("message", event => {
+ resolve(event.data.content);
+ });
+
+ content.navigator.serviceWorker.controller.postMessage({
+ action: "fetch",
+ url: `cookies.sjs?${query}`,
+ });
+ });
+ });
+}
+
+function unregisterSW(browser) {
+ return SpecialPowers.spawn(browser, [], async _ => {
+ const regs = await content.navigator.serviceWorker.getRegistrations();
+ for (const reg of regs) {
+ await reg.unregister();
+ }
+ });
+}
+
+/**
+ * Verify a first-party service worker can access both SameSite=None and
+ * SameSite=Lax cookies set in the first-party context.
+ */
+add_task(async function testCookiesWithFirstPartyServiceWorker() {
+ info("Open a tab");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_TOP_PAGE
+ );
+
+ info("Writing cookies to the first-party context.");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
+ const cookies = [
+ "foo=bar; SameSite=None; Secure",
+ "fooLax=barLax; SameSite=Lax; Secure",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info("Register a service worker and trigger a fetch request to get cookies.");
+ await registerSW(tab.linkedBrowser);
+ let cookieStr = await fetchCookiesFromSW(tab.linkedBrowser);
+
+ is(cookieStr, "foo=bar; fooLax=barLax", "The cookies are expected");
+
+ info("Set cookies from the service worker.");
+ await setCookiesFromSW(tab.linkedBrowser, [
+ "foo=barSW; SameSite=None; Secure",
+ "fooLax=barLaxSW; SameSite=Lax; Secure",
+ ]);
+
+ info("Get cookies from the service worker.");
+ cookieStr = await fetchCookiesFromSW(tab.linkedBrowser);
+
+ is(cookieStr, "foo=barSW; fooLax=barLaxSW", "The cookies are expected");
+
+ await unregisterSW(tab.linkedBrowser);
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
+
+/**
+ * Verify a cross-site service worker can only access cookies set in the
+ * same cross-site context.
+ */
+add_task(async function testCookiesWithCrossSiteServiceWorker() {
+ // Disable blocking third-party cookies.
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", false]],
+ });
+
+ info("Open a cross-site tab");
+ let crossSiteTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_CROSS_SITE_PAGE
+ );
+
+ info("Writing cookies to the cross site in the first-party context.");
+ await SpecialPowers.spawn(crossSiteTab.linkedBrowser, [], async _ => {
+ const cookies = [
+ "foo=bar; SameSite=None; Secure",
+ "fooLax=barLax; SameSite=Lax; Secure",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info("Open a tab");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_TOP_PAGE
+ );
+
+ info("Load a cross-site iframe");
+ let crossSiteBc = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [TEST_CROSS_SITE_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info("Write cookies in the cross-site iframe");
+ await SpecialPowers.spawn(crossSiteBc, [], async _ => {
+ const cookies = [
+ "foo=crossBar; SameSite=None; Secure",
+ "fooLax=crossBarLax; SameSite=Lax; Secure",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info(
+ "Register a service worker and trigger a fetch request to get cookies in cross-site context."
+ );
+ await registerSW(crossSiteBc);
+ let cookieStr = await fetchCookiesFromSW(crossSiteBc);
+
+ is(
+ cookieStr,
+ "foo=crossBar",
+ "Only the SameSite=None cookie set in the third-party iframe is available."
+ );
+
+ info("Set cookies from the third-party service worker.");
+ await setCookiesFromSW(crossSiteBc, [
+ "foo=crossBarSW; SameSite=None; Secure",
+ "fooLax=crossBarLaxSW; SameSite=Lax; Secure",
+ ]);
+
+ info("Get cookies from the third-party service worker.");
+ cookieStr = await fetchCookiesFromSW(crossSiteBc);
+
+ is(
+ cookieStr,
+ "foo=crossBarSW",
+ "Only the SameSite=None cookie set in the third-party service worker is available."
+ );
+
+ await unregisterSW(crossSiteBc);
+ BrowserTestUtils.removeTab(crossSiteTab);
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
+
+/**
+ * Verify a cross-site service worker can only access partitioned cookies set in
+ * the same cross-site context if third-party cookies are blocked.
+ */
+add_task(async function testPartitionedCookiesWithCrossSiteServiceWorker() {
+ // Enable blocking third-party cookies.
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", true]],
+ });
+
+ info("Open a tab");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_TOP_PAGE
+ );
+
+ info("Load a cross-site iframe");
+ let crossSiteBc = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [TEST_CROSS_SITE_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info("Write cookies in the cross-site iframe");
+ await SpecialPowers.spawn(crossSiteBc, [], async _ => {
+ const cookies = [
+ "foo=crossBar; SameSite=None; Secure",
+ "fooLax=crossBarLax; SameSite=Lax; Secure",
+ "fooPartitioned=crossBar; SameSite=None; Secure; Partitioned;",
+ "fooLaxPartitioned=crossBarLax; SameSite=Lax; Secure; Partitioned;",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info(
+ "Register a service worker and trigger a fetch request to get cookies in cross-site context."
+ );
+ await registerSW(crossSiteBc);
+ let cookieStr = await fetchCookiesFromSW(crossSiteBc);
+
+ is(
+ cookieStr,
+ "fooPartitioned=crossBar",
+ "Only the SameSite=None partitioned cookie set in the third-party iframe is available."
+ );
+
+ info("Set cookies from the third-party service worker.");
+ await setCookiesFromSW(crossSiteBc, [
+ "foo=crossBarSW; SameSite=None; Secure",
+ "fooLax=crossBarLaxSW; SameSite=Lax; Secure",
+ "fooPartitioned=crossBarSW; SameSite=None; Secure; Partitioned;",
+ "fooLaxPartitioned=crossBarLaxSW; SameSite=Lax; Secure; Partitioned;",
+ ]);
+
+ info("Get cookies from the third-party service worker.");
+ cookieStr = await fetchCookiesFromSW(crossSiteBc);
+
+ is(
+ cookieStr,
+ "fooPartitioned=crossBarSW",
+ "Only the SameSite=None partitioned cookie set in the third-party service worker is available."
+ );
+
+ await unregisterSW(crossSiteBc);
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
+
+/**
+ * Verify a ABA service worker can only access cookies set in the ABA context.
+ */
+add_task(async function testCookiesWithABAServiceWorker() {
+ // Disable blocking third-party cookies.
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", false]],
+ });
+
+ info("Open a tab");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_TOP_PAGE
+ );
+
+ info("Writing cookies to the first-party context.");
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async _ => {
+ const cookies = [
+ "foo=bar; SameSite=None; Secure",
+ "fooLax=barLax; SameSite=Lax; Secure",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info("Load a ABA iframe");
+ let crossSiteBc = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [TEST_CROSS_SITE_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ let ABABc = await SpecialPowers.spawn(
+ crossSiteBc,
+ [TEST_TOP_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info(
+ "Register a service worker and trigger a fetch request to get cookies in the ABA context."
+ );
+
+ await registerSW(ABABc);
+ let cookieStr = await fetchCookiesFromSW(ABABc);
+ is(cookieStr, "", "No cookie should be available in ABA context.");
+
+ info("Set cookies in the ABA iframe");
+ await SpecialPowers.spawn(ABABc, [], async _ => {
+ const cookies = [
+ "fooABA=barABA; SameSite=None; Secure",
+ "fooABALax=BarABALax; SameSite=Lax; Secure",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info("Get cookies in the ABA service worker.");
+ cookieStr = await fetchCookiesFromSW(ABABc);
+
+ is(
+ cookieStr,
+ "fooABA=barABA",
+ "Only the SameSite=None cookie set in ABA iframe is available."
+ );
+
+ info("Set cookies from the service worker in ABA context");
+ await setCookiesFromSW(ABABc, [
+ "fooABA=barABASW; SameSite=None; Secure",
+ "fooABALax=BarABALaxSW; SameSite=Lax; Secure",
+ ]);
+
+ info("Get cookies from the service worker in the ABA context.");
+ cookieStr = await fetchCookiesFromSW(ABABc);
+
+ is(
+ cookieStr,
+ "fooABA=barABASW",
+ "Only the SameSite=None cookie set in ABA service worker is available."
+ );
+
+ await unregisterSW(ABABc);
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
+
+/**
+ * Verify a ABA service worker can only access partitioned cookies set in the
+ * ABA context if third-party cookies are blocked.
+ */
+add_task(async function testCookiesWithABAServiceWorker() {
+ // Disable blocking third-party cookies.
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", true]],
+ });
+
+ info("Open a tab");
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TEST_TOP_PAGE
+ );
+
+ info("Load a ABA iframe");
+ let crossSiteBc = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [TEST_CROSS_SITE_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ let ABABc = await SpecialPowers.spawn(
+ crossSiteBc,
+ [TEST_TOP_PAGE],
+ async url => {
+ let ifr = content.document.createElement("iframe");
+
+ await new content.Promise(resolve => {
+ ifr.onload = resolve;
+ content.document.body.appendChild(ifr);
+ ifr.src = url;
+ });
+
+ return ifr.browsingContext;
+ }
+ );
+
+ info("Set cookies in the ABA iframe");
+ await SpecialPowers.spawn(ABABc, [], async _ => {
+ const cookies = [
+ "fooABA=barABA; SameSite=None; Secure",
+ "fooABALax=BarABALax; SameSite=Lax; Secure",
+ "fooABAPartitioned=barABA; SameSite=None; Secure; Partitioned;",
+ "fooABALaxPartitioned=BarABALax; SameSite=Lax; Secure; Partitioned;",
+ ];
+
+ let query = "";
+
+ for (let cookie of cookies) {
+ query += `Set-Cookie=${cookie}&`;
+ }
+
+ await content.fetch(`cookies.sjs?${query}`);
+ });
+
+ info(
+ "Register a service worker and trigger a fetch request to get cookies in the ABA context."
+ );
+
+ await registerSW(ABABc);
+
+ info("Get cookies in the ABA service worker.");
+ let cookieStr = await fetchCookiesFromSW(ABABc);
+
+ is(
+ cookieStr,
+ "fooABAPartitioned=barABA",
+ "Only the SameSite=None partitioned cookie set in ABA iframe is available."
+ );
+
+ info("Set cookies from the service worker in ABA context");
+ await setCookiesFromSW(ABABc, [
+ "fooABA=barABASW; SameSite=None; Secure",
+ "fooABALax=BarABALaxSW; SameSite=Lax; Secure",
+ "fooABAPartitioned=barABASW; SameSite=None; Secure; Partitioned;",
+ "fooABALaxPartitioned=BarABALaxSW; SameSite=Lax; Secure; Partitioned;",
+ ]);
+
+ info("Get cookies from the service worker in the ABA context.");
+ cookieStr = await fetchCookiesFromSW(ABABc);
+
+ is(
+ cookieStr,
+ "fooABAPartitioned=barABASW",
+ "Only the SameSite=None partitioned cookie set in ABA service worker is available."
+ );
+
+ await unregisterSW(ABABc);
+ BrowserTestUtils.removeTab(tab);
+ Services.cookies.removeAll();
+});
diff --git a/netwerk/cookie/test/browser/chips.sjs b/netwerk/cookie/test/browser/chips.sjs
new file mode 100644
index 0000000000..e60109f6fa
--- /dev/null
+++ b/netwerk/cookie/test/browser/chips.sjs
@@ -0,0 +1,28 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+ var params = new URLSearchParams(aRequest.queryString);
+
+ // Get Cookie header string.
+ if (params.has("get")) {
+ if (aRequest.hasHeader("Cookie")) {
+ let cookie = aRequest.getHeader("Cookie");
+ aResponse.write(cookie);
+ }
+ return;
+ }
+
+ // Set a partitioned and a unpartitioned cookie.
+ if (params.has("set")) {
+ aResponse.setHeader(
+ "Set-Cookie",
+ "cookie=partitioned; Partitioned; SameSite=None; Secure",
+ true
+ );
+ aResponse.setHeader(
+ "Set-Cookie",
+ "cookie=unpartitioned; SameSite=None; Secure",
+ true
+ );
+ }
+}
diff --git a/netwerk/cookie/test/browser/cookies.sjs b/netwerk/cookie/test/browser/cookies.sjs
new file mode 100644
index 0000000000..9beb861d44
--- /dev/null
+++ b/netwerk/cookie/test/browser/cookies.sjs
@@ -0,0 +1,17 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ let query = new URLSearchParams(aRequest.queryString);
+
+ if (query.has("Set-Cookie")) {
+ for (let value of query.getAll("Set-Cookie")) {
+ aResponse.setHeader("Set-Cookie", value, true);
+ }
+ return;
+ }
+
+ let cookieStr = "";
+ if (aRequest.hasHeader("Cookie")) {
+ cookieStr = aRequest.getHeader("Cookie");
+ }
+ aResponse.write(cookieStr);
+}
diff --git a/netwerk/cookie/test/browser/serviceWorker.js b/netwerk/cookie/test/browser/serviceWorker.js
new file mode 100644
index 0000000000..b2eab40ce4
--- /dev/null
+++ b/netwerk/cookie/test/browser/serviceWorker.js
@@ -0,0 +1,21 @@
+self.addEventListener("install", function () {
+ self.skipWaiting();
+});
+
+self.addEventListener("activate", function (event) {
+ event.waitUntil(self.clients.claim());
+});
+
+self.addEventListener("message", function (event) {
+ if (event.data.action === "fetch") {
+ fetch(event.data.url)
+ .then(response => response.text())
+ .then(data => {
+ self.clients.matchAll().then(clients => {
+ clients.forEach(client => {
+ client.postMessage({ content: data });
+ });
+ });
+ });
+ }
+});