summaryrefslogtreecommitdiffstats
path: root/dom/workers/test/serviceworkers/fetch
diff options
context:
space:
mode:
Diffstat (limited to 'dom/workers/test/serviceworkers/fetch')
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/beacon.sjs43
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/context_test.js135
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs6
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/index.html422
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js8
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/parentworker.js4
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/ping.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/realaudio.oggbin0 -> 6416 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/realimg.jpgbin0 -> 3595 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/sharedworker.js5
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/worker.js1
-rw-r--r--dom/workers/test/serviceworkers/fetch/context/xml.xml3
-rw-r--r--dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs17
-rw-r--r--dom/workers/test/serviceworkers/fetch/fetch_tests.js416
-rw-r--r--dom/workers/test/serviceworkers/fetch/fetch_worker_script.js29
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/embedder.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js11
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/image.html13
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/realindex.html8
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^2
-rw-r--r--dom/workers/test/serviceworkers/fetch/hsts/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js15
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/https_test.js23
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/index.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/register.html20
-rw-r--r--dom/workers/test/serviceworkers/fetch/https/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html29
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js41
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js15
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/index.html20
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html9
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/register.html16
-rw-r--r--dom/workers/test/serviceworkers/fetch/imagecache/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js28
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/index.html183
-rw-r--r--dom/workers/test/serviceworkers/fetch/interrupt.sjs20
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js29
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/realindex.html6
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/https/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/index.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/origin_test.js41
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/realindex.html6
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/origin/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/plugin/plugins.html43
-rw-r--r--dom/workers/test/serviceworkers/fetch/plugin/worker.js14
-rw-r--r--dom/workers/test/serviceworkers/fetch/real-file.txt1
-rw-r--r--dom/workers/test/serviceworkers/fetch/redirect.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/index.html7
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs4
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/redirector.html2
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js17
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/secret.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/requesturl/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/index.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js5
-rw-r--r--dom/workers/test/serviceworkers/fetch/sandbox/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html10
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^1
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.pngbin0 -> 87 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.pngbin0 -> 123 bytes
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html13
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html4
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html14
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html12
-rw-r--r--dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js11
90 files changed, 2095 insertions, 0 deletions
diff --git a/dom/workers/test/serviceworkers/fetch/context/beacon.sjs b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs
new file mode 100644
index 000000000..8401bc29b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/beacon.sjs
@@ -0,0 +1,43 @@
+/*
+ * This is based on dom/tests/mochitest/beacon/beacon-originheader-handler.sjs.
+ */
+
+function handleRequest(request, response)
+{
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/plain", false);
+
+ // case XHR-REQUEST: the xhr-request tries to query the
+ // stored context from the beacon request.
+ if (request.queryString == "queryContext") {
+ var context = getState("interceptContext");
+ // if the beacon already stored the context - return.
+ if (context) {
+ response.write(context);
+ setState("interceptContext", "");
+ return;
+ }
+ // otherwise wait for the beacon request
+ response.processAsync();
+ setObjectState("sw-xhr-response", response);
+ return;
+ }
+
+ // case BEACON-REQUEST: get the beacon context and
+ // store the context on the server.
+ var context = request.queryString;
+ setState("interceptContext", context);
+
+ // if there is an xhr-request waiting, return the context now.
+ try{
+ getObjectState("sw-xhr-response", function(xhrResponse) {
+ if (!xhrResponse) {
+ return;
+ }
+ setState("interceptContext", "");
+ xhrResponse.write(context);
+ xhrResponse.finish();
+ });
+ } catch(e) {
+ }
+}
diff --git a/dom/workers/test/serviceworkers/fetch/context/context_test.js b/dom/workers/test/serviceworkers/fetch/context/context_test.js
new file mode 100644
index 000000000..b98d2ab3c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/context_test.js
@@ -0,0 +1,135 @@
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("index.html") >= 0 ||
+ event.request.url.indexOf("register.html") >= 0 ||
+ event.request.url.indexOf("unregister.html") >= 0 ||
+ event.request.url.indexOf("ping.html") >= 0 ||
+ event.request.url.indexOf("xml.xml") >= 0 ||
+ event.request.url.indexOf("csp-violate.sjs") >= 0) {
+ // Handle pass-through requests
+ event.respondWith(fetch(event.request));
+ } else if (event.request.url.indexOf("fetch.txt") >= 0) {
+ var body = event.request.context == "fetch" ?
+ "so fetch" : "so unfetch";
+ event.respondWith(new Response(body));
+ } else if (event.request.url.indexOf("img.jpg") >= 0) {
+ if (event.request.context == "image") {
+ event.respondWith(fetch("realimg.jpg"));
+ }
+ } else if (event.request.url.indexOf("responsive.jpg") >= 0) {
+ if (event.request.context == "imageset") {
+ event.respondWith(fetch("realimg.jpg"));
+ }
+ } else if (event.request.url.indexOf("audio.ogg") >= 0) {
+ if (event.request.context == "audio") {
+ event.respondWith(fetch("realaudio.ogg"));
+ }
+ } else if (event.request.url.indexOf("video.ogg") >= 0) {
+ if (event.request.context == "video") {
+ event.respondWith(fetch("realaudio.ogg"));
+ }
+ } else if (event.request.url.indexOf("beacon.sjs") >= 0) {
+ if (event.request.url.indexOf("queryContext") == -1) {
+ event.respondWith(fetch("beacon.sjs?" + event.request.context));
+ } else {
+ event.respondWith(fetch(event.request));
+ }
+ } else if (event.request.url.indexOf("csp-report.sjs") >= 0) {
+ respondToServiceWorker(event, "csp-report");
+ } else if (event.request.url.indexOf("embed") >= 0) {
+ respondToServiceWorker(event, "embed");
+ } else if (event.request.url.indexOf("object") >= 0) {
+ respondToServiceWorker(event, "object");
+ } else if (event.request.url.indexOf("font") >= 0) {
+ respondToServiceWorker(event, "font");
+ } else if (event.request.url.indexOf("iframe") >= 0) {
+ if (event.request.context == "iframe") {
+ event.respondWith(fetch("context_test.js"));
+ }
+ } else if (event.request.url.indexOf("frame") >= 0) {
+ if (event.request.context == "frame") {
+ event.respondWith(fetch("context_test.js"));
+ }
+ } else if (event.request.url.indexOf("newwindow") >= 0) {
+ respondToServiceWorker(event, "newwindow");
+ } else if (event.request.url.indexOf("ping") >= 0) {
+ respondToServiceWorker(event, "ping");
+ } else if (event.request.url.indexOf("plugin") >= 0) {
+ respondToServiceWorker(event, "plugin");
+ } else if (event.request.url.indexOf("script.js") >= 0) {
+ if (event.request.context == "script") {
+ event.respondWith(new Response(""));
+ }
+ } else if (event.request.url.indexOf("style.css") >= 0) {
+ respondToServiceWorker(event, "style");
+ } else if (event.request.url.indexOf("track") >= 0) {
+ respondToServiceWorker(event, "track");
+ } else if (event.request.url.indexOf("xhr") >= 0) {
+ if (event.request.context == "xmlhttprequest") {
+ event.respondWith(new Response(""));
+ }
+ } else if (event.request.url.indexOf("xslt") >= 0) {
+ respondToServiceWorker(event, "xslt");
+ } else if (event.request.url.indexOf("myworker") >= 0) {
+ if (event.request.context == "worker") {
+ event.respondWith(fetch("worker.js"));
+ }
+ } else if (event.request.url.indexOf("myparentworker") >= 0) {
+ if (event.request.context == "worker") {
+ event.respondWith(fetch("parentworker.js"));
+ }
+ } else if (event.request.url.indexOf("mysharedworker") >= 0) {
+ if (event.request.context == "sharedworker") {
+ event.respondWith(fetch("sharedworker.js"));
+ }
+ } else if (event.request.url.indexOf("myparentsharedworker") >= 0) {
+ if (event.request.context == "sharedworker") {
+ event.respondWith(fetch("parentsharedworker.js"));
+ }
+ } else if (event.request.url.indexOf("cache") >= 0) {
+ var cache;
+ var origContext = event.request.context;
+ event.respondWith(caches.open("cache")
+ .then(function(c) {
+ cache = c;
+ // Store the Request in the cache.
+ return cache.put(event.request, new Response("fake"));
+ }).then(function() {
+ // Read it back.
+ return cache.keys(event.request);
+ }).then(function(res) {
+ var req = res[0];
+ // Check to see if the context remained the same.
+ var success = req.context === origContext;
+ return clients.matchAll()
+ .then(function(clients) {
+ // Report it back to the main page.
+ clients.forEach(function(c) {
+ c.postMessage({data: "cache", success: success});
+ });
+ })}).then(function() {
+ // Cleanup.
+ return caches.delete("cache");
+ }).then(function() {
+ return new Response("ack");
+ }));
+ }
+ // Fail any request that we don't know about.
+ try {
+ event.respondWith(Promise.reject(event.request.url));
+ dump("Fetch event received invalid context value " + event.request.context +
+ " for " + event.request.url + "\n");
+ } catch(e) {
+ // Eat up the possible InvalidStateError exception that we may get if some
+ // code above has called respondWith too.
+ }
+});
+
+function respondToServiceWorker(event, data) {
+ event.respondWith(clients.matchAll()
+ .then(function(clients) {
+ clients.forEach(function(c) {
+ c.postMessage({data: data, context: event.request.context});
+ });
+ return new Response("ack");
+ }));
+}
diff --git a/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs
new file mode 100644
index 000000000..4c3e76d15
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/csp-violate.sjs
@@ -0,0 +1,6 @@
+function handleRequest(request, response)
+{
+ response.setHeader("Content-Security-Policy", "default-src 'none'; report-uri /tests/dom/workers/test/serviceworkers/fetch/context/csp-report.sjs", false);
+ response.setHeader("Content-Type", "text/html", false);
+ response.write("<link rel=stylesheet href=style.css>");
+}
diff --git a/dom/workers/test/serviceworkers/fetch/context/index.html b/dom/workers/test/serviceworkers/fetch/context/index.html
new file mode 100644
index 000000000..c6dfef99c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/index.html
@@ -0,0 +1,422 @@
+<!DOCTYPE html>
+<script>
+ var isAndroid = navigator.userAgent.includes("Android");
+ var isB2G = !isAndroid && /Mobile|Tablet/.test(navigator.userAgent);
+
+ function ok(v, msg) {
+ window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
+ }
+
+ function is(a, b, msg) {
+ ok(a === b, msg + ", expected '" + b + "', got '" + a + "'");
+ }
+
+ function todo(v, msg) {
+ window.parent.postMessage({status: "todo", result: !!v, message: msg}, "*");
+ }
+
+ function finish() {
+ window.parent.postMessage({status: "done"}, "*");
+ }
+
+ function testFetch() {
+ return fetch("fetch.txt").then(function(r) {
+ return r.text();
+ }).then(function(body) {
+ is(body, "so fetch", "A fetch() Request should have the 'fetch' context");
+ });
+ }
+
+ function testImage() {
+ return new Promise(function(resolve, reject) {
+ var img = document.createElement("img");
+ img.src = "img.jpg";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testImageSrcSet() {
+ return new Promise(function(resolve, reject) {
+ var img = document.createElement("img");
+ img.srcset = "responsive.jpg 100w";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testPicture() {
+ return new Promise(function(resolve, reject) {
+ var pic = document.createElement("picture");
+ var img = document.createElement("img");
+ pic.appendChild(img);
+ img.src = "responsive.jpg?picture";
+ // The service worker will respond with an existing image only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ img.onload = resolve;
+ img.onerror = reject;
+ });
+ }
+
+ function testAudio() {
+ return new Promise(function(resolve, reject) {
+ var audio = document.createElement("audio");
+ audio.src = "audio.ogg";
+ audio.preload = "metadata";
+ // The service worker will respond with an existing audio only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ audio.onloadedmetadata = resolve;
+ audio.onerror = reject;
+ });
+ }
+
+ function testVideo() {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ video.src = "video.ogg";
+ video.preload = "metadata";
+ // The service worker will respond with an existing video only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ video.onloadedmetadata = resolve;
+ video.onerror = reject;
+ });
+ }
+
+ function testBeacon() {
+ ok(navigator.sendBeacon("beacon.sjs"), "Sending the beacon should succeed");
+ // query the context from beacon.sjs
+ return fetch("beacon.sjs?queryContext")
+ .then(function(r) {
+ return r.text();
+ }).then(function(body) {
+ is(body, "beacon", "The context for the intercepted beacon should be correct");
+ });
+ }
+
+ function testCSPReport() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "csp-violate.sjs";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "csp-report") {
+ is(e.data.context, "cspreport", "Expected the cspreport context on a CSP violation report");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testEmbed() {
+ return Promise.resolve().then(function() {
+ todo(false, "<embed> tag is not currently intercepted. See Bug 1168676.");
+ });
+
+ return new Promise(function(resolve, reject) {
+ var embed = document.createElement("embed");
+ embed.src = "embed";
+ document.documentElement.appendChild(embed);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "embed") {
+ is(e.data.context, "embed", "Expected the object context on an embed");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testObject() {
+ return Promise.resolve().then(function() {
+ todo(false, "<object> tag is not currently intercepted. See Bug 1168676");
+ });
+
+ return new Promise(function(resolve, reject) {
+ var object = document.createElement("object");
+ object.data = "object";
+ document.documentElement.appendChild(object);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "object") {
+ is(e.data.context, "object", "Expected the object context on an object");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testFont() {
+ return new Promise(function(resolve, reject) {
+ var css = '@font-face { font-family: "sw-font"; src: url("font"); }';
+ css += '* { font-family: "sw-font"; }';
+ var style = document.createElement("style");
+ style.appendChild(document.createTextNode(css));
+ document.documentElement.appendChild(style);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "font") {
+ is(e.data.context, "font", "Expected the font context on an font");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testIFrame() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "iframe";
+ document.documentElement.appendChild(iframe);
+ // The service worker will respond with an existing document only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ iframe.onload = resolve;
+ iframe.onerror = reject;
+ });
+ }
+
+ function testFrame() {
+ return new Promise(function(resolve, reject) {
+ var frame = document.createElement("frame");
+ frame.src = "frame";
+ document.documentElement.appendChild(frame);
+ // The service worker will respond with an existing document only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ frame.onload = resolve;
+ frame.onerror = reject;
+ });
+ }
+
+ function testInternal() {
+ if (isB2G) {
+ // We can't open new windows on b2g, so skip this part of the test there.
+ return Promise.resolve();
+ }
+ return new Promise(function(resolve, reject) {
+ // Test this with a new window opened through script. There are of course
+ // other possible ways of testing this too.
+ var win = window.open("newwindow", "_blank", "width=100,height=100");
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "newwindow") {
+ is(e.data.context, "internal", "Expected the internal context on a newly opened window");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ win.close();
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testPing() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "ping.html";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "ping") {
+ is(e.data.context, "ping", "Expected the ping context on an anchor ping");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testPlugin() {
+ return Promise.resolve().then(function() {
+ todo(false, "plugins are not currently intercepted. See Bug 1168676.");
+ });
+
+ var isMobile = /Mobile|Tablet/.test(navigator.userAgent);
+ if (isMobile || parent.isMulet()) {
+ // We can't use plugins on mobile, so skip this part of the test there.
+ return Promise.resolve();
+ }
+
+ return new Promise(function(resolve, reject) {
+ var embed = document.createElement("embed");
+ embed.type = "application/x-test";
+ embed.setAttribute("posturl", "plugin");
+ embed.setAttribute("postmode", "stream");
+ embed.setAttribute("streammode", "normal");
+ embed.setAttribute("src", "fetch.txt");
+ document.documentElement.appendChild(embed);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "plugin") {
+ is(e.data.context, "plugin", "Expected the plugin context on a request coming from a plugin");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ // Without this, the test leaks in e10s!
+ embed.parentNode.removeChild(embed);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testScript() {
+ return new Promise(function(resolve, reject) {
+ var script = document.createElement("script");
+ script.src = "script.js";
+ document.documentElement.appendChild(script);
+ // The service worker will respond with an existing script only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ script.onload = resolve;
+ script.onerror = reject;
+ });
+ }
+
+ function testStyle() {
+ return new Promise(function(resolve, reject) {
+ var link = document.createElement("link");
+ link.rel = "stylesheet";
+ link.href = "style.css";
+ document.documentElement.appendChild(link);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "style") {
+ is(e.data.context, "style", "Expected the style context on a request coming from a stylesheet");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testTrack() {
+ return new Promise(function(resolve, reject) {
+ var video = document.createElement("video");
+ var track = document.createElement("track");
+ track.src = "track";
+ video.appendChild(track);
+ document.documentElement.appendChild(video);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "track") {
+ is(e.data.context, "track", "Expected the track context on a request coming from a track");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testXHR() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "xhr", true);
+ xhr.send();
+ // The service worker will respond with an existing resource only if the
+ // Request has the correct context, otherwise the Promise will get
+ // rejected and the test will fail.
+ xhr.onload = resolve;
+ xhr.onerror = reject;
+ });
+ }
+
+ function testXSLT() {
+ return new Promise(function(resolve, reject) {
+ var iframe = document.createElement("iframe");
+ iframe.src = "xml.xml";
+ document.documentElement.appendChild(iframe);
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "xslt") {
+ is(e.data.context, "xslt", "Expected the xslt context on an XSLT stylesheet");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ // Without this, the test leaks in e10s!
+ iframe.parentNode.removeChild(iframe);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ function testWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("myworker");
+ worker.onmessage = function(e) {
+ if (e.data == "ack") {
+ worker.terminate();
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testNestedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("myparentworker");
+ worker.onmessage = function(e) {
+ if (e.data == "ack") {
+ worker.terminate();
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testSharedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new SharedWorker("mysharedworker");
+ worker.port.start();
+ worker.port.onmessage = function(e) {
+ if (e.data == "ack") {
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testNestedWorkerInSharedWorker() {
+ return new Promise(function(resolve, reject) {
+ var worker = new SharedWorker("myparentsharedworker");
+ worker.port.start();
+ worker.port.onmessage = function(e) {
+ if (e.data == "ack") {
+ resolve();
+ }
+ };
+ worker.onerror = reject;
+ });
+ }
+
+ function testCache() {
+ return new Promise(function(resolve, reject) {
+ // Issue an XHR that will be intercepted by the SW in order to start off
+ // the test with a RequestContext value that is not the default ("fetch").
+ // This needs to run inside a fetch event handler because synthesized
+ // RequestContext objects can only have the "fetch" context, and we'd
+ // prefer to test the more general case of some other RequestContext value.
+ var xhr = new XMLHttpRequest();
+ xhr.open("get", "cache", true);
+ xhr.send();
+ navigator.serviceWorker.addEventListener("message", function onMessage(e) {
+ if (e.data.data == "cache") {
+ ok(e.data.success, "The RequestContext can be persisted in the cache.");
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ resolve();
+ }
+ }, false);
+ });
+ }
+
+ var testName = location.search.substr(1);
+ window[testName]().then(function() {
+ finish();
+ }, function(e) {
+ ok(false, "A promise was rejected: " + e);
+ finish();
+ });
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js
new file mode 100644
index 000000000..eac8d5e71
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/parentsharedworker.js
@@ -0,0 +1,8 @@
+onconnect = function(e) {
+ e.ports[0].start();
+ var worker = new Worker("myworker?shared");
+ worker.onmessage = function(e2) {
+ e.ports[0].postMessage(e2.data);
+ self.close();
+ };
+};
diff --git a/dom/workers/test/serviceworkers/fetch/context/parentworker.js b/dom/workers/test/serviceworkers/fetch/context/parentworker.js
new file mode 100644
index 000000000..839fb6640
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/parentworker.js
@@ -0,0 +1,4 @@
+var worker = new Worker("myworker");
+worker.onmessage = function(e) {
+ postMessage(e.data);
+};
diff --git a/dom/workers/test/serviceworkers/fetch/context/ping.html b/dom/workers/test/serviceworkers/fetch/context/ping.html
new file mode 100644
index 000000000..b1bebe41e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/ping.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+ onload = function() {
+ document.querySelector("a").click();
+ };
+</script>
+<a ping="ping" href="fetch.txt">link</a>
diff --git a/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg
new file mode 100644
index 000000000..1a41623f8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/realaudio.ogg
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/context/realimg.jpg b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg
new file mode 100644
index 000000000..5b920f7c0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/realimg.jpg
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/context/register.html b/dom/workers/test/serviceworkers/fetch/context/register.html
new file mode 100644
index 000000000..6528d0eae
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/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("context_test.js", {scope: "."});
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/context/sharedworker.js b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js
new file mode 100644
index 000000000..94dca5839
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/sharedworker.js
@@ -0,0 +1,5 @@
+onconnect = function(e) {
+ e.ports[0].start();
+ e.ports[0].postMessage("ack");
+ self.close();
+};
diff --git a/dom/workers/test/serviceworkers/fetch/context/unregister.html b/dom/workers/test/serviceworkers/fetch/context/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/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/workers/test/serviceworkers/fetch/context/worker.js b/dom/workers/test/serviceworkers/fetch/context/worker.js
new file mode 100644
index 000000000..e26e5bc69
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/worker.js
@@ -0,0 +1 @@
+postMessage("ack");
diff --git a/dom/workers/test/serviceworkers/fetch/context/xml.xml b/dom/workers/test/serviceworkers/fetch/context/xml.xml
new file mode 100644
index 000000000..69c64adf1
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/context/xml.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="xslt"?>
+<root/>
diff --git a/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs
new file mode 100644
index 000000000..abacdd2ad
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/deliver-gzip.sjs
@@ -0,0 +1,17 @@
+function handleRequest(request, response) {
+ // The string "hello" repeated 10 times followed by newline. Compressed using gzip.
+ var 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);
+
+ var bos = Components.classes["@mozilla.org/binaryoutputstream;1"]
+ .createInstance(Components.interfaces.nsIBinaryOutputStream);
+ bos.setOutputStream(response.bodyOutputStream);
+
+ bos.writeByteArray(bytes, bytes.length);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/fetch_tests.js b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
new file mode 100644
index 000000000..54da1b66e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -0,0 +1,416 @@
+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(hops.toSource());
+}
+
+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();
+});
+
+var expectedUncompressedResponse = "";
+for (var 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 == "opaque", "intercepted non-opaque response for no-cors request should resolve to opaque response.");
+ finish();
+}, function(e) {
+ my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail.");
+ finish();
+});
+
+function arrayBufferFromString(str) {
+ var arr = new Uint8Array(str.length);
+ for (var 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");
+ 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/workers/test/serviceworkers/fetch/fetch_worker_script.js b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js
new file mode 100644
index 000000000..61efb647c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/fetch_worker_script.js
@@ -0,0 +1,29 @@
+function my_ok(v, msg) {
+ postMessage({type: "ok", value: v, msg: 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/workers/test/serviceworkers/fetch/hsts/embedder.html b/dom/workers/test/serviceworkers/fetch/hsts/embedder.html
new file mode 100644
index 000000000..c98555423
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/hsts/index.html"></iframe>
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js
new file mode 100644
index 000000000..ab54164ed
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/hsts_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("index.html") >= 0) {
+ event.respondWith(fetch("realindex.html"));
+ } else if (event.request.url.indexOf("image-20px.png") >= 0) {
+ if (event.request.url.indexOf("https://") == 0) {
+ event.respondWith(fetch("image-40px.png"));
+ } else {
+ event.respondWith(Response.error());
+ }
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png
new file mode 100644
index 000000000..ae6a8a6b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/image-20px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png
new file mode 100644
index 000000000..fe391dc8a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/image-40px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/image.html b/dom/workers/test/serviceworkers/fetch/hsts/image.html
new file mode 100644
index 000000000..cadbdef5a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/image.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<script>
+onload=function(){
+ var img = new Image();
+ img.src = "http://example.com/tests/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/hsts/realindex.html b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html
new file mode 100644
index 000000000..b3d1d527e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/realindex.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ var securityInfoPresent = !!SpecialPowers.wrap(document).docShell.currentDocumentChannel.securityInfo;
+ window.parent.postMessage({status: "protocol",
+ data: location.protocol,
+ securityInfoPresent: securityInfoPresent},
+ "*");
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/register.html b/dom/workers/test/serviceworkers/fetch/hsts/register.html
new file mode 100644
index 000000000..bcdc146ae
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/hsts/register.html^headers^ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^
new file mode 100644
index 000000000..a46bf65bd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/hsts/register.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60
diff --git a/dom/workers/test/serviceworkers/fetch/hsts/unregister.html b/dom/workers/test/serviceworkers/fetch/hsts/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js
new file mode 100644
index 000000000..48f7b9307
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/https_test.js
@@ -0,0 +1,15 @@
+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.indexOf("index.html") >= 0) {
+ event.respondWith(new Promise(function(resolve, reject) {
+ caches.match(event.request).then(function(response) {
+ resolve(response.clone());
+ });
+ }));
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html
new file mode 100644
index 000000000..a43554844
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/index.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/register.html
new file mode 100644
index 000000000..41774f70d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html b/dom/workers/test/serviceworkers/fetch/https/clonedresponse/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/https/https_test.js b/dom/workers/test/serviceworkers/fetch/https/https_test.js
new file mode 100644
index 000000000..6f87bb5ee
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js
@@ -0,0 +1,23 @@
+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.indexOf("index.html") >= 0) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.indexOf("synth-sw.html") >= 0) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.indexOf("synth-window.html") >= 0) {
+ event.respondWith(caches.match(event.request));
+ } else if (event.request.url.indexOf("synth.html") >= 0) {
+ event.respondWith(new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>',
+ {headers:{"Content-Type": "text/html"}}));
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/https/index.html b/dom/workers/test/serviceworkers/fetch/https/index.html
new file mode 100644
index 000000000..a43554844
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/https/index.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/https/register.html b/dom/workers/test/serviceworkers/fetch/https/register.html
new file mode 100644
index 000000000..fa666fe95
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/https/unregister.html b/dom/workers/test/serviceworkers/fetch/https/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png
new file mode 100644
index 000000000..ae6a8a6b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-20px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png
new file mode 100644
index 000000000..fe391dc8a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/image-40px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/index.html
new file mode 100644
index 000000000..426c27a73
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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: width,
+ width2: width2,
+ url: url,
+ url2: 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/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js
new file mode 100644
index 000000000..1922111df
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/maxage_test.js
@@ -0,0 +1,41 @@
+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: suffix, url: 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: headers,
+ });
+ });
+}
+
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("image.png") >= 0) {
+ event.respondWith(synthesizeImage(""));
+ } else if (event.request.url.indexOf("image2.png") >= 0) {
+ event.respondWith(synthesizeImage("2"));
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/register.html
new file mode 100644
index 000000000..af4dde2e2
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache-maxage/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/imagecache/image-20px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png
new file mode 100644
index 000000000..ae6a8a6b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-20px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png
new file mode 100644
index 000000000..fe391dc8a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/imagecache/image-40px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js b/dom/workers/test/serviceworkers/fetch/imagecache/imagecache_test.js
new file mode 100644
index 000000000..598d8213f
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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.indexOf("image-20px.png") >= 0) {
+ event.respondWith(synthesizeImage());
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/index.html b/dom/workers/test/serviceworkers/fetch/imagecache/index.html
new file mode 100644
index 000000000..93b30f184
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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: width,
+ url: 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/workers/test/serviceworkers/fetch/imagecache/postmortem.html b/dom/workers/test/serviceworkers/fetch/imagecache/postmortem.html
new file mode 100644
index 000000000..72a650d26
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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: width}, "*");
+};
+</script>
+<img src="image-20px.png">
diff --git a/dom/workers/test/serviceworkers/fetch/imagecache/register.html b/dom/workers/test/serviceworkers/fetch/imagecache/register.html
new file mode 100644
index 000000000..f6d1eb382
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/imagecache/unregister.html b/dom/workers/test/serviceworkers/fetch/imagecache/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js
new file mode 100644
index 000000000..0f08ba74e
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/https_test.js
@@ -0,0 +1,28 @@
+function sendResponseToParent(response) {
+ return `
+ <!DOCTYPE html>
+ <script>
+ window.parent.postMessage({status: "done", data: "${response}"}, "*");
+ </script>
+ `;
+}
+
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("index.html") >= 0) {
+ 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/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/register.html
new file mode 100644
index 000000000..41774f70d
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html b/dom/workers/test/serviceworkers/fetch/importscript-mixedcontent/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/index.html b/dom/workers/test/serviceworkers/fetch/index.html
new file mode 100644
index 000000000..4db0fb139
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/index.html
@@ -0,0 +1,183 @@
+<!--
+ 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 type="text/javascript" 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: 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 xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_synthetic.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(!content.includes('parsererror'), "Load synthetic cross origin XML Document should be allowed");
+ finish();
+ };
+
+ gExpected++;
+ var xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_cors.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(!content.includes('parsererror'), "Load CORS cross origin XML Document should be allowed");
+ finish();
+ };
+
+ gExpected++;
+ var xmlDoc = document.implementation.createDocument(null, null, null);
+ xmlDoc.load('load_cross_origin_xml_document_opaque.xml');
+ xmlDoc.onload = function(evt) {
+ var content = new XMLSerializer().serializeToString(evt.target);
+ my_ok(content.includes('parsererror'), "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/workers/test/serviceworkers/fetch/interrupt.sjs b/dom/workers/test/serviceworkers/fetch/interrupt.sjs
new file mode 100644
index 000000000..f6fe870ef
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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.results.NS_BINDING_ABORTED;
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs
new file mode 100644
index 000000000..7266925ea
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/index-https.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
new file mode 100644
index 000000000..9839fc5f0
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/origin_test.js
@@ -0,0 +1,29 @@
+var prefix = "/tests/dom/workers/test/serviceworkers/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.indexOf("index-cached-https.sjs") >= 0) {
+ 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/workers/test/serviceworkers/fetch/origin/https/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html
new file mode 100644
index 000000000..87f348945
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^
new file mode 100644
index 000000000..5ed82fd06
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/https/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: https://example.com
diff --git a/dom/workers/test/serviceworkers/fetch/origin/https/register.html b/dom/workers/test/serviceworkers/fetch/origin/https/register.html
new file mode 100644
index 000000000..2e99adba5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/origin/https/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/https/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/origin/index-to-https.sjs b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs
new file mode 100644
index 000000000..1cc916ff3
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/index-to-https.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader("Location", "https://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/index.sjs b/dom/workers/test/serviceworkers/fetch/origin/index.sjs
new file mode 100644
index 000000000..a79588e76
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/index.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/origin/realindex.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/origin/origin_test.js b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
new file mode 100644
index 000000000..d2be9573b
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/origin_test.js
@@ -0,0 +1,41 @@
+var prefix = "/tests/dom/workers/test/serviceworkers/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.indexOf("index-cached.sjs") >= 0) {
+ event.respondWith(
+ self.caches.open("origin-cache")
+ .then(c => {
+ return c.match(prefix + 'index.sjs');
+ })
+ );
+ } else if (event.request.url.indexOf("index-to-https-cached.sjs") >= 0) {
+ 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/workers/test/serviceworkers/fetch/origin/realindex.html b/dom/workers/test/serviceworkers/fetch/origin/realindex.html
new file mode 100644
index 000000000..87f348945
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/origin/realindex.html^headers^ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^
new file mode 100644
index 000000000..3a6a85d89
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/origin/realindex.html^headers^
@@ -0,0 +1 @@
+Access-Control-Allow-Origin: http://mochi.test:8888
diff --git a/dom/workers/test/serviceworkers/fetch/origin/register.html b/dom/workers/test/serviceworkers/fetch/origin/register.html
new file mode 100644
index 000000000..2e99adba5
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/origin/unregister.html b/dom/workers/test/serviceworkers/fetch/origin/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/plugin/plugins.html b/dom/workers/test/serviceworkers/fetch/plugin/plugins.html
new file mode 100644
index 000000000..78e31b3c2
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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.context === "object") {
+ ok(false, "<object> should not be intercepted");
+ } else if (e.data.context === "embed") {
+ ok(false, "<embed> should not be intercepted");
+ } else if (e.data.context === "fetch" && e.data.resource === "foo.txt") {
+ navigator.serviceWorker.removeEventListener("message", onMessage);
+ finish();
+ }
+ }, false);
+
+ 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/workers/test/serviceworkers/fetch/plugin/worker.js b/dom/workers/test/serviceworkers/fetch/plugin/worker.js
new file mode 100644
index 000000000..e97d06205
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/plugin/worker.js
@@ -0,0 +1,14 @@
+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({context: event.request.context,
+ resource: resource});
+ }
+ });
+ })
+ );
+});
diff --git a/dom/workers/test/serviceworkers/fetch/real-file.txt b/dom/workers/test/serviceworkers/fetch/real-file.txt
new file mode 100644
index 000000000..3ca2088ec
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/real-file.txt
@@ -0,0 +1 @@
+This is a real file.
diff --git a/dom/workers/test/serviceworkers/fetch/redirect.sjs b/dom/workers/test/serviceworkers/fetch/redirect.sjs
new file mode 100644
index 000000000..dab558f4a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/requesturl/index.html b/dom/workers/test/serviceworkers/fetch/requesturl/index.html
new file mode 100644
index 000000000..bc3e400a9
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/requesturl/redirect.sjs b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs
new file mode 100644
index 000000000..7b92fec20
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs
@@ -0,0 +1,4 @@
+function handleRequest(request, response) {
+ response.setStatusLine(null, 308, "Permanent Redirect");
+ response.setHeader("Location", "http://example.org/tests/dom/workers/test/serviceworkers/fetch/requesturl/secret.html", false);
+}
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html
new file mode 100644
index 000000000..73bf4af49
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/redirector.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<meta http-equiv="refresh" content="3;URL=/tests/dom/workers/test/serviceworkers/fetch/requesturl/redirect.sjs">
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/register.html b/dom/workers/test/serviceworkers/fetch/requesturl/register.html
new file mode 100644
index 000000000..19a2e022c
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js
new file mode 100644
index 000000000..c8be3daf4
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/requesturl_test.js
@@ -0,0 +1,17 @@
+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/workers/test/serviceworkers/fetch/requesturl/secret.html b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html
new file mode 100644
index 000000000..694c33635
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/requesturl/secret.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+secret stuff
+<script>
+ window.parent.postMessage({status: "done"}, "*");
+</script>
diff --git a/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html b/dom/workers/test/serviceworkers/fetch/requesturl/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/sandbox/index.html b/dom/workers/test/serviceworkers/fetch/sandbox/index.html
new file mode 100644
index 000000000..1094a3995
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html b/dom/workers/test/serviceworkers/fetch/sandbox/intercepted_index.html
new file mode 100644
index 000000000..87261a495
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/sandbox/register.html b/dom/workers/test/serviceworkers/fetch/sandbox/register.html
new file mode 100644
index 000000000..427b1a8da
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js
new file mode 100644
index 000000000..1ed351794
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/sandbox/sandbox_test.js
@@ -0,0 +1,5 @@
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("index.html") >= 0) {
+ event.respondWith(fetch("intercepted_index.html"));
+ }
+});
diff --git a/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html b/dom/workers/test/serviceworkers/fetch/sandbox/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html
new file mode 100644
index 000000000..6098a45dd
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/index.html"></iframe>
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^
new file mode 100644
index 000000000..602d9dc38
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/embedder.html^headers^
@@ -0,0 +1 @@
+Content-Security-Policy: upgrade-insecure-requests
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png
new file mode 100644
index 000000000..ae6a8a6b8
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-20px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png
new file mode 100644
index 000000000..fe391dc8a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image-40px.png
Binary files differ
diff --git a/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/image.html
new file mode 100644
index 000000000..34e24e35a
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/realindex.html
new file mode 100644
index 000000000..aaa255aad
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/register.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/register.html
new file mode 100644
index 000000000..6309b9b21
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/unregister.html
new file mode 100644
index 000000000..1f13508fa
--- /dev/null
+++ b/dom/workers/test/serviceworkers/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/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js
new file mode 100644
index 000000000..ab54164ed
--- /dev/null
+++ b/dom/workers/test/serviceworkers/fetch/upgrade-insecure/upgrade-insecure_test.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", function(event) {
+ if (event.request.url.indexOf("index.html") >= 0) {
+ event.respondWith(fetch("realindex.html"));
+ } else if (event.request.url.indexOf("image-20px.png") >= 0) {
+ if (event.request.url.indexOf("https://") == 0) {
+ event.respondWith(fetch("image-40px.png"));
+ } else {
+ event.respondWith(Response.error());
+ }
+ }
+});