summaryrefslogtreecommitdiffstats
path: root/dom/fetch/tests
diff options
context:
space:
mode:
Diffstat (limited to 'dom/fetch/tests')
-rw-r--r--dom/fetch/tests/browser.ini23
-rw-r--r--dom/fetch/tests/browser_blobFromFile.js64
-rw-r--r--dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js139
-rw-r--r--dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js145
-rw-r--r--dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js145
-rw-r--r--dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js150
-rw-r--r--dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js164
-rw-r--r--dom/fetch/tests/crashtests/1577196.html26
-rw-r--r--dom/fetch/tests/crashtests/1664514.html5
-rw-r--r--dom/fetch/tests/crashtests/crashtests.list2
-rw-r--r--dom/fetch/tests/crashtests/url.url5
-rw-r--r--dom/fetch/tests/credentialless_resource.sjs21
-rw-r--r--dom/fetch/tests/credentialless_worker.sjs25
-rw-r--r--dom/fetch/tests/mochitest.ini2
-rw-r--r--dom/fetch/tests/open_credentialless_document.sjs28
-rw-r--r--dom/fetch/tests/store_header.sjs23
-rw-r--r--dom/fetch/tests/test_ext_response_constructor.html47
-rw-r--r--dom/fetch/tests/test_invalid_header_exception.html39
18 files changed, 1053 insertions, 0 deletions
diff --git a/dom/fetch/tests/browser.ini b/dom/fetch/tests/browser.ini
new file mode 100644
index 0000000000..6fd8244288
--- /dev/null
+++ b/dom/fetch/tests/browser.ini
@@ -0,0 +1,23 @@
+[DEFAULT]
+[browser_blobFromFile.js]
+[browser_origin_trial_coep_credentialless_fetch_1.js]
+support-files =
+ open_credentialless_document.sjs
+ store_header.sjs
+[browser_origin_trial_coep_credentialless_fetch_2.js]
+support-files =
+ open_credentialless_document.sjs
+ store_header.sjs
+[browser_origin_trial_coep_credentialless_fetch_3.js]
+support-files =
+ open_credentialless_document.sjs
+ store_header.sjs
+[browser_origin_trial_coep_credentialless_worker.js]
+support-files =
+ open_credentialless_document.sjs
+ store_header.sjs
+ credentialless_worker.sjs
+[browser_origin_trial_coep_credentialless_cache.js]
+support-files =
+ open_credentialless_document.sjs
+ credentialless_resource.sjs
diff --git a/dom/fetch/tests/browser_blobFromFile.js b/dom/fetch/tests/browser_blobFromFile.js
new file mode 100644
index 0000000000..07a8c6669e
--- /dev/null
+++ b/dom/fetch/tests/browser_blobFromFile.js
@@ -0,0 +1,64 @@
+add_task(async function test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.tabs.remote.separateFileUriProcess", true]],
+ });
+
+ let fileData = "";
+ for (var i = 0; i < 100; ++i) {
+ fileData += "hello world!";
+ }
+
+ let file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIDirectoryService)
+ .QueryInterface(Ci.nsIProperties)
+ .get("ProfD", Ci.nsIFile);
+ file.append("file.txt");
+ file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o600);
+
+ let outStream = Cc[
+ "@mozilla.org/network/file-output-stream;1"
+ ].createInstance(Ci.nsIFileOutputStream);
+ outStream.init(
+ file,
+ 0x02 | 0x08 | 0x20, // write, create, truncate
+ 0o666,
+ 0
+ );
+ outStream.write(fileData, fileData.length);
+ outStream.close();
+
+ let fileHandler = Cc["@mozilla.org/network/io-service;1"]
+ .getService(Ci.nsIIOService)
+ .getProtocolHandler("file")
+ .QueryInterface(Ci.nsIFileProtocolHandler);
+
+ let fileURL = fileHandler.getURLSpecFromActualFile(file);
+
+ info("Opening url: " + fileURL);
+ let tab = BrowserTestUtils.addTab(gBrowser, fileURL);
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let blob = await SpecialPowers.spawn(
+ browser,
+ [file.leafName],
+ function (fileName) {
+ return new content.window.Promise(resolve => {
+ content.window
+ .fetch(fileName)
+ .then(r => r.blob())
+ .then(blob => resolve(blob));
+ });
+ }
+ );
+
+ ok(File.isInstance(blob), "We have a file");
+
+ is(blob.size, file.fileSize, "The size matches");
+ is(blob.name, file.leafName, "The name is correct");
+
+ file.remove(false);
+
+ gBrowser.removeTab(tab);
+});
diff --git a/dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js b/dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js
new file mode 100644
index 0000000000..c9b72a3393
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js
@@ -0,0 +1,139 @@
+const TOP_LEVEL_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const RESOURCE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://test1.example.com"
+ ) + "credentialless_resource.sjs";
+
+async function store(storer, url, requestCredentialMode) {
+ await SpecialPowers.spawn(
+ storer.linkedBrowser,
+ [url, requestCredentialMode],
+ async function (url, requestCredentialMode) {
+ const cache = await content.caches.open("v1");
+ const fetchRequest = new content.Request(url, {
+ mode: "no-cors",
+ credentials: requestCredentialMode,
+ });
+
+ const fetchResponse = await content.fetch(fetchRequest);
+ content.wrappedJSObject.console.log(fetchResponse.headers);
+ await cache.put(fetchRequest, fetchResponse);
+ }
+ );
+}
+
+async function retrieve(retriever, resourceURL) {
+ return await SpecialPowers.spawn(
+ retriever.linkedBrowser,
+ [resourceURL],
+ async function (url) {
+ const cache = await content.caches.open("v1");
+ try {
+ await cache.match(url);
+ return "retrieved";
+ } catch (error) {
+ return "error";
+ }
+ }
+ );
+}
+
+async function testCache(
+ storer,
+ storeRequestCredentialMode,
+ resourceCOEP,
+ retriever,
+ expectation
+) {
+ const resourceURL = RESOURCE_URL + "?" + resourceCOEP;
+
+ await store(storer, resourceURL, storeRequestCredentialMode);
+ const result = await retrieve(retriever, resourceURL);
+
+ is(result, expectation);
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.coep.credentialless", false],
+ ["dom.origin-trials.enabled", true],
+ ["dom.origin-trials.test-key.enabled", true],
+ ],
+ });
+
+ const noneTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TOP_LEVEL_URL
+ );
+ const requireCorpTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TOP_LEVEL_URL + "?requirecorp"
+ );
+ const credentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ TOP_LEVEL_URL + "?credentialless"
+ );
+
+ await testCache(noneTab, "include", "", noneTab, "retrieved");
+ await testCache(noneTab, "include", "", credentiallessTab, "error");
+ await testCache(noneTab, "omit", "", credentiallessTab, "retrieved");
+ await testCache(
+ noneTab,
+ "include",
+ "corp_cross_origin",
+ credentiallessTab,
+ "retrieved"
+ );
+ await testCache(noneTab, "include", "", requireCorpTab, "error");
+ await testCache(
+ noneTab,
+ "include",
+ "corp_cross_origin",
+ requireCorpTab,
+ "retrieved"
+ );
+ await testCache(credentiallessTab, "include", "", noneTab, "retrieved");
+ await testCache(
+ credentiallessTab,
+ "include",
+ "",
+ credentiallessTab,
+ "retrieved"
+ );
+ await testCache(credentiallessTab, "include", "", requireCorpTab, "error");
+ await testCache(
+ requireCorpTab,
+ "include",
+ "corp_cross_origin",
+ noneTab,
+ "retrieved"
+ );
+ await testCache(
+ requireCorpTab,
+ "include",
+ "corp_cross_origin",
+ credentiallessTab,
+ "retrieved"
+ );
+ await testCache(
+ requireCorpTab,
+ "include",
+ "corp_cross_origin",
+ requireCorpTab,
+ "retrieved"
+ );
+
+ await BrowserTestUtils.removeTab(noneTab);
+ await BrowserTestUtils.removeTab(requireCorpTab);
+ await BrowserTestUtils.removeTab(credentiallessTab);
+});
diff --git a/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js
new file mode 100644
index 0000000000..773c15e72a
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js
@@ -0,0 +1,145 @@
+const TOP_LEVEL_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const GET_STATE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ "store_header.sjs?addcookie";
+
+ const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ fetchRequestURL
+ );
+
+ await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function () {
+ content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+ });
+ await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+ fetchOrigin,
+ isCredentialless,
+ useMetaTag,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResult
+) {
+ let params = [];
+ if (isCredentialless) {
+ params.push("credentialless");
+ }
+ if (useMetaTag) {
+ params.push("meta");
+ }
+
+ let topLevelUrl = TOP_LEVEL_URL;
+ if (params.length) {
+ topLevelUrl += "?" + params.join("&");
+ }
+
+ const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ topLevelUrl
+ );
+
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ fetchOrigin
+ ) + "store_header.sjs?checkheader";
+
+ await SpecialPowers.spawn(
+ noCredentiallessTab.linkedBrowser,
+ [
+ !useMetaTag && isCredentialless,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ GET_STATE_URL,
+ expectedCookieResult,
+ ],
+ async function (
+ sharedArrayBufferEnabled,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ getStateURL,
+ expectedCookieResult
+ ) {
+ if (sharedArrayBufferEnabled) {
+ ok(content.crossOriginIsolated);
+ }
+ // When store_header.sjs receives this request, it will store
+ // whether it has received the cookie as a shared state.
+ await content.fetch(fetchRequestURL, {
+ mode: fetchRequestMode,
+ credentials: fetchRequestCrendentials,
+ });
+
+ // This request is used to get the saved state from the
+ // previous fetch request.
+ const response = await content.fetch(getStateURL, {
+ mode: "cors",
+ });
+ const text = await response.text();
+ is(text, expectedCookieResult);
+ }
+ );
+
+ await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+ origin,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResultForNoCredentialless,
+ expectedCookieResultForCredentialless
+) {
+ for (let credentialless of [true, false]) {
+ for (let meta of [true, false]) {
+ await testOrigin(
+ origin,
+ credentialless,
+ meta,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ credentialless
+ ? expectedCookieResultForCredentialless
+ : expectedCookieResultForNoCredentialless
+ );
+ }
+ }
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.coep.credentialless", false],
+ ["dom.origin-trials.enabled", true],
+ ["dom.origin-trials.test-key.enabled", true],
+ ],
+ });
+
+ await addCookieToOrigin(SAME_ORIGIN);
+ await addCookieToOrigin(CROSS_ORIGIN);
+
+ // Cookies never sent with omit
+ await doTest(SAME_ORIGIN, "no-cors", "omit", "noCookie", "noCookie");
+ await doTest(SAME_ORIGIN, "cors", "omit", "noCookie", "noCookie");
+ await doTest(CROSS_ORIGIN, "no-cors", "omit", "noCookie", "noCookie");
+ await doTest(CROSS_ORIGIN, "cors", "omit", "noCookie", "noCookie");
+});
diff --git a/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js
new file mode 100644
index 0000000000..7a1e6879ac
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js
@@ -0,0 +1,145 @@
+const TOP_LEVEL_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const GET_STATE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ "store_header.sjs?addcookie";
+
+ const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ fetchRequestURL
+ );
+
+ await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function () {
+ content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+ });
+ await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+ fetchOrigin,
+ isCredentialless,
+ useMetaTag,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResult
+) {
+ let params = [];
+ if (isCredentialless) {
+ params.push("credentialless");
+ }
+ if (useMetaTag) {
+ params.push("meta");
+ }
+
+ let topLevelUrl = TOP_LEVEL_URL;
+ if (params.length) {
+ topLevelUrl += "?" + params.join("&");
+ }
+
+ const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ topLevelUrl
+ );
+
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ fetchOrigin
+ ) + "store_header.sjs?checkheader";
+
+ await SpecialPowers.spawn(
+ noCredentiallessTab.linkedBrowser,
+ [
+ !useMetaTag && isCredentialless,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ GET_STATE_URL,
+ expectedCookieResult,
+ ],
+ async function (
+ sharedArrayBufferEnabled,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ getStateURL,
+ expectedCookieResult
+ ) {
+ if (sharedArrayBufferEnabled) {
+ ok(content.crossOriginIsolated);
+ }
+ // When store_header.sjs receives this request, it will store
+ // whether it has received the cookie as a shared state.
+ await content.fetch(fetchRequestURL, {
+ mode: fetchRequestMode,
+ credentials: fetchRequestCrendentials,
+ });
+
+ // This request is used to get the saved state from the
+ // previous fetch request.
+ const response = await content.fetch(getStateURL, {
+ mode: "cors",
+ });
+ const text = await response.text();
+ is(text, expectedCookieResult);
+ }
+ );
+
+ await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+ origin,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResultForNoCredentialless,
+ expectedCookieResultForCredentialless
+) {
+ for (let credentialless of [true, false]) {
+ for (let meta of [true, false]) {
+ await testOrigin(
+ origin,
+ credentialless,
+ meta,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ credentialless
+ ? expectedCookieResultForCredentialless
+ : expectedCookieResultForNoCredentialless
+ );
+ }
+ }
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.coep.credentialless", false],
+ ["dom.origin-trials.enabled", true],
+ ["dom.origin-trials.test-key.enabled", true],
+ ],
+ });
+
+ await addCookieToOrigin(SAME_ORIGIN);
+ await addCookieToOrigin(CROSS_ORIGIN);
+
+ // Same-origin request contains Cookies.
+ await doTest(SAME_ORIGIN, "no-cors", "include", "hasCookie", "hasCookie");
+ await doTest(SAME_ORIGIN, "cors", "include", "hasCookie", "hasCookie");
+ await doTest(SAME_ORIGIN, "no-cors", "same-origin", "hasCookie", "hasCookie");
+ await doTest(SAME_ORIGIN, "cors", "same-origin", "hasCookie", "hasCookie");
+});
diff --git a/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js
new file mode 100644
index 0000000000..a1a1edb2ff
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js
@@ -0,0 +1,150 @@
+const TOP_LEVEL_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const GET_STATE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ "store_header.sjs?addcookie";
+
+ const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ fetchRequestURL
+ );
+
+ await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function () {
+ content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+ });
+ await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+ fetchOrigin,
+ isCredentialless,
+ useMetaTag,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResult
+) {
+ let params = [];
+ if (isCredentialless) {
+ params.push("credentialless");
+ }
+ if (useMetaTag) {
+ params.push("meta");
+ }
+
+ let topLevelUrl = TOP_LEVEL_URL;
+ if (params.length) {
+ topLevelUrl += "?" + params.join("&");
+ }
+
+ const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ topLevelUrl
+ );
+
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ fetchOrigin
+ ) + "store_header.sjs?checkheader";
+
+ await SpecialPowers.spawn(
+ noCredentiallessTab.linkedBrowser,
+ [
+ !useMetaTag && isCredentialless,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ GET_STATE_URL,
+ expectedCookieResult,
+ ],
+ async function (
+ sharedArrayBufferEnabled,
+ fetchRequestURL,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ getStateURL,
+ expectedCookieResult
+ ) {
+ if (sharedArrayBufferEnabled) {
+ ok(content.crossOriginIsolated);
+ }
+ // When store_header.sjs receives this request, it will store
+ // whether it has received the cookie as a shared state.
+ await content.fetch(fetchRequestURL, {
+ mode: fetchRequestMode,
+ credentials: fetchRequestCrendentials,
+ });
+
+ // This request is used to get the saved state from the
+ // previous fetch request.
+ const response = await content.fetch(getStateURL, {
+ mode: "cors",
+ });
+ const text = await response.text();
+ is(text, expectedCookieResult);
+ }
+ );
+
+ await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+ origin,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ expectedCookieResultForNoCredentialless,
+ expectedCookieResultForCredentialless
+) {
+ for (let credentialless of [true, false]) {
+ for (let meta of [true, false]) {
+ await testOrigin(
+ origin,
+ credentialless,
+ meta,
+ fetchRequestMode,
+ fetchRequestCrendentials,
+ credentialless
+ ? expectedCookieResultForCredentialless
+ : expectedCookieResultForNoCredentialless
+ );
+ }
+ }
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.coep.credentialless", false],
+ ["dom.origin-trials.enabled", true],
+ ["dom.origin-trials.test-key.enabled", true],
+ ],
+ });
+
+ await addCookieToOrigin(SAME_ORIGIN);
+ await addCookieToOrigin(CROSS_ORIGIN);
+
+ // Cross-origin CORS requests contains Cookies, if credentials mode is set to
+ // 'include'. This does not depends on COEP.
+ await doTest(CROSS_ORIGIN, "cors", "include", "hasCookie", "hasCookie");
+ await doTest(CROSS_ORIGIN, "cors", "same-origin", "noCookie", "noCookie");
+
+ // Cross-origin no-CORS requests includes Cookies when:
+ // 1. credentials mode is 'include'
+ // 2. COEP: is not credentialless.
+ await doTest(CROSS_ORIGIN, "no-cors", "include", "hasCookie", "noCookie");
+ await doTest(CROSS_ORIGIN, "no-cors", "same-origin", "noCookie", "noCookie");
+});
diff --git a/dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js b/dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js
new file mode 100644
index 0000000000..be77add88e
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js
@@ -0,0 +1,164 @@
+const TOP_LEVEL_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "open_credentialless_document.sjs";
+
+const WORKER_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "credentialless_worker.sjs";
+
+const GET_STATE_URL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+ ) + "store_header.sjs?getstate";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const WORKER_USES_CREDENTIALLESS = "credentialless";
+const WORKER_NOT_USE_CREDENTIALLESS = "";
+
+async function addCookieToOrigin(origin) {
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+ "store_header.sjs?addcookie";
+
+ const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ fetchRequestURL
+ );
+
+ await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function () {
+ content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+ });
+ await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+ fetchOrigin,
+ isCredentialless,
+ workerUsesCredentialless,
+ expectedCookieResult
+) {
+ let topLevelUrl = TOP_LEVEL_URL;
+ if (isCredentialless) {
+ topLevelUrl += "?credentialless";
+ }
+ const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ topLevelUrl
+ );
+
+ const fetchRequestURL =
+ getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ fetchOrigin
+ ) + "store_header.sjs?checkheader";
+
+ let workerScriptURL = WORKER_URL + "?" + workerUsesCredentialless;
+
+ await SpecialPowers.spawn(
+ noCredentiallessTab.linkedBrowser,
+ [fetchRequestURL, GET_STATE_URL, workerScriptURL, expectedCookieResult],
+ async function (
+ fetchRequestURL,
+ getStateURL,
+ workerScriptURL,
+ expectedCookieResult
+ ) {
+ const worker = new content.Worker(workerScriptURL, {});
+
+ // When the worker receives this message, it'll send
+ // a fetch request to fetchRequestURL, and fetchRequestURL
+ // will store whether it has received the cookie as a
+ // shared state.
+ worker.postMessage(fetchRequestURL);
+
+ if (expectedCookieResult == "error") {
+ await new Promise(r => {
+ worker.onerror = function () {
+ ok(true, "worker has error");
+ r();
+ };
+ });
+ } else {
+ await new Promise(r => {
+ worker.addEventListener("message", async function () {
+ // This request is used to get the saved state from the
+ // previous fetch request.
+ const response = await content.fetch(getStateURL, {
+ mode: "cors",
+ });
+ const text = await response.text();
+ is(text, expectedCookieResult);
+ r();
+ });
+ });
+ }
+ }
+ );
+ await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function dedicatedWorkerTest(
+ origin,
+ workerCOEP,
+ expectedCookieResultForNoCredentialless,
+ expectedCookieResultForCredentialless
+) {
+ await testOrigin(
+ origin,
+ false,
+ workerCOEP,
+ expectedCookieResultForNoCredentialless
+ );
+ await testOrigin(
+ origin,
+ true,
+ workerCOEP,
+ expectedCookieResultForCredentialless
+ );
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.coep.credentialless", false], // Explicitly set credentialless to false because we want to test origin trial
+ ["dom.origin-trials.enabled", true],
+ ["dom.origin-trials.test-key.enabled", true],
+ ],
+ });
+
+ await addCookieToOrigin(SAME_ORIGIN);
+ await addCookieToOrigin(CROSS_ORIGIN);
+
+ await dedicatedWorkerTest(
+ SAME_ORIGIN,
+ WORKER_NOT_USE_CREDENTIALLESS,
+ "hasCookie",
+ "error"
+ );
+ await dedicatedWorkerTest(
+ SAME_ORIGIN,
+ WORKER_USES_CREDENTIALLESS,
+ "hasCookie",
+ "hasCookie"
+ );
+
+ await dedicatedWorkerTest(
+ CROSS_ORIGIN,
+ WORKER_NOT_USE_CREDENTIALLESS,
+ "hasCookie",
+ "error"
+ );
+ await dedicatedWorkerTest(
+ CROSS_ORIGIN,
+ WORKER_USES_CREDENTIALLESS,
+ "noCookie",
+ "noCookie"
+ );
+});
diff --git a/dom/fetch/tests/crashtests/1577196.html b/dom/fetch/tests/crashtests/1577196.html
new file mode 100644
index 0000000000..a2cdae06d0
--- /dev/null
+++ b/dom/fetch/tests/crashtests/1577196.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script>
+
+function test() {
+ const xhr = new XMLHttpRequest();
+ const frame = document.createElement("frame");
+ document.documentElement.appendChild(frame);
+ const win = frame.contentWindow;
+ const div = document.createElement("div");
+ div.insertBefore(frame, div.childNodes[0]);
+ SpecialPowers.forceCC();
+ SpecialPowers.gc();
+ xhr.open("P", "", false);
+ xhr.send();
+ win.fetch(null, {}).then(function(arg14) {});
+}
+
+for (let i = 0; i < 10; i++) {
+ test();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/fetch/tests/crashtests/1664514.html b/dom/fetch/tests/crashtests/1664514.html
new file mode 100644
index 0000000000..b033d842fa
--- /dev/null
+++ b/dom/fetch/tests/crashtests/1664514.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<script>
+ fetch("./url.url")
+</script>
diff --git a/dom/fetch/tests/crashtests/crashtests.list b/dom/fetch/tests/crashtests/crashtests.list
new file mode 100644
index 0000000000..f79a122734
--- /dev/null
+++ b/dom/fetch/tests/crashtests/crashtests.list
@@ -0,0 +1,2 @@
+load 1577196.html
+load 1664514.html
diff --git a/dom/fetch/tests/crashtests/url.url b/dom/fetch/tests/crashtests/url.url
new file mode 100644
index 0000000000..95e9cf8db0
--- /dev/null
+++ b/dom/fetch/tests/crashtests/url.url
@@ -0,0 +1,5 @@
+[{000214A0-0000-0000-C000-000000000046}]
+Prop3=19,11
+[InternetShortcut]
+IDList=
+URL=http://localhost:8000/
diff --git a/dom/fetch/tests/credentialless_resource.sjs b/dom/fetch/tests/credentialless_resource.sjs
new file mode 100644
index 0000000000..72d0d738ec
--- /dev/null
+++ b/dom/fetch/tests/credentialless_resource.sjs
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+// small red image
+const IMG_BYTES = atob(
+ "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+ "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
+);
+
+function handleRequest(request, response) {
+ response.seizePower();
+ response.write("HTTP/1.1 200 OK\r\n");
+ response.write("Content-Type: image/png\r\n");
+ if (request.queryString === "corp_cross_origin") {
+ response.write("Cross-Origin-Resource-Policy: cross-origin\r\n");
+ }
+ response.write("\r\n");
+ response.write(IMG_BYTES);
+ response.finish();
+}
diff --git a/dom/fetch/tests/credentialless_worker.sjs b/dom/fetch/tests/credentialless_worker.sjs
new file mode 100644
index 0000000000..a9e2197d18
--- /dev/null
+++ b/dom/fetch/tests/credentialless_worker.sjs
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const WORKER = `
+ onmessage = function(event) {
+ fetch(event.data, {
+ mode: "no-cors",
+ credentials: "include"
+ }).then(function() {
+ postMessage("fetch done");
+ });
+ }
+`;
+
+function handleRequest(request, response) {
+ if (request.queryString === "credentialless") {
+ response.setHeader("Cross-Origin-Embedder-Policy", "credentialless", true);
+ }
+
+ response.setHeader("Content-Type", "application/javascript", false);
+ response.setStatusLine(request.httpVersion, "200", "Found");
+ response.write(WORKER);
+}
diff --git a/dom/fetch/tests/mochitest.ini b/dom/fetch/tests/mochitest.ini
new file mode 100644
index 0000000000..32eb841faf
--- /dev/null
+++ b/dom/fetch/tests/mochitest.ini
@@ -0,0 +1,2 @@
+[test_ext_response_constructor.html]
+[test_invalid_header_exception.html]
diff --git a/dom/fetch/tests/open_credentialless_document.sjs b/dom/fetch/tests/open_credentialless_document.sjs
new file mode 100644
index 0000000000..6269e73d78
--- /dev/null
+++ b/dom/fetch/tests/open_credentialless_document.sjs
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Created with: mktoken --origin 'https://example.com' --feature CoepCredentialless --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8
+const TOKEN =
+ "Az+DK2Kczk8Xz1cAlD+TkvPZmuM2uJZ2CFefbp2hLuCU9FbUqxWTyQ2tEYr50r0syKELcOZLAPaABw8aYTLHn5YAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IkNvZXBDcmVkZW50aWFsbGVzcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9";
+
+function handleRequest(request, response) {
+ let params = (request.queryString || "").split("&");
+ if (params.includes("credentialless")) {
+ response.setHeader("Cross-Origin-Embedder-Policy", "credentialless");
+ // Enables the SharedArrayBuffer feature
+ response.setHeader("Cross-Origin-Opener-Policy", "same-origin");
+ } else if (params.includes("requirecorp")) {
+ response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
+ }
+ let html = "<!doctype html>";
+ if (!params.includes("meta")) {
+ response.setHeader("Origin-Trial", TOKEN);
+ } else {
+ html += `<meta http-equiv="origin-trial" content="${TOKEN}">`;
+ }
+ html += "<body>Hello, world!</body>";
+ response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+ response.setStatusLine(request.httpVersion, "200", "Found");
+ response.write(html);
+}
diff --git a/dom/fetch/tests/store_header.sjs b/dom/fetch/tests/store_header.sjs
new file mode 100644
index 0000000000..3290a09995
--- /dev/null
+++ b/dom/fetch/tests/store_header.sjs
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const key = "store_header";
+function handleRequest(request, response) {
+ response.setHeader("Content-Type", "text/plain");
+ response.setHeader("Access-Control-Allow-Origin", "https://example.com");
+ response.setHeader("Access-Control-Allow-Credentials", "true");
+
+ if (request.queryString === "getstate") {
+ response.write(getSharedState(key));
+ } else if (request.queryString === "checkheader") {
+ if (request.hasHeader("Cookie")) {
+ setSharedState(key, "hasCookie");
+ } else {
+ setSharedState(key, "noCookie");
+ }
+ } else {
+ // This is the first request which sets the cookie
+ }
+}
diff --git a/dom/fetch/tests/test_ext_response_constructor.html b/dom/fetch/tests/test_ext_response_constructor.html
new file mode 100644
index 0000000000..e4b6cf4eb2
--- /dev/null
+++ b/dom/fetch/tests/test_ext_response_constructor.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test `Response` constructor in a WebExtension</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ add_task(async function testResponseConstructor() {
+ /* eslint-env webextensions */
+ function contentScript() {
+ new Response();
+ browser.test.notifyPass("done");
+ }
+
+ const extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ matches: ["<all_urls>"],
+ js: ["content_script.js"],
+ },
+ ],
+ },
+
+ files: {
+ "content_script.js": contentScript,
+ },
+ });
+
+ await extension.startup();
+
+ const win = window.open("https://example.com");
+ await extension.awaitFinish("done");
+ win.close();
+
+ await extension.unload();
+ });
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/dom/fetch/tests/test_invalid_header_exception.html b/dom/fetch/tests/test_invalid_header_exception.html
new file mode 100644
index 0000000000..710afffcc6
--- /dev/null
+++ b/dom/fetch/tests/test_invalid_header_exception.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1629390
+-->
+<head>
+ <title>Test for Bug 1629390</title>
+ <meta charset="UTF-8">
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1629390">Mozilla Bug 1629390</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1629390 **/
+let headerNames = [
+ ['a˻b', 'a\uFFFD\uFFFDb'],
+ ['Àaª', '\uFFFDa\uFFFD'],
+ ['˻b', '\uFFFD\uFFFDb'],
+ ['\xEAa', '\uFFFDa'],
+ ['\xC2\x7F', '\uFFFD\x7F'],
+];
+for (let [invalid, corrected] of headerNames) {
+ try {
+ new Headers([[invalid, '']]);
+ } catch(e) {
+ is(e.message, `Headers constructor: ${corrected} is an invalid header name.`);
+ }
+}
+
+</script>
+</pre>
+</body>
+</html>