summaryrefslogtreecommitdiffstats
path: root/netwerk/cookie/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /netwerk/cookie/test
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'netwerk/cookie/test')
-rw-r--r--netwerk/cookie/test/browser/browser.toml42
-rw-r--r--netwerk/cookie/test/browser/browser_broadcastChannel.js80
-rw-r--r--netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js128
-rw-r--r--netwerk/cookie/test/browser/browser_cookie_purge_sync.js141
-rw-r--r--netwerk/cookie/test/browser/browser_cookies.js53
-rw-r--r--netwerk/cookie/test/browser/browser_cookies_ipv6.js57
-rw-r--r--netwerk/cookie/test/browser/browser_domCache.js25
-rw-r--r--netwerk/cookie/test/browser/browser_indexedDB.js84
-rw-r--r--netwerk/cookie/test/browser/browser_originattributes.js121
-rw-r--r--netwerk/cookie/test/browser/browser_oversize.js96
-rw-r--r--netwerk/cookie/test/browser/browser_partitionedConsole.js201
-rw-r--r--netwerk/cookie/test/browser/browser_partitioned_telemetry.js134
-rw-r--r--netwerk/cookie/test/browser/browser_sameSiteConsole.js133
-rw-r--r--netwerk/cookie/test/browser/browser_serviceWorker.js45
-rw-r--r--netwerk/cookie/test/browser/browser_sharedWorker.js18
-rw-r--r--netwerk/cookie/test/browser/browser_storage.js43
-rw-r--r--netwerk/cookie/test/browser/file_empty.html2
-rw-r--r--netwerk/cookie/test/browser/file_empty.js1
-rw-r--r--netwerk/cookie/test/browser/head.js201
-rw-r--r--netwerk/cookie/test/browser/oversize.sjs17
-rw-r--r--netwerk/cookie/test/browser/partitioned.sjs32
-rw-r--r--netwerk/cookie/test/browser/sameSite.sjs7
-rw-r--r--netwerk/cookie/test/browser/server.sjs9
-rw-r--r--netwerk/cookie/test/mochitest/cookie.sjs166
-rw-r--r--netwerk/cookie/test/mochitest/cookiesHelper.js63
-rw-r--r--netwerk/cookie/test/mochitest/empty.html1
-rw-r--r--netwerk/cookie/test/mochitest/mochitest.toml27
-rw-r--r--netwerk/cookie/test/mochitest/test_document_cookie.html20
-rw-r--r--netwerk/cookie/test/mochitest/test_document_cookie_notification.html32
-rw-r--r--netwerk/cookie/test/mochitest/test_fetch.html20
-rw-r--r--netwerk/cookie/test/mochitest/test_image.html24
-rw-r--r--netwerk/cookie/test/mochitest/test_metaTag.html24
-rw-r--r--netwerk/cookie/test/mochitest/test_script.html25
-rw-r--r--netwerk/cookie/test/mochitest/test_sharedWorker.html50
-rw-r--r--netwerk/cookie/test/mochitest/test_worker.html31
-rw-r--r--netwerk/cookie/test/mochitest/test_xhr.html25
-rw-r--r--netwerk/cookie/test/mochitest/test_xmlDocument.html37
-rw-r--r--netwerk/cookie/test/unit/test_baseDomain_publicsuffix.js105
-rw-r--r--netwerk/cookie/test/unit/test_bug1155169.js96
-rw-r--r--netwerk/cookie/test/unit/test_bug1321912.js99
-rw-r--r--netwerk/cookie/test/unit/test_bug643051.js43
-rw-r--r--netwerk/cookie/test/unit/test_eviction.js199
-rw-r--r--netwerk/cookie/test/unit/test_getCookieSince.js72
-rw-r--r--netwerk/cookie/test/unit/test_migrateCookieLifetimePref.js65
-rw-r--r--netwerk/cookie/test/unit/test_parser_0001.js32
-rw-r--r--netwerk/cookie/test/unit/test_parser_0019.js32
-rw-r--r--netwerk/cookie/test/unit/test_rawSameSite.js125
-rw-r--r--netwerk/cookie/test/unit/test_schemeMap.js216
-rw-r--r--netwerk/cookie/test/unit/test_timestamp_fixup.js130
-rw-r--r--netwerk/cookie/test/unit/xpcshell.toml26
50 files changed, 3455 insertions, 0 deletions
diff --git a/netwerk/cookie/test/browser/browser.toml b/netwerk/cookie/test/browser/browser.toml
new file mode 100644
index 0000000000..05a302ddbd
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser.toml
@@ -0,0 +1,42 @@
+[DEFAULT]
+
+support-files = [
+ "file_empty.html",
+ "file_empty.js",
+ "head.js"
+]
+
+["browser_broadcastChannel.js"]
+
+["browser_cookie_insecure_overwrites_secure.js"]
+
+["browser_cookie_purge_sync.js"]
+
+["browser_cookies.js"]
+support-files = ["server.sjs"]
+
+["browser_cookies_ipv6.js"]
+
+["browser_domCache.js"]
+
+["browser_indexedDB.js"]
+
+["browser_originattributes.js"]
+
+["browser_oversize.js"]
+support-files = ["oversize.sjs"]
+
+["browser_partitionedConsole.js"]
+support-files = ["partitioned.sjs"]
+
+["browser_partitioned_telemetry.js"]
+support-files = ["partitioned.sjs"]
+
+["browser_sameSiteConsole.js"]
+support-files = ["sameSite.sjs"]
+
+["browser_serviceWorker.js"]
+
+["browser_sharedWorker.js"]
+
+["browser_storage.js"]
diff --git a/netwerk/cookie/test/browser/browser_broadcastChannel.js b/netwerk/cookie/test/browser/browser_broadcastChannel.js
new file mode 100644
index 0000000000..ee561e6f0c
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_broadcastChannel.js
@@ -0,0 +1,80 @@
+// BroadcastChannel is not considered part of CookieJar. It's not allowed to
+// communicate with other windows with different cookie jar settings.
+"use strict";
+
+CookiePolicyHelper.runTest("BroadcastChannel", {
+ cookieJarAccessAllowed: async w => {
+ new w.BroadcastChannel("hello");
+ ok(true, "BroadcastChannel be used");
+ },
+
+ cookieJarAccessDenied: async w => {
+ try {
+ new w.BroadcastChannel("hello");
+ ok(false, "BroadcastChannel cannot be used!");
+ } catch (e) {
+ ok(true, "BroadcastChannel cannot be used!");
+ is(e.name, "SecurityError", "We want a security error message.");
+ }
+ },
+});
+
+CookiePolicyHelper.runTest("BroadcastChannel in workers", {
+ cookieJarAccessAllowed: async w => {
+ function nonBlockingCode() {
+ new BroadcastChannel("hello");
+ postMessage(true);
+ }
+
+ let blob = new w.Blob([
+ nonBlockingCode.toString() + "; nonBlockingCode();",
+ ]);
+ ok(blob, "Blob has been created");
+
+ let blobURL = w.URL.createObjectURL(blob);
+ ok(blobURL, "Blob URL has been created");
+
+ let worker = new w.Worker(blobURL);
+ ok(worker, "Worker has been created");
+
+ await new w.Promise((resolve, reject) => {
+ worker.onmessage = function (e) {
+ if (e) {
+ resolve();
+ } else {
+ reject();
+ }
+ };
+ });
+ },
+
+ cookieJarAccessDenied: async w => {
+ function blockingCode() {
+ try {
+ new BroadcastChannel("hello");
+ postMessage(false);
+ } catch (e) {
+ postMessage(e.name == "SecurityError");
+ }
+ }
+
+ let blob = new w.Blob([blockingCode.toString() + "; blockingCode();"]);
+ ok(blob, "Blob has been created");
+
+ let blobURL = w.URL.createObjectURL(blob);
+ ok(blobURL, "Blob URL has been created");
+
+ let worker = new w.Worker(blobURL);
+ ok(worker, "Worker has been created");
+
+ await new w.Promise((resolve, reject) => {
+ worker.onmessage = function (e) {
+ if (e) {
+ resolve();
+ } else {
+ reject();
+ }
+ };
+ });
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js b/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js
new file mode 100644
index 0000000000..7461e5cc16
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookie_insecure_overwrites_secure.js
@@ -0,0 +1,128 @@
+/* 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/. */
+
+let { HttpServer } = ChromeUtils.importESModule(
+ "resource://testing-common/httpd.sys.mjs"
+);
+
+const urlPath = "/browser/netwerk/cookie/test/browser/file_empty.html";
+const baseDomain = "example.com";
+
+// eslint doesn't like http
+// eslint-disable-next-line @microsoft/sdl/no-insecure-url
+const URL_INSECURE_COM = "http://" + baseDomain + urlPath;
+const URL_SECURE_COM = "https://" + baseDomain + urlPath;
+
+// common cookie strings
+const COOKIE_BASIC = "foo=one";
+const COOKIE_OTHER = "foo=two";
+const COOKIE_THIRD = "foo=three";
+const COOKIE_FORTH = "foo=four";
+
+function securify(cookie) {
+ return cookie + "; Secure";
+}
+
+registerCleanupFunction(() => {
+ Services.prefs.clearUserPref("dom.security.https_first");
+ Services.prefs.clearUserPref("network.cookie.cookieBehavior");
+ Services.prefs.clearUserPref(
+ "network.cookieJarSettings.unblocked_for_testing"
+ );
+ Services.prefs.clearUserPref("network.cookie.sameSite.laxByDefault");
+ Services.prefs.clearUserPref("network.cookie.sameSite.noneRequiresSecure");
+ Services.prefs.clearUserPref("network.cookie.sameSite.schemeful");
+ info("Cleaning up the test");
+});
+
+async function setup() {
+ // HTTPS-First would interfere with this test.
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+
+ Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", false);
+ Services.prefs.setBoolPref(
+ "network.cookie.sameSite.noneRequiresSecure",
+ false
+ );
+ Services.prefs.setBoolPref("network.cookie.sameSite.schemeful", true);
+ Services.cookies.removeAll();
+}
+add_task(setup);
+
+// note:
+// 1. The URL scheme will not matter for insecure cookies, since
+// cookies are not "schemeful" in this sense.
+// So an insecure cookie set anywhere will be visible on http and https sites
+// Secure cookies are different, they will only be visible from https sites
+// and will prevent cookie setting of the same name on insecure sites.
+//
+// 2. The different processes (tabs) shouldn't matter since
+// cookie adds/changes are distributed to other processes on a need-to-know
+// basis.
+
+add_task(async function test_insecure_cant_overwrite_secure_via_doc() {
+ // insecure
+ const tab1 = BrowserTestUtils.addTab(gBrowser, URL_INSECURE_COM);
+ const browser = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // secure
+ const tab2 = BrowserTestUtils.addTab(gBrowser, URL_SECURE_COM);
+ const browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+
+ // init with insecure cookie on insecure origin child process
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_BASIC, COOKIE_BASIC],
+ (cookie, expected) => {
+ content.document.cookie = cookie;
+ is(content.document.cookie, expected);
+ }
+ );
+
+ // insecure cookie visible on secure origin process (sanity check)
+ await SpecialPowers.spawn(browser2, [COOKIE_BASIC], expected => {
+ is(content.document.cookie, expected);
+ });
+
+ // overwrite insecure cookie on secure origin with secure cookie (sanity check)
+ await SpecialPowers.spawn(
+ browser2,
+ [securify(COOKIE_OTHER), COOKIE_OTHER],
+ (cookie, expected) => {
+ content.document.cookie = cookie;
+ is(content.document.cookie, expected);
+ }
+ );
+
+ // insecure cookie will NOT overwrite the secure one on insecure origin
+ // and cookie.document appears blank
+ await SpecialPowers.spawn(browser, [COOKIE_THIRD, ""], (cookie, expected) => {
+ content.document.cookie = cookie; // quiet failure here
+ is(content.document.cookie, expected);
+ });
+
+ // insecure cookie will overwrite secure cookie on secure origin
+ // a bit weird, but this is normal
+ await SpecialPowers.spawn(
+ browser2,
+ [COOKIE_FORTH, COOKIE_FORTH],
+ (cookie, expected) => {
+ content.document.cookie = cookie;
+ is(content.document.cookie, expected);
+ }
+ );
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+ Services.cookies.removeAll();
+});
diff --git a/netwerk/cookie/test/browser/browser_cookie_purge_sync.js b/netwerk/cookie/test/browser/browser_cookie_purge_sync.js
new file mode 100644
index 0000000000..186797d058
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookie_purge_sync.js
@@ -0,0 +1,141 @@
+/* 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/. */
+
+// This test checks that cookie purge broadcast correctly
+// updates content process memory as triggered by:
+// 1. stale-cookie update from content process
+// 2. delete cookie by host from parent process
+// 3. clear all cookies from parent process
+
+const URL_EXAMPLE = "https://example.com";
+const COOKIE_NAMEVALUE = "name=value";
+const COOKIE_NAMEVALUE_2 = COOKIE_NAMEVALUE + "2";
+const COOKIE_STRING = COOKIE_NAMEVALUE + "; Secure; SameSite=None";
+const COOKIE_STRING_2 = COOKIE_NAMEVALUE_2 + "; Secure; SameSite=None";
+const MAX_AGE_OLD = 2; // seconds
+const MAX_AGE_NEW = 100; // 100 sec
+
+registerCleanupFunction(() => {
+ info("Cleaning up the test setup");
+ Services.prefs.clearUserPref("network.cookie.sameSite.laxByDefault");
+ Services.cookies.removeAll();
+});
+
+add_setup(async function () {
+ info("Setting up the test");
+ Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", false);
+});
+
+function waitForNotificationPromise(notification, expected) {
+ return new Promise(resolve => {
+ function observer(subject, topic, data) {
+ is(content.document.cookie, expected);
+ Services.obs.removeObserver(observer, notification);
+ resolve();
+ }
+ Services.obs.addObserver(observer, notification);
+ });
+}
+
+add_task(async function test_purge_sync_batch_and_deleted() {
+ const tab1 = BrowserTestUtils.addTab(gBrowser, URL_EXAMPLE);
+ const browser = gBrowser.getBrowserForTab(tab1);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ const tab2 = BrowserTestUtils.addTab(gBrowser, URL_EXAMPLE);
+ const browser2 = gBrowser.getBrowserForTab(tab2);
+ await BrowserTestUtils.browserLoaded(browser2);
+
+ let firstCookieAdded = SpecialPowers.spawn(
+ browser2,
+ ["content-added-cookie", COOKIE_NAMEVALUE],
+ waitForNotificationPromise
+ );
+ await TestUtils.waitForTick(); // waiting helps --verify
+
+ // set old cookie in tab 1 and check it in tab 2
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_STRING, MAX_AGE_OLD],
+ (cookie, max_age) => {
+ content.document.cookie = cookie + ";Max-Age=" + max_age;
+ }
+ );
+ await firstCookieAdded;
+
+ // wait until the first cookie expires
+ await SpecialPowers.spawn(browser2, [], () => {
+ return ContentTaskUtils.waitForCondition(
+ () => content.document.cookie == "",
+ "cookie did not expire in time",
+ 200
+ ).catch(msg => {
+ is(false, "Cookie did not expire in time");
+ });
+ });
+
+ // BATCH_DELETED/BatchDeleted pathway
+ let batchDeletedPromise = SpecialPowers.spawn(
+ browser,
+ ["content-batch-deleted-cookies", COOKIE_NAMEVALUE_2],
+ waitForNotificationPromise
+ );
+ await TestUtils.waitForTick(); // waiting helps --verify
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_STRING_2, MAX_AGE_NEW],
+ (cookie, max_age) => {
+ content.document.cookie = cookie + ";Max-Age=" + max_age;
+ }
+ );
+ await batchDeletedPromise;
+
+ // COOKIE_DELETED/RemoveCookie pathway
+ let cookieRemovedPromise = SpecialPowers.spawn(
+ browser,
+ ["content-removed-cookie", ""],
+ waitForNotificationPromise
+ );
+ let cookieRemovedPromise2 = SpecialPowers.spawn(
+ browser2,
+ ["content-removed-cookie", ""],
+ waitForNotificationPromise
+ );
+ await TestUtils.waitForTick();
+ Services.cookies.removeCookiesFromExactHost(
+ "example.com",
+ JSON.stringify({})
+ );
+ await cookieRemovedPromise;
+ await cookieRemovedPromise2;
+
+ // cleanup prep
+ let anotherCookieAdded = SpecialPowers.spawn(
+ browser2,
+ ["content-added-cookie", COOKIE_NAMEVALUE],
+ waitForNotificationPromise
+ );
+ await TestUtils.waitForTick(); // waiting helps --verify
+ await SpecialPowers.spawn(
+ browser,
+ [COOKIE_STRING, MAX_AGE_NEW],
+ (cookie, max_age) => {
+ content.document.cookie = cookie + ";Max-Age=" + max_age;
+ }
+ );
+ await anotherCookieAdded;
+
+ // ALL_COOKIES_CLEARED/RemoveAll pathway
+ let cleanup = SpecialPowers.spawn(
+ browser2,
+ ["content-removed-all-cookies", ""],
+ waitForNotificationPromise
+ );
+ await TestUtils.waitForTick();
+ Services.cookies.removeAll();
+ await cleanup;
+
+ BrowserTestUtils.removeTab(tab1);
+ BrowserTestUtils.removeTab(tab2);
+});
diff --git a/netwerk/cookie/test/browser/browser_cookies.js b/netwerk/cookie/test/browser/browser_cookies.js
new file mode 100644
index 0000000000..8a0d8332bf
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookies.js
@@ -0,0 +1,53 @@
+"use strict";
+
+CookiePolicyHelper.runTest("document.cookies", {
+ cookieJarAccessAllowed: async _ => {
+ let hasCookie = !!content.document.cookie.length;
+
+ await content
+ .fetch("server.sjs")
+ .then(r => r.text())
+ .then(text => {
+ is(
+ text,
+ hasCookie ? "cookie-present" : "cookie-not-present",
+ "document.cookie is consistent with fetch requests"
+ );
+ });
+
+ content.document.cookie = "name=value";
+ ok(content.document.cookie.includes("name=value"), "Some cookies for me");
+ ok(content.document.cookie.includes("foopy=1"), "Some cookies for me");
+
+ await content
+ .fetch("server.sjs")
+ .then(r => r.text())
+ .then(text => {
+ is(text, "cookie-present", "We should have cookies");
+ });
+
+ ok(!!content.document.cookie.length, "Some Cookies for me");
+ },
+
+ cookieJarAccessDenied: async _ => {
+ is(content.document.cookie, "", "No cookies for me");
+ content.document.cookie = "name=value";
+ is(content.document.cookie, "", "No cookies for me");
+
+ await content
+ .fetch("server.sjs")
+ .then(r => r.text())
+ .then(text => {
+ is(text, "cookie-not-present", "We should not have cookies");
+ });
+ // Let's do it twice.
+ await content
+ .fetch("server.sjs")
+ .then(r => r.text())
+ .then(text => {
+ is(text, "cookie-not-present", "We should not have cookies");
+ });
+
+ is(content.document.cookie, "", "Still no cookies for me");
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_cookies_ipv6.js b/netwerk/cookie/test/browser/browser_cookies_ipv6.js
new file mode 100644
index 0000000000..088a76f4e8
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_cookies_ipv6.js
@@ -0,0 +1,57 @@
+"use strict";
+
+let { HttpServer } = ChromeUtils.importESModule(
+ "resource://testing-common/httpd.sys.mjs"
+);
+
+let gHttpServer = null;
+let ip = "[::1]";
+
+function contentHandler(metadata, response) {
+ response.setStatusLine(metadata.httpVersion, 200, "Ok");
+ response.setHeader("Content-Type", "text/html", false);
+ let body = `
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+ <meta charset='utf-8'>
+ <title>Cookie ipv6 Test</title>
+ </head>
+ <body>
+ </body>
+ </html>`;
+ response.bodyOutputStream.write(body, body.length);
+}
+
+add_task(async _ => {
+ if (!gHttpServer) {
+ gHttpServer = new HttpServer();
+ gHttpServer.registerPathHandler("/content", contentHandler);
+ gHttpServer._start(-1, ip);
+ }
+
+ registerCleanupFunction(() => {
+ gHttpServer.stop(() => {
+ gHttpServer = null;
+ });
+ });
+
+ let serverPort = gHttpServer.identity.primaryPort;
+ let testURL = `http://${ip}:${serverPort}/content`;
+
+ // Let's open our tab.
+ const tab = BrowserTestUtils.addTab(gBrowser, testURL);
+ gBrowser.selectedTab = tab;
+
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Test if we can set and get document.cookie successfully.
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.cookie = "foo=bar";
+ is(content.document.cookie, "foo=bar");
+ });
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/netwerk/cookie/test/browser/browser_domCache.js b/netwerk/cookie/test/browser/browser_domCache.js
new file mode 100644
index 0000000000..5f1aa84d83
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_domCache.js
@@ -0,0 +1,25 @@
+"use strict";
+
+CookiePolicyHelper.runTest("DOM Cache", {
+ cookieJarAccessAllowed: async w => {
+ await w.caches.open("wow").then(
+ _ => {
+ ok(true, "DOM Cache can be used!");
+ },
+ _ => {
+ ok(false, "DOM Cache can be used!");
+ }
+ );
+ },
+
+ cookieJarAccessDenied: async w => {
+ await w.caches.open("wow").then(
+ _ => {
+ ok(false, "DOM Cache cannot be used!");
+ },
+ _ => {
+ ok(true, "DOM Cache cannot be used!");
+ }
+ );
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_indexedDB.js b/netwerk/cookie/test/browser/browser_indexedDB.js
new file mode 100644
index 0000000000..7f417077eb
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_indexedDB.js
@@ -0,0 +1,84 @@
+"use strict";
+
+CookiePolicyHelper.runTest("IndexedDB", {
+ cookieJarAccessAllowed: async w => {
+ w.indexedDB.open("test", "1");
+ ok(true, "IDB should be allowed");
+ },
+
+ cookieJarAccessDenied: async w => {
+ try {
+ w.indexedDB.open("test", "1");
+ ok(false, "IDB should be blocked");
+ } catch (e) {
+ ok(true, "IDB should be blocked");
+ is(e.name, "SecurityError", "We want a security error message.");
+ }
+ },
+});
+
+CookiePolicyHelper.runTest("IndexedDB in workers", {
+ cookieJarAccessAllowed: async w => {
+ function nonBlockCode() {
+ indexedDB.open("test", "1");
+ postMessage(true);
+ }
+
+ let blob = new w.Blob([nonBlockCode.toString() + "; nonBlockCode();"]);
+ ok(blob, "Blob has been created");
+
+ let blobURL = w.URL.createObjectURL(blob);
+ ok(blobURL, "Blob URL has been created");
+
+ let worker = new w.Worker(blobURL);
+ ok(worker, "Worker has been created");
+
+ await new w.Promise((resolve, reject) => {
+ worker.onmessage = function (e) {
+ if (e.data) {
+ resolve();
+ } else {
+ reject();
+ }
+ };
+
+ worker.onerror = function (e) {
+ reject();
+ };
+ });
+ },
+
+ cookieJarAccessDenied: async w => {
+ function blockCode() {
+ try {
+ indexedDB.open("test", "1");
+ postMessage(false);
+ } catch (e) {
+ postMessage(e.name == "SecurityError");
+ }
+ }
+
+ let blob = new w.Blob([blockCode.toString() + "; blockCode();"]);
+ ok(blob, "Blob has been created");
+
+ let blobURL = w.URL.createObjectURL(blob);
+ ok(blobURL, "Blob URL has been created");
+
+ let worker = new w.Worker(blobURL);
+ ok(worker, "Worker has been created");
+
+ await new w.Promise((resolve, reject) => {
+ worker.onmessage = function (e) {
+ if (e.data) {
+ resolve();
+ } else {
+ reject();
+ }
+ };
+
+ worker.onerror = function (e) {
+ reject();
+ };
+ });
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_originattributes.js b/netwerk/cookie/test/browser/browser_originattributes.js
new file mode 100644
index 0000000000..fab7e67b2e
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_originattributes.js
@@ -0,0 +1,121 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const USER_CONTEXTS = ["default", "personal", "work"];
+
+const COOKIE_NAMES = ["cookie0", "cookie1", "cookie2"];
+
+const TEST_URL =
+ "http://example.com/browser/netwerk/cookie/test/browser/file_empty.html";
+
+// opens `uri' in a new tab with the provided userContextId and focuses it.
+// returns the newly opened tab
+async function openTabInUserContext(uri, userContextId) {
+ // open the tab in the correct userContextId
+ let tab = BrowserTestUtils.addTab(gBrowser, uri, { userContextId });
+
+ // select tab and make sure its browser is focused
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ // wait for tab load
+ await BrowserTestUtils.browserLoaded(browser);
+
+ return { tab, browser };
+}
+
+add_setup(async function () {
+ // make sure userContext is enabled.
+ await new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ { set: [["privacy.userContext.enabled", true]] },
+ resolve
+ );
+ });
+});
+
+add_task(async function test() {
+ // load the page in 3 different contexts and set a cookie
+ // which should only be visible in that context
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // open our tab in the given user context
+ let { tab, browser } = await openTabInUserContext(TEST_URL, userContextId);
+
+ await SpecialPowers.spawn(
+ browser,
+ [{ names: COOKIE_NAMES, value: USER_CONTEXTS[userContextId] }],
+ function (opts) {
+ for (let name of opts.names) {
+ content.document.cookie = name + "=" + opts.value;
+ }
+ }
+ );
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+ }
+
+ let expectedValues = USER_CONTEXTS.slice(0);
+ await checkCookies(expectedValues, "before removal");
+
+ // remove cookies that belongs to user context id #1
+ Services.cookies.removeCookiesWithOriginAttributes(
+ JSON.stringify({ userContextId: 1 })
+ );
+
+ expectedValues[1] = undefined;
+ await checkCookies(expectedValues, "after removal");
+});
+
+async function checkCookies(expectedValues, time) {
+ for (let userContextId of Object.keys(expectedValues)) {
+ let cookiesFromTitle = await getCookiesFromJS(userContextId);
+ let cookiesFromManager = getCookiesFromManager(userContextId);
+
+ let expectedValue = expectedValues[userContextId];
+ for (let name of COOKIE_NAMES) {
+ is(
+ cookiesFromTitle[name],
+ expectedValue,
+ `User context ${userContextId}: ${name} should be correct from title ${time}`
+ );
+ is(
+ cookiesFromManager[name],
+ expectedValue,
+ `User context ${userContextId}: ${name} should be correct from manager ${time}`
+ );
+ }
+ }
+}
+
+function getCookiesFromManager(userContextId) {
+ let cookies = {};
+ let allCookies = Services.cookies.getCookiesWithOriginAttributes(
+ JSON.stringify({ userContextId })
+ );
+ for (let cookie of allCookies) {
+ cookies[cookie.name] = cookie.value;
+ }
+ return cookies;
+}
+
+async function getCookiesFromJS(userContextId) {
+ let { tab, browser } = await openTabInUserContext(TEST_URL, userContextId);
+
+ // get the cookies
+ let cookieString = await SpecialPowers.spawn(browser, [], function () {
+ return content.document.cookie;
+ });
+
+ // check each item in the title and validate it meets expectatations
+ let cookies = {};
+ for (let cookie of cookieString.split(";")) {
+ let [name, value] = cookie.trim().split("=");
+ cookies[name] = value;
+ }
+
+ gBrowser.removeTab(tab);
+ return cookies;
+}
diff --git a/netwerk/cookie/test/browser/browser_oversize.js b/netwerk/cookie/test/browser/browser_oversize.js
new file mode 100644
index 0000000000..f6e1f8a70b
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_oversize.js
@@ -0,0 +1,96 @@
+"use strict";
+
+const OVERSIZE_DOMAIN = "http://example.com/";
+const OVERSIZE_PATH = "browser/netwerk/cookie/test/browser/";
+const OVERSIZE_TOP_PAGE = OVERSIZE_DOMAIN + OVERSIZE_PATH + "oversize.sjs";
+
+add_task(async _ => {
+ const expected = [];
+
+ const consoleListener = {
+ observe(what) {
+ if (!(what instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+
+ info("Console Listener: " + what);
+ for (let i = expected.length - 1; i >= 0; --i) {
+ const e = expected[i];
+
+ if (what.message.includes(e.match)) {
+ ok(true, "Message received: " + e.match);
+ expected.splice(i, 1);
+ e.resolve();
+ }
+ }
+ },
+ };
+
+ Services.console.registerListener(consoleListener);
+
+ registerCleanupFunction(() =>
+ Services.console.unregisterListener(consoleListener)
+ );
+
+ const netPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “a” is invalid because its size is too big. Max size is 4096 B.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “b” is invalid because its path size is too big. Max size is 1024 B.",
+ });
+ }),
+ ];
+
+ // Let's open our tab.
+ const tab = BrowserTestUtils.addTab(gBrowser, OVERSIZE_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Let's wait for the first set of console events.
+ await Promise.all(netPromises);
+
+ // the DOM list of events.
+ const domPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “d” is invalid because its size is too big. Max size is 4096 B.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “e” is invalid because its path size is too big. Max size is 1024 B.",
+ });
+ }),
+ ];
+
+ // Let's use document.cookie
+ SpecialPowers.spawn(browser, [], () => {
+ const maxBytesPerCookie = 4096;
+ const maxBytesPerCookiePath = 1024;
+ content.document.cookie = "d=" + Array(maxBytesPerCookie + 1).join("x");
+ content.document.cookie =
+ "e=f; path=/" + Array(maxBytesPerCookiePath + 1).join("x");
+ });
+
+ // Let's wait for the dom events.
+ await Promise.all(domPromises);
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/netwerk/cookie/test/browser/browser_partitionedConsole.js b/netwerk/cookie/test/browser/browser_partitionedConsole.js
new file mode 100644
index 0000000000..ec834bfbcf
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_partitionedConsole.js
@@ -0,0 +1,201 @@
+"use strict";
+
+const DOMAIN = "https://example.com/";
+const PATH = "browser/netwerk/cookie/test/browser/";
+const TOP_PAGE = DOMAIN + PATH + "file_empty.html";
+
+// Run the test with CHIPS disabled, expecting a warning message
+add_task(async _ => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", false]],
+ });
+
+ const expected = [];
+
+ const consoleListener = {
+ observe(what) {
+ if (!(what instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+
+ info("Console Listener: " + what);
+ for (let i = expected.length - 1; i >= 0; --i) {
+ const e = expected[i];
+
+ if (what.message.includes(e.match)) {
+ ok(true, "Message received: " + e.match);
+ expected.splice(i, 1);
+ e.resolve();
+ }
+ }
+ },
+ };
+
+ Services.console.registerListener(consoleListener);
+
+ registerCleanupFunction(() =>
+ Services.console.unregisterListener(consoleListener)
+ );
+
+ const netPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “a” will soon be rejected because it is foreign and does not have the “Partitioned“ attribute.",
+ });
+ }),
+ ];
+
+ // Let's open our tab.
+ const tab = BrowserTestUtils.addTab(gBrowser, TOP_PAGE);
+ gBrowser.selectedTab = tab;
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Set cookies with cross-site HTTP
+ await SpecialPowers.spawn(browser, [], async function () {
+ await content.fetch(
+ "https://example.org/browser/netwerk/cookie/test/browser/partitioned.sjs",
+ { credentials: "include" }
+ );
+ });
+
+ // Let's wait for the first set of console events.
+ await Promise.all(netPromises);
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
+
+// Run the test with CHIPS enabled, expecting a different warning message
+add_task(async _ => {
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", true]],
+ });
+
+ const expected = [];
+
+ const consoleListener = {
+ observe(what) {
+ if (!(what instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+
+ info("Console Listener: " + what);
+ for (let i = expected.length - 1; i >= 0; --i) {
+ const e = expected[i];
+
+ if (what.message.includes(e.match)) {
+ ok(true, "Message received: " + e.match);
+ expected.splice(i, 1);
+ e.resolve();
+ }
+ }
+ },
+ };
+
+ Services.console.registerListener(consoleListener);
+
+ registerCleanupFunction(() =>
+ Services.console.unregisterListener(consoleListener)
+ );
+
+ const netPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “a” has been rejected because it is foreign and does not have the “Partitioned“ attribute.",
+ });
+ }),
+ ];
+
+ // Let's open our tab.
+ const tab = BrowserTestUtils.addTab(gBrowser, TOP_PAGE);
+ gBrowser.selectedTab = tab;
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Set cookies with cross-site HTTP
+ await SpecialPowers.spawn(browser, [], async function () {
+ await content.fetch(
+ "https://example.org/browser/netwerk/cookie/test/browser/partitioned.sjs",
+ { credentials: "include" }
+ );
+ });
+
+ // Let's wait for the first set of console events.
+ await Promise.all(netPromises);
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
+
+// Run the test with CHIPS enabled, ensuring the partitioned cookies require
+// secure context.
+add_task(async function partitionedAttrRequiresSecure() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior.optInPartitioning", true]],
+ });
+
+ // Clear all cookies before testing.
+ Services.cookies.removeAll();
+
+ const expected = [];
+
+ const consoleListener = {
+ observe(what) {
+ if (!(what instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+
+ info("Console Listener: " + what);
+ for (let i = expected.length - 1; i >= 0; --i) {
+ const e = expected[i];
+
+ if (what.message.includes(e.match)) {
+ ok(true, "Message received: " + e.match);
+ expected.splice(i, 1);
+ e.resolve();
+ }
+ }
+ },
+ };
+
+ Services.console.registerListener(consoleListener);
+
+ registerCleanupFunction(() =>
+ Services.console.unregisterListener(consoleListener)
+ );
+
+ const netPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “c” has been rejected because it has the “Partitioned” attribute but is missing the “secure” attribute.",
+ });
+ }),
+ ];
+
+ // Let's open our tab.
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TOP_PAGE);
+
+ // Set cookies with cross-site HTTP
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ await content.fetch(
+ "https://example.org/browser/netwerk/cookie/test/browser/partitioned.sjs?nosecure",
+ { credentials: "include" }
+ );
+ });
+
+ // Let's wait for the first set of console events.
+ await Promise.all(netPromises);
+
+ // Ensure no cookie is set.
+ is(Services.cookies.cookies.length, 0, "No cookie is set.");
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/netwerk/cookie/test/browser/browser_partitioned_telemetry.js b/netwerk/cookie/test/browser/browser_partitioned_telemetry.js
new file mode 100644
index 0000000000..f89bcdd189
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_partitioned_telemetry.js
@@ -0,0 +1,134 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const TEST_URL =
+ "https://example.com/browser/netwerk/cookie/test/browser/file_empty.html";
+
+async function validateTelemetryValues(
+ { setCookies, setForeigns, setPartitioneds, setForeignPartitioneds },
+ message
+) {
+ await Services.fog.testFlushAllChildren();
+ let setCookieTelemetry = Glean.networking.setCookie.testGetValue();
+ is(
+ setCookieTelemetry ?? undefined,
+ setCookies,
+ message + " - all set cookies"
+ );
+ let foreignTelemetry = Glean.networking.setCookieForeign.testGetValue();
+ is(
+ foreignTelemetry?.numerator,
+ setForeigns,
+ message + " - foreign set cookies"
+ );
+ is(
+ foreignTelemetry?.denominator,
+ setCookies,
+ message + " - foreign set cookies denominator"
+ );
+ let partitonedTelemetry =
+ Glean.networking.setCookiePartitioned.testGetValue();
+ is(
+ partitonedTelemetry?.numerator,
+ setPartitioneds,
+ message + " - partitioned set cookies"
+ );
+ is(
+ partitonedTelemetry?.denominator,
+ setCookies,
+ message + " - partitioned set cookies denominator"
+ );
+ let foreignPartitonedTelemetry =
+ Glean.networking.setCookieForeignPartitioned.testGetValue();
+ is(
+ foreignPartitonedTelemetry?.numerator,
+ setForeignPartitioneds,
+ message + " - foreign partitioned set cookies"
+ );
+ is(
+ foreignPartitonedTelemetry?.denominator,
+ setCookies,
+ message + " - foreign partitioned set cookies denominator"
+ );
+}
+
+add_task(async () => {
+ await Services.fog.testFlushAllChildren();
+ Services.fog.testResetFOG();
+ await validateTelemetryValues({}, "initially empty");
+
+ // open a browser window for the test
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Set cookies with Javascript
+ await SpecialPowers.spawn(browser, [], function () {
+ content.document.cookie = "a=1; Partitioned; SameSite=None; Secure";
+ content.document.cookie = "b=2; SameSite=None; Secure";
+ });
+ await validateTelemetryValues(
+ {
+ setCookies: 2,
+ setForeigns: 0,
+ setPartitioneds: 1,
+ setForeignPartitioneds: 0,
+ },
+ "javascript cookie"
+ );
+
+ // Set cookies with HTTP
+ await SpecialPowers.spawn(browser, [], async function () {
+ await content.fetch("partitioned.sjs");
+ });
+ await validateTelemetryValues(
+ {
+ setCookies: 4,
+ setForeigns: 0,
+ setPartitioneds: 2,
+ setForeignPartitioneds: 0,
+ },
+ "same site fetch"
+ );
+
+ // Set cookies with cross-site HTTP
+ await SpecialPowers.spawn(browser, [], async function () {
+ await content.fetch(
+ "https://example.org/browser/netwerk/cookie/test/browser/partitioned.sjs",
+ { credentials: "include" }
+ );
+ });
+ await validateTelemetryValues(
+ {
+ setCookies: 6,
+ setForeigns: 2,
+ setPartitioneds: 3,
+ setForeignPartitioneds: 1,
+ },
+ "foreign fetch"
+ );
+
+ // Set cookies with cross-site HTTP redirect
+ await SpecialPowers.spawn(browser, [], async function () {
+ await content.fetch(
+ encodeURI(
+ "https://example.org/browser/netwerk/cookie/test/browser/partitioned.sjs?redirect=https://example.com/browser/netwerk/cookie/test/browser/partitioned.sjs?nocookie"
+ ),
+ { credentials: "include" }
+ );
+ });
+
+ await validateTelemetryValues(
+ {
+ setCookies: 8,
+ setForeigns: 4,
+ setPartitioneds: 4,
+ setForeignPartitioneds: 2,
+ },
+ "foreign fetch redirect"
+ );
+
+ // remove the tab
+ gBrowser.removeTab(tab);
+});
diff --git a/netwerk/cookie/test/browser/browser_sameSiteConsole.js b/netwerk/cookie/test/browser/browser_sameSiteConsole.js
new file mode 100644
index 0000000000..84527296b2
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_sameSiteConsole.js
@@ -0,0 +1,133 @@
+"use strict";
+
+const SAMESITE_DOMAIN = "http://example.com/";
+const SAMESITE_PATH = "browser/netwerk/cookie/test/browser/";
+const SAMESITE_TOP_PAGE = SAMESITE_DOMAIN + SAMESITE_PATH + "sameSite.sjs";
+
+add_task(async _ => {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["network.cookie.sameSite.laxByDefault", true],
+ ["network.cookie.sameSite.noneRequiresSecure", true],
+ ],
+ });
+
+ const expected = [];
+
+ const consoleListener = {
+ observe(what) {
+ if (!(what instanceof Ci.nsIConsoleMessage)) {
+ return;
+ }
+
+ info("Console Listener: " + what);
+ for (let i = expected.length - 1; i >= 0; --i) {
+ const e = expected[i];
+
+ if (what.message.includes(e.match)) {
+ ok(true, "Message received: " + e.match);
+ expected.splice(i, 1);
+ e.resolve();
+ }
+ }
+ },
+ };
+
+ Services.console.registerListener(consoleListener);
+
+ registerCleanupFunction(() =>
+ Services.console.unregisterListener(consoleListener)
+ );
+
+ const netPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “a” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “b” rejected because it has the “SameSite=None” attribute but is missing the “secure” attribute.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Invalid “SameSite“ value for cookie “c”. The supported values are: “Lax“, “Strict“, “None“.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “c” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.",
+ });
+ }),
+ ];
+
+ // Let's open our tab.
+ const tab = BrowserTestUtils.addTab(gBrowser, SAMESITE_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ const browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Let's wait for the first set of console events.
+ await Promise.all(netPromises);
+
+ // the DOM list of events.
+ const domPromises = [
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “d” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “e” rejected because it has the “SameSite=None” attribute but is missing the “secure” attribute.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Invalid “SameSite“ value for cookie “f”. The supported values are: “Lax“, “Strict“, “None“.",
+ });
+ }),
+
+ new Promise(resolve => {
+ expected.push({
+ resolve,
+ match:
+ "Cookie “f” has “SameSite” policy set to “Lax” because it is missing a “SameSite” attribute, and “SameSite=Lax” is the default value for this attribute.",
+ });
+ }),
+ ];
+
+ // Let's use document.cookie
+ SpecialPowers.spawn(browser, [], () => {
+ content.document.cookie = "d=4";
+ content.document.cookie = "e=5; sameSite=none";
+ content.document.cookie = "f=6; sameSite=batmat";
+ });
+
+ // Let's wait for the dom events.
+ await Promise.all(domPromises);
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/netwerk/cookie/test/browser/browser_serviceWorker.js b/netwerk/cookie/test/browser/browser_serviceWorker.js
new file mode 100644
index 0000000000..2a5c963535
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_serviceWorker.js
@@ -0,0 +1,45 @@
+"use strict";
+
+CookiePolicyHelper.runTest("ServiceWorker", {
+ prefs: [
+ ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+ ["dom.ipc.processCount", 1],
+ ["dom.serviceWorkers.enabled", true],
+ ["dom.serviceWorkers.testing.enabled", true],
+ ],
+
+ cookieJarAccessAllowed: async w => {
+ await w.navigator.serviceWorker
+ .register("file_empty.js")
+ .then(
+ reg => {
+ ok(true, "ServiceWorker can be used!");
+ return reg;
+ },
+ _ => {
+ ok(false, "ServiceWorker cannot be used! " + _);
+ }
+ )
+ .then(
+ reg => reg.unregister(),
+ _ => {
+ ok(false, "unregister failed");
+ }
+ )
+ .catch(e => ok(false, "Promise rejected: " + e));
+ },
+
+ cookieJarAccessDenied: async w => {
+ await w.navigator.serviceWorker
+ .register("file_empty.js")
+ .then(
+ _ => {
+ ok(false, "ServiceWorker cannot be used!");
+ },
+ _ => {
+ ok(true, "ServiceWorker cannot be used!");
+ }
+ )
+ .catch(e => ok(false, "Promise rejected: " + e));
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_sharedWorker.js b/netwerk/cookie/test/browser/browser_sharedWorker.js
new file mode 100644
index 0000000000..88a8b3f0e7
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_sharedWorker.js
@@ -0,0 +1,18 @@
+"use strict";
+
+CookiePolicyHelper.runTest("SharedWorker", {
+ cookieJarAccessAllowed: async w => {
+ new w.SharedWorker("a.js", "foo");
+ ok(true, "SharedWorker is allowed");
+ },
+
+ cookieJarAccessDenied: async w => {
+ try {
+ new w.SharedWorker("a.js", "foo");
+ ok(false, "SharedWorker cannot be used!");
+ } catch (e) {
+ ok(true, "SharedWorker cannot be used!");
+ is(e.name, "SecurityError", "We want a security error message.");
+ }
+ },
+});
diff --git a/netwerk/cookie/test/browser/browser_storage.js b/netwerk/cookie/test/browser/browser_storage.js
new file mode 100644
index 0000000000..1e37b1a367
--- /dev/null
+++ b/netwerk/cookie/test/browser/browser_storage.js
@@ -0,0 +1,43 @@
+"use strict";
+
+CookiePolicyHelper.runTest("SessionStorage", {
+ cookieJarAccessAllowed: async w => {
+ try {
+ w.sessionStorage.foo = 42;
+ ok(true, "SessionStorage works");
+ } catch (e) {
+ ok(false, "SessionStorage works");
+ }
+ },
+
+ cookieJarAccessDenied: async w => {
+ try {
+ w.sessionStorage.foo = 42;
+ ok(false, "SessionStorage doesn't work");
+ } catch (e) {
+ ok(true, "SessionStorage doesn't work");
+ is(e.name, "SecurityError", "We want a security error message.");
+ }
+ },
+});
+
+CookiePolicyHelper.runTest("LocalStorage", {
+ cookieJarAccessAllowed: async w => {
+ try {
+ w.localStorage.foo = 42;
+ ok(true, "LocalStorage works");
+ } catch (e) {
+ ok(false, "LocalStorage works");
+ }
+ },
+
+ cookieJarAccessDenied: async w => {
+ try {
+ w.localStorage.foo = 42;
+ ok(false, "LocalStorage doesn't work");
+ } catch (e) {
+ ok(true, "LocalStorage doesn't work");
+ is(e.name, "SecurityError", "We want a security error message.");
+ }
+ },
+});
diff --git a/netwerk/cookie/test/browser/file_empty.html b/netwerk/cookie/test/browser/file_empty.html
new file mode 100644
index 0000000000..78b64149c4
--- /dev/null
+++ b/netwerk/cookie/test/browser/file_empty.html
@@ -0,0 +1,2 @@
+<html><body>
+</body></html>
diff --git a/netwerk/cookie/test/browser/file_empty.js b/netwerk/cookie/test/browser/file_empty.js
new file mode 100644
index 0000000000..3053583c76
--- /dev/null
+++ b/netwerk/cookie/test/browser/file_empty.js
@@ -0,0 +1 @@
+/* nothing here */
diff --git a/netwerk/cookie/test/browser/head.js b/netwerk/cookie/test/browser/head.js
new file mode 100644
index 0000000000..609ad683db
--- /dev/null
+++ b/netwerk/cookie/test/browser/head.js
@@ -0,0 +1,201 @@
+const { PermissionTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PermissionTestUtils.sys.mjs"
+);
+
+const BEHAVIOR_ACCEPT = Ci.nsICookieService.BEHAVIOR_ACCEPT;
+const BEHAVIOR_REJECT = Ci.nsICookieService.BEHAVIOR_REJECT;
+
+const PERM_DEFAULT = Ci.nsICookiePermission.ACCESS_DEFAULT;
+const PERM_ALLOW = Ci.nsICookiePermission.ACCESS_ALLOW;
+const PERM_DENY = Ci.nsICookiePermission.ACCESS_DENY;
+
+const TEST_DOMAIN = "https://example.com/";
+const TEST_PATH = "browser/netwerk/cookie/test/browser/";
+const TEST_TOP_PAGE = TEST_DOMAIN + TEST_PATH + "file_empty.html";
+
+// Helper to eval() provided cookieJarAccessAllowed and cookieJarAccessDenied
+// toString()ed optionally async function in freshly created tabs with
+// BEHAVIOR_ACCEPT and BEHAVIOR_REJECT configured, respectively, in a number of
+// permutations. This includes verifying that changing the permission while the
+// page is open still results in the state of the permission when the
+// document/global was created still applying. Code will execute in the
+// ContentTask.spawn frame-script context, use content to access the underlying
+// page.
+this.CookiePolicyHelper = {
+ runTest(testName, config) {
+ // Testing allowed to blocked by cookie behavior
+ this._createTest(
+ testName,
+ config.cookieJarAccessAllowed,
+ config.cookieJarAccessDenied,
+ config.prefs,
+ {
+ fromBehavior: BEHAVIOR_ACCEPT,
+ toBehavior: BEHAVIOR_REJECT,
+ fromPermission: PERM_DEFAULT,
+ toPermission: PERM_DEFAULT,
+ }
+ );
+
+ // Testing blocked to allowed by cookie behavior
+ this._createTest(
+ testName,
+ config.cookieJarAccessDenied,
+ config.cookieJarAccessAllowed,
+ config.prefs,
+ {
+ fromBehavior: BEHAVIOR_REJECT,
+ toBehavior: BEHAVIOR_ACCEPT,
+ fromPermission: PERM_DEFAULT,
+ toPermission: PERM_DEFAULT,
+ }
+ );
+
+ // Testing allowed to blocked by cookie permission
+ this._createTest(
+ testName,
+ config.cookieJarAccessAllowed,
+ config.cookieJarAccessDenied,
+ config.prefs,
+ {
+ fromBehavior: BEHAVIOR_REJECT,
+ toBehavior: BEHAVIOR_REJECT,
+ fromPermission: PERM_ALLOW,
+ toPermission: PERM_DEFAULT,
+ }
+ );
+
+ // Testing blocked to allowed by cookie permission
+ this._createTest(
+ testName,
+ config.cookieJarAccessDenied,
+ config.cookieJarAccessAllowed,
+ config.prefs,
+ {
+ fromBehavior: BEHAVIOR_ACCEPT,
+ toBehavior: BEHAVIOR_ACCEPT,
+ fromPermission: PERM_DENY,
+ toPermission: PERM_DEFAULT,
+ }
+ );
+ },
+
+ _createTest(testName, goodCb, badCb, prefs, config) {
+ add_task(async _ => {
+ info("Starting " + testName + ": " + config.toSource());
+
+ await SpecialPowers.flushPrefEnv();
+
+ if (prefs) {
+ await SpecialPowers.pushPrefEnv({ set: prefs });
+ }
+
+ // Let's set the first cookie pref.
+ PermissionTestUtils.add(TEST_DOMAIN, "cookie", config.fromPermission);
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior", config.fromBehavior]],
+ });
+
+ // Let's open a tab and load content.
+ let tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Let's create an iframe.
+ await SpecialPowers.spawn(
+ browser,
+ [{ url: TEST_TOP_PAGE }],
+ async obj => {
+ return new content.Promise(resolve => {
+ let ifr = content.document.createElement("iframe");
+ ifr.setAttribute("id", "iframe");
+ ifr.src = obj.url;
+ ifr.onload = () => resolve();
+ content.document.body.appendChild(ifr);
+ });
+ }
+ );
+
+ // Let's exec the "good" callback.
+ info(
+ "Executing the test after setting the cookie behavior to " +
+ config.fromBehavior +
+ " and permission to " +
+ config.fromPermission
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [{ callback: goodCb.toString() }],
+ async obj => {
+ let runnableStr = `(() => {return (${obj.callback});})();`;
+ let runnable = eval(runnableStr); // eslint-disable-line no-eval
+ await runnable(content);
+
+ let ifr = content.document.getElementById("iframe");
+ await runnable(ifr.contentWindow);
+ }
+ );
+
+ // Now, let's change the cookie settings
+ PermissionTestUtils.add(TEST_DOMAIN, "cookie", config.toPermission);
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior", config.toBehavior]],
+ });
+
+ // We still want the good callback to succeed.
+ info(
+ "Executing the test after setting the cookie behavior to " +
+ config.toBehavior +
+ " and permission to " +
+ config.toPermission
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [{ callback: goodCb.toString() }],
+ async obj => {
+ let runnableStr = `(() => {return (${obj.callback});})();`;
+ let runnable = eval(runnableStr); // eslint-disable-line no-eval
+ await runnable(content);
+
+ let ifr = content.document.getElementById("iframe");
+ await runnable(ifr.contentWindow);
+ }
+ );
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+
+ // Let's open a new tab and load content again.
+ tab = BrowserTestUtils.addTab(gBrowser, TEST_TOP_PAGE);
+ gBrowser.selectedTab = tab;
+
+ browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ // Let's exec the "bad" callback.
+ info("Executing the test in a new tab");
+ await SpecialPowers.spawn(
+ browser,
+ [{ callback: badCb.toString() }],
+ async obj => {
+ let runnableStr = `(() => {return (${obj.callback});})();`;
+ let runnable = eval(runnableStr); // eslint-disable-line no-eval
+ await runnable(content);
+ }
+ );
+
+ // Let's close the tab.
+ BrowserTestUtils.removeTab(tab);
+
+ // Cleanup.
+ await new Promise(resolve => {
+ Services.clearData.deleteData(
+ Ci.nsIClearDataService.CLEAR_ALL,
+ resolve
+ );
+ });
+ });
+ },
+};
diff --git a/netwerk/cookie/test/browser/oversize.sjs b/netwerk/cookie/test/browser/oversize.sjs
new file mode 100644
index 0000000000..dfe2f31645
--- /dev/null
+++ b/netwerk/cookie/test/browser/oversize.sjs
@@ -0,0 +1,17 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+ const maxBytesPerCookie = 4096;
+ const maxBytesPerCookiePath = 1024;
+
+ aResponse.setHeader(
+ "Set-Cookie",
+ "a=" + Array(maxBytesPerCookie + 1).join("x"),
+ true
+ );
+ aResponse.setHeader(
+ "Set-Cookie",
+ "b=c; path=/" + Array(maxBytesPerCookiePath + 1).join("x"),
+ true
+ );
+}
diff --git a/netwerk/cookie/test/browser/partitioned.sjs b/netwerk/cookie/test/browser/partitioned.sjs
new file mode 100644
index 0000000000..5649b88f2f
--- /dev/null
+++ b/netwerk/cookie/test/browser/partitioned.sjs
@@ -0,0 +1,32 @@
+function handleRequest(aRequest, aResponse) {
+ if (aRequest.hasHeader("Origin")) {
+ let origin = aRequest.getHeader("Origin");
+ aResponse.setHeader("Access-Control-Allow-Origin", origin);
+ aResponse.setHeader("Access-Control-Allow-Credentials", "true");
+ }
+
+ var params = new URLSearchParams(aRequest.queryString);
+ if (params.has("redirect")) {
+ aResponse.setHeader("Location", params.get("redirect"));
+ aResponse.setStatusLine(aRequest.httpVersion, 302);
+ } else {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ }
+
+ if (params.has("nocookie")) {
+ return;
+ }
+
+ if (params.has("nosecure")) {
+ aResponse.setHeader("Set-Cookie", "c=3; Partitioned;", true);
+
+ return;
+ }
+
+ aResponse.setHeader("Set-Cookie", "a=1; SameSite=None; Secure", true);
+ aResponse.setHeader(
+ "Set-Cookie",
+ "b=2; Partitioned; SameSite=None; Secure",
+ true
+ );
+}
diff --git a/netwerk/cookie/test/browser/sameSite.sjs b/netwerk/cookie/test/browser/sameSite.sjs
new file mode 100644
index 0000000000..a19624d2cb
--- /dev/null
+++ b/netwerk/cookie/test/browser/sameSite.sjs
@@ -0,0 +1,7 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+ aResponse.setHeader("Set-Cookie", "a=1", true);
+ aResponse.setHeader("Set-Cookie", "b=2; sameSite=none", true);
+ aResponse.setHeader("Set-Cookie", "c=3; sameSite=batman", true);
+}
diff --git a/netwerk/cookie/test/browser/server.sjs b/netwerk/cookie/test/browser/server.sjs
new file mode 100644
index 0000000000..86835914bb
--- /dev/null
+++ b/netwerk/cookie/test/browser/server.sjs
@@ -0,0 +1,9 @@
+function handleRequest(aRequest, aResponse) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ if (aRequest.hasHeader("Cookie")) {
+ aResponse.write("cookie-present");
+ } else {
+ aResponse.setHeader("Set-Cookie", "foopy=1");
+ aResponse.write("cookie-not-present");
+ }
+}
diff --git a/netwerk/cookie/test/mochitest/cookie.sjs b/netwerk/cookie/test/mochitest/cookie.sjs
new file mode 100644
index 0000000000..75d4e638b4
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/cookie.sjs
@@ -0,0 +1,166 @@
+function handleRequest(aRequest, aResponse) {
+ let parts = aRequest.queryString.split("&");
+ if (parts.includes("window")) {
+ aResponse.setStatusLine(aRequest.httpVersion, 200);
+ aResponse.setHeader("Content-Type", "text/html");
+ aResponse.setHeader("Clear-Site-Data", '"cache", "cookies", "storage"');
+ aResponse.write("<body><h1>Welcome</h1></body>");
+ return;
+ }
+
+ if (parts.includes("fetch")) {
+ setState(
+ "data",
+ JSON.stringify({ type: "fetch", hasCookie: aRequest.hasHeader("Cookie") })
+ );
+ aResponse.write("Hello world!");
+ return;
+ }
+
+ if (parts.includes("xhr")) {
+ setState(
+ "data",
+ JSON.stringify({ type: "xhr", hasCookie: aRequest.hasHeader("Cookie") })
+ );
+ aResponse.write("Hello world!");
+ return;
+ }
+
+ if (parts.includes("image")) {
+ setState(
+ "data",
+ JSON.stringify({ type: "image", hasCookie: aRequest.hasHeader("Cookie") })
+ );
+
+ // A 1x1 PNG image.
+ // Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+ const IMAGE = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAA" +
+ "ACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="
+ );
+
+ aResponse.setHeader("Content-Type", "image/png", false);
+ aResponse.write(IMAGE);
+ return;
+ }
+
+ if (parts.includes("script")) {
+ setState(
+ "data",
+ JSON.stringify({
+ type: "script",
+ hasCookie: aRequest.hasHeader("Cookie"),
+ })
+ );
+
+ aResponse.setHeader("Content-Type", "text/javascript", false);
+ aResponse.write("window.scriptLoaded();");
+ return;
+ }
+
+ if (parts.includes("worker")) {
+ setState(
+ "data",
+ JSON.stringify({
+ type: "worker",
+ hasCookie: aRequest.hasHeader("Cookie"),
+ })
+ );
+
+ function w() {
+ onmessage = e => {
+ if (e.data == "subworker") {
+ importScripts("cookie.sjs?subworker&" + Math.random());
+ postMessage(42);
+ return;
+ }
+
+ if (e.data == "fetch") {
+ fetch("cookie.sjs?fetch&" + Math.random())
+ .then(r => r.text())
+ .then(_ => postMessage(42));
+ return;
+ }
+
+ if (e.data == "xhr") {
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", "cookie.sjs?xhr&" + Math.random());
+ xhr.send();
+ xhr.onload = _ => postMessage(42);
+ }
+ };
+ postMessage(42);
+ }
+
+ aResponse.setHeader("Content-Type", "text/javascript", false);
+ aResponse.write(w.toString() + "; w();");
+ return;
+ }
+
+ if (parts.includes("subworker")) {
+ setState(
+ "data",
+ JSON.stringify({
+ type: "subworker",
+ hasCookie: aRequest.hasHeader("Cookie"),
+ })
+ );
+ aResponse.setHeader("Content-Type", "text/javascript", false);
+ aResponse.write("42");
+ return;
+ }
+
+ if (parts.includes("sharedworker")) {
+ setState(
+ "data",
+ JSON.stringify({
+ type: "sharedworker",
+ hasCookie: aRequest.hasHeader("Cookie"),
+ })
+ );
+
+ // This function is exported as a string.
+ /* eslint-disable no-undef */
+ function w() {
+ onconnect = e => {
+ e.ports[0].onmessage = evt => {
+ if (evt.data == "subworker") {
+ importScripts("cookie.sjs?subworker&" + Math.random());
+ e.ports[0].postMessage(42);
+ return;
+ }
+
+ if (evt.data == "fetch") {
+ fetch("cookie.sjs?fetch&" + Math.random())
+ .then(r => r.text())
+ .then(_ => e.ports[0].postMessage(42));
+ return;
+ }
+
+ if (evt.data == "xhr") {
+ let xhr = new XMLHttpRequest();
+ xhr.open("GET", "cookie.sjs?xhr&" + Math.random());
+ xhr.send();
+ xhr.onload = _ => e.ports[0].postMessage(42);
+ }
+ };
+ e.ports[0].postMessage(42);
+ };
+ }
+ /* eslint-enable no-undef */
+
+ aResponse.setHeader("Content-Type", "text/javascript", false);
+ aResponse.write(w.toString() + "; w();");
+ return;
+ }
+
+ if (parts.includes("last")) {
+ let data = getState("data");
+ setState("data", "");
+ aResponse.write(data);
+ return;
+ }
+
+ aResponse.setStatusLine(aRequest.httpVersion, 400);
+ aResponse.write("Invalid request");
+}
diff --git a/netwerk/cookie/test/mochitest/cookiesHelper.js b/netwerk/cookie/test/mochitest/cookiesHelper.js
new file mode 100644
index 0000000000..cbff91f2f2
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/cookiesHelper.js
@@ -0,0 +1,63 @@
+const ALLOWED = 0;
+const BLOCKED = 1;
+
+async function cleanupData() {
+ await new Promise(resolve => {
+ const chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ addMessageListener("go", __ => {
+ Services.clearData.deleteData(
+ Services.clearData.CLEAR_COOKIES |
+ Services.clearData.CLEAR_ALL_CACHES |
+ Services.clearData.CLEAR_DOM_STORAGES,
+ ___ => {
+ sendAsyncMessage("done");
+ }
+ );
+ });
+ });
+
+ chromeScript.addMessageListener("done", _ => {
+ chromeScript.destroy();
+ resolve();
+ });
+
+ chromeScript.sendAsyncMessage("go");
+ });
+}
+
+async function checkLastRequest(type, state) {
+ let json = await fetch("cookie.sjs?last&" + Math.random()).then(r =>
+ r.json()
+ );
+ is(json.type, type, "Type: " + type);
+ is(json.hasCookie, state == ALLOWED, "Fetch has cookies");
+}
+
+async function runTests(currentTest) {
+ await cleanupData();
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior", 2]],
+ });
+ let windowBlocked = window.open("cookie.sjs?window&" + Math.random());
+ await new Promise(resolve => {
+ windowBlocked.onload = resolve;
+ });
+ await currentTest(windowBlocked, BLOCKED);
+ windowBlocked.close();
+
+ await cleanupData();
+ await SpecialPowers.pushPrefEnv({
+ set: [["network.cookie.cookieBehavior", 1]],
+ });
+ let windowAllowed = window.open("cookie.sjs?window&" + Math.random());
+ await new Promise(resolve => {
+ windowAllowed.onload = resolve;
+ });
+ await currentTest(windowAllowed, ALLOWED);
+ windowAllowed.close();
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
diff --git a/netwerk/cookie/test/mochitest/empty.html b/netwerk/cookie/test/mochitest/empty.html
new file mode 100644
index 0000000000..cd161cc52d
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/empty.html
@@ -0,0 +1 @@
+<h1>Nothing here</h1>
diff --git a/netwerk/cookie/test/mochitest/mochitest.toml b/netwerk/cookie/test/mochitest/mochitest.toml
new file mode 100644
index 0000000000..fdf26a7e94
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/mochitest.toml
@@ -0,0 +1,27 @@
+[DEFAULT]
+scheme = "https"
+support-files = [
+ "cookie.sjs",
+ "cookiesHelper.js",
+]
+
+["test_document_cookie.html"]
+
+["test_document_cookie_notification.html"]
+
+["test_fetch.html"]
+
+["test_image.html"]
+
+["test_metaTag.html"]
+
+["test_script.html"]
+
+["test_sharedWorker.html"]
+
+["test_worker.html"]
+
+["test_xhr.html"]
+
+["test_xmlDocument.html"]
+support-files = ["empty.html"]
diff --git a/netwerk/cookie/test/mochitest/test_document_cookie.html b/netwerk/cookie/test/mochitest/test_document_cookie.html
new file mode 100644
index 0000000000..86e7c7f661
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_document_cookie.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for document.cookie when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ is(w.document.cookie.length, 0, "No cookie to start!");
+ w.document.cookie = "name=value";
+ is(w.document.cookie.includes("name=value"), state == ALLOWED, "Some cookies for me");
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_document_cookie_notification.html b/netwerk/cookie/test/mochitest/test_document_cookie_notification.html
new file mode 100644
index 0000000000..b84b6ed045
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_document_cookie_notification.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for document.cookie setter + notification</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+
+function Listener() {
+ SpecialPowers.addObserver(this, "document-set-cookie");
+}
+
+Listener.prototype = {
+ observe(aSubject, aTopic, aData) {
+ is(aTopic, "document-set-cookie", "Notification received");
+ ok(aData.startsWith("a="), "Right cookie received");
+
+ SpecialPowers.removeObserver(this, "document-set-cookie");
+ SimpleTest.finish();
+ }
+}
+
+const cl = new Listener();
+document.cookie = "a=" + Math.random();
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_fetch.html b/netwerk/cookie/test/mochitest/test_fetch.html
new file mode 100644
index 0000000000..315d0d7624
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_fetch.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies + fetch when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+ await w.fetch("cookie.sjs?fetch&" + Math.random()).then(r => r.text());
+ await checkLastRequest("fetch", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_image.html b/netwerk/cookie/test/mochitest/test_image.html
new file mode 100644
index 0000000000..4a49d64169
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_image.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies and image loading when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+
+ let image = new w.Image();
+ image.src = "cookie.sjs?image&" + Math.random();
+ w.document.body.appendChild(image);
+ await new w.Promise(resolve => { image.onload = resolve; });
+ await checkLastRequest("image", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_metaTag.html b/netwerk/cookie/test/mochitest/test_metaTag.html
new file mode 100644
index 0000000000..48d360d5b2
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_metaTag.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for meta tag</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript">
+
+document.addEventListener("DOMContentLoaded", _ => {
+ try {
+ document.write('<meta content=a http-equiv="set-cookie">');
+ } catch (e) {}
+
+ ok(true, "No crash!");
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_script.html b/netwerk/cookie/test/mochitest/test_script.html
new file mode 100644
index 0000000000..9f4b9f846d
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_script.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies + script loading when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+
+ let p = new w.Promise(resolve => { w.scriptLoaded = resolve; });
+ let script = document.createElement("script");
+ script.src = "cookie.sjs?script&" + Math.random();
+ w.document.body.appendChild(script);
+ await p;
+ await checkLastRequest("script", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_sharedWorker.html b/netwerk/cookie/test/mochitest/test_sharedWorker.html
new file mode 100644
index 0000000000..c29bf86a88
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_sharedWorker.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies + SharedWorker loading when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+
+ if (state == BLOCKED) {
+ try {
+ new w.SharedWorker("cookie.sjs?sharedworker&" + Math.random());
+ ok(false, "SharedWorker should not be allowed!");
+ } catch (ex) {
+ ok(true, "SharedWorker should not be allowed!");
+ }
+ return;
+ }
+
+ let p = new w.SharedWorker("cookie.sjs?sharedworker&" + Math.random());
+ await new w.Promise(resolve => { p.port.onmessage = resolve; });
+ await checkLastRequest("sharedworker", state);
+
+ await new w.Promise(resolve => {
+ p.port.postMessage("subworker");
+ p.port.onmessage = resolve;
+ });
+ await checkLastRequest("subworker", state);
+
+ await new w.Promise(resolve => {
+ p.port.postMessage("fetch");
+ p.port.onmessage = resolve;
+ });
+ await checkLastRequest("fetch", state);
+
+ await new w.Promise(resolve => {
+ p.port.postMessage("xhr");
+ p.port.onmessage = resolve;
+ });
+ await checkLastRequest("xhr", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_worker.html b/netwerk/cookie/test/mochitest/test_worker.html
new file mode 100644
index 0000000000..37ab222bce
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_worker.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies + worker loading when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+
+ let p = new w.Worker("cookie.sjs?worker&" + Math.random());
+ await new w.Promise(resolve => { p.onmessage = resolve; });
+ await checkLastRequest("worker", state);
+
+ await new w.Promise(resolve => { p.postMessage("subworker"); p.onmessage = resolve; });
+ await checkLastRequest("subworker", state);
+
+ await new w.Promise(resolve => { p.postMessage("fetch"); p.onmessage = resolve; });
+ await checkLastRequest("fetch", state);
+
+ await new w.Promise(resolve => { p.postMessage("xhr"); p.onmessage = resolve; });
+ await checkLastRequest("xhr", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_xhr.html b/netwerk/cookie/test/mochitest/test_xhr.html
new file mode 100644
index 0000000000..d00b5690f8
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_xhr.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for cookies + XHR when the policy changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript" src="cookiesHelper.js"></script>
+</head>
+<body>
+<script type="application/javascript">
+
+runTests(async (w, state) => {
+ w.document.cookie = "name=value";
+ await new w.Promise(resolve => {
+ let xhr = new w.XMLHttpRequest();
+ xhr.open("GET", "cookie.sjs?xhr&" + Math.random());
+ xhr.send();
+ xhr.onload = resolve;
+ });
+ await checkLastRequest("xhr", state);
+});
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/mochitest/test_xmlDocument.html b/netwerk/cookie/test/mochitest/test_xmlDocument.html
new file mode 100644
index 0000000000..91417c98c4
--- /dev/null
+++ b/netwerk/cookie/test/mochitest/test_xmlDocument.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Document constructor</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript">
+
+let w;
+
+SpecialPowers.pushPrefEnv({set: [
+ ["dom.storage_access.enabled", true],
+ ["dom.storage_access.prompt.testing", true],
+ ["dom.storage_access.prompt.testing.allow", true],
+ ["dom.testing.sync-content-blocking-notifications", true],
+ ["network.cookie.cookieBehavior", 0],
+]}).then(_ => {
+ return new Promise(resolve => {
+ w = window.open("empty.html");
+ w.onload = resolve;
+ });
+}).then(_ => {
+ const doc = new w.Document();
+ return doc.requestStorageAccess().catch(__ => {});
+}).then(___ => {
+ w.close();
+ ok(true, "No crash!");
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/netwerk/cookie/test/unit/test_baseDomain_publicsuffix.js b/netwerk/cookie/test/unit/test_baseDomain_publicsuffix.js
new file mode 100644
index 0000000000..94f01b778e
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_baseDomain_publicsuffix.js
@@ -0,0 +1,105 @@
+"use strict";
+
+add_task(async () => {
+ const HOST = "www.bbc.co.uk";
+ Assert.equal(
+ Services.eTLD.getBaseDomainFromHost(HOST),
+ "bbc.co.uk",
+ "Sanity check: HOST is an eTLD + 1 with subdomain"
+ );
+
+ const tests = [
+ {
+ // Correct baseDomain: eTLD + 1.
+ baseDomain: "bbc.co.uk",
+ name: "originally_bbc_co_uk",
+ },
+ {
+ // Incorrect baseDomain: Part of public suffix list.
+ baseDomain: "uk",
+ name: "originally_uk",
+ },
+ {
+ // Incorrect baseDomain: Part of public suffix list.
+ baseDomain: "co.uk",
+ name: "originally_co_uk",
+ },
+ {
+ // Incorrect baseDomain: eTLD + 2.
+ baseDomain: "www.bbc.co.uk",
+ name: "originally_www_bbc_co_uk",
+ },
+ ];
+
+ do_get_profile();
+
+ let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ dbFile.append("cookies.sqlite");
+ let conn = Services.storage.openDatabase(dbFile);
+
+ conn.schemaVersion = 10;
+ conn.executeSimpleSQL("DROP TABLE IF EXISTS moz_cookies");
+ conn.executeSimpleSQL(
+ "CREATE TABLE moz_cookies (" +
+ "id INTEGER PRIMARY KEY, " +
+ "baseDomain TEXT, " +
+ "originAttributes TEXT NOT NULL DEFAULT '', " +
+ "name TEXT, " +
+ "value TEXT, " +
+ "host TEXT, " +
+ "path TEXT, " +
+ "expiry INTEGER, " +
+ "lastAccessed INTEGER, " +
+ "creationTime INTEGER, " +
+ "isSecure INTEGER, " +
+ "isHttpOnly INTEGER, " +
+ "inBrowserElement INTEGER DEFAULT 0, " +
+ "sameSite INTEGER DEFAULT 0, " +
+ "rawSameSite INTEGER DEFAULT 0, " +
+ "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" +
+ ")"
+ );
+
+ function addCookie(baseDomain, host, name) {
+ conn.executeSimpleSQL(
+ "INSERT INTO moz_cookies(" +
+ "baseDomain, host, name, value, path, expiry, " +
+ "lastAccessed, creationTime, isSecure, isHttpOnly) VALUES (" +
+ `'${baseDomain}', '${host}', '${name}', 'thevalue', '/', ` +
+ (Date.now() + 3600000) +
+ "," +
+ Date.now() +
+ "," +
+ Date.now() +
+ ", 1, 1)"
+ );
+ }
+
+ // Prepare the database.
+ for (let { baseDomain, name } of tests) {
+ addCookie(baseDomain, HOST, name);
+ }
+ // Domain cookies are not supported for IP addresses.
+ addCookie("127.0.0.1", ".127.0.0.1", "invalid_host");
+ conn.close();
+
+ let cs = Services.cookies;
+
+ // Count excludes the invalid_host cookie.
+ Assert.equal(cs.cookies.length, tests.length, "Expected number of cookies");
+
+ // Check whether the database has the expected value,
+ // despite the incorrect baseDomain.
+ for (let { name } of tests) {
+ Assert.ok(
+ cs.cookieExists(HOST, "/", name, {}),
+ "Should find cookie with name: " + name
+ );
+ }
+
+ Assert.equal(
+ cs.cookieExists("127.0.0.1", "/", "invalid_host", {}),
+ false,
+ "Should ignore database row with invalid host name"
+ );
+});
diff --git a/netwerk/cookie/test/unit/test_bug1155169.js b/netwerk/cookie/test/unit/test_bug1155169.js
new file mode 100644
index 0000000000..2bf8bd768d
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_bug1155169.js
@@ -0,0 +1,96 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+const URI = Services.io.newURI("http://example.org/");
+
+const { COOKIE_CHANGED, COOKIE_ADDED } = Ci.nsICookieNotification;
+
+function run_test() {
+ // Allow all cookies.
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+
+ // Clear cookies.
+ Services.cookies.removeAll();
+
+ // Add a new cookie.
+ setCookie("foo=bar", {
+ type: COOKIE_ADDED,
+ isSession: true,
+ isSecure: false,
+ isHttpOnly: false,
+ });
+
+ // Update cookie with isHttpOnly=true.
+ setCookie("foo=bar; HttpOnly", {
+ type: COOKIE_CHANGED,
+ isSession: true,
+ isSecure: false,
+ isHttpOnly: true,
+ });
+
+ // Update cookie with isSecure=true.
+ setCookie("foo=bar; Secure", {
+ type: COOKIE_CHANGED,
+ isSession: true,
+ isSecure: true,
+ isHttpOnly: false,
+ });
+
+ // Update cookie with isSession=false.
+ let expiry = new Date();
+ expiry.setUTCFullYear(expiry.getUTCFullYear() + 2);
+ setCookie(`foo=bar; Expires=${expiry.toGMTString()}`, {
+ type: COOKIE_CHANGED,
+ isSession: false,
+ isSecure: false,
+ isHttpOnly: false,
+ });
+
+ // Reset cookie.
+ setCookie("foo=bar", {
+ type: COOKIE_CHANGED,
+ isSession: true,
+ isSecure: false,
+ isHttpOnly: false,
+ });
+}
+
+function setCookie(value, expected) {
+ function setCookieInternal(valueInternal, expectedInternal = null) {
+ function observer(subject) {
+ if (!expectedInternal) {
+ do_throw("no notification expected");
+ return;
+ }
+
+ let notification = subject.QueryInterface(Ci.nsICookieNotification);
+
+ // Check we saw the right notification.
+ Assert.equal(notification.action, expectedInternal.type);
+
+ // Check cookie details.
+ let cookie = notification.cookie.QueryInterface(Ci.nsICookie);
+ Assert.equal(cookie.isSession, expectedInternal.isSession);
+ Assert.equal(cookie.isSecure, expectedInternal.isSecure);
+ Assert.equal(cookie.isHttpOnly, expectedInternal.isHttpOnly);
+ }
+
+ Services.obs.addObserver(observer, "cookie-changed");
+
+ let channel = NetUtil.newChannel({
+ uri: URI,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ Services.cookies.setCookieStringFromHttp(URI, valueInternal, channel);
+ Services.obs.removeObserver(observer, "cookie-changed");
+ }
+
+ // Check that updating/inserting the cookie works.
+ setCookieInternal(value, expected);
+
+ // Check that we ignore identical cookies.
+ setCookieInternal(value);
+}
diff --git a/netwerk/cookie/test/unit/test_bug1321912.js b/netwerk/cookie/test/unit/test_bug1321912.js
new file mode 100644
index 0000000000..fd24f15bbf
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_bug1321912.js
@@ -0,0 +1,99 @@
+do_get_profile();
+const dirSvc = Services.dirsvc;
+
+let dbFile = dirSvc.get("ProfD", Ci.nsIFile);
+dbFile.append("cookies.sqlite");
+
+let storage = Services.storage;
+let properties = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag
+);
+properties.setProperty("shared", true);
+let conn = storage.openDatabase(dbFile);
+
+// Write the schema v7 to the database.
+conn.schemaVersion = 7;
+conn.executeSimpleSQL(
+ "CREATE TABLE moz_cookies (" +
+ "id INTEGER PRIMARY KEY, " +
+ "baseDomain TEXT, " +
+ "originAttributes TEXT NOT NULL DEFAULT '', " +
+ "name TEXT, " +
+ "value TEXT, " +
+ "host TEXT, " +
+ "path TEXT, " +
+ "expiry INTEGER, " +
+ "lastAccessed INTEGER, " +
+ "creationTime INTEGER, " +
+ "isSecure INTEGER, " +
+ "isHttpOnly INTEGER, " +
+ "appId INTEGER DEFAULT 0, " +
+ "inBrowserElement INTEGER DEFAULT 0, " +
+ "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" +
+ ")"
+);
+conn.executeSimpleSQL(
+ "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, " +
+ "originAttributes)"
+);
+
+conn.executeSimpleSQL("PRAGMA synchronous = OFF");
+conn.executeSimpleSQL("PRAGMA journal_mode = WAL");
+conn.executeSimpleSQL("PRAGMA wal_autocheckpoint = 16");
+
+let now = Date.now();
+conn.executeSimpleSQL(
+ "INSERT INTO moz_cookies(" +
+ "baseDomain, host, name, value, path, expiry, " +
+ "lastAccessed, creationTime, isSecure, isHttpOnly) VALUES (" +
+ "'foo.com', '.foo.com', 'foo', 'bar=baz', '/', " +
+ now +
+ ", " +
+ now +
+ ", " +
+ now +
+ ", 1, 1)"
+);
+
+// Now start the cookie service, and then check the fields in the table.
+// Get sessionCookies to wait for the initialization in cookie thread
+Services.cookies.sessionCookies;
+
+Assert.equal(conn.schemaVersion, 13);
+let stmt = conn.createStatement(
+ "SELECT sql FROM sqlite_master " +
+ "WHERE type = 'table' AND " +
+ " name = 'moz_cookies'"
+);
+try {
+ Assert.ok(stmt.executeStep());
+ let sql = stmt.getString(0);
+ Assert.equal(sql.indexOf("appId"), -1);
+} finally {
+ stmt.finalize();
+}
+
+stmt = conn.createStatement(
+ "SELECT * FROM moz_cookies " +
+ "WHERE host = '.foo.com' AND " +
+ " name = 'foo' AND " +
+ " value = 'bar=baz' AND " +
+ " path = '/' AND " +
+ " expiry = " +
+ now +
+ " AND " +
+ " lastAccessed = " +
+ now +
+ " AND " +
+ " creationTime = " +
+ now +
+ " AND " +
+ " isSecure = 1 AND " +
+ " isHttpOnly = 1"
+);
+try {
+ Assert.ok(stmt.executeStep());
+} finally {
+ stmt.finalize();
+}
+conn.close();
diff --git a/netwerk/cookie/test/unit/test_bug643051.js b/netwerk/cookie/test/unit/test_bug643051.js
new file mode 100644
index 0000000000..35b37a5889
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_bug643051.js
@@ -0,0 +1,43 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+const { CookieXPCShellUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/CookieXPCShellUtils.sys.mjs"
+);
+
+CookieXPCShellUtils.init(this);
+CookieXPCShellUtils.createServer({ hosts: ["example.net"] });
+
+add_task(async () => {
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+
+ // Allow all cookies.
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+
+ let uri = NetUtil.newURI("http://example.org/");
+ let channel = NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ let set = "foo=bar\nbaz=foo";
+ let expected = "foo=bar; baz=foo";
+ Services.cookies.setCookieStringFromHttp(uri, set, channel);
+
+ let actual = Services.cookies.getCookieStringFromHttp(uri, channel);
+ Assert.equal(actual, expected);
+
+ await CookieXPCShellUtils.setCookieToDocument("http://example.net/", set);
+ actual = await CookieXPCShellUtils.getCookieStringFromDocument(
+ "http://example.net/"
+ );
+
+ expected = "foo=bar";
+ Assert.equal(actual, expected);
+ Services.prefs.clearUserPref("dom.security.https_first");
+});
diff --git a/netwerk/cookie/test/unit/test_eviction.js b/netwerk/cookie/test/unit/test_eviction.js
new file mode 100644
index 0000000000..8c0073a107
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_eviction.js
@@ -0,0 +1,199 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+const BASE_HOST = "example.org";
+
+const { CookieXPCShellUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/CookieXPCShellUtils.sys.mjs"
+);
+
+CookieXPCShellUtils.init(this);
+CookieXPCShellUtils.createServer({ hosts: ["example.org"] });
+
+add_task(async function test_basic_eviction() {
+ do_get_profile();
+
+ Services.prefs.setIntPref("network.cookie.staleThreshold", 0);
+ Services.prefs.setIntPref("network.cookie.quotaPerHost", 2);
+ Services.prefs.setIntPref("network.cookie.maxPerHost", 5);
+
+ // We don't want to have CookieJarSettings blocking this test.
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+
+ const BASE_URI = Services.io.newURI("http://" + BASE_HOST);
+ const FOO_PATH = Services.io.newURI("http://" + BASE_HOST + "/foo/");
+ const BAR_PATH = Services.io.newURI("http://" + BASE_HOST + "/bar/");
+
+ await setCookie("session_foo_path_1", null, "/foo", null, FOO_PATH);
+ await setCookie("session_foo_path_2", null, "/foo", null, FOO_PATH);
+ await setCookie("session_foo_path_3", null, "/foo", null, FOO_PATH);
+ await setCookie("session_foo_path_4", null, "/foo", null, FOO_PATH);
+ await setCookie("session_foo_path_5", null, "/foo", null, FOO_PATH);
+ verifyCookies(
+ [
+ "session_foo_path_1",
+ "session_foo_path_2",
+ "session_foo_path_3",
+ "session_foo_path_4",
+ "session_foo_path_5",
+ ],
+ BASE_URI
+ );
+
+ // Check if cookies are evicted by creation time.
+ await setCookie("session_foo_path_6", null, "/foo", null, FOO_PATH);
+ verifyCookies(
+ ["session_foo_path_4", "session_foo_path_5", "session_foo_path_6"],
+ BASE_URI
+ );
+
+ await setCookie("session_bar_path_1", null, "/bar", null, BAR_PATH);
+ await setCookie("session_bar_path_2", null, "/bar", null, BAR_PATH);
+
+ verifyCookies(
+ [
+ "session_foo_path_4",
+ "session_foo_path_5",
+ "session_foo_path_6",
+ "session_bar_path_1",
+ "session_bar_path_2",
+ ],
+ BASE_URI
+ );
+
+ // Check if cookies are evicted by last accessed time.
+ await CookieXPCShellUtils.getCookieStringFromDocument(FOO_PATH.spec);
+
+ await setCookie("session_foo_path_7", null, "/foo", null, FOO_PATH);
+ verifyCookies(
+ ["session_foo_path_5", "session_foo_path_6", "session_foo_path_7"],
+ BASE_URI
+ );
+
+ const EXPIRED_TIME = 3;
+
+ await setCookie(
+ "non_session_expired_foo_path_1",
+ null,
+ "/foo",
+ EXPIRED_TIME,
+ FOO_PATH
+ );
+ await setCookie(
+ "non_session_expired_foo_path_2",
+ null,
+ "/foo",
+ EXPIRED_TIME,
+ FOO_PATH
+ );
+ verifyCookies(
+ [
+ "session_foo_path_5",
+ "session_foo_path_6",
+ "session_foo_path_7",
+ "non_session_expired_foo_path_1",
+ "non_session_expired_foo_path_2",
+ ],
+ BASE_URI
+ );
+
+ // Check if expired cookies are evicted first.
+ await new Promise(resolve => do_timeout(EXPIRED_TIME * 1000, resolve));
+ await setCookie("session_foo_path_8", null, "/foo", null, FOO_PATH);
+ verifyCookies(
+ ["session_foo_path_6", "session_foo_path_7", "session_foo_path_8"],
+ BASE_URI
+ );
+
+ Services.cookies.removeAll();
+});
+
+// Verify that the given cookie names exist, and are ordered from least to most recently accessed
+function verifyCookies(names, uri) {
+ Assert.equal(Services.cookies.countCookiesFromHost(uri.host), names.length);
+ let actual_cookies = [];
+ for (let cookie of Services.cookies.getCookiesFromHost(uri.host, {})) {
+ actual_cookies.push(cookie);
+ }
+ if (names.length != actual_cookies.length) {
+ let left = names.filter(function (n) {
+ return (
+ actual_cookies.findIndex(function (c) {
+ return c.name == n;
+ }) == -1
+ );
+ });
+ let right = actual_cookies
+ .filter(function (c) {
+ return (
+ names.findIndex(function (n) {
+ return c.name == n;
+ }) == -1
+ );
+ })
+ .map(function (c) {
+ return c.name;
+ });
+ if (left.length) {
+ info("unexpected cookies: " + left);
+ }
+ if (right.length) {
+ info("expected cookies: " + right);
+ }
+ }
+ Assert.equal(names.length, actual_cookies.length);
+ actual_cookies.sort(function (a, b) {
+ if (a.lastAccessed < b.lastAccessed) {
+ return -1;
+ }
+ if (a.lastAccessed > b.lastAccessed) {
+ return 1;
+ }
+ return 0;
+ });
+ for (var i = 0; i < names.length; i++) {
+ Assert.equal(names[i], actual_cookies[i].name);
+ Assert.equal(names[i].startsWith("session"), actual_cookies[i].isSession);
+ }
+}
+
+var lastValue = 0;
+function setCookie(name, domain, path, maxAge, url) {
+ let value = name + "=" + ++lastValue;
+ var s = "setting cookie " + value;
+ if (domain) {
+ value += "; Domain=" + domain;
+ s += " (d=" + domain + ")";
+ }
+ if (path) {
+ value += "; Path=" + path;
+ s += " (p=" + path + ")";
+ }
+ if (maxAge) {
+ value += "; Max-Age=" + maxAge;
+ s += " (non-session)";
+ } else {
+ s += " (session)";
+ }
+ s += " for " + url.spec;
+ info(s);
+
+ let channel = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ Services.cookies.setCookieStringFromHttp(url, value, channel);
+
+ return new Promise(function (resolve) {
+ // Windows XP has low precision timestamps that cause our cookie eviction
+ // algorithm to produce different results from other platforms. We work around
+ // this by ensuring that there's a clear gap between each cookie update.
+ do_timeout(10, resolve);
+ });
+}
diff --git a/netwerk/cookie/test/unit/test_getCookieSince.js b/netwerk/cookie/test/unit/test_getCookieSince.js
new file mode 100644
index 0000000000..e58624b6a1
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_getCookieSince.js
@@ -0,0 +1,72 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function setCookie(name, url) {
+ let value = `${name}=${Math.random()}; Path=/; Max-Age=1000; sameSite=none; Secure`;
+ info(`Setting cookie ${value} for ${url.spec}`);
+
+ let channel = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ Services.cookies.setCookieStringFromHttp(url, value, channel);
+}
+
+async function sleep() {
+ await new Promise(resolve => do_timeout(1000, resolve));
+}
+
+function checkSorting(cookies) {
+ for (let i = 1; i < cookies.length; ++i) {
+ Assert.greater(
+ cookies[i].creationTime,
+ cookies[i - 1].creationTime,
+ "Cookie " + cookies[i].name
+ );
+ }
+}
+
+add_task(async function () {
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+
+ await setCookie("A", Services.io.newURI("https://example.com/A/"));
+ await sleep();
+
+ await setCookie("B", Services.io.newURI("https://foo.bar/B/"));
+ await sleep();
+
+ await setCookie("C", Services.io.newURI("https://example.org/C/"));
+ await sleep();
+
+ await setCookie("D", Services.io.newURI("https://example.com/D/"));
+ await sleep();
+
+ Assert.equal(Services.cookies.cookies.length, 4, "Cookie check");
+
+ const cookies = Services.cookies.getCookiesSince(0);
+ Assert.equal(cookies.length, 4, "We retrieve all the 4 cookies");
+ checkSorting(cookies);
+
+ let someCookies = Services.cookies.getCookiesSince(
+ cookies[0].creationTime + 1
+ );
+ Assert.equal(someCookies.length, 3, "We retrieve some cookies");
+ checkSorting(someCookies);
+
+ someCookies = Services.cookies.getCookiesSince(cookies[1].creationTime + 1);
+ Assert.equal(someCookies.length, 2, "We retrieve some cookies");
+ checkSorting(someCookies);
+
+ someCookies = Services.cookies.getCookiesSince(cookies[2].creationTime + 1);
+ Assert.equal(someCookies.length, 1, "We retrieve some cookies");
+ checkSorting(someCookies);
+
+ someCookies = Services.cookies.getCookiesSince(cookies[3].creationTime + 1);
+ Assert.equal(someCookies.length, 0, "We retrieve some cookies");
+});
diff --git a/netwerk/cookie/test/unit/test_migrateCookieLifetimePref.js b/netwerk/cookie/test/unit/test_migrateCookieLifetimePref.js
new file mode 100644
index 0000000000..088a909709
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_migrateCookieLifetimePref.js
@@ -0,0 +1,65 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ Tests that
+ - the migration code runs,
+ - the sanitize on shutdown prefs for profiles with the network.cookie.lifetimePolicy enabled are set to true,
+ - the previous settings for clearOnShutdown prefs will not be applied due to sanitizeOnShutdown being disabled
+ - the network.cookie.lifetimePolicy is disabled afterwards.
+*/
+add_task(async function migrateSanitizationPrefsClearCleaningPrefs() {
+ // Former network.cookie.lifetimePolicy values ACCEPT_SESSION/ACCEPT_NORMALLY are not available anymore
+ // 2 = ACCEPT_SESSION
+ Services.prefs.setIntPref("network.cookie.lifetimePolicy", 2);
+ Services.prefs.setBoolPref("privacy.sanitize.sanitizeOnShutdown", false);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", false);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", false);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.offlineApps", false);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.downloads", true);
+ Services.prefs.setBoolPref("privacy.clearOnShutdown.sessions", true);
+
+ // The migration code is called in cookieService::Init
+ Services.cookies;
+
+ // Former network.cookie.lifetimePolicy values ACCEPT_SESSION/ACCEPT_NORMALLY are not available anymore
+ // 0 = ACCEPT_NORMALLY
+ Assert.equal(
+ Services.prefs.getIntPref("network.cookie.lifetimePolicy", 0),
+ 0,
+ "Cookie lifetime policy is off"
+ );
+
+ Assert.ok(
+ Services.prefs.getBoolPref("privacy.sanitize.sanitizeOnShutdown"),
+ "Sanitize on shutdown is set"
+ );
+
+ Assert.ok(
+ Services.prefs.getBoolPref("privacy.clearOnShutdown.cookies"),
+ "Clearing cookies on shutdown is selected"
+ );
+
+ Assert.ok(
+ Services.prefs.getBoolPref("privacy.clearOnShutdown.cache"),
+ "Clearing cache on shutdown is still selected"
+ );
+
+ Assert.ok(
+ Services.prefs.getBoolPref("privacy.clearOnShutdown.offlineApps"),
+ "Clearing offline apps on shutdown is selected"
+ );
+
+ Assert.ok(
+ !Services.prefs.getBoolPref("privacy.clearOnShutdown.downloads"),
+ "Clearing downloads on shutdown is not set anymore"
+ );
+ Assert.ok(
+ !Services.prefs.getBoolPref("privacy.clearOnShutdown.sessions"),
+ "Clearing active logins on shutdown is not set anymore"
+ );
+
+ Services.prefs.resetPrefs();
+
+ delete Services.cookies;
+});
diff --git a/netwerk/cookie/test/unit/test_parser_0001.js b/netwerk/cookie/test/unit/test_parser_0001.js
new file mode 100644
index 0000000000..acc2e919ef
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_parser_0001.js
@@ -0,0 +1,32 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function inChildProcess() {
+ return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ }
+
+ let uri = NetUtil.newURI("http://example.org/");
+ let channel = NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ let set = "foo=bar";
+ Services.cookies.setCookieStringFromHttp(uri, set, channel);
+
+ let expected = "foo=bar";
+ let actual = Services.cookies.getCookieStringFromHttp(uri, channel);
+ Assert.equal(actual, expected);
+}
diff --git a/netwerk/cookie/test/unit/test_parser_0019.js b/netwerk/cookie/test/unit/test_parser_0019.js
new file mode 100644
index 0000000000..7ba0d4ef79
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_parser_0019.js
@@ -0,0 +1,32 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function inChildProcess() {
+ return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+function run_test() {
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ }
+
+ let uri = NetUtil.newURI("http://example.org/");
+ let channel = NetUtil.newChannel({
+ uri,
+ loadUsingSystemPrincipal: true,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_DOCUMENT,
+ });
+
+ let set = "foo=b;max-age=3600, c=d;path=/";
+ Services.cookies.setCookieStringFromHttp(uri, set, channel);
+
+ let expected = "foo=b";
+ let actual = Services.cookies.getCookieStringFromHttp(uri, channel);
+ Assert.equal(actual, expected);
+}
diff --git a/netwerk/cookie/test/unit/test_rawSameSite.js b/netwerk/cookie/test/unit/test_rawSameSite.js
new file mode 100644
index 0000000000..dc739ef852
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_rawSameSite.js
@@ -0,0 +1,125 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function inChildProcess() {
+ return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+add_task(async _ => {
+ do_get_profile();
+
+ let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ dbFile.append("cookies.sqlite");
+
+ let storage = Services.storage;
+ let properties = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag
+ );
+ properties.setProperty("shared", true);
+ let conn = storage.openDatabase(dbFile);
+
+ conn.schemaVersion = 9;
+ conn.executeSimpleSQL("DROP TABLE IF EXISTS moz_cookies");
+ conn.executeSimpleSQL(
+ "CREATE TABLE moz_cookies (" +
+ "id INTEGER PRIMARY KEY, " +
+ "baseDomain TEXT, " +
+ "originAttributes TEXT NOT NULL DEFAULT '', " +
+ "name TEXT, " +
+ "value TEXT, " +
+ "host TEXT, " +
+ "path TEXT, " +
+ "expiry INTEGER, " +
+ "lastAccessed INTEGER, " +
+ "creationTime INTEGER, " +
+ "isSecure INTEGER, " +
+ "isHttpOnly INTEGER, " +
+ "inBrowserElement INTEGER DEFAULT 0, " +
+ "sameSite INTEGER DEFAULT 0, " +
+ "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" +
+ ")"
+ );
+ conn.close();
+
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ Services.prefs.setBoolPref("network.cookie.sameSite.laxByDefault", true);
+ Services.prefs.setBoolPref(
+ "network.cookie.sameSite.noneRequiresSecure",
+ true
+ );
+ }
+
+ let uri = NetUtil.newURI("http://example.org/");
+
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+
+ let channel = NetUtil.newChannel({
+ uri,
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ let tests = [
+ {
+ cookie: "foo=b;max-age=3600, c=d;path=/; sameSite=strict",
+ sameSite: 2,
+ rawSameSite: 2,
+ },
+ {
+ cookie: "foo=b;max-age=3600, c=d;path=/; sameSite=lax",
+ sameSite: 1,
+ rawSameSite: 1,
+ },
+ { cookie: "foo=b;max-age=3600, c=d;path=/", sameSite: 1, rawSameSite: 0 },
+ ];
+
+ for (let i = 0; i < tests.length; ++i) {
+ let test = tests[i];
+
+ let promise = new Promise(resolve => {
+ function observer(subject, topic, data) {
+ Services.obs.removeObserver(observer, "cookie-saved-on-disk");
+ resolve();
+ }
+
+ Services.obs.addObserver(observer, "cookie-saved-on-disk");
+ });
+
+ Services.cookies.setCookieStringFromHttp(uri, test.cookie, channel);
+
+ await promise;
+
+ conn = storage.openDatabase(dbFile);
+ Assert.equal(conn.schemaVersion, 13);
+
+ let stmt = conn.createStatement(
+ "SELECT sameSite, rawSameSite FROM moz_cookies"
+ );
+
+ let success = stmt.executeStep();
+ Assert.ok(success);
+
+ let sameSite = stmt.getInt32(0);
+ let rawSameSite = stmt.getInt32(1);
+ stmt.finalize();
+
+ Assert.equal(sameSite, test.sameSite);
+ Assert.equal(rawSameSite, test.rawSameSite);
+
+ Services.cookies.removeAll();
+
+ stmt.finalize();
+ conn.close();
+ }
+});
diff --git a/netwerk/cookie/test/unit/test_schemeMap.js b/netwerk/cookie/test/unit/test_schemeMap.js
new file mode 100644
index 0000000000..041c24033a
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_schemeMap.js
@@ -0,0 +1,216 @@
+const { NetUtil } = ChromeUtils.importESModule(
+ "resource://gre/modules/NetUtil.sys.mjs"
+);
+
+function inChildProcess() {
+ return Services.appinfo.processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
+}
+
+const { CookieXPCShellUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/CookieXPCShellUtils.sys.mjs"
+);
+
+let CookieXPCShellUtilsInitialized = false;
+function maybeInitializeCookieXPCShellUtils() {
+ if (!CookieXPCShellUtilsInitialized) {
+ CookieXPCShellUtilsInitialized = true;
+ CookieXPCShellUtils.init(this);
+
+ CookieXPCShellUtils.createServer({ hosts: ["example.org"] });
+ }
+}
+
+// Don't pick up default permissions from profile.
+Services.prefs.setCharPref("permissions.manager.defaultsUrl", "");
+
+add_task(async _ => {
+ do_get_profile();
+
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ }
+
+ info("Let's set a cookie from HTTP example.org");
+
+ let uri = NetUtil.newURI("http://example.org/");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ let channel = NetUtil.newChannel({
+ uri,
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ Services.cookies.setCookieStringFromHttp(uri, "a=b; sameSite=lax", channel);
+
+ let cookies = Services.cookies.getCookiesFromHost("example.org", {});
+ Assert.equal(cookies.length, 1, "We expect 1 cookie only");
+
+ Assert.equal(cookies[0].schemeMap, Ci.nsICookie.SCHEME_HTTP, "HTTP Scheme");
+
+ info("Let's set a cookie from HTTPS example.org");
+
+ uri = NetUtil.newURI("https://example.org/");
+ principal = Services.scriptSecurityManager.createContentPrincipal(uri, {});
+ channel = NetUtil.newChannel({
+ uri,
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ Services.cookies.setCookieStringFromHttp(uri, "a=b; sameSite=lax", channel);
+
+ cookies = Services.cookies.getCookiesFromHost("example.org", {});
+ Assert.equal(cookies.length, 1, "We expect 1 cookie only");
+
+ Assert.equal(
+ cookies[0].schemeMap,
+ Ci.nsICookie.SCHEME_HTTP | Ci.nsICookie.SCHEME_HTTPS,
+ "HTTP + HTTPS Schemes"
+ );
+
+ Services.cookies.removeAll();
+});
+
+[true, false].forEach(schemefulComparison => {
+ add_task(async () => {
+ do_get_profile();
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+
+ maybeInitializeCookieXPCShellUtils();
+
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setBoolPref(
+ "network.cookie.sameSite.schemeful",
+ schemefulComparison
+ );
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ }
+
+ info(
+ `Testing schemefulSameSite=${schemefulComparison}. Let's set a cookie from HTTPS example.org`
+ );
+
+ let https_uri = NetUtil.newURI("https://example.org/");
+ let https_principal = Services.scriptSecurityManager.createContentPrincipal(
+ https_uri,
+ {}
+ );
+ let same_site_channel = NetUtil.newChannel({
+ uri: https_uri,
+ loadingPrincipal: https_principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ Services.cookies.setCookieStringFromHttp(
+ https_uri,
+ "a=b; sameSite=lax",
+ same_site_channel
+ );
+
+ let cookies = Services.cookies.getCookieStringFromHttp(
+ https_uri,
+ same_site_channel
+ );
+ Assert.equal(cookies, "a=b", "Cookies match");
+
+ let http_uri = NetUtil.newURI("http://example.org/");
+ let http_principal = Services.scriptSecurityManager.createContentPrincipal(
+ http_uri,
+ {}
+ );
+ let cross_site_channel = NetUtil.newChannel({
+ uri: https_uri,
+ loadingPrincipal: http_principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ cookies = Services.cookies.getCookieStringFromHttp(
+ http_uri,
+ cross_site_channel
+ );
+ if (schemefulComparison) {
+ Assert.equal(cookies, "", "No http(s) cookie for different scheme!");
+ } else {
+ Assert.equal(cookies, "a=b", "http(s) Cookie even for differentscheme!");
+ }
+
+ // SameSite cookies are included via document.domain
+ cookies = await CookieXPCShellUtils.getCookieStringFromDocument(
+ http_uri.spec
+ );
+ Assert.equal(cookies, "a=b", "document.cookie even for different scheme!");
+
+ Services.cookies.removeAll();
+ Services.prefs.clearUserPref("dom.security.https_first");
+ });
+});
+
+add_task(async _ => {
+ do_get_profile();
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+
+ // Allow all cookies if the pref service is available in this process.
+ if (!inChildProcess()) {
+ Services.prefs.setIntPref("network.cookie.cookieBehavior", 0);
+ Services.prefs.setBoolPref(
+ "network.cookieJarSettings.unblocked_for_testing",
+ true
+ );
+ }
+
+ info("Let's set a cookie without scheme");
+ Services.cookies.add(
+ "example.org",
+ "/",
+ "a",
+ "b",
+ false,
+ false,
+ false,
+ Math.floor(Date.now() / 1000 + 1000),
+ {},
+ Ci.nsICookie.SAMESITE_LAX,
+ Ci.nsICookie.SCHEME_UNSET
+ );
+
+ let cookies = Services.cookies.getCookiesFromHost("example.org", {});
+ Assert.equal(cookies.length, 1, "We expect 1 cookie only");
+ Assert.equal(cookies[0].schemeMap, Ci.nsICookie.SCHEME_UNSET, "Unset scheme");
+
+ ["https", "http"].forEach(scheme => {
+ let uri = NetUtil.newURI(scheme + "://example.org/");
+ let principal = Services.scriptSecurityManager.createContentPrincipal(
+ uri,
+ {}
+ );
+ let channel = NetUtil.newChannel({
+ uri,
+ loadingPrincipal: principal,
+ securityFlags: Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER,
+ });
+
+ cookies = Services.cookies.getCookieStringFromHttp(uri, channel);
+ Assert.equal(cookies, "a=b", "Cookie for unset scheme");
+ });
+
+ Services.cookies.removeAll();
+ Services.prefs.clearUserPref("dom.security.https_first");
+});
diff --git a/netwerk/cookie/test/unit/test_timestamp_fixup.js b/netwerk/cookie/test/unit/test_timestamp_fixup.js
new file mode 100644
index 0000000000..a6e9642ad7
--- /dev/null
+++ b/netwerk/cookie/test/unit/test_timestamp_fixup.js
@@ -0,0 +1,130 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+
+const USEC_PER_SEC = 1000 * 1000;
+const ONE_DAY = 60 * 60 * 24 * USEC_PER_SEC;
+const ONE_YEAR = ONE_DAY * 365;
+const LAST_ACCESSED_DIFF = 10 * ONE_YEAR;
+const CREATION_DIFF = 100 * ONE_YEAR;
+
+function initDB(conn, now) {
+ // Write the schema v7 to the database.
+ conn.schemaVersion = 7;
+ conn.executeSimpleSQL(
+ "CREATE TABLE moz_cookies (" +
+ "id INTEGER PRIMARY KEY, " +
+ "baseDomain TEXT, " +
+ "originAttributes TEXT NOT NULL DEFAULT '', " +
+ "name TEXT, " +
+ "value TEXT, " +
+ "host TEXT, " +
+ "path TEXT, " +
+ "expiry INTEGER, " +
+ "lastAccessed INTEGER, " +
+ "creationTime INTEGER, " +
+ "isSecure INTEGER, " +
+ "isHttpOnly INTEGER, " +
+ "appId INTEGER DEFAULT 0, " +
+ "inBrowserElement INTEGER DEFAULT 0, " +
+ "CONSTRAINT moz_uniqueid UNIQUE (name, host, path, originAttributes)" +
+ ")"
+ );
+ conn.executeSimpleSQL(
+ "CREATE INDEX moz_basedomain ON moz_cookies (baseDomain, " +
+ "originAttributes)"
+ );
+
+ conn.executeSimpleSQL("PRAGMA synchronous = OFF");
+ conn.executeSimpleSQL("PRAGMA journal_mode = WAL");
+ conn.executeSimpleSQL("PRAGMA wal_autocheckpoint = 16");
+
+ conn.executeSimpleSQL(
+ `INSERT INTO moz_cookies(baseDomain, host, name, value, path, expiry, lastAccessed, creationTime, isSecure, isHttpOnly)
+ VALUES ('foo.com', '.foo.com', 'foo', 'bar=baz', '/',
+ ${now + ONE_DAY}, ${now + LAST_ACCESSED_DIFF} , ${
+ now + CREATION_DIFF
+ } , 1, 1)`
+ );
+}
+
+add_task(async function test_timestamp_fixup() {
+ let now = Date.now() * 1000; // date in microseconds
+ Services.prefs.setBoolPref("network.cookie.fixup_on_db_load", true);
+ do_get_profile();
+ let dbFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
+ dbFile.append("cookies.sqlite");
+ let conn = Services.storage.openDatabase(dbFile);
+ initDB(conn, now);
+
+ if (AppConstants.platform != "android") {
+ Services.fog.initializeFOG();
+ }
+ Services.fog.testResetFOG();
+
+ // Now start the cookie service, and then check the fields in the table.
+ // Get sessionCookies to wait for the initialization in cookie thread
+ Assert.lessOrEqual(
+ Math.floor(Services.cookies.cookies[0].creationTime / 1000),
+ now
+ );
+ Assert.equal(conn.schemaVersion, 13);
+
+ Assert.equal(
+ await Glean.networking.cookieTimestampFixedCount.creationTime.testGetValue(),
+ 1,
+ "One fixup of creation time"
+ );
+ Assert.equal(
+ await Glean.networking.cookieTimestampFixedCount.lastAccessed.testGetValue(),
+ 1,
+ "One fixup of lastAccessed"
+ );
+ {
+ let { values } =
+ await Glean.networking.cookieCreationFixupDiff.testGetValue();
+ info(JSON.stringify(values));
+ let keys = Object.keys(values).splice(-2, 2);
+ Assert.equal(keys.length, 2, "There should be two entries in telemetry");
+ Assert.equal(values[keys[0]], 1, "First entry should have value 1");
+ Assert.equal(values[keys[1]], 0, "Second entry should have value 0");
+ const creationDiffInSeconds = CREATION_DIFF / USEC_PER_SEC;
+ Assert.lessOrEqual(
+ parseInt(keys[0]),
+ creationDiffInSeconds,
+ "The bucket should be smaller than time diff"
+ );
+ Assert.lessOrEqual(
+ creationDiffInSeconds,
+ parseInt(keys[1]),
+ "The next bucket should be larger than time diff"
+ );
+ }
+
+ {
+ let { values } =
+ await Glean.networking.cookieAccessFixupDiff.testGetValue();
+ info(JSON.stringify(values));
+ let keys = Object.keys(values).splice(-2, 2);
+ Assert.equal(keys.length, 2, "There should be two entries in telemetry");
+ Assert.equal(values[keys[0]], 1, "First entry should have value 1");
+ Assert.equal(values[keys[1]], 0, "Second entry should have value 0");
+ info(now);
+ const lastAccessedDiffInSeconds = LAST_ACCESSED_DIFF / USEC_PER_SEC;
+ Assert.lessOrEqual(
+ parseInt(keys[0]),
+ lastAccessedDiffInSeconds,
+ "The bucket should be smaller than time diff"
+ );
+ Assert.lessOrEqual(
+ lastAccessedDiffInSeconds,
+ parseInt(keys[1]),
+ "The next bucket should be larger than time diff"
+ );
+ }
+
+ conn.close();
+});
diff --git a/netwerk/cookie/test/unit/xpcshell.toml b/netwerk/cookie/test/unit/xpcshell.toml
new file mode 100644
index 0000000000..694d3cb847
--- /dev/null
+++ b/netwerk/cookie/test/unit/xpcshell.toml
@@ -0,0 +1,26 @@
+[DEFAULT]
+head = ""
+
+["test_baseDomain_publicsuffix.js"]
+
+["test_bug643051.js"]
+
+["test_bug1155169.js"]
+
+["test_bug1321912.js"]
+
+["test_eviction.js"]
+
+["test_getCookieSince.js"]
+
+["test_migrateCookieLifetimePref.js"]
+
+["test_parser_0001.js"]
+
+["test_parser_0019.js"]
+
+["test_rawSameSite.js"]
+
+["test_schemeMap.js"]
+
+["test_timestamp_fixup.js"]