summaryrefslogtreecommitdiffstats
path: root/dom/serviceworkers/test/fetch
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/serviceworkers/test/fetch.js33
-rw-r--r--dom/serviceworkers/test/fetch/cookie/cookie_test.js11
-rw-r--r--dom/serviceworkers/test/fetch/cookie/register.html19
-rw-r--r--dom/serviceworkers/test/fetch/cookie/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/deliver-gzip.sjs21
-rw-r--r--dom/serviceworkers/test/fetch/fetch_tests.js716
-rw-r--r--dom/serviceworkers/test/fetch/fetch_worker_script.js28
-rw-r--r--dom/serviceworkers/test/fetch/hsts/embedder.html7
-rw-r--r--dom/serviceworkers/test/fetch/hsts/hsts_test.js11
-rw-r--r--dom/serviceworkers/test/fetch/hsts/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/serviceworkers/test/fetch/hsts/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/serviceworkers/test/fetch/hsts/image.html13
-rw-r--r--dom/serviceworkers/test/fetch/hsts/realindex.html8
-rw-r--r--dom/serviceworkers/test/fetch/hsts/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/hsts/register.html^headers^2
-rw-r--r--dom/serviceworkers/test/fetch/hsts/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/https/clonedresponse/https_test.js19
-rw-r--r--dom/serviceworkers/test/fetch/https/clonedresponse/index.html4
-rw-r--r--dom/serviceworkers/test/fetch/https/clonedresponse/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/https/clonedresponse/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/https/https_test.js31
-rw-r--r--dom/serviceworkers/test/fetch/https/index.html4
-rw-r--r--dom/serviceworkers/test/fetch/https/register.html20
-rw-r--r--dom/serviceworkers/test/fetch/https/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/index.html29
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/maxage_test.js45
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/imagecache-maxage/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/imagecache_test.js15
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/index.html20
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/postmortem.html9
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/register.html16
-rw-r--r--dom/serviceworkers/test/fetch/imagecache/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/importscript-mixedcontent/https_test.js31
-rw-r--r--dom/serviceworkers/test/fetch/importscript-mixedcontent/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/importscript-mixedcontent/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/index.html191
-rw-r--r--dom/serviceworkers/test/fetch/interrupt.sjs20
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/index-https.sjs8
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/origin_test.js29
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/realindex.html6
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/realindex.html^headers^1
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/origin/https/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/origin/index-to-https.sjs8
-rw-r--r--dom/serviceworkers/test/fetch/origin/index.sjs8
-rw-r--r--dom/serviceworkers/test/fetch/origin/origin_test.js38
-rw-r--r--dom/serviceworkers/test/fetch/origin/realindex.html6
-rw-r--r--dom/serviceworkers/test/fetch/origin/realindex.html^headers^1
-rw-r--r--dom/serviceworkers/test/fetch/origin/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/origin/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/plugin/plugins.html43
-rw-r--r--dom/serviceworkers/test/fetch/plugin/worker.js15
-rw-r--r--dom/serviceworkers/test/fetch/real-file.txt1
-rw-r--r--dom/serviceworkers/test/fetch/redirect.sjs4
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/index.html7
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/redirect.sjs8
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/redirector.html2
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/requesturl_test.js21
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/secret.html5
-rw-r--r--dom/serviceworkers/test/fetch/requesturl/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/sandbox/index.html5
-rw-r--r--dom/serviceworkers/test/fetch/sandbox/intercepted_index.html5
-rw-r--r--dom/serviceworkers/test/fetch/sandbox/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/sandbox/sandbox_test.js5
-rw-r--r--dom/serviceworkers/test/fetch/sandbox/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html10
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html^headers^1
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/image.html13
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/realindex.html4
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/register.html14
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/unregister.html12
-rw-r--r--dom/serviceworkers/test/fetch/upgrade-insecure/upgrade-insecure_test.js11
-rw-r--r--dom/serviceworkers/test/fetch_event_worker.js364
81 files changed, 2222 insertions, 0 deletions
diff --git a/dom/serviceworkers/test/fetch.js b/dom/serviceworkers/test/fetch.js
new file mode 100644
index 0000000000..bf1bb4acb3
--- /dev/null
+++ b/dom/serviceworkers/test/fetch.js
@@ -0,0 +1,33 @@
+function get_query_params(url) {
+ var search = new URL(url).search;
+ if (!search) {
+ return {};
+ }
+ var ret = {};
+ var params = search.substring(1).split("&");
+ params.forEach(function (param) {
+ var element = param.split("=");
+ ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+ });
+ return ret;
+}
+
+addEventListener("fetch", function (event) {
+ if (event.request.url.includes("fail.html")) {
+ event.respondWith(fetch("hello.html", { integrity: "abc" }));
+ } else if (event.request.url.includes("fake.html")) {
+ event.respondWith(fetch("hello.html"));
+ } else if (event.request.url.includes("file_js_cache")) {
+ event.respondWith(fetch(event.request));
+ } else if (event.request.url.includes("redirect")) {
+ let param = get_query_params(event.request.url);
+ let url = param.url;
+ let mode = param.mode;
+
+ event.respondWith(fetch(url, { mode }));
+ }
+});
+
+addEventListener("activate", function (event) {
+ event.waitUntil(clients.claim());
+});
diff --git a/dom/serviceworkers/test/fetch/cookie/cookie_test.js b/dom/serviceworkers/test/fetch/cookie/cookie_test.js
new file mode 100644
index 0000000000..4102b4b341
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/cookie/cookie_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("synth.html")) {
+ var body =
+ "<script>" +
+ 'window.parent.postMessage({status: "done", cookie: document.cookie}, "*");' +
+ "</script>";
+ event.respondWith(
+ new Response(body, { headers: { "Content-Type": "text/html" } })
+ );
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/cookie/register.html b/dom/serviceworkers/test/fetch/cookie/register.html
new file mode 100644
index 0000000000..99eabaf0a2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/cookie/register.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script src="../../utils.js"></script>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ document.cookie = "foo=bar";
+
+ navigator.serviceWorker.register("cookie_test.js", {scope: "."})
+ .then(reg => {
+ return waitForState(reg.installing, "activated", reg);
+ }).then(done);
+</script>
diff --git a/dom/serviceworkers/test/fetch/cookie/unregister.html b/dom/serviceworkers/test/fetch/cookie/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/cookie/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/deliver-gzip.sjs b/dom/serviceworkers/test/fetch/deliver-gzip.sjs
new file mode 100644
index 0000000000..d7cbdef06e
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/deliver-gzip.sjs
@@ -0,0 +1,21 @@
+"use strict";
+
+function handleRequest(request, response) {
+ // The string "hello" repeated 10 times followed by newline. Compressed using gzip.
+ // prettier-ignore
+ let bytes = [0x1f, 0x8b, 0x08, 0x08, 0x4d, 0xe2, 0xf9, 0x54, 0x00, 0x03, 0x68,
+ 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0xcb, 0x48, 0xcd, 0xc9, 0xc9, 0xcf,
+ 0x20, 0x85, 0xe0, 0x02, 0x00, 0xf5, 0x4b, 0x38, 0xcf, 0x33, 0x00,
+ 0x00, 0x00];
+
+ response.setHeader("Content-Encoding", "gzip", false);
+ response.setHeader("Content-Length", "" + bytes.length, false);
+ response.setHeader("Content-Type", "text/plain", false);
+
+ let bos = Components.classes[
+ "@mozilla.org/binaryoutputstream;1"
+ ].createInstance(Components.interfaces.nsIBinaryOutputStream);
+ bos.setOutputStream(response.bodyOutputStream);
+
+ bos.writeByteArray(bytes);
+}
diff --git a/dom/serviceworkers/test/fetch/fetch_tests.js b/dom/serviceworkers/test/fetch/fetch_tests.js
new file mode 100644
index 0000000000..69b8a89679
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/fetch_tests.js
@@ -0,0 +1,716 @@
+var origin = "http://mochi.test:8888";
+
+function fetchXHRWithMethod(name, method, onload, onerror, headers) {
+ expectAsyncResult();
+
+ onload =
+ onload ||
+ function () {
+ my_ok(false, "XHR load should not complete successfully");
+ finish();
+ };
+ onerror =
+ onerror ||
+ function () {
+ my_ok(
+ false,
+ "XHR load for " + name + " should be intercepted successfully"
+ );
+ finish();
+ };
+
+ var x = new XMLHttpRequest();
+ x.open(method, name, true);
+ x.onload = function () {
+ onload(x);
+ };
+ x.onerror = function () {
+ onerror(x);
+ };
+ headers = headers || [];
+ headers.forEach(function (header) {
+ x.setRequestHeader(header[0], header[1]);
+ });
+ x.send();
+}
+
+var corsServerPath =
+ "/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs";
+var corsServerURL = "http://example.com" + corsServerPath;
+
+function redirectURL(hops) {
+ return (
+ hops[0].server +
+ corsServerPath +
+ "?hop=1&hops=" +
+ encodeURIComponent(JSON.stringify(hops))
+ );
+}
+
+function fetchXHR(name, onload, onerror, headers) {
+ return fetchXHRWithMethod(name, "GET", onload, onerror, headers);
+}
+
+fetchXHR("bare-synthesized.txt", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "synthesized response body",
+ "load should have synthesized response"
+ );
+ finish();
+});
+
+fetchXHR("test-respondwith-response.txt", function (xhr) {
+ my_ok(
+ xhr.status == 200,
+ "test-respondwith-response load should be successful"
+ );
+ my_ok(
+ xhr.responseText == "test-respondwith-response response body",
+ "load should have response"
+ );
+ finish();
+});
+
+fetchXHR("synthesized-404.txt", function (xhr) {
+ my_ok(xhr.status == 404, "load should 404");
+ my_ok(
+ xhr.responseText == "synthesized response body",
+ "404 load should have synthesized response"
+ );
+ finish();
+});
+
+fetchXHR("synthesized-headers.txt", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.getResponseHeader("X-Custom-Greeting") === "Hello",
+ "custom header should be set"
+ );
+ my_ok(
+ xhr.responseText == "synthesized response body",
+ "custom header load should have synthesized response"
+ );
+ finish();
+});
+
+fetchXHR("synthesized-redirect-real-file.txt", function (xhr) {
+ dump("Got status AARRGH " + xhr.status + " " + xhr.responseText + "\n");
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "This is a real file.\n",
+ "Redirect to real file should complete."
+ );
+ finish();
+});
+
+fetchXHR("synthesized-redirect-twice-real-file.txt", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "This is a real file.\n",
+ "Redirect to real file (twice) should complete."
+ );
+ finish();
+});
+
+fetchXHR("synthesized-redirect-synthesized.txt", function (xhr) {
+ my_ok(xhr.status == 200, "synth+redirect+synth load should be successful");
+ my_ok(
+ xhr.responseText == "synthesized response body",
+ "load should have redirected+synthesized response"
+ );
+ finish();
+});
+
+fetchXHR("synthesized-redirect-twice-synthesized.txt", function (xhr) {
+ my_ok(
+ xhr.status == 200,
+ "synth+redirect+synth (twice) load should be successful"
+ );
+ my_ok(
+ xhr.responseText == "synthesized response body",
+ "load should have redirected+synthesized (twice) response"
+ );
+ finish();
+});
+
+fetchXHR("redirect.sjs", function (xhr) {
+ my_ok(xhr.status == 404, "redirected load should be uninterrupted");
+ finish();
+});
+
+fetchXHR("ignored.txt", function (xhr) {
+ my_ok(xhr.status == 404, "load should be uninterrupted");
+ finish();
+});
+
+fetchXHR("rejected.txt", null, function (xhr) {
+ my_ok(xhr.status == 0, "load should not complete");
+ finish();
+});
+
+fetchXHR("nonresponse.txt", null, function (xhr) {
+ my_ok(xhr.status == 0, "load should not complete");
+ finish();
+});
+
+fetchXHR("nonresponse2.txt", null, function (xhr) {
+ my_ok(xhr.status == 0, "load should not complete");
+ finish();
+});
+
+fetchXHR("nonpromise.txt", null, function (xhr) {
+ my_ok(xhr.status == 0, "load should not complete");
+ finish();
+});
+
+fetchXHR(
+ "headers.txt",
+ function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(xhr.responseText == "1", "request header checks should have passed");
+ finish();
+ },
+ null,
+ [
+ ["X-Test1", "header1"],
+ ["X-Test2", "header2"],
+ ]
+);
+
+fetchXHR("http://user:pass@mochi.test:8888/user-pass", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "http://user:pass@mochi.test:8888/user-pass",
+ "The username and password should be preserved"
+ );
+ finish();
+});
+
+fetchXHR("readable-stream.txt", function (xhr) {
+ my_ok(xhr.status == 200, "loading completed");
+ my_ok(xhr.responseText == "Hello!", "The message is correct!");
+ finish();
+});
+
+fetchXHR(
+ "readable-stream-locked.txt",
+ function (xhr) {
+ my_ok(false, "This should not be called!");
+ finish();
+ },
+ function () {
+ my_ok(true, "The exception has been correctly handled!");
+ finish();
+ }
+);
+
+fetchXHR(
+ "readable-stream-with-exception.txt",
+ function (xhr) {
+ my_ok(false, "This should not be called!");
+ finish();
+ },
+ function () {
+ my_ok(true, "The exception has been correctly handled!");
+ finish();
+ }
+);
+
+fetchXHR(
+ "readable-stream-with-exception2.txt",
+ function (xhr) {
+ my_ok(false, "This should not be called!");
+ finish();
+ },
+ function () {
+ my_ok(true, "The exception has been correctly handled!");
+ finish();
+ }
+);
+
+fetchXHR(
+ "readable-stream-already-consumed.txt",
+ function (xhr) {
+ my_ok(false, "This should not be called!");
+ finish();
+ },
+ function () {
+ my_ok(true, "The exception has been correctly handled!");
+ finish();
+ }
+);
+
+var expectedUncompressedResponse = "";
+for (let i = 0; i < 10; ++i) {
+ expectedUncompressedResponse += "hello";
+}
+expectedUncompressedResponse += "\n";
+
+// ServiceWorker does not intercept, at which point the network request should
+// be correctly decoded.
+fetchXHR("deliver-gzip.sjs", function (xhr) {
+ my_ok(xhr.status == 200, "network gzip load should be successful");
+ my_ok(
+ xhr.responseText == expectedUncompressedResponse,
+ "network gzip load should have synthesized response."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Encoding") == "gzip",
+ "network Content-Encoding should be gzip."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Length") == "35",
+ "network Content-Length should be of original gzipped file."
+ );
+ finish();
+});
+
+fetchXHR("hello.gz", function (xhr) {
+ my_ok(xhr.status == 200, "gzip load should be successful");
+ my_ok(
+ xhr.responseText == expectedUncompressedResponse,
+ "gzip load should have synthesized response."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Encoding") == "gzip",
+ "Content-Encoding should be gzip."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Length") == "35",
+ "Content-Length should be of original gzipped file."
+ );
+ finish();
+});
+
+fetchXHR("hello-after-extracting.gz", function (xhr) {
+ my_ok(xhr.status == 200, "gzip load after extracting should be successful");
+ my_ok(
+ xhr.responseText == expectedUncompressedResponse,
+ "gzip load after extracting should have synthesized response."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Encoding") == "gzip",
+ "Content-Encoding after extracting should be gzip."
+ );
+ my_ok(
+ xhr.getResponseHeader("Content-Length") == "35",
+ "Content-Length after extracting should be of original gzipped file."
+ );
+ finish();
+});
+
+fetchXHR(corsServerURL + "?status=200&allowOrigin=*", function (xhr) {
+ my_ok(
+ xhr.status == 200,
+ "cross origin load with correct headers should be successful"
+ );
+ my_ok(
+ xhr.getResponseHeader("access-control-allow-origin") == null,
+ "cors headers should be filtered out"
+ );
+ finish();
+});
+
+// Verify origin header is sent properly even when we have a no-intercept SW.
+var uriOrigin = encodeURIComponent(origin);
+fetchXHR(
+ "http://example.org" +
+ corsServerPath +
+ "?ignore&status=200&origin=" +
+ uriOrigin +
+ "&allowOrigin=" +
+ uriOrigin,
+ function (xhr) {
+ my_ok(
+ xhr.status == 200,
+ "cross origin load with correct headers should be successful"
+ );
+ my_ok(
+ xhr.getResponseHeader("access-control-allow-origin") == null,
+ "cors headers should be filtered out"
+ );
+ finish();
+ }
+);
+
+// Verify that XHR is considered CORS tainted even when original URL is same-origin
+// redirected to cross-origin.
+fetchXHR(
+ redirectURL([
+ { server: origin },
+ { server: "http://example.org", allowOrigin: origin },
+ ]),
+ function (xhr) {
+ my_ok(
+ xhr.status == 200,
+ "cross origin load with correct headers should be successful"
+ );
+ my_ok(
+ xhr.getResponseHeader("access-control-allow-origin") == null,
+ "cors headers should be filtered out"
+ );
+ finish();
+ }
+);
+
+// Test that CORS preflight requests cannot be intercepted. Performs a
+// cross-origin XHR that the SW chooses not to intercept. This requires a
+// preflight request, which the SW must not be allowed to intercept.
+fetchXHR(
+ corsServerURL + "?status=200&allowOrigin=*",
+ null,
+ function (xhr) {
+ my_ok(
+ xhr.status == 0,
+ "cross origin load with incorrect headers should be a failure"
+ );
+ finish();
+ },
+ [["X-Unsafe", "unsafe"]]
+);
+
+// Test that CORS preflight requests cannot be intercepted. Performs a
+// cross-origin XHR that the SW chooses to intercept and respond with a
+// cross-origin fetch. This requires a preflight request, which the SW must not
+// be allowed to intercept.
+fetchXHR(
+ "http://example.org" + corsServerPath + "?status=200&allowOrigin=*",
+ null,
+ function (xhr) {
+ my_ok(
+ xhr.status == 0,
+ "cross origin load with incorrect headers should be a failure"
+ );
+ finish();
+ },
+ [["X-Unsafe", "unsafe"]]
+);
+
+// Test that when the page fetches a url the controlling SW forces a redirect to
+// another location. This other location fetch should also be intercepted by
+// the SW.
+fetchXHR("something.txt", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "something else response body",
+ "load should have something else"
+ );
+ finish();
+});
+
+// Test fetch will internally get it's SkipServiceWorker flag set. The request is
+// made from the SW through fetch(). fetch() fetches a server-side JavaScript
+// file that force a redirect. The redirect location fetch does not go through
+// the SW.
+fetchXHR("redirect_serviceworker.sjs", function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "// empty worker, always succeed!\n",
+ "load should have redirection content"
+ );
+ finish();
+});
+
+fetchXHR(
+ "empty-header",
+ function (xhr) {
+ my_ok(xhr.status == 200, "load should be successful");
+ my_ok(
+ xhr.responseText == "emptyheader",
+ "load should have the expected content"
+ );
+ finish();
+ },
+ null,
+ [["emptyheader", ""]]
+);
+
+expectAsyncResult();
+fetch(
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*"
+).then(
+ function (res) {
+ my_ok(res.ok, "Valid CORS request should receive valid response");
+ my_ok(res.type == "cors", "Response type should be CORS");
+ res.text().then(function (body) {
+ my_ok(
+ body === "<res>hello pass</res>\n",
+ "cors response body should match"
+ );
+ finish();
+ });
+ },
+ function (e) {
+ my_ok(false, "CORS Fetch failed");
+ finish();
+ }
+);
+
+expectAsyncResult();
+fetch(
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200",
+ { mode: "no-cors" }
+).then(
+ function (res) {
+ my_ok(res.type == "opaque", "Response type should be opaque");
+ my_ok(res.status == 0, "Status should be 0");
+ res.text().then(function (body) {
+ my_ok(body === "", "opaque response body should be empty");
+ finish();
+ });
+ },
+ function (e) {
+ my_ok(false, "no-cors Fetch failed");
+ finish();
+ }
+);
+
+expectAsyncResult();
+fetch("opaque-on-same-origin").then(
+ function (res) {
+ my_ok(
+ false,
+ "intercepted opaque response for non no-cors request should fail."
+ );
+ finish();
+ },
+ function (e) {
+ my_ok(
+ true,
+ "intercepted opaque response for non no-cors request should fail."
+ );
+ finish();
+ }
+);
+
+expectAsyncResult();
+fetch("http://example.com/opaque-no-cors", { mode: "no-cors" }).then(
+ function (res) {
+ my_ok(
+ res.type == "opaque",
+ "intercepted opaque response for no-cors request should have type opaque."
+ );
+ finish();
+ },
+ function (e) {
+ my_ok(
+ false,
+ "intercepted opaque response for no-cors request should pass."
+ );
+ finish();
+ }
+);
+
+expectAsyncResult();
+fetch("http://example.com/cors-for-no-cors", { mode: "no-cors" }).then(
+ function (res) {
+ my_ok(
+ res.type == "cors",
+ "synthesize CORS response should result in outer CORS response"
+ );
+ finish();
+ },
+ function (e) {
+ my_ok(false, "cors-for-no-cors request should not reject");
+ finish();
+ }
+);
+
+function arrayBufferFromString(str) {
+ var arr = new Uint8Array(str.length);
+ for (let i = 0; i < str.length; ++i) {
+ arr[i] = str.charCodeAt(i);
+ }
+ return arr;
+}
+
+expectAsyncResult();
+fetch(new Request("body-simple", { method: "POST", body: "my body" }))
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body == "my bodymy body",
+ "the body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+fetch(
+ new Request("body-arraybufferview", {
+ method: "POST",
+ body: arrayBufferFromString("my body"),
+ })
+)
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body == "my bodymy body",
+ "the ArrayBufferView body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+fetch(
+ new Request("body-arraybuffer", {
+ method: "POST",
+ body: arrayBufferFromString("my body").buffer,
+ })
+)
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body == "my bodymy body",
+ "the ArrayBuffer body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+var usp = new URLSearchParams();
+usp.set("foo", "bar");
+usp.set("baz", "qux");
+fetch(new Request("body-urlsearchparams", { method: "POST", body: usp }))
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body == "foo=bar&baz=quxfoo=bar&baz=qux",
+ "the URLSearchParams body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+var fd = new FormData();
+fd.set("foo", "bar");
+fd.set("baz", "qux");
+fetch(new Request("body-formdata", { method: "POST", body: fd }))
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body.indexOf('Content-Disposition: form-data; name="foo"\r\n\r\nbar') <
+ body.indexOf('Content-Disposition: form-data; name="baz"\r\n\r\nqux'),
+ "the FormData body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+fetch(
+ new Request("body-blob", {
+ method: "POST",
+ body: new Blob(new String("my body")),
+ })
+)
+ .then(function (res) {
+ return res.text();
+ })
+ .then(function (body) {
+ my_ok(
+ body == "my bodymy body",
+ "the Blob body of the intercepted fetch should be visible in the SW"
+ );
+ finish();
+ });
+
+expectAsyncResult();
+fetch("interrupt.sjs").then(
+ function (res) {
+ my_ok(true, "interrupted fetch succeeded");
+ res.text().then(
+ function (body) {
+ my_ok(false, "interrupted fetch shouldn't have complete body");
+ finish();
+ },
+ function () {
+ my_ok(true, "interrupted fetch shouldn't have complete body");
+ finish();
+ }
+ );
+ },
+ function (e) {
+ my_ok(false, "interrupted fetch failed");
+ finish();
+ }
+);
+
+["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"].forEach(function (method) {
+ fetchXHRWithMethod("xhr-method-test.txt", method, function (xhr) {
+ my_ok(xhr.status == 200, method + " load should be successful");
+ if (method === "HEAD") {
+ my_ok(
+ xhr.responseText == "",
+ method + "load should not have synthesized response"
+ );
+ } else {
+ my_ok(
+ xhr.responseText == "intercepted " + method,
+ method + " load should have synthesized response"
+ );
+ }
+ finish();
+ });
+});
+
+expectAsyncResult();
+fetch(new Request("empty-header", { headers: { emptyheader: "" } }))
+ .then(function (res) {
+ return res.text();
+ })
+ .then(
+ function (body) {
+ my_ok(
+ body == "emptyheader",
+ "The empty header was observed in the fetch event"
+ );
+ finish();
+ },
+ function (err) {
+ my_ok(false, "A promise was rejected with " + err);
+ finish();
+ }
+ );
+
+expectAsyncResult();
+fetch("fetchevent-extendable")
+ .then(function (res) {
+ return res.text();
+ })
+ .then(
+ function (body) {
+ my_ok(body == "extendable", "FetchEvent inherits from ExtendableEvent");
+ finish();
+ },
+ function (err) {
+ my_ok(false, "A promise was rejected with " + err);
+ finish();
+ }
+ );
+
+expectAsyncResult();
+fetch("fetchevent-request")
+ .then(function (res) {
+ return res.text();
+ })
+ .then(
+ function (body) {
+ my_ok(body == "non-nullable", "FetchEvent.request must be non-nullable");
+ finish();
+ },
+ function (err) {
+ my_ok(false, "A promise was rejected with " + err);
+ finish();
+ }
+ );
diff --git a/dom/serviceworkers/test/fetch/fetch_worker_script.js b/dom/serviceworkers/test/fetch/fetch_worker_script.js
new file mode 100644
index 0000000000..6eb0b18a77
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/fetch_worker_script.js
@@ -0,0 +1,28 @@
+function my_ok(v, msg) {
+ postMessage({ type: "ok", value: v, msg });
+}
+
+function finish() {
+ postMessage("finish");
+}
+
+function expectAsyncResult() {
+ postMessage("expect");
+}
+
+expectAsyncResult();
+try {
+ var success = false;
+ importScripts("nonexistent_imported_script.js");
+} catch (x) {}
+
+my_ok(success, "worker imported script should be intercepted");
+finish();
+
+function check_intercepted_script() {
+ success = true;
+}
+
+importScripts("fetch_tests.js");
+
+finish(); //corresponds to the gExpected increment before creating this worker
diff --git a/dom/serviceworkers/test/fetch/hsts/embedder.html b/dom/serviceworkers/test/fetch/hsts/embedder.html
new file mode 100644
index 0000000000..ad44809042
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/embedder.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ window.onmessage = function(e) {
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
+<iframe src="http://example.com/tests/dom/serviceworkers/test/fetch/hsts/index.html"></iframe>
diff --git a/dom/serviceworkers/test/fetch/hsts/hsts_test.js b/dom/serviceworkers/test/fetch/hsts/hsts_test.js
new file mode 100644
index 0000000000..74b9ed23ba
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/hsts_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ event.respondWith(fetch("realindex.html"));
+ } else if (event.request.url.includes("image-20px.png")) {
+ if (event.request.url.indexOf("https://") == 0) {
+ event.respondWith(fetch("image-40px.png"));
+ } else {
+ event.respondWith(Response.error());
+ }
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/hsts/image-20px.png b/dom/serviceworkers/test/fetch/hsts/image-20px.png
new file mode 100644
index 0000000000..ae6a8a6b88
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/image-20px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/hsts/image-40px.png b/dom/serviceworkers/test/fetch/hsts/image-40px.png
new file mode 100644
index 0000000000..fe391dc8a2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/image-40px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/hsts/image.html b/dom/serviceworkers/test/fetch/hsts/image.html
new file mode 100644
index 0000000000..7036ea954e
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/image.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+onload=function(){
+ var img = new Image();
+ img.src = "http://example.com/tests/dom/serviceworkers/test/fetch/hsts/image-20px.png";
+ img.onload = function() {
+ window.parent.postMessage({status: "image", data: img.width}, "*");
+ };
+ img.onerror = function() {
+ window.parent.postMessage({status: "image", data: "error"}, "*");
+ };
+};
+</script>
diff --git a/dom/serviceworkers/test/fetch/hsts/realindex.html b/dom/serviceworkers/test/fetch/hsts/realindex.html
new file mode 100644
index 0000000000..e7d282fe83
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/realindex.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ var securityInfoPresent = !!SpecialPowers.wrap(window).docShell.currentDocumentChannel.securityInfo;
+ window.parent.postMessage({status: "protocol",
+ data: location.protocol,
+ securityInfoPresent},
+ "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/hsts/register.html b/dom/serviceworkers/test/fetch/hsts/register.html
new file mode 100644
index 0000000000..bcdc146aec
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("hsts_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/hsts/register.html^headers^ b/dom/serviceworkers/test/fetch/hsts/register.html^headers^
new file mode 100644
index 0000000000..a46bf65bd9
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/register.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60
diff --git a/dom/serviceworkers/test/fetch/hsts/unregister.html b/dom/serviceworkers/test/fetch/hsts/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/hsts/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/clonedresponse/https_test.js b/dom/serviceworkers/test/fetch/https/clonedresponse/https_test.js
new file mode 100644
index 0000000000..8ab34123af
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/clonedresponse/https_test.js
@@ -0,0 +1,19 @@
+self.addEventListener("install", function (event) {
+ event.waitUntil(
+ caches.open("cache").then(function (cache) {
+ return cache.add("index.html");
+ })
+ );
+});
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ event.respondWith(
+ new Promise(function (resolve, reject) {
+ caches.match(event.request).then(function (response) {
+ resolve(response.clone());
+ });
+ })
+ );
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/https/clonedresponse/index.html b/dom/serviceworkers/test/fetch/https/clonedresponse/index.html
new file mode 100644
index 0000000000..a435548443
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/clonedresponse/index.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/clonedresponse/register.html b/dom/serviceworkers/test/fetch/https/clonedresponse/register.html
new file mode 100644
index 0000000000..41774f70d1
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/clonedresponse/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/clonedresponse/unregister.html b/dom/serviceworkers/test/fetch/https/clonedresponse/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/clonedresponse/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/https_test.js b/dom/serviceworkers/test/fetch/https/https_test.js
new file mode 100644
index 0000000000..5f20690bb5
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/https_test.js
@@ -0,0 +1,31 @@
+self.addEventListener("install", function (event) {
+ event.waitUntil(
+ caches.open("cache").then(function (cache) {
+ var synth = new Response(
+ '<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-sw"}, "*");</script>',
+ { headers: { "Content-Type": "text/html" } }
+ );
+ return Promise.all([
+ cache.add("index.html"),
+ cache.put("synth-sw.html", synth),
+ ]);
+ })
+ );
+});
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.includes("synth-sw.html")) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.includes("synth-window.html")) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.includes("synth.html")) {
+ event.respondWith(
+ new Response(
+ '<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>',
+ { headers: { "Content-Type": "text/html" } }
+ )
+ );
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/https/index.html b/dom/serviceworkers/test/fetch/https/index.html
new file mode 100644
index 0000000000..a435548443
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/index.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/register.html b/dom/serviceworkers/test/fetch/https/register.html
new file mode 100644
index 0000000000..fa666fe957
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/register.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(reg => {
+ return window.caches.open("cache").then(function(cache) {
+ var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-window"}, "*");</scri' + 'pt>',
+ {headers:{"Content-Type": "text/html"}});
+ return cache.put('synth-window.html', synth).then(_ => done(reg));
+ });
+ });
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/https/unregister.html b/dom/serviceworkers/test/fetch/https/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/https/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/image-20px.png b/dom/serviceworkers/test/fetch/imagecache-maxage/image-20px.png
new file mode 100644
index 0000000000..ae6a8a6b88
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/image-20px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/image-40px.png b/dom/serviceworkers/test/fetch/imagecache-maxage/image-40px.png
new file mode 100644
index 0000000000..fe391dc8a2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/image-40px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/index.html b/dom/serviceworkers/test/fetch/imagecache-maxage/index.html
new file mode 100644
index 0000000000..0d4c52eedd
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/index.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<script>
+var width, url, width2, url2;
+function maybeReport() {
+ if (width !== undefined && url !== undefined &&
+ width2 !== undefined && url2 !== undefined) {
+ window.parent.postMessage({status: "result",
+ width,
+ width2,
+ url,
+ url2}, "*");
+ }
+}
+onload = function() {
+ width = document.querySelector("img").width;
+ width2 = document.querySelector("img").width;
+ maybeReport();
+};
+navigator.serviceWorker.onmessage = function(event) {
+ if (event.data.suffix == "2") {
+ url2 = event.data.url;
+ } else {
+ url = event.data.url;
+ }
+ maybeReport();
+};
+</script>
+<img src="image.png">
+<img src="image2.png">
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/maxage_test.js b/dom/serviceworkers/test/fetch/imagecache-maxage/maxage_test.js
new file mode 100644
index 0000000000..c664e07c28
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/maxage_test.js
@@ -0,0 +1,45 @@
+function synthesizeImage(suffix) {
+ // Serve image-20px for the first page, and image-40px for the second page.
+ return clients
+ .matchAll()
+ .then(clients => {
+ var url = "image-20px.png";
+ clients.forEach(client => {
+ if (client.url.indexOf("?new") > 0) {
+ url = "image-40px.png";
+ }
+ client.postMessage({ suffix, url });
+ });
+ return fetch(url);
+ })
+ .then(response => {
+ return response.arrayBuffer();
+ })
+ .then(ab => {
+ var headers;
+ if (suffix == "") {
+ headers = {
+ "Content-Type": "image/png",
+ Date: "Tue, 1 Jan 1990 01:02:03 GMT",
+ "Cache-Control": "max-age=1",
+ };
+ } else {
+ headers = {
+ "Content-Type": "image/png",
+ "Cache-Control": "no-cache",
+ };
+ }
+ return new Response(ab, {
+ status: 200,
+ headers,
+ });
+ });
+}
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("image.png")) {
+ event.respondWith(synthesizeImage(""));
+ } else if (event.request.url.includes("image2.png")) {
+ event.respondWith(synthesizeImage("2"));
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/register.html b/dom/serviceworkers/test/fetch/imagecache-maxage/register.html
new file mode 100644
index 0000000000..af4dde2e29
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("maxage_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/imagecache-maxage/unregister.html b/dom/serviceworkers/test/fetch/imagecache-maxage/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache-maxage/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/imagecache/image-20px.png b/dom/serviceworkers/test/fetch/imagecache/image-20px.png
new file mode 100644
index 0000000000..ae6a8a6b88
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/image-20px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/imagecache/image-40px.png b/dom/serviceworkers/test/fetch/imagecache/image-40px.png
new file mode 100644
index 0000000000..fe391dc8a2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/image-40px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/imagecache/imagecache_test.js b/dom/serviceworkers/test/fetch/imagecache/imagecache_test.js
new file mode 100644
index 0000000000..cd8f522728
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/imagecache_test.js
@@ -0,0 +1,15 @@
+function synthesizeImage() {
+ return clients.matchAll().then(clients => {
+ var url = "image-40px.png";
+ clients.forEach(client => {
+ client.postMessage(url);
+ });
+ return fetch(url);
+ });
+}
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("image-20px.png")) {
+ event.respondWith(synthesizeImage());
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/imagecache/index.html b/dom/serviceworkers/test/fetch/imagecache/index.html
new file mode 100644
index 0000000000..f634f68bb7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/index.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<script>
+var width, url;
+function maybeReport() {
+ if (width !== undefined && url !== undefined) {
+ window.parent.postMessage({status: "result",
+ width,
+ url}, "*");
+ }
+}
+onload = function() {
+ width = document.querySelector("img").width;
+ maybeReport();
+};
+navigator.serviceWorker.onmessage = function(event) {
+ url = event.data;
+ maybeReport();
+};
+</script>
+<img src="image-20px.png">
diff --git a/dom/serviceworkers/test/fetch/imagecache/postmortem.html b/dom/serviceworkers/test/fetch/imagecache/postmortem.html
new file mode 100644
index 0000000000..53356cd02c
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/postmortem.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+onload = function() {
+ var width = document.querySelector("img").width;
+ window.parent.postMessage({status: "postmortem",
+ width}, "*");
+};
+</script>
+<img src="image-20px.png">
diff --git a/dom/serviceworkers/test/fetch/imagecache/register.html b/dom/serviceworkers/test/fetch/imagecache/register.html
new file mode 100644
index 0000000000..f6d1eb382f
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/register.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Load the image here to put it in the image cache -->
+<img src="image-20px.png">
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("imagecache_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/imagecache/unregister.html b/dom/serviceworkers/test/fetch/imagecache/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/imagecache/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/importscript-mixedcontent/https_test.js b/dom/serviceworkers/test/fetch/importscript-mixedcontent/https_test.js
new file mode 100644
index 0000000000..138ca768aa
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/importscript-mixedcontent/https_test.js
@@ -0,0 +1,31 @@
+function sendResponseToParent(response) {
+ return `
+ <!DOCTYPE html>
+ <script>
+ window.parent.postMessage({status: "done", data: "${response}"}, "*");
+ </script>
+ `;
+}
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ var response = "good";
+ try {
+ importScripts("http://example.org/tests/dom/workers/test/foreign.js");
+ } catch (e) {
+ dump("Got error " + e + " when importing the script\n");
+ }
+ if (response === "good") {
+ try {
+ importScripts("/tests/dom/workers/test/redirect_to_foreign.sjs");
+ } catch (e) {
+ dump("Got error " + e + " when importing the script\n");
+ }
+ }
+ event.respondWith(
+ new Response(sendResponseToParent(response), {
+ headers: { "Content-Type": "text/html" },
+ })
+ );
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/importscript-mixedcontent/register.html b/dom/serviceworkers/test/fetch/importscript-mixedcontent/register.html
new file mode 100644
index 0000000000..41774f70d1
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/importscript-mixedcontent/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("https_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/importscript-mixedcontent/unregister.html b/dom/serviceworkers/test/fetch/importscript-mixedcontent/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/importscript-mixedcontent/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/index.html b/dom/serviceworkers/test/fetch/index.html
new file mode 100644
index 0000000000..693810c6fc
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/index.html
@@ -0,0 +1,191 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Bug 94048 - test install event.</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<div id="style-test" style="background-color: white"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+ function my_ok(result, msg) {
+ window.opener.postMessage({status: "ok", result, message: msg}, "*");
+ }
+
+ function check_intercepted_script() {
+ document.getElementById('intercepted-script').test_result =
+ document.currentScript == document.getElementById('intercepted-script');
+ }
+
+ function fetchXHR(name, onload, onerror, headers) {
+ gExpected++;
+
+ onload = onload || function() {
+ my_ok(false, "load should not complete successfully");
+ finish();
+ };
+ onerror = onerror || function() {
+ my_ok(false, "load should be intercepted successfully");
+ finish();
+ };
+
+ var x = new XMLHttpRequest();
+ x.open('GET', name, true);
+ x.onload = function() { onload(x) };
+ x.onerror = function() { onerror(x) };
+ headers = headers || [];
+ headers.forEach(function(header) {
+ x.setRequestHeader(header[0], header[1]);
+ });
+ x.send();
+ }
+
+ var gExpected = 0;
+ var gEncountered = 0;
+ function finish() {
+ gEncountered++;
+ if (gEncountered == gExpected) {
+ window.opener.postMessage({status: "done"}, "*");
+ }
+ }
+
+ function test_onload(creator, complete) {
+ gExpected++;
+ var elem = creator();
+ elem.onload = function() {
+ complete.call(elem);
+ finish();
+ };
+ elem.onerror = function() {
+ my_ok(false, elem.tagName + " load should complete successfully");
+ finish();
+ };
+ document.body.appendChild(elem);
+ }
+
+ function expectAsyncResult() {
+ gExpected++;
+ }
+
+ my_ok(navigator.serviceWorker.controller != null, "should be controlled");
+</script>
+<script src="fetch_tests.js"></script>
+<script>
+ test_onload(function() {
+ var elem = document.createElement('img');
+ elem.src = "nonexistent_image.gifs";
+ elem.id = 'intercepted-img';
+ return elem;
+ }, function() {
+ my_ok(this.complete, "image should be complete");
+ my_ok(this.naturalWidth == 1 && this.naturalHeight == 1, "image should be 1x1 gif");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('script');
+ elem.id = 'intercepted-script';
+ elem.src = "nonexistent_script.js";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "script load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('link');
+ elem.href = "nonexistent_stylesheet.css";
+ elem.rel = "stylesheet";
+ return elem;
+ }, function() {
+ var styled = document.getElementById('style-test');
+ my_ok(window.getComputedStyle(styled).backgroundColor == 'rgb(0, 0, 0)',
+ "stylesheet load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('iframe');
+ elem.id = 'intercepted-iframe';
+ elem.src = "nonexistent_page.html";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "iframe load should be intercepted");
+ });
+
+ test_onload(function() {
+ var elem = document.createElement('iframe');
+ elem.id = 'intercepted-iframe-2';
+ elem.src = "navigate.html";
+ return elem;
+ }, function() {
+ my_ok(this.test_result, "iframe should successfully load");
+ });
+
+ gExpected++;
+ var xhr = new XMLHttpRequest();
+ xhr.addEventListener("load", function(evt) {
+ my_ok(evt.target.responseXML === null, "Load synthetic cross origin XML Document should be allowed");
+ finish();
+ });
+ xhr.responseType = "document";
+ xhr.open("GET", "load_cross_origin_xml_document_synthetic.xml");
+ xhr.send();
+
+ gExpected++;
+ fetch(
+ "load_cross_origin_xml_document_cors.xml",
+ {mode: "same-origin"}
+ ).then(function(response) {
+ // issue: https://github.com/whatwg/fetch/issues/629
+ my_ok(false, "Load CORS cross origin XML Document should not be allowed");
+ finish();
+ }, function(error) {
+ my_ok(true, "Load CORS cross origin XML Document should not be allowed");
+ finish();
+ });
+
+ gExpected++;
+ fetch(
+ "load_cross_origin_xml_document_opaque.xml",
+ {mode: "same-origin"}
+ ).then(function(response) {
+ my_ok(false, "Load opaque cross origin XML Document should not be allowed");
+ finish();
+ }, function(error) {
+ my_ok(true, "Load opaque cross origin XML Document should not be allowed");
+ finish();
+ });
+
+ gExpected++;
+ var worker = new Worker('nonexistent_worker_script.js');
+ worker.onmessage = function(e) {
+ my_ok(e.data == "worker-intercept-success", "worker load intercepted");
+ finish();
+ };
+ worker.onerror = function() {
+ my_ok(false, "worker load should be intercepted");
+ };
+
+ gExpected++;
+ var worker = new Worker('fetch_worker_script.js');
+ worker.onmessage = function(e) {
+ if (e.data == "finish") {
+ finish();
+ } else if (e.data == "expect") {
+ gExpected++;
+ } else if (e.data.type == "ok") {
+ my_ok(e.data.value, "Fetch test on worker: " + e.data.msg);
+ }
+ };
+ worker.onerror = function() {
+ my_ok(false, "worker should not cause any errors");
+ };
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/serviceworkers/test/fetch/interrupt.sjs b/dom/serviceworkers/test/fetch/interrupt.sjs
new file mode 100644
index 0000000000..6e5deeb832
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/interrupt.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+ var body = "a";
+ for (var i = 0; i < 20; i++) {
+ body += body;
+ }
+
+ response.seizePower();
+ response.write("HTTP/1.1 200 OK\r\n");
+ var count = 10;
+ response.write("Content-Length: " + body.length * count + "\r\n");
+ response.write("Content-Type: text/plain; charset=utf-8\r\n");
+ response.write("Cache-Control: no-cache, must-revalidate\r\n");
+ response.write("\r\n");
+
+ for (var i = 0; i < count; i++) {
+ response.write(body);
+ }
+
+ throw Components.Exception("", Components.results.NS_BINDING_ABORTED);
+}
diff --git a/dom/serviceworkers/test/fetch/origin/https/index-https.sjs b/dom/serviceworkers/test/fetch/origin/https/index-https.sjs
new file mode 100644
index 0000000000..5250467ec7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/index-https.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader(
+ "Location",
+ "https://example.org/tests/dom/serviceworkers/test/fetch/origin/https/realindex.html",
+ false
+ );
+}
diff --git a/dom/serviceworkers/test/fetch/origin/https/origin_test.js b/dom/serviceworkers/test/fetch/origin/https/origin_test.js
new file mode 100644
index 0000000000..d148de2d83
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/origin_test.js
@@ -0,0 +1,29 @@
+var prefix = "/tests/dom/serviceworkers/test/fetch/origin/https/";
+
+function addOpaqueRedirect(cache, file) {
+ return fetch(new Request(prefix + file, { redirect: "manual" })).then(
+ function (response) {
+ return cache.put(prefix + file, response);
+ }
+ );
+}
+
+self.addEventListener("install", function (event) {
+ event.waitUntil(
+ self.caches.open("origin-cache").then(c => {
+ return addOpaqueRedirect(c, "index-https.sjs");
+ })
+ );
+});
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index-cached-https.sjs")) {
+ event.respondWith(
+ self.caches.open("origin-cache").then(c => {
+ return c.match(prefix + "index-https.sjs");
+ })
+ );
+ } else {
+ event.respondWith(fetch(event.request));
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/origin/https/realindex.html b/dom/serviceworkers/test/fetch/origin/https/realindex.html
new file mode 100644
index 0000000000..87f3489455
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/realindex.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ window.opener.postMessage({status: "domain", data: document.domain}, "*");
+ window.opener.postMessage({status: "origin", data: location.origin}, "*");
+ window.opener.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/origin/https/realindex.html^headers^ b/dom/serviceworkers/test/fetch/origin/https/realindex.html^headers^
new file mode 100644
index 0000000000..5ed82fd065
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: https://example.com
diff --git a/dom/serviceworkers/test/fetch/origin/https/register.html b/dom/serviceworkers/test/fetch/origin/https/register.html
new file mode 100644
index 0000000000..2e99adba53
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("origin_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/origin/https/unregister.html b/dom/serviceworkers/test/fetch/origin/https/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/https/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/origin/index-to-https.sjs b/dom/serviceworkers/test/fetch/origin/index-to-https.sjs
new file mode 100644
index 0000000000..2505c03686
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/index-to-https.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader(
+ "Location",
+ "https://example.org/tests/dom/serviceworkers/test/fetch/origin/realindex.html",
+ false
+ );
+}
diff --git a/dom/serviceworkers/test/fetch/origin/index.sjs b/dom/serviceworkers/test/fetch/origin/index.sjs
new file mode 100644
index 0000000000..ca11827792
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/index.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader(
+ "Location",
+ "http://example.org/tests/dom/serviceworkers/test/fetch/origin/realindex.html",
+ false
+ );
+}
diff --git a/dom/serviceworkers/test/fetch/origin/origin_test.js b/dom/serviceworkers/test/fetch/origin/origin_test.js
new file mode 100644
index 0000000000..d57f10cc2a
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/origin_test.js
@@ -0,0 +1,38 @@
+var prefix = "/tests/dom/serviceworkers/test/fetch/origin/";
+
+function addOpaqueRedirect(cache, file) {
+ return fetch(new Request(prefix + file, { redirect: "manual" })).then(
+ function (response) {
+ return cache.put(prefix + file, response);
+ }
+ );
+}
+
+self.addEventListener("install", function (event) {
+ event.waitUntil(
+ self.caches.open("origin-cache").then(c => {
+ return Promise.all([
+ addOpaqueRedirect(c, "index.sjs"),
+ addOpaqueRedirect(c, "index-to-https.sjs"),
+ ]);
+ })
+ );
+});
+
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index-cached.sjs")) {
+ event.respondWith(
+ self.caches.open("origin-cache").then(c => {
+ return c.match(prefix + "index.sjs");
+ })
+ );
+ } else if (event.request.url.includes("index-to-https-cached.sjs")) {
+ event.respondWith(
+ self.caches.open("origin-cache").then(c => {
+ return c.match(prefix + "index-to-https.sjs");
+ })
+ );
+ } else {
+ event.respondWith(fetch(event.request));
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/origin/realindex.html b/dom/serviceworkers/test/fetch/origin/realindex.html
new file mode 100644
index 0000000000..87f3489455
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/realindex.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+ window.opener.postMessage({status: "domain", data: document.domain}, "*");
+ window.opener.postMessage({status: "origin", data: location.origin}, "*");
+ window.opener.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/origin/realindex.html^headers^ b/dom/serviceworkers/test/fetch/origin/realindex.html^headers^
new file mode 100644
index 0000000000..3a6a85d894
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/serviceworkers/test/fetch/origin/register.html b/dom/serviceworkers/test/fetch/origin/register.html
new file mode 100644
index 0000000000..2e99adba53
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("origin_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/origin/unregister.html b/dom/serviceworkers/test/fetch/origin/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/origin/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/plugin/plugins.html b/dom/serviceworkers/test/fetch/plugin/plugins.html
new file mode 100644
index 0000000000..b268f6d99e
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/plugin/plugins.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<script>
+ var obj, embed;
+
+ function ok(v, msg) {
+ window.opener.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function finish() {
+ document.documentElement.removeChild(obj);
+ document.documentElement.removeChild(embed);
+ window.opener.postMessage({status: "done"}, "*");
+ }
+
+ function test_object() {
+ obj = document.createElement("object");
+ obj.setAttribute('data', "object");
+ document.documentElement.appendChild(obj);
+ }
+
+ function test_embed() {
+ embed = document.createElement("embed");
+ embed.setAttribute('src', "embed");
+ document.documentElement.appendChild(embed);
+ }
+
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.destination === "object") {
+ ok(false, "<object> should not be intercepted");
+ } else if (e.data.destination === "embed") {
+ ok(false, "<embed> should not be intercepted");
+ } else if (e.data.destination === "" && e.data.resource === "foo.txt") {
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ finish();
+ }
+ });
+
+ test_object();
+ test_embed();
+ // SW will definitely intercept fetch API, use this to see if plugins are
+ // intercepted before fetch().
+ fetch("foo.txt");
+</script>
diff --git a/dom/serviceworkers/test/fetch/plugin/worker.js b/dom/serviceworkers/test/fetch/plugin/worker.js
new file mode 100644
index 0000000000..9849c9e0d5
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/plugin/worker.js
@@ -0,0 +1,15 @@
+self.addEventListener("fetch", function (event) {
+ var resource = event.request.url.split("/").pop();
+ event.waitUntil(
+ clients.matchAll().then(clients => {
+ clients.forEach(client => {
+ if (client.url.includes("plugins.html")) {
+ client.postMessage({
+ destination: event.request.destination,
+ resource,
+ });
+ }
+ });
+ })
+ );
+});
diff --git a/dom/serviceworkers/test/fetch/real-file.txt b/dom/serviceworkers/test/fetch/real-file.txt
new file mode 100644
index 0000000000..3ca2088ec0
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/real-file.txt
@@ -0,0 +1 @@
+This is a real file.
diff --git a/dom/serviceworkers/test/fetch/redirect.sjs b/dom/serviceworkers/test/fetch/redirect.sjs
new file mode 100644
index 0000000000..dab558f4a8
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/redirect.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+ response.setStatusLine(request.httpVersion, 301, "Moved Permanently");
+ response.setHeader("Location", "synthesized-redirect-twice-real-file.txt");
+}
diff --git a/dom/serviceworkers/test/fetch/requesturl/index.html b/dom/serviceworkers/test/fetch/requesturl/index.html
new file mode 100644
index 0000000000..bc3e400a94
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/index.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.onmessage = window.onmessage = e => {
+ window.parent.postMessage(e.data, "*");
+ };
+</script>
+<iframe src="redirector.html"></iframe>
diff --git a/dom/serviceworkers/test/fetch/requesturl/redirect.sjs b/dom/serviceworkers/test/fetch/requesturl/redirect.sjs
new file mode 100644
index 0000000000..ae50a78aef
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/redirect.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader(
+ "Location",
+ "http://example.org/tests/dom/serviceworkers/test/fetch/requesturl/secret.html",
+ false
+ );
+}
diff --git a/dom/serviceworkers/test/fetch/requesturl/redirector.html b/dom/serviceworkers/test/fetch/requesturl/redirector.html
new file mode 100644
index 0000000000..0a3afab9ee
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/redirector.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<meta http-equiv="refresh" content="3;URL=/tests/dom/serviceworkers/test/fetch/requesturl/redirect.sjs">
diff --git a/dom/serviceworkers/test/fetch/requesturl/register.html b/dom/serviceworkers/test/fetch/requesturl/register.html
new file mode 100644
index 0000000000..19a2e022c2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("requesturl_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/requesturl/requesturl_test.js b/dom/serviceworkers/test/fetch/requesturl/requesturl_test.js
new file mode 100644
index 0000000000..4d2680538f
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/requesturl_test.js
@@ -0,0 +1,21 @@
+addEventListener("fetch", event => {
+ var url = event.request.url;
+ var badURL = url.indexOf("secret.html") > -1;
+ event.respondWith(
+ new Promise(resolve => {
+ clients.matchAll().then(clients => {
+ for (var client of clients) {
+ if (client.url.indexOf("index.html") > -1) {
+ client.postMessage({
+ status: "ok",
+ result: !badURL,
+ message: "Should not find a bad URL (" + url + ")",
+ });
+ break;
+ }
+ }
+ resolve(fetch(event.request));
+ });
+ })
+ );
+});
diff --git a/dom/serviceworkers/test/fetch/requesturl/secret.html b/dom/serviceworkers/test/fetch/requesturl/secret.html
new file mode 100644
index 0000000000..694c336355
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/secret.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+secret stuff
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/requesturl/unregister.html b/dom/serviceworkers/test/fetch/requesturl/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/requesturl/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/sandbox/index.html b/dom/serviceworkers/test/fetch/sandbox/index.html
new file mode 100644
index 0000000000..1094a3995d
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/sandbox/index.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "ok", result: true, message: "The iframe is not being intercepted"}, "*");
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/sandbox/intercepted_index.html b/dom/serviceworkers/test/fetch/sandbox/intercepted_index.html
new file mode 100644
index 0000000000..87261a495f
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/sandbox/intercepted_index.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "ok", result: false, message: "The iframe is being intercepted"}, "*");
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/sandbox/register.html b/dom/serviceworkers/test/fetch/sandbox/register.html
new file mode 100644
index 0000000000..427b1a8da9
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/sandbox/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("sandbox_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/sandbox/sandbox_test.js b/dom/serviceworkers/test/fetch/sandbox/sandbox_test.js
new file mode 100644
index 0000000000..310cea0d16
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/sandbox/sandbox_test.js
@@ -0,0 +1,5 @@
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ event.respondWith(fetch("intercepted_index.html"));
+ }
+});
diff --git a/dom/serviceworkers/test/fetch/sandbox/unregister.html b/dom/serviceworkers/test/fetch/sandbox/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/sandbox/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html b/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html
new file mode 100644
index 0000000000..e99209aa4d
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script>
+ window.onmessage = function(e) {
+ window.parent.postMessage(e.data, "*");
+ if (e.data.status == "protocol") {
+ document.querySelector("iframe").src = "image.html";
+ }
+ };
+</script>
+<iframe src="http://example.com/tests/dom/serviceworkers/test/fetch/upgrade-insecure/index.html"></iframe>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html^headers^ b/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html^headers^
new file mode 100644
index 0000000000..602d9dc38d
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/embedder.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: upgrade-insecure-requests
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/image-20px.png b/dom/serviceworkers/test/fetch/upgrade-insecure/image-20px.png
new file mode 100644
index 0000000000..ae6a8a6b88
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/image-20px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/image-40px.png b/dom/serviceworkers/test/fetch/upgrade-insecure/image-40px.png
new file mode 100644
index 0000000000..fe391dc8a2
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/image-40px.png
Binary files differ
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/image.html b/dom/serviceworkers/test/fetch/upgrade-insecure/image.html
new file mode 100644
index 0000000000..dfcfd80014
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/image.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+onload=function(){
+ var img = new Image();
+ img.src = "http://example.com/tests/dom/serviceworkers/test/fetch/upgrade-insecure/image-20px.png";
+ img.onload = function() {
+ window.parent.postMessage({status: "image", data: img.width}, "*");
+ };
+ img.onerror = function() {
+ window.parent.postMessage({status: "image", data: "error"}, "*");
+ };
+};
+</script>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/realindex.html b/dom/serviceworkers/test/fetch/upgrade-insecure/realindex.html
new file mode 100644
index 0000000000..aaa255aad3
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/realindex.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "protocol", data: location.protocol}, "*");
+</script>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/register.html b/dom/serviceworkers/test/fetch/upgrade-insecure/register.html
new file mode 100644
index 0000000000..6309b9b218
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/register.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function done(reg) {
+ ok(reg.active, "The active worker should be available.");
+ window.parent.postMessage({status: "registrationdone"}, "*");
+ }
+
+ navigator.serviceWorker.ready.then(done);
+ navigator.serviceWorker.register("upgrade-insecure_test.js", {scope: "."});
+</script>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/unregister.html b/dom/serviceworkers/test/fetch/upgrade-insecure/unregister.html
new file mode 100644
index 0000000000..1f13508fa7
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/unregister.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+ navigator.serviceWorker.getRegistration(".").then(function(registration) {
+ registration.unregister().then(function(success) {
+ if (success) {
+ window.parent.postMessage({status: "unregistrationdone"}, "*");
+ }
+ }, function(e) {
+ dump("Unregistering the SW failed with " + e + "\n");
+ });
+ });
+</script>
diff --git a/dom/serviceworkers/test/fetch/upgrade-insecure/upgrade-insecure_test.js b/dom/serviceworkers/test/fetch/upgrade-insecure/upgrade-insecure_test.js
new file mode 100644
index 0000000000..74b9ed23ba
--- /dev/null
+++ b/dom/serviceworkers/test/fetch/upgrade-insecure/upgrade-insecure_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function (event) {
+ if (event.request.url.includes("index.html")) {
+ event.respondWith(fetch("realindex.html"));
+ } else if (event.request.url.includes("image-20px.png")) {
+ if (event.request.url.indexOf("https://") == 0) {
+ event.respondWith(fetch("image-40px.png"));
+ } else {
+ event.respondWith(Response.error());
+ }
+ }
+});
diff --git a/dom/serviceworkers/test/fetch_event_worker.js b/dom/serviceworkers/test/fetch_event_worker.js
new file mode 100644
index 0000000000..b022ca4175
--- /dev/null
+++ b/dom/serviceworkers/test/fetch_event_worker.js
@@ -0,0 +1,364 @@
+// eslint-disable-next-line complexity
+onfetch = function (ev) {
+ if (ev.request.url.includes("ignore")) {
+ return;
+ }
+
+ if (ev.request.url.includes("bare-synthesized.txt")) {
+ ev.respondWith(
+ Promise.resolve(new Response("synthesized response body", {}))
+ );
+ } else if (ev.request.url.includes("file_CrossSiteXHR_server.sjs")) {
+ // N.B. this response would break the rules of CORS if it were allowed, but
+ // this test relies upon the preflight request not being intercepted and
+ // thus this response should not be used.
+ if (ev.request.method == "OPTIONS") {
+ ev.respondWith(
+ new Response("", {
+ headers: {
+ "Access-Control-Allow-Origin": "*",
+ "Access-Control-Allow-Headers": "X-Unsafe",
+ },
+ })
+ );
+ } else if (ev.request.url.includes("example.org")) {
+ ev.respondWith(fetch(ev.request));
+ }
+ } else if (ev.request.url.includes("synthesized-404.txt")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("synthesized response body", { status: 404 })
+ )
+ );
+ } else if (ev.request.url.includes("synthesized-headers.txt")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("synthesized response body", {
+ headers: {
+ "X-Custom-Greeting": "Hello",
+ },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("test-respondwith-response.txt")) {
+ ev.respondWith(new Response("test-respondwith-response response body", {}));
+ } else if (ev.request.url.includes("synthesized-redirect-real-file.txt")) {
+ ev.respondWith(Promise.resolve(Response.redirect("fetch/real-file.txt")));
+ } else if (
+ ev.request.url.includes("synthesized-redirect-twice-real-file.txt")
+ ) {
+ ev.respondWith(
+ Promise.resolve(Response.redirect("synthesized-redirect-real-file.txt"))
+ );
+ } else if (ev.request.url.includes("synthesized-redirect-synthesized.txt")) {
+ ev.respondWith(Promise.resolve(Response.redirect("bare-synthesized.txt")));
+ } else if (
+ ev.request.url.includes("synthesized-redirect-twice-synthesized.txt")
+ ) {
+ ev.respondWith(
+ Promise.resolve(Response.redirect("synthesized-redirect-synthesized.txt"))
+ );
+ } else if (ev.request.url.includes("rejected.txt")) {
+ ev.respondWith(Promise.reject());
+ } else if (ev.request.url.includes("nonresponse.txt")) {
+ ev.respondWith(Promise.resolve(5));
+ } else if (ev.request.url.includes("nonresponse2.txt")) {
+ ev.respondWith(Promise.resolve({}));
+ } else if (ev.request.url.includes("nonpromise.txt")) {
+ try {
+ // This should coerce to Promise(5) instead of throwing
+ ev.respondWith(5);
+ } catch (e) {
+ // test is expecting failure, so return a success if we get a thrown
+ // exception
+ ev.respondWith(new Response("respondWith(5) threw " + e));
+ }
+ } else if (ev.request.url.includes("headers.txt")) {
+ var ok = true;
+ ok &= ev.request.headers.get("X-Test1") == "header1";
+ ok &= ev.request.headers.get("X-Test2") == "header2";
+ ev.respondWith(Promise.resolve(new Response(ok.toString(), {})));
+ } else if (ev.request.url.includes("readable-stream.txt")) {
+ ev.respondWith(
+ new Response(
+ new ReadableStream({
+ start(controller) {
+ controller.enqueue(
+ new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21])
+ );
+ controller.close();
+ },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("readable-stream-locked.txt")) {
+ let stream = new ReadableStream({
+ start(controller) {
+ controller.enqueue(
+ new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21])
+ );
+ controller.close();
+ },
+ });
+
+ ev.respondWith(new Response(stream));
+
+ // This locks the stream.
+ stream.getReader();
+ } else if (ev.request.url.includes("readable-stream-with-exception.txt")) {
+ ev.respondWith(
+ new Response(
+ new ReadableStream({
+ start(controller) {},
+ pull() {
+ throw "EXCEPTION!";
+ },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("readable-stream-with-exception2.txt")) {
+ ev.respondWith(
+ new Response(
+ new ReadableStream({
+ _controller: null,
+ _count: 0,
+
+ start(controller) {
+ this._controller = controller;
+ },
+ pull() {
+ if (++this._count == 5) {
+ throw "EXCEPTION 2!";
+ }
+ this._controller.enqueue(new Uint8Array([this._count]));
+ },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("readable-stream-already-consumed.txt")) {
+ let r = new Response(
+ new ReadableStream({
+ start(controller) {
+ controller.enqueue(
+ new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x21])
+ );
+ controller.close();
+ },
+ })
+ );
+
+ r.blob();
+
+ ev.respondWith(r);
+ } else if (ev.request.url.includes("user-pass")) {
+ ev.respondWith(new Response(ev.request.url));
+ } else if (ev.request.url.includes("nonexistent_image.gif")) {
+ var imageAsBinaryString = atob(
+ "R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs"
+ );
+ var imageLength = imageAsBinaryString.length;
+
+ // If we just pass |imageAsBinaryString| to the Response constructor, an
+ // encoding conversion occurs that corrupts the image. Instead, we need to
+ // convert it to a typed array.
+ // typed array.
+ var imageAsArray = new Uint8Array(imageLength);
+ for (var i = 0; i < imageLength; ++i) {
+ imageAsArray[i] = imageAsBinaryString.charCodeAt(i);
+ }
+
+ ev.respondWith(
+ Promise.resolve(
+ new Response(imageAsArray, { headers: { "Content-Type": "image/gif" } })
+ )
+ );
+ } else if (ev.request.url.includes("nonexistent_script.js")) {
+ ev.respondWith(
+ Promise.resolve(new Response("check_intercepted_script();", {}))
+ );
+ } else if (ev.request.url.includes("nonexistent_stylesheet.css")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("#style-test { background-color: black !important; }", {
+ headers: {
+ "Content-Type": "text/css",
+ },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("nonexistent_page.html")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response(
+ "<script>window.frameElement.test_result = true;</script>",
+ {
+ headers: {
+ "Content-Type": "text/html",
+ },
+ }
+ )
+ )
+ );
+ } else if (ev.request.url.includes("navigate.html")) {
+ var requests = [
+ // should not throw
+ new Request(ev.request),
+ new Request(ev.request, undefined),
+ new Request(ev.request, null),
+ new Request(ev.request, {}),
+ new Request(ev.request, { someUnrelatedProperty: 42 }),
+ new Request(ev.request, { method: "GET" }),
+ ];
+ ev.respondWith(
+ Promise.resolve(
+ new Response(
+ "<script>window.frameElement.test_result = true;</script>",
+ {
+ headers: {
+ "Content-Type": "text/html",
+ },
+ }
+ )
+ )
+ );
+ } else if (ev.request.url.includes("nonexistent_worker_script.js")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("postMessage('worker-intercept-success')", {
+ headers: { "Content-Type": "text/javascript" },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("nonexistent_imported_script.js")) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("check_intercepted_script();", {
+ headers: { "Content-Type": "text/javascript" },
+ })
+ )
+ );
+ } else if (ev.request.url.includes("deliver-gzip")) {
+ // Don't handle the request, this will make Necko perform a network request, at
+ // which point SetApplyConversion must be re-enabled, otherwise the request
+ // will fail.
+ return;
+ } else if (ev.request.url.includes("hello.gz")) {
+ ev.respondWith(fetch("fetch/deliver-gzip.sjs"));
+ } else if (ev.request.url.includes("hello-after-extracting.gz")) {
+ ev.respondWith(
+ fetch("fetch/deliver-gzip.sjs").then(function (res) {
+ return res.text().then(function (body) {
+ return new Response(body, {
+ status: res.status,
+ statusText: res.statusText,
+ headers: res.headers,
+ });
+ });
+ })
+ );
+ } else if (ev.request.url.includes("opaque-on-same-origin")) {
+ var url =
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200";
+ ev.respondWith(fetch(url, { mode: "no-cors" }));
+ } else if (ev.request.url.includes("opaque-no-cors")) {
+ if (ev.request.mode != "no-cors") {
+ ev.respondWith(Promise.reject());
+ return;
+ }
+
+ var url =
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200";
+ ev.respondWith(fetch(url, { mode: ev.request.mode }));
+ } else if (ev.request.url.includes("cors-for-no-cors")) {
+ if (ev.request.mode != "no-cors") {
+ ev.respondWith(Promise.reject());
+ return;
+ }
+
+ var url =
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*";
+ ev.respondWith(fetch(url));
+ } else if (ev.request.url.includes("example.com")) {
+ ev.respondWith(fetch(ev.request));
+ } else if (ev.request.url.includes("body-")) {
+ ev.respondWith(
+ ev.request.text().then(function (body) {
+ return new Response(body + body);
+ })
+ );
+ } else if (ev.request.url.includes("something.txt")) {
+ ev.respondWith(Response.redirect("fetch/somethingelse.txt"));
+ } else if (ev.request.url.includes("somethingelse.txt")) {
+ ev.respondWith(new Response("something else response body", {}));
+ } else if (ev.request.url.includes("redirect_serviceworker.sjs")) {
+ // The redirect_serviceworker.sjs server-side JavaScript file redirects to
+ // 'http://mochi.test:8888/tests/dom/serviceworkers/test/worker.js'
+ // The redirected fetch should not go through the SW since the original
+ // fetch was initiated from a SW.
+ ev.respondWith(fetch("redirect_serviceworker.sjs"));
+ } else if (
+ ev.request.url.includes("load_cross_origin_xml_document_synthetic.xml")
+ ) {
+ ev.respondWith(
+ Promise.resolve(
+ new Response("<response>body</response>", {
+ headers: { "Content-Type": "text/xtml" },
+ })
+ )
+ );
+ } else if (
+ ev.request.url.includes("load_cross_origin_xml_document_cors.xml")
+ ) {
+ if (ev.request.mode != "same-origin") {
+ ev.respondWith(Promise.reject());
+ return;
+ }
+
+ var url =
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*";
+ ev.respondWith(fetch(url, { mode: "cors" }));
+ } else if (
+ ev.request.url.includes("load_cross_origin_xml_document_opaque.xml")
+ ) {
+ if (ev.request.mode != "same-origin") {
+ Promise.resolve(
+ new Response("<error>Invalid Request mode</error>", {
+ headers: { "Content-Type": "text/xtml" },
+ })
+ );
+ return;
+ }
+
+ var url =
+ "http://example.com/tests/dom/security/test/cors/file_CrossSiteXHR_server.sjs?status=200";
+ ev.respondWith(fetch(url, { mode: "no-cors" }));
+ } else if (ev.request.url.includes("xhr-method-test.txt")) {
+ ev.respondWith(new Response("intercepted " + ev.request.method));
+ } else if (ev.request.url.includes("empty-header")) {
+ if (
+ !ev.request.headers.has("emptyheader") ||
+ ev.request.headers.get("emptyheader") !== ""
+ ) {
+ ev.respondWith(Promise.reject());
+ return;
+ }
+ ev.respondWith(new Response("emptyheader"));
+ } else if (ev.request.url.includes("fetchevent-extendable")) {
+ if (ev instanceof ExtendableEvent) {
+ ev.respondWith(new Response("extendable"));
+ } else {
+ ev.respondWith(Promise.reject());
+ }
+ } else if (ev.request.url.includes("fetchevent-request")) {
+ var threw = false;
+ try {
+ new FetchEvent("foo");
+ } catch (e) {
+ if (e.name == "TypeError") {
+ threw = true;
+ }
+ } finally {
+ ev.respondWith(new Response(threw ? "non-nullable" : "nullable"));
+ }
+ }
+};