summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/service-workers/service-worker/resources
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /testing/web-platform/tests/service-workers/service-worker/resources
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'testing/web-platform/tests/service-workers/service-worker/resources')
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/404.py5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html26
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html13
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/blank.html2
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js14
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-frame.html12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-worker.js71
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/client-navigated-frame.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html64
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js53
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js4
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js24
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js21
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py9
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/dummy.html2
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/dummy.txt1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/empty.js0
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js20
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js75
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html16
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control.py69
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html294
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html210
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html72
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html22
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js19
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html59
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js46
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js151
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html66
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html71
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html80
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html71
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js27
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js13
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js18
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html35
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html67
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js24
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html179
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js22
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html35
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js149
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js17
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html19
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js26
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js26
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js107
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html29
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js10
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py11
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py2
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html26
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js29
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/loaded.html9
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py10
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py4
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/mint-new-worker.py25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigate-window-worker.js21
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html66
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js75
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js13
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js2
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js2
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/other.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js58
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/performance-timeline-worker.js58
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js20
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js10
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js19
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/redirect.py25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html39
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html16
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js32
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/request-headers.py6
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html4
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/service-worker-csp-worker.py153
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/silence.ogabin0 -> 12983 bytes
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/simple.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/simple.txt1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js24
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js21
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/square.pngbin0 -> 18299 bytes
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/success.py8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js227
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js163
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html16
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/update-recovery-worker.py25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py46
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js12
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/websocket.js7
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html39
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js13
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js49
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/resources/xhr.js6
133 files changed, 4169 insertions, 0 deletions
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/404.py b/testing/web-platform/tests/service-workers/service-worker/resources/404.py
new file mode 100644
index 000000000..235a3d4ff
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/404.py
@@ -0,0 +1,5 @@
+# iframe does not fire onload event if the response's content-type is not
+# text/plain or text/html so this script exists if you want to test a 404 load
+# in an iframe.
+def main(req, res):
+ return 404, [('Content-Type', 'text/plain')], "Page not found"
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html
new file mode 100644
index 000000000..428ad92c7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.install.html
@@ -0,0 +1,26 @@
+<html manifest="appcache-ordering.manifest">
+<script>
+var handled = false;
+
+function installComplete() {
+ if (handled)
+ return;
+ handled = true;
+ window.parent.notify_appcache_installed(true);
+}
+
+function installFailed() {
+ if (handled)
+ return;
+ handled = true;
+ window.parent.notify_appcache_installed(false);
+}
+
+applicationCache.oncached = installComplete;
+applicationCache.onnoupdate = installComplete;
+applicationCache.onupdateready = installFailed;
+applicationCache.onerror = installFailed;
+applicationCache.onobsolete = installFailed;
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html
new file mode 100644
index 000000000..485ab1771
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.html
@@ -0,0 +1,13 @@
+<html> <!-- Intentionally does NOT include a manifest attribute -->
+<body>
+<!-- This should FALLBACK to ordering.is_appcached.js as specified in manifest
+ when the appcache is present -->
+<script src="appcache-ordering.is-appcached404.js"></script>
+<script>
+
+// If the script of the fallback resource loaded, is_appcached will be defined.
+window.parent.notify_is_appcached(typeof is_appcached != 'undefined');
+
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js
new file mode 100644
index 000000000..a562b6f1c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.is-appcached.js
@@ -0,0 +1 @@
+var is_appcached = true;
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest
new file mode 100644
index 000000000..0deed0e91
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/appcache-ordering.manifest
@@ -0,0 +1,7 @@
+CACHE MANIFEST
+
+appcache-ordering.is-appcached.html
+
+FALLBACK:
+appcache-ordering.is-appcached404.js appcache-ordering.is-appcached.js
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/blank.html b/testing/web-platform/tests/service-workers/service-worker/resources/blank.html
new file mode 100644
index 000000000..a3c3a4689
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/blank.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<title>Empty doc</title>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js
new file mode 100644
index 000000000..317feb1a0
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/claim-worker.js
@@ -0,0 +1,14 @@
+self.addEventListener('message', function(event) {
+ self.clients.claim()
+ .then(function(result) {
+ if (result !== undefined) {
+ event.data.port.postMessage(
+ 'FAIL: claim() should be resolved with undefined');
+ return;
+ }
+ event.data.port.postMessage('PASS');
+ })
+ .catch(function(error) {
+ event.data.port.postMessage('FAIL: exception: ' + error.name);
+ });
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-frame.html b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-frame.html
new file mode 100644
index 000000000..7e186f8ee
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script>
+ fetch("clientId")
+ .then(function(response) {
+ return response.text();
+ })
+ .then(function(text) {
+ parent.postMessage({id: text}, "*");
+ });
+</script>
+<body style="background-color: red;"></body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-worker.js
new file mode 100644
index 000000000..3f2424078
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigate-worker.js
@@ -0,0 +1,71 @@
+importScripts("worker-testharness.js");
+importScripts("test-helpers.sub.js");
+importScripts("get-host-info.sub.js")
+importScripts("testharness-helpers.js")
+
+self.onfetch = function(e) {
+ if (e.request.url.indexOf("client-navigate-frame.html") >= 0) {
+ if (e.clientId === null) {
+ e.respondWith(fetch(e.request));
+ } else {
+ e.respondWith(Response.error());
+ }
+ return;
+ }
+ e.respondWith(new Response(e.clientId));
+};
+
+function pass(test, url) {
+ return { result: test,
+ url: url };
+}
+
+function fail(test, reason) {
+ return { result: "FAILED " + test + " " + reason }
+}
+
+self.onmessage = function(e) {
+ var port = e.data.port;
+ var test = e.data.test;
+ var clientId = e.data.clientId;
+ var clientUrl = "";
+ if (test === "test_client_navigate_success") {
+ promise_test(function(t) {
+ this.add_cleanup(() => port.postMessage(pass(test, clientUrl)));
+ return self.clients.get(clientId)
+ .then(client => client.navigate("client-navigated-frame.html"))
+ .then(client => {
+ clientUrl = client.url;
+ assert_true(client instanceof WindowClient);
+ })
+ .catch(unreached_rejection(t));
+ }, "Return value should be instance of WindowClient");
+ } else if (test === "test_client_navigate_failure") {
+ promise_test(function(t) {
+ return self.clients.get(clientId)
+ .then(client => assert_promise_rejects(client.navigate("http://example.com")))
+ .catch(unreached_rejection(t));
+ }, "Navigating to different origin should reject");
+
+ promise_test(function(t) {
+ this.add_cleanup(function() { port.postMessage(pass(test, "")); });
+ return self.clients.get(clientId)
+ .then(client => promise_rejects(t, new TypeError(), client.navigate("about:blank")))
+ .catch(unreached_rejection(t));
+ }, "Navigating to about:blank should reject with TypeError")
+ } else if (test === "test_client_navigate_redirect") {
+ var host_info = get_host_info();
+ var url = new URL(host_info['HTTPS_REMOTE_ORIGIN']).toString() +
+ new URL("client-navigated-frame.html", location).pathname.substring(1);
+ promise_test(function(t) {
+ this.add_cleanup(() => port.postMessage(pass(test, clientUrl)));
+ return self.clients.get(clientId)
+ .then(client => client.navigate("redirect.py?Redirect=" + url))
+ .then(client => {
+ clientUrl = (client && client.url) || ""
+ assert_true(client === null);
+ })
+ .catch(unreached_rejection(t));
+ }, "Redirecting to another origin should resolve with null");
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/client-navigated-frame.html b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigated-frame.html
new file mode 100644
index 000000000..307f7f9ac
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/client-navigated-frame.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<body style="background-color: green;"></body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html
new file mode 100644
index 000000000..27143d4b9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<script>
+
+ fetch("clientId")
+ .then(function(response) {
+ return response.text();
+ })
+ .then(function(text) {
+ parent.postMessage({clientId: text}, "*");
+ });
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html
new file mode 100644
index 000000000..cbd3dcc6f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-other-origin.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<script src="get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js"></script>
+<script>
+var host_info = get_host_info();
+var SCOPE = 'blank.html?clients-get';
+var SCRIPT = 'clients-get-worker.js';
+
+var registration;
+var worker;
+var wait_for_worker_promise = navigator.serviceWorker.getRegistration(SCOPE)
+ .then(function(reg) {
+ if (reg)
+ return reg.unregister();
+ })
+ .then(function() {
+ return navigator.serviceWorker.register(SCRIPT, {scope: SCOPE});
+ })
+ .then(function(reg) {
+ registration = reg;
+ worker = reg.installing;
+ return new Promise(function(resolve) {
+ worker.addEventListener('statechange', function() {
+ if (worker.state == 'activated')
+ resolve();
+ });
+ });
+ });
+
+function send_result(result) {
+ window.parent.postMessage(
+ {result: result},
+ host_info['HTTPS_ORIGIN']);
+}
+
+window.addEventListener("message", on_message, false);
+
+function on_message(e) {
+ if (e.origin != host_info['HTTPS_ORIGIN']) {
+ console.error('invalid origin: ' + e.origin);
+ return;
+ }
+ if (e.data.message == 'get_client_id') {
+ var otherOriginClientId = e.data.clientId;
+ wait_for_worker_promise
+ .then(function() {
+ return with_iframe(SCOPE);
+ })
+ .then(function(iframe) {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function(e) {
+ navigator.serviceWorker.getRegistration(SCOPE)
+ .then(function(reg) {
+ reg.unregister();
+ send_result(e.data);
+ });
+ };
+ iframe.contentWindow.navigator.serviceWorker.controller.postMessage(
+ {port:channel.port2, clientId: otherOriginClientId,
+ message: 'get_other_client_id'}, [channel.port2]);
+ })
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js
new file mode 100644
index 000000000..9ac2c2264
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-get-worker.js
@@ -0,0 +1,53 @@
+self.onfetch = function(e) {
+ if (e.request.url.indexOf("clients-get-frame.html") >= 0) {
+ if (e.clientId === null) {
+ e.respondWith(fetch(e.request));
+ } else {
+ e.respondWith(Response.error());
+ }
+ return;
+ }
+ e.respondWith(new Response(e.clientId));
+};
+
+self.onmessage = function(e) {
+ var port = e.data.port;
+ if (e.data.message == 'get_client_ids') {
+ var clientIds = e.data.clientIds;
+ var message = [];
+
+ Promise.all(
+ clientIds.map(function(clientId) {
+ return self.clients.get(clientId);
+ }).concat(self.clients.get("invalid-id"))
+ ).then(function(clients) {
+ clients.forEach(function(client) {
+ if (client instanceof Client) {
+ message.push([client.visibilityState,
+ client.focused,
+ client.url,
+ client.frameType]);
+ } else {
+ message.push(client);
+ }
+ });
+ port.postMessage(message);
+ });
+ } else if (e.data.message == 'get_other_client_id') {
+ var clientId = e.data.clientId;
+ var message;
+
+ self.clients.get(clientId)
+ .then(function(client) {
+ if (client instanceof Client) {
+ message = [client.visibilityState,
+ client.focused,
+ client.url,
+ client.frameType];
+ } else {
+ message = client;
+ }
+ port.postMessage(message);
+ });
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html
new file mode 100644
index 000000000..51b4dca03
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-iframe.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<title>Empty doc</title>
+<!--
+ Change the page URL using the History API to ensure that ServiceWorkerClient
+ uses the creation URL.
+-->
+<body onload="history.pushState({}, 'title', 'new-url.html')">
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js
new file mode 100644
index 000000000..1ae72fb89
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js
@@ -0,0 +1,4 @@
+onconnect = function(e) {
+ var port = e.ports[0];
+ port.postMessage('started');
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
new file mode 100644
index 000000000..f0ae90d81
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
@@ -0,0 +1,24 @@
+self.onmessage = function(e) {
+ var port = e.data.port;
+ var options = e.data.options;
+
+ self.clients.matchAll(options).then(function(clients) {
+ var message = [];
+ clients.forEach(function(client) {
+ var frame_type = client.frameType;
+ if (client.url.indexOf('clients-matchall-include-uncontrolled.https.html') > -1 &&
+ client.frameType == 'auxiliary') {
+ // The test tab might be opened using window.open() by the test framework.
+ // In that case, just pretend it's top-level!
+ frame_type = 'top-level';
+ }
+ message.push([client.visibilityState,
+ client.focused,
+ client.url,
+ frame_type]);
+ });
+ // Sort by url
+ message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
+ port.postMessage(message);
+ });
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js
new file mode 100644
index 000000000..82b24459b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-shared-worker-interceptor.js
@@ -0,0 +1,7 @@
+var worker_text = 'onconnect = function(e) { e.ports[0].postMessage("worker loading intercepted by service worker"); };';
+
+self.onfetch = function(event) {
+ if (event.request.url.indexOf('dummy-shared-worker.js') != -1)
+ event.respondWith(new Response(worker_text));
+};
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js
new file mode 100644
index 000000000..43244e1d9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-interceptor.js
@@ -0,0 +1,21 @@
+importScripts('get-host-info.sub.js');
+
+var worker_text = 'postMessage("worker loading intercepted by service worker"); ';
+
+self.onfetch = function(event) {
+ if (event.request.url.indexOf('synthesized') != -1) {
+ event.respondWith(new Response(worker_text));
+ } else if (event.request.url.indexOf('same-origin') != -1) {
+ event.respondWith(fetch('dummy-worker-script.py'));
+ } else if (event.request.url.indexOf('cors') != -1) {
+ var path = (new URL('dummy-worker-script.py', self.location)).pathname;
+ var url = get_host_info()['HTTPS_REMOTE_ORIGIN'] + path;
+ var mode = "no-cors";
+ if (event.request.url.indexOf('no-cors') == -1) {
+ url += '?ACAOrigin=*';
+ mode = "cors";
+ }
+ event.respondWith(fetch(url, { mode: mode }));
+ }
+};
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py
new file mode 100644
index 000000000..6f40b5ed6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/dummy-worker-script.py
@@ -0,0 +1,9 @@
+def main(request, response):
+ headers = []
+
+ if "ACAOrigin" in request.GET:
+ for item in request.GET["ACAOrigin"].split(","):
+ headers.append(("Access-Control-Allow-Origin", item))
+
+ return headers, "postMessage('dummy-worker-script loaded');"
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/dummy.html b/testing/web-platform/tests/service-workers/service-worker/resources/dummy.html
new file mode 100644
index 000000000..12a179980
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/dummy.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body>Hello world
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/dummy.txt b/testing/web-platform/tests/service-workers/service-worker/resources/dummy.txt
new file mode 100644
index 000000000..802992c42
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/dummy.txt
@@ -0,0 +1 @@
+Hello world
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js
new file mode 100644
index 000000000..49ceb2648
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/empty-worker.js
@@ -0,0 +1 @@
+// Do nothing.
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/empty.js b/testing/web-platform/tests/service-workers/service-worker/resources/empty.js
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/empty.js
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js
new file mode 100644
index 000000000..d45a50556
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/end-to-end-worker.js
@@ -0,0 +1,7 @@
+onmessage = function(e) {
+ var message = e.data;
+ if (typeof message === 'object' && 'port' in message) {
+ var response = 'Ack for: ' + message.from;
+ message.port.postMessage(response);
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js
new file mode 100644
index 000000000..80a218867
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/events-worker.js
@@ -0,0 +1,12 @@
+var eventsSeen = [];
+
+function handler(event) { eventsSeen.push(event.type); }
+
+['activate', 'install'].forEach(function(type) {
+ self.addEventListener(type, handler);
+ });
+
+onmessage = function(e) {
+ var message = e.data;
+ message.port.postMessage({events: eventsSeen});
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
new file mode 100644
index 000000000..d77238d93
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-async-waituntil.js
@@ -0,0 +1,20 @@
+var result = 'FAIL: did not throw.';
+
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage(result);
+ });
+
+self.addEventListener('install', function(event) {
+ self.installEvent = event;
+ });
+
+self.addEventListener('activate', function(event) {
+ try {
+ self.installEvent.waitUntil(new Promise(function(){}));
+ } catch (error) {
+ if (error.name == 'InvalidStateError')
+ result = 'PASS';
+ else
+ result = 'FAIL: unexpected exception: ' + error;
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js
new file mode 100644
index 000000000..48fdf1b99
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/extendable-event-waituntil.js
@@ -0,0 +1,75 @@
+var pendingPorts = [];
+var portResolves = [];
+
+onmessage = function(e) {
+ var message = e.data;
+ if ('port' in message) {
+ var resolve = self.portResolves.shift();
+ if (resolve)
+ resolve(message.port);
+ else
+ self.pendingPorts.push(message.port);
+ }
+};
+
+function fulfillPromise() {
+ return new Promise(function(resolve) {
+ // Make sure the oninstall/onactivate callback returns first.
+ Promise.resolve().then(function() {
+ var port = self.pendingPorts.shift();
+ if (port)
+ resolve(port);
+ else
+ self.portResolves.push(resolve);
+ });
+ }).then(function(port) {
+ port.postMessage('SYNC');
+ return new Promise(function(resolve) {
+ port.onmessage = function(e) {
+ if (e.data == 'ACK')
+ resolve();
+ };
+ });
+ });
+}
+
+function rejectPromise() {
+ return new Promise(function(resolve, reject) {
+ // Make sure the oninstall/onactivate callback returns first.
+ Promise.resolve().then(reject);
+ });
+}
+
+function stripScopeName(url) {
+ return url.split('/').slice(-1)[0];
+}
+
+oninstall = function(e) {
+ switch (stripScopeName(self.location.href)) {
+ case 'install-fulfilled':
+ e.waitUntil(fulfillPromise());
+ break;
+ case 'install-rejected':
+ e.waitUntil(rejectPromise());
+ break;
+ case 'install-multiple-fulfilled':
+ e.waitUntil(fulfillPromise());
+ e.waitUntil(fulfillPromise());
+ break;
+ case 'install-reject-precedence':
+ e.waitUntil(fulfillPromise());
+ e.waitUntil(rejectPromise());
+ break;
+ }
+};
+
+onactivate = function(e) {
+ switch (stripScopeName(self.location.href)) {
+ case 'activate-fulfilled':
+ e.waitUntil(fulfillPromise());
+ break;
+ case 'activate-rejected':
+ e.waitUntil(rejectPromise());
+ break;
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js
new file mode 100644
index 000000000..517f289fb
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fail-on-fetch-worker.js
@@ -0,0 +1,5 @@
+importScripts('worker-testharness.js');
+
+this.addEventListener('fetch', function(event) {
+ event.respondWith(new Response('ERROR'));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html
new file mode 100644
index 000000000..0ffab1af5
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control-login.html
@@ -0,0 +1,16 @@
+<script>
+// Set authentication info
+window.addEventListener("message", function(evt) {
+ var port = evt.ports[0];
+ document.cookie = 'cookie=' + evt.data.cookie;
+ var xhr = new XMLHttpRequest();
+ xhr.addEventListener('load', function() {
+ port.postMessage({msg: 'LOGIN FINISHED'});
+ }, false);
+ xhr.open('GET',
+ './fetch-access-control.py?Auth',
+ true,
+ evt.data.username, evt.data.password);
+ xhr.send();
+ }, false);
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control.py b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control.py
new file mode 100644
index 000000000..862718ad0
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-access-control.py
@@ -0,0 +1,69 @@
+import base64
+import json
+
+def main(request, response):
+ headers = []
+ headers.append(('X-ServiceWorker-ServerHeader', 'SetInTheServer'))
+
+ if "ACAOrigin" in request.GET:
+ for item in request.GET["ACAOrigin"].split(","):
+ headers.append(("Access-Control-Allow-Origin", item))
+
+ for suffix in ["Headers", "Methods", "Credentials"]:
+ query = "ACA%s" % suffix
+ header = "Access-Control-Allow-%s" % suffix
+ if query in request.GET:
+ headers.append((header, request.GET[query]))
+
+ if "ACEHeaders" in request.GET:
+ headers.append(("Access-Control-Expose-Headers", request.GET[query]))
+
+ if ("Auth" in request.GET and not request.auth.username) or "AuthFail" in request.GET:
+ status = 401
+ headers.append(('WWW-Authenticate', 'Basic realm="Restricted"'))
+ body = 'Authentication canceled'
+ return status, headers, body
+
+ if "PNGIMAGE" in request.GET:
+ headers.append(("Content-Type", "image/png"))
+ body = base64.decodestring("iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1B"
+ "AACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/KfgQLABKXJBqMG"
+ "jBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=")
+ return headers, body
+
+
+ username = request.auth.username if request.auth.username else "undefined"
+ password = request.auth.password if request.auth.username else "undefined"
+ cookie = request.cookies['cookie'].value if 'cookie' in request.cookies else "undefined"
+
+ files = []
+ for key, values in request.POST.iteritems():
+ assert len(values) == 1
+ value = values[0]
+ if not hasattr(value, "file"):
+ continue
+ data = value.file.read()
+ files.append({"key": key,
+ "name": value.file.name,
+ "type": value.type,
+ "error": 0, #TODO,
+ "size": len(data),
+ "content": data})
+
+ get_data = {key:request.GET[key] for key,value in request.GET.iteritems()}
+ post_data = {key:request.POST[key] for key,value in request.POST.iteritems()
+ if not hasattr(request.POST[key], "file")}
+ headers_data = {key:request.headers[key] for key,value in request.headers.iteritems()}
+
+ data = {"jsonpResult": "success",
+ "method": request.method,
+ "headers": headers_data,
+ "body": request.body,
+ "files": files,
+ "GET": get_data,
+ "POST": post_data,
+ "username": username,
+ "password": password,
+ "cookie": cookie}
+
+ return headers, "report( %s )" % json.dumps(data)
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html
new file mode 100644
index 000000000..3822971e8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-canvas-tainting-iframe.html
@@ -0,0 +1,294 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var image_path = base_path() + 'fetch-access-control.py?PNGIMAGE';
+var host_info = get_host_info();
+var params = get_query_params(location.href);
+
+var NOT_TAINTED = 'NOT_TAINTED';
+var TAINTED = 'TAINTED';
+var LOAD_ERROR = 'LOAD_ERROR';
+
+function get_query_params(url) {
+ var search = (new URL(url)).search;
+ if (!search) {
+ return {};
+ }
+ var ret = {};
+ var params = search.substring(1).split('&');
+ params.forEach(function(param) {
+ var element = param.split('=');
+ ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+ });
+ return ret;
+}
+
+function create_test_case_promise(url, cross_origin) {
+ return new Promise(function(resolve) {
+ var img = new Image();
+ if (cross_origin != '') {
+ img.crossOrigin = cross_origin;
+ }
+ img.onload = function() {
+ try {
+ var canvas = document.createElement('canvas');
+ canvas.width = 100;
+ canvas.height = 100;
+ var context = canvas.getContext('2d');
+ context.drawImage(img, 0, 0);
+ context.getImageData(0, 0, 100, 100);
+ resolve(NOT_TAINTED);
+ } catch (e) {
+ resolve(TAINTED);
+ }
+ };
+ img.onerror = function() {
+ resolve(LOAD_ERROR);
+ }
+ img.src = url;
+ });
+}
+
+function create_test_promise(url, cross_origin, expected_result) {
+ if (params['cache']) {
+ url += "&cache";
+ }
+
+ return new Promise(function(resolve, reject) {
+ create_test_case_promise(url, cross_origin)
+ .then(function(result) {
+ if (result == expected_result) {
+ resolve();
+ } else {
+ reject('Result of url:' + url + ' ' +
+ ' cross_origin: ' + cross_origin + ' must be ' +
+ expected_result + ' but ' + result);
+ }
+ })
+ });
+}
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ var image_url = host_info['HTTPS_ORIGIN'] + image_path;
+ var remote_image_url = host_info['HTTPS_REMOTE_ORIGIN'] + image_path;
+ Promise.all([
+ // Reject tests
+ create_test_promise(image_url + '&reject', '', LOAD_ERROR),
+ create_test_promise(image_url + '&reject', 'anonymous', LOAD_ERROR),
+ create_test_promise(
+ image_url + '&reject', 'use-credentials', LOAD_ERROR),
+ // Fallback tests
+ create_test_promise(
+ image_url + '&ignore',
+ '',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url + '&ignore',
+ '',
+ TAINTED),
+ create_test_promise(
+ remote_image_url + '&ignore',
+ 'anonymous',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ignore',
+ 'anonymous',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url + '&ignore',
+ 'use-credentials',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ignore',
+ 'use-credentials',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true&ignore',
+ 'use-credentials',
+ NOT_TAINTED),
+
+ // Credential test (fallback)
+ create_test_promise(
+ image_url + '&Auth&ignore',
+ '',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url + '&Auth&ignore',
+ '',
+ TAINTED),
+ create_test_promise(
+ remote_image_url + '&Auth&ignore',
+ 'anonymous',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&Auth&ignore',
+ 'use-credentials',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&Auth&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ignore',
+ 'use-credentials',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url + '&Auth&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true&ignore',
+ 'use-credentials',
+ NOT_TAINTED),
+
+ // Basic response
+ create_test_promise(
+ image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ '',
+ NOT_TAINTED),
+ create_test_promise(
+ image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ 'anonymous',
+ NOT_TAINTED),
+ create_test_promise(
+ image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ 'use-credentials',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ '',
+ TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ 'anonymous',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=same-origin&url=' + encodeURIComponent(image_url),
+ 'use-credentials',
+ NOT_TAINTED),
+
+ // Opaque response
+ create_test_promise(
+ image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ '',
+ TAINTED),
+ create_test_promise(
+ image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ 'anonymous',
+ LOAD_ERROR),
+ create_test_promise(
+ image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ 'use-credentials',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ '',
+ TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ 'anonymous',
+ LOAD_ERROR),
+ create_test_promise(
+ remote_image_url +
+ '&mode=no-cors&url=' + encodeURIComponent(remote_image_url),
+ 'use-credentials',
+ LOAD_ERROR),
+
+ // CORS response
+ create_test_promise(
+ image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ '',
+ LOAD_ERROR), // We expect LOAD_ERROR since the server doesn't respond
+ // with an Access-Control-Allow-Credentials header.
+ create_test_promise(
+ image_url +
+ '&mode=cors&credentials=same-origin&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ '',
+ NOT_TAINTED),
+ create_test_promise(
+ image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'anonymous',
+ NOT_TAINTED),
+ create_test_promise(
+ image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'use-credentials',
+ LOAD_ERROR), // We expect LOAD_ERROR since the server doesn't respond
+ // with an Access-Control-Allow-Credentials header.
+ create_test_promise(
+ image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(
+ remote_image_url +
+ '&ACACredentials=true&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'use-credentials',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ '',
+ LOAD_ERROR), // We expect LOAD_ERROR since the server doesn't respond
+ // with an Access-Control-Allow-Credentials header.
+ create_test_promise(
+ remote_image_url +
+ '&mode=cors&credentials=same-origin&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ '',
+ TAINTED), // The cross-origin no-cors request is immediately tainted.
+ // Since this happens before the service worker interception,
+ // it does not matter what kind of response it returns.
+ // The result will always be tainted.
+ create_test_promise(
+ remote_image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'anonymous',
+ NOT_TAINTED),
+ create_test_promise(
+ remote_image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(remote_image_url +
+ '&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'use-credentials',
+ LOAD_ERROR), // We expect LOAD_ERROR since the server doesn't respond
+ // with an Access-Control-Allow-Credentials header.
+ create_test_promise(
+ remote_image_url +
+ '&mode=cors&url=' +
+ encodeURIComponent(
+ remote_image_url +
+ '&ACACredentials=true&ACAOrigin=' + host_info['HTTPS_ORIGIN']),
+ 'use-credentials',
+ NOT_TAINTED)
+ ])
+ .then(function() {
+ port.postMessage({results: 'finish'});
+ })
+ .catch(function(e) {
+ port.postMessage({results: 'failure:' + e});
+ });
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html
new file mode 100644
index 000000000..48f618397
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-cors-xhr-iframe.html
@@ -0,0 +1,210 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var path = base_path() + 'fetch-access-control.py';
+var host_info = get_host_info();
+var SUCCESS = 'SUCCESS';
+var FAIL = 'FAIL';
+
+function create_test_case_promise(url, with_credentials) {
+ return new Promise(function(resolve) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ if (xhr.status == 200) {
+ resolve(SUCCESS);
+ } else {
+ resolve("STATUS" + xhr.status);
+ }
+ }
+ xhr.onerror = function() {
+ resolve(FAIL);
+ }
+ xhr.responseType = 'text';
+ xhr.withCredentials = with_credentials;
+ xhr.open('GET', url, true);
+ xhr.send();
+ });
+}
+
+
+function create_test_promise(url, with_credentials, expected_result) {
+ return new Promise(function(resolve, reject) {
+ create_test_case_promise(url, with_credentials)
+ .then(function(result) {
+ if (result == expected_result) {
+ resolve();
+ } else {
+ reject('Result of url:' + url + ' ' +
+ ' with_credentials: ' + with_credentials + ' must be ' +
+ expected_result + ' but ' + result);
+ }
+ })
+ });
+}
+
+function create_serial_promise(test_cases) {
+ var promise = Promise.resolve();
+ test_cases.forEach(function(test_case) {
+ promise = promise.then(function() {
+ return create_test_promise(test_case[0], test_case[1], test_case[2]);
+ });
+ });
+ return promise;
+}
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ var url = host_info['HTTPS_ORIGIN'] + path;
+ var remote_url = host_info['HTTPS_REMOTE_ORIGIN'] + path;
+ // If the 4th value of the item of TEST_CASES is true, the test case outputs
+ // warning messages. So such tests must be executed in serial to match the
+ // expected output text.
+ var TEST_CASES = [
+ // Reject tests
+ [url + '?reject', false, FAIL],
+ [url + '?reject', true, FAIL],
+ [remote_url + '?reject', false, FAIL],
+ [remote_url + '?reject', true, FAIL],
+ // Event handler exception tests
+ [url + '?throw', false, FAIL],
+ [url + '?throw', true, FAIL],
+ [remote_url + '?throw', false, FAIL],
+ [remote_url + '?throw', true, FAIL],
+ // Reject(resolve-null) tests
+ [url + '?resolve-null', false, FAIL],
+ [url + '?resolve-null', true, FAIL],
+ [remote_url + '?resolve-null', false, FAIL],
+ [remote_url + '?resolve-null', true, FAIL],
+ // Fallback tests
+ [url + '?ignore', false, SUCCESS],
+ [url + '?ignore', true, SUCCESS],
+ [remote_url + '?ignore', false, FAIL, true], // Executed in serial.
+ [remote_url + '?ignore', true, FAIL, true], // Executed in serial.
+ [
+ remote_url + '?ACAOrigin=' + host_info['HTTPS_ORIGIN'] + '&ignore',
+ false, SUCCESS
+ ],
+ [
+ remote_url + '?ACAOrigin=' + host_info['HTTPS_ORIGIN'] + '&ignore',
+ true, FAIL, true // Executed in serial.
+ ],
+ [
+ remote_url + '?ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true&ignore',
+ true, SUCCESS
+ ],
+ // Credential test (fallback)
+ [url + '?Auth&ignore', false, SUCCESS],
+ [url + '?Auth&ignore', true, SUCCESS],
+ [remote_url + '?Auth&ignore', false, FAIL, true], // Executed in serial.
+ [remote_url + '?Auth&ignore', true, FAIL, true], // Executed in serial.
+ [
+ remote_url + '?Auth&ACAOrigin=' + host_info['HTTPS_ORIGIN'] + '&ignore',
+ false, 'STATUS401'
+ ],
+ [
+ remote_url + '?Auth&ACAOrigin=' + host_info['HTTPS_ORIGIN'] + '&ignore',
+ true, FAIL, true // Executed in serial.
+ ],
+ [
+ remote_url + '?Auth&ACAOrigin=' + host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true&ignore',
+ true, SUCCESS
+ ],
+ // Basic response
+ [
+ url + '?mode=same-origin&url=' + encodeURIComponent(url),
+ false, SUCCESS
+ ],
+ [
+ url + '?mode=same-origin&url=' + encodeURIComponent(url),
+ false, SUCCESS
+ ],
+ [
+ remote_url + '?mode=same-origin&url=' + encodeURIComponent(url),
+ false, SUCCESS
+ ],
+ [
+ remote_url + '?mode=same-origin&url=' + encodeURIComponent(url),
+ false, SUCCESS
+ ],
+ // Opaque response
+ [
+ url + '?mode=no-cors&url=' + encodeURIComponent(remote_url),
+ false, FAIL
+ ],
+ [
+ url + '?mode=no-cors&url=' + encodeURIComponent(remote_url),
+ false, FAIL
+ ],
+ [
+ remote_url + '?mode=no-cors&url=' + encodeURIComponent(remote_url),
+ false, FAIL
+ ],
+ [
+ remote_url + '?mode=no-cors&url=' + encodeURIComponent(remote_url),
+ false, FAIL
+ ],
+ // CORS response
+ [
+ url + '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN']),
+ false, SUCCESS
+ ],
+ [
+ url + '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN']),
+ true, FAIL
+ ],
+ [
+ url + '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true'),
+ true, SUCCESS
+ ],
+ [
+ remote_url + '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN']),
+ false, SUCCESS
+ ],
+ [
+ remote_url +
+ '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN']),
+ true, FAIL
+ ],
+ [
+ remote_url +
+ '?mode=cors&url=' +
+ encodeURIComponent(remote_url + '?ACAOrigin=' +
+ host_info['HTTPS_ORIGIN'] +
+ '&ACACredentials=true'),
+ true, SUCCESS
+ ]
+ ];
+ var promises = [];
+ var serial_tests = [];
+ for (var i = 0; i < TEST_CASES.length ; ++i) {
+ if (!TEST_CASES[i][3]) {
+ promises.push(create_test_promise(TEST_CASES[i][0],
+ TEST_CASES[i][1],
+ TEST_CASES[i][2]));
+ } else {
+ serial_tests.push(TEST_CASES[i]);
+ }
+ }
+ promises.push(create_serial_promise(serial_tests));
+ Promise.all(promises)
+ .then(function() {
+ port.postMessage({results: 'finish'});
+ })
+ .catch(function(e) {
+ port.postMessage({results: 'failure:' + e});
+ });
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html
new file mode 100644
index 000000000..cf7175bc8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html
@@ -0,0 +1,72 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var image_path = base_path() + 'fetch-access-control.py?PNGIMAGE';
+var host_info = get_host_info();
+var results = '';
+var port = undefined;
+
+function test1() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test2();
+ };
+ img.onerror = function() {
+ results += 'FAIL(1)';
+ test2();
+ };
+ img.src = host_info['HTTPS_ORIGIN'] + image_path;
+}
+
+function test2() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(2)';
+ test3();
+ };
+ img.onerror = function() {
+ test3();
+ };
+ img.src = host_info['HTTPS_REMOTE_ORIGIN'] + image_path;
+}
+
+function test3() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test4();
+ };
+ img.onerror = function() {
+ results += 'FAIL(3)';
+ test4();
+ };
+ img.src = './dummy?url=' +
+ encodeURIComponent(host_info['HTTPS_ORIGIN'] + image_path);
+}
+
+function test4() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(4)';
+ finish();
+ };
+ img.onerror = function() {
+ finish();
+ };
+ img.src = './dummy?mode=no-cors&url=' +
+ encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + image_path);
+}
+
+function finish() {
+ results += 'finish';
+ port.postMessage({results: results});
+}
+
+window.addEventListener('message', function(evt) {
+ port = evt.ports[0];
+ test1();
+ }, false);
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers
new file mode 100644
index 000000000..300efe049
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-csp-iframe.html.sub.headers
@@ -0,0 +1 @@
+Content-Security-Policy: img-src https://{{host}}:{{ports[https][0]}}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html
new file mode 100644
index 000000000..bf8a6d5ce
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-after-navigation-within-page-iframe.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<script>
+function fetch_url(url) {
+ return new Promise(function(resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.addEventListener('load', function(event) {
+ if (request.status == 200)
+ resolve(request.response);
+ else
+ reject(new Error('fetch_url: ' + request.statusText + " : " + url));
+ });
+ request.addEventListener('error', function(event) {
+ reject(new Error('fetch_url encountered an error: ' + url));
+ });
+ request.addEventListener('abort', function(event) {
+ reject(new Error('fetch_url was aborted: ' + url));
+ });
+ request.open('GET', url);
+ request.send();
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
new file mode 100644
index 000000000..7f66d20df
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-async-respond-with-worker.js
@@ -0,0 +1,19 @@
+var result;
+
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage(result);
+ });
+
+self.addEventListener('fetch', function(event) {
+ setTimeout(function() {
+ try {
+ event.respondWith(new Response());
+ result = 'FAIL: did not throw';
+ } catch (error) {
+ if (error.name == 'InvalidStateError')
+ result = 'PASS';
+ else
+ result = 'FAIL: Unexpected exception: ' + error;
+ }
+ }, 0);
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html
new file mode 100644
index 000000000..a4c9307e7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-controllee-iframe.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<script>
+function fetch_url(url) {
+ return new Promise(function(resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.addEventListener('load', function(event) {
+ resolve();
+ });
+ request.addEventListener('error', function(event) {
+ reject();
+ });
+ request.open('GET', url);
+ request.send();
+ });
+}
+
+function make_test(testcase) {
+ var name = testcase.name;
+ return fetch_url(window.location.href + '?' + name)
+ .then(
+ function() {
+ if (testcase.expect_load)
+ return Promise.resolve();
+ return Promise.reject(new Error(
+ name + ': expected network error but loaded'));
+ },
+ function() {
+ if (!testcase.expect_load)
+ return Promise.resolve();
+ return Promise.reject(new Error(
+ name + ': expected to load but got network error'));
+ });
+}
+
+function run_tests() {
+ var tests = [
+ { name: 'prevent-default-and-respond-with', expect_load: true },
+ { name: 'prevent-default', expect_load: false },
+ { name: 'reject', expect_load: false },
+ { name: 'unused-body', expect_load: true },
+ { name: 'used-body', expect_load: false },
+ { name: 'unused-fetched-body', expect_load: true },
+ { name: 'used-fetched-body', expect_load: false }
+ ].map(make_test);
+
+ Promise.all(tests)
+ .then(function() {
+ window.parent.notify_test_done('PASS');
+ })
+ .catch(function(error) {
+ window.parent.notify_test_done('FAIL: ' + error.message);
+ });
+}
+
+if (!navigator.serviceWorker.controller)
+ window.parent.notify_done('FAIL: no controller');
+else
+ run_tests();
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js
new file mode 100644
index 000000000..52d4c8e86
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-network-error-worker.js
@@ -0,0 +1,46 @@
+// Test that multiple fetch handlers do not confuse the implementation.
+self.addEventListener('fetch', function(event) {});
+
+self.addEventListener('fetch', function(event) {
+ var testcase = new URL(event.request.url).search;
+ switch (testcase) {
+ case '?reject':
+ event.respondWith(Promise.reject());
+ break;
+ case '?prevent-default':
+ event.preventDefault();
+ break;
+ case '?prevent-default-and-respond-with':
+ event.preventDefault();
+ break;
+ case '?unused-body':
+ event.respondWith(new Response('body'));
+ break;
+ case '?used-body':
+ var res = new Response('body');
+ res.text();
+ event.respondWith(res);
+ break;
+ case '?unused-fetched-body':
+ event.respondWith(fetch('other.html').then(function(res){
+ return res;
+ }));
+ break;
+ case '?used-fetched-body':
+ event.respondWith(fetch('other.html').then(function(res){
+ res.text();
+ return res;
+ }));
+ break;
+ }
+ });
+
+self.addEventListener('fetch', function(event) {});
+
+self.addEventListener('fetch', function(event) {
+ var testcase = new URL(event.request.url).search;
+ if (testcase == '?prevent-default-and-respond-with')
+ event.respondWith(new Response('responding!'));
+ });
+
+self.addEventListener('fetch', function(event) {});
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html
new file mode 100644
index 000000000..7548d8af7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-redirect-iframe.html
@@ -0,0 +1,25 @@
+<script>
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ var data = evt.data;
+ fetch(new Request(data.url, data.request_init)).then(function(response) {
+ if (data.request_init.mode === 'no-cors' && data.redirect_dest != 'same-origin') {
+ if (response.type === data.expected_type &&
+ (response.type === 'opaque' || response.type === 'opaqueredirect') &&
+ response.redirected === data.expected_redirected) {
+ return {result: 'success', detail: ''};
+ } else {
+ return {result: 'failure',
+ detail: 'expected type ' + data.expected_type +
+ ', expected redirected ' + data.expected_redirected +
+ ' response'};
+ }
+ }
+ return response.json();
+ }).then(function(body) {
+ port.postMessage({result: body.result, detail: body.detail});
+ }).catch(function(e) {
+ port.postMessage({result: 'reject', detail: e.toString()});
+ });
+});
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js
new file mode 100644
index 000000000..18da049d6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-respond-with-stops-propagation-worker.js
@@ -0,0 +1,15 @@
+var result = null;
+
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage(result);
+ });
+
+self.addEventListener('fetch', function(event) {
+ if (!result)
+ result = 'PASS';
+ event.respondWith(new Response());
+ });
+
+self.addEventListener('fetch', function(event) {
+ result = 'FAIL: fetch event propagated';
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
new file mode 100644
index 000000000..55ba4ab4d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-event-test-worker.js
@@ -0,0 +1,151 @@
+function handleString(event) {
+ event.respondWith(new Response('Test string'));
+}
+
+function handleBlob(event) {
+ event.respondWith(new Response(new Blob(['Test blob'])));
+}
+
+function handleReferrer(event) {
+ event.respondWith(new Response(new Blob(
+ ['Referrer: ' + event.request.referrer])));
+}
+
+function handleReferrerPolicy(event) {
+ event.respondWith(new Response(new Blob(
+ ['ReferrerPolicy: ' + event.request.referrerPolicy])));
+}
+
+function handleReferrerFull(event) {
+ event.respondWith(new Response(new Blob(
+ ['Referrer: ' + event.request.referrer + '\n' +
+ 'ReferrerPolicy: ' + event.request.referrerPolicy])));
+}
+
+function handleClientId(event) {
+ var body;
+ if (event.clientId !== null) {
+ body = 'Client ID Found: ' + event.clientId;
+ } else {
+ body = 'Client ID Not Found';
+ }
+ event.respondWith(new Response(body));
+}
+
+function handleNullBody(event) {
+ event.respondWith(new Response());
+}
+
+function handleFetch(event) {
+ event.respondWith(fetch('other.html'));
+}
+
+function handleFormPost(event) {
+ event.respondWith(new Promise(function(resolve) {
+ event.request.text()
+ .then(function(result) {
+ resolve(new Response(event.request.method + ':' +
+ event.request.headers.get('Content-Type') + ':' +
+ result));
+ });
+ }));
+}
+
+function handleMultipleRespondWith(event) {
+ var logForMultipleRespondWith = '';
+ for (var i = 0; i < 3; ++i) {
+ logForMultipleRespondWith += '(' + i + ')';
+ try {
+ event.respondWith(new Promise(function(resolve) {
+ setTimeout(function() {
+ resolve(new Response(logForMultipleRespondWith));
+ }, 0);
+ }));
+ } catch (e) {
+ logForMultipleRespondWith += '[' + e.name + ']';
+ }
+ }
+}
+
+var lastResponseForUsedCheck = undefined;
+
+function handleUsedCheck(event) {
+ if (!lastResponseForUsedCheck) {
+ event.respondWith(fetch('other.html').then(function(response) {
+ lastResponseForUsedCheck = response;
+ return response;
+ }));
+ } else {
+ event.respondWith(new Response(
+ 'bodyUsed: ' + lastResponseForUsedCheck.bodyUsed));
+ }
+}
+function handleFragmentCheck(event) {
+ var body;
+ if (event.request.url.indexOf('#') === -1) {
+ body = 'Fragment Not Found';
+ } else {
+ body = 'Fragment Found :' +
+ event.request.url.substring(event.request.url.indexOf('#'));
+ }
+ event.respondWith(new Response(body));
+}
+function handleCache(event) {
+ event.respondWith(new Response(event.request.cache));
+}
+function handleEventSource(event) {
+ if (event.request.mode === 'navigate') {
+ return;
+ }
+ var data = {
+ mode: event.request.mode,
+ cache: event.request.cache,
+ credentials: event.request.credentials
+ };
+ var body = 'data:' + JSON.stringify(data) + '\n\n';
+ event.respondWith(new Response(body, {
+ headers: { 'Content-Type': 'text/event-stream' }
+ }
+ ));
+}
+
+function handleIntegrity(event) {
+ event.respondWith(new Response(event.request.integrity));
+}
+
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ var handlers = [
+ { pattern: '?string', fn: handleString },
+ { pattern: '?blob', fn: handleBlob },
+ { pattern: '?referrerFull', fn: handleReferrerFull },
+ { pattern: '?referrerPolicy', fn: handleReferrerPolicy },
+ { pattern: '?referrer', fn: handleReferrer },
+ { pattern: '?clientId', fn: handleClientId },
+ { pattern: '?ignore', fn: function() {} },
+ { pattern: '?null', fn: handleNullBody },
+ { pattern: '?fetch', fn: handleFetch },
+ { pattern: '?form-post', fn: handleFormPost },
+ { pattern: '?multiple-respond-with', fn: handleMultipleRespondWith },
+ { pattern: '?used-check', fn: handleUsedCheck },
+ { pattern: '?fragment-check', fn: handleFragmentCheck },
+ { pattern: '?cache', fn: handleCache },
+ { pattern: '?eventsource', fn: handleEventSource },
+ { pattern: '?integrity', fn: handleIntegrity },
+ ];
+
+ var handler = null;
+ for (var i = 0; i < handlers.length; ++i) {
+ if (url.indexOf(handlers[i].pattern) != -1) {
+ handler = handlers[i];
+ break;
+ }
+ }
+
+ if (handler) {
+ handler.fn(event);
+ } else {
+ event.respondWith(new Response(new Blob(
+ ['Service Worker got an unexpected request: ' + url])));
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html
new file mode 100644
index 000000000..e0f32f754
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-header-visibility-iframe.html
@@ -0,0 +1,66 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+ var host_info = get_host_info();
+ var uri = document.location + '?check-ua-header';
+
+ var headers = new Headers();
+ headers.set('User-Agent', 'custom_ua');
+
+ // Check the custom UA case
+ fetch(uri, { headers: headers }).then(function(response) {
+ return response.text();
+ }).then(function(text) {
+ if (text == 'custom_ua') {
+ parent.postMessage('PASS', '*');
+ } else {
+ parent.postMessage('withUA FAIL - expected "custom_ua", got "' + text + '"', '*');
+ }
+ }).catch(function(err) {
+ parent.postMessage('withUA FAIL - unexpected error: ' + err, '*');
+ });
+
+ // Check the default UA case
+ fetch(uri, {}).then(function(response) {
+ return response.text();
+ }).then(function(text) {
+ if (text == 'NO_UA') {
+ parent.postMessage('PASS', '*');
+ } else {
+ parent.postMessage('noUA FAIL - expected "NO_UA", got "' + text + '"', '*');
+ }
+ }).catch(function(err) {
+ parent.postMessage('noUA FAIL - unexpected error: ' + err, '*');
+ });
+
+ var uri = document.location + '?check-accept-header';
+ var headers = new Headers();
+ headers.set('Accept', 'hmm');
+
+ // Check for custom accept header
+ fetch(uri, { headers: headers }).then(function(response) {
+ return response.text();
+ }).then(function(text) {
+ if (text === headers.get('Accept')) {
+ parent.postMessage('PASS', '*');
+ } else {
+ parent.postMessage('custom accept FAIL - expected ' + headers.get('Accept') +
+ ' got "' + text + '"', '*');
+ }
+ }).catch(function(err) {
+ parent.postMessage('custom accept FAIL - unexpected error: ' + err, '*');
+ });
+
+ // Check for default accept header
+ fetch(uri).then(function(response) {
+ return response.text();
+ }).then(function(text) {
+ if (text === '*/*') {
+ parent.postMessage('PASS', '*');
+ } else {
+ parent.postMessage('accept FAIL - expected */* got "' + text + '"', '*');
+ }
+ }).catch(function(err) {
+ parent.postMessage('accept FAIL - unexpected error: ' + err, '*');
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html
new file mode 100644
index 000000000..a7db229ce
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-inscope.html
@@ -0,0 +1,71 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var image_path = base_path() + 'fetch-access-control.py?PNGIMAGE';
+var host_info = get_host_info();
+var results = '';
+
+function test1() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test2();
+ };
+ img.onerror = function() {
+ results += 'FAIL(1)';
+ test2();
+ };
+ img.src = './dummy?url=' +
+ encodeURIComponent(host_info['HTTPS_ORIGIN'] + image_path);
+}
+
+function test2() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test3();
+ };
+ img.onerror = function() {
+ results += 'FAIL(2)';
+ test3();
+ };
+ img.src = './dummy?mode=no-cors&url=' +
+ encodeURIComponent(host_info['HTTPS_REMOTE_ORIGIN'] + image_path);
+}
+
+function test3() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(3)';
+ test4();
+ };
+ img.onerror = function() {
+ test4();
+ };
+ img.src = './dummy?mode=no-cors&url=' +
+ encodeURIComponent(host_info['HTTP_ORIGIN'] + image_path);
+}
+
+function test4() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(4)';
+ finish();
+ };
+ img.onerror = function() {
+ finish();
+ };
+ img.src = './dummy?mode=no-cors&url=' +
+ encodeURIComponent(host_info['HTTP_REMOTE_ORIGIN'] + image_path);
+}
+
+function finish() {
+ results += 'finish';
+ window.parent.postMessage({results: results}, host_info['HTTPS_ORIGIN']);
+}
+</script>
+
+<body onload='test1();'>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html
new file mode 100644
index 000000000..cec00cb25
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe-inscope-to-outscope.html
@@ -0,0 +1,80 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var image_path = base_path() + 'fetch-access-control.py?PNGIMAGE';
+var host_info = get_host_info();
+var results = '';
+
+function test1() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test2();
+ };
+ img.onerror = function() {
+ results += 'FAIL(1)';
+ test2();
+ };
+ img.src = host_info['HTTPS_ORIGIN'] + image_path;
+}
+
+function test2() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ test3();
+ };
+ img.onerror = function() {
+ results += 'FAIL(2)';
+ test3();
+ };
+ img.src = host_info['HTTPS_REMOTE_ORIGIN'] + image_path;
+}
+
+function test3() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(3)';
+ test4();
+ };
+ img.onerror = function() {
+ test4();
+ };
+ img.src = host_info['HTTP_ORIGIN'] + image_path;
+}
+
+function test4() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ results += 'FAIL(4)';
+ test5();
+ };
+ img.onerror = function() {
+ test5();
+ };
+ img.src = host_info['HTTP_REMOTE_ORIGIN'] + image_path;
+}
+
+function test5() {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = function() {
+ finish();
+ };
+ img.onerror = function() {
+ results += 'FAIL(5)';
+ finish();
+ };
+ img.src = './dummy?generate-png';
+}
+
+function finish() {
+ results += 'finish';
+ window.parent.postMessage({results: results}, host_info['HTTPS_ORIGIN']);
+}
+</script>
+
+<body onload='test1();'>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html
new file mode 100644
index 000000000..961fd2ab6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-mixed-content-iframe.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var params = get_query_params(location.href);
+var SCOPE = 'fetch-mixed-content-iframe-inscope-to-' + params['target'] + '.html';
+var URL = 'fetch-rewrite-worker.js';
+var host_info = get_host_info();
+
+window.addEventListener('message', on_message, false);
+
+navigator.serviceWorker.getRegistration(SCOPE)
+ .then(function(registration) {
+ if (registration)
+ return registration.unregister();
+ })
+ .then(function() {
+ return navigator.serviceWorker.register(URL, {scope: SCOPE});
+ })
+ .then(function(registration) {
+ return new Promise(function(resolve) {
+ registration.addEventListener('updatefound', function() {
+ resolve(registration.installing);
+ });
+ });
+ })
+ .then(function(worker) {
+ worker.addEventListener('statechange', on_state_change);
+ })
+ .catch(function(reason) {
+ window.parent.postMessage({results: 'FAILURE: ' + reason.message},
+ host_info['HTTPS_ORIGIN']);
+ });
+
+function on_state_change(event) {
+ if (event.target.state != 'activated')
+ return;
+ var frame = document.createElement('iframe');
+ frame.src = SCOPE;
+ document.body.appendChild(frame);
+}
+
+function on_message(e) {
+ navigator.serviceWorker.getRegistration(SCOPE)
+ .then(function(registration) {
+ if (registration)
+ return registration.unregister();
+ })
+ .then(function() {
+ window.parent.postMessage(e.data, host_info['HTTPS_ORIGIN']);
+ })
+ .catch(function(reason) {
+ window.parent.postMessage({results: 'FAILURE: ' + reason.message},
+ host_info['HTTPS_ORIGIN']);
+ });
+}
+
+function get_query_params(url) {
+ var search = (new URL(url)).search;
+ if (!search) {
+ return {};
+ }
+ var ret = {};
+ var params = search.substring(1).split('&');
+ params.forEach(function(param) {
+ var element = param.split('=');
+ ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+ });
+ return ret;
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html
new file mode 100644
index 000000000..0edf2e7f9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-iframe.html
@@ -0,0 +1 @@
+<link href="./fetch-request-css-base-url-style.css" rel="stylesheet" type="text/css">
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css
new file mode 100644
index 000000000..7643f667a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-style.css
@@ -0,0 +1 @@
+body { background-image: url("./dummy.png");}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js
new file mode 100644
index 000000000..0d9244ec7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-css-base-url-worker.js
@@ -0,0 +1,27 @@
+importScripts('../resources/get-host-info.sub.js');
+importScripts('test-helpers.sub.js');
+
+var port = undefined;
+
+self.onmessage = function(e) {
+ var message = e.data;
+ if ('port' in message) {
+ port = message.port;
+ port.postMessage({ready: true});
+ }
+};
+
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('fetch-request-css-base-url-style.css') != -1) {
+ event.respondWith(fetch(
+ get_host_info()['HTTPS_REMOTE_ORIGIN'] + base_path() +
+ 'fetch-request-css-base-url-style.css',
+ {mode: 'no-cors'}));
+ } else if (url.indexOf('dummy.png') != -1) {
+ port.postMessage({
+ url: event.request.url,
+ referrer: event.request.referrer
+ });
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html
new file mode 100644
index 000000000..f00d24e37
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-iframe.html
@@ -0,0 +1,15 @@
+<script>
+function xhr(url) {
+ return new Promise(function(resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.addEventListener(
+ 'error',
+ function(event) { reject(event); });
+ request.addEventListener(
+ 'load',
+ function(event) { resolve(request.response); });
+ request.open('GET', url);
+ request.send();
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js
new file mode 100644
index 000000000..3b028b24b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-fallback-worker.js
@@ -0,0 +1,13 @@
+var requests = [];
+
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage({requests: requests});
+ requests = [];
+ });
+
+self.addEventListener('fetch', function(event) {
+ requests.push({
+ url: event.request.url,
+ mode: event.request.mode
+ });
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html
new file mode 100644
index 000000000..68a1a8687
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-iframe.html
@@ -0,0 +1 @@
+<script src="empty.js"></script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js
new file mode 100644
index 000000000..2bd59d739
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-no-freshness-headers-worker.js
@@ -0,0 +1,18 @@
+var requests = [];
+
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage({requests: requests});
+ });
+
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ var headers = [];
+ for (var header of event.request.headers) {
+ headers.push(header);
+ }
+ requests.push({
+ url: url,
+ headers: headers
+ });
+ event.respondWith(fetch(event.request));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html
new file mode 100644
index 000000000..ffd76bfc4
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-redirect-iframe.html
@@ -0,0 +1,35 @@
+<script>
+function xhr(url) {
+ return new Promise(function(resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.addEventListener(
+ 'error',
+ function(event) { reject(event); });
+ request.addEventListener(
+ 'load',
+ function(event) { resolve(request.response); });
+ request.open('GET', url);
+ request.send();
+ });
+}
+
+function load_image(url) {
+ return new Promise(function(resolve, reject) {
+ var img = document.createElement('img');
+ document.body.appendChild(img);
+ img.onload = resolve;
+ img.onerror = reject;
+ img.src = url;
+ });
+}
+
+function load_audio(url) {
+ return new Promise(function(resolve, reject) {
+ var audio = document.createElement('audio');
+ document.body.appendChild(audio);
+ audio.oncanplay = resolve;
+ audio.onerror = reject;
+ audio.src = url;
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
new file mode 100644
index 000000000..93b038dd6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-iframe.https.html
@@ -0,0 +1,67 @@
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<body>
+<script>
+
+function load_image(url, cross_origin) {
+ var img = document.createElement('img');
+ if (cross_origin != '') {
+ img.crossOrigin = cross_origin;
+ }
+ img.src = url;
+}
+
+function load_script(url, cross_origin) {
+ var script = document.createElement('script');
+ script.src = url;
+ if (cross_origin != '') {
+ script.crossOrigin = cross_origin;
+ }
+ document.body.appendChild(script);
+}
+
+function load_css(url, cross_origin) {
+ var link = document.createElement('link');
+ link.rel = 'stylesheet'
+ link.href = url;
+ link.type = 'text/css';
+ if (cross_origin != '') {
+ link.crossOrigin = cross_origin;
+ }
+ document.body.appendChild(link);
+}
+
+function load_font(url) {
+ var fontFace = new FontFace('test', 'url(' + url + ')');
+ fontFace.load();
+}
+
+function load_css_image(url, type) {
+ var div = document.createElement('div');
+ document.body.appendChild(div);
+ div.style[type] = 'url(' + url + ')';
+}
+
+function load_css_image_set(url, type) {
+ var div = document.createElement('div');
+ document.body.appendChild(div);
+ div.style[type] = '-webkit-image-set(url(' + url + ') 1x)';
+}
+
+function load_script_with_integrity(url, integrity) {
+ var script = document.createElement('script');
+ script.src = url;
+ script.integrity = integrity;
+ document.body.appendChild(script);
+}
+
+function load_css_with_integrity(url, integrity) {
+ var link = document.createElement('link');
+ link.rel = 'stylesheet'
+ link.href = url;
+ link.type = 'text/css';
+ link.integrity = integrity;
+ document.body.appendChild(link);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
new file mode 100644
index 000000000..900b63c62
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-resources-worker.js
@@ -0,0 +1,24 @@
+var requests = [];
+var port = undefined;
+
+self.onmessage = function(e) {
+ var message = e.data;
+ if ('port' in message) {
+ port = message.port;
+ port.postMessage({ready: true});
+ }
+};
+
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('dummy?test') == -1) {
+ return;
+ }
+ port.postMessage({
+ url: url,
+ mode: event.request.mode,
+ credentials: event.request.credentials,
+ integrity: event.request.integrity
+ });
+ event.respondWith(Promise.reject());
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html
new file mode 100644
index 000000000..26c6b7344
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-iframe.https.html
@@ -0,0 +1,179 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var port;
+var host_info = get_host_info();
+
+function assert_equals(a, b) {
+ port.postMessage({results: 'equals', got: a, expected: b});
+}
+
+function get_boundary(headers) {
+ var reg = new RegExp('multipart\/form-data; boundary=(.*)');
+ for (var i = 0; i < headers.length; ++i) {
+ if (headers[i][0] != 'content-type') {
+ continue;
+ }
+ var regResult = reg.exec(headers[i][1]);
+ if (!regResult) {
+ continue;
+ }
+ return regResult[1];
+ }
+ return '';
+}
+
+function create_file_system_file(file_name, data) {
+ return new Promise(function(resolve, reject) {
+ webkitRequestFileSystem(TEMPORARY, 1024, function(fs) {
+ fs.root.getFile(
+ file_name, {create: true, exclusive: true},
+ function(fileEntry) {
+ fileEntry.createWriter(function(fileWriter) {
+ fileWriter.onwriteend = function(e) {
+ fileEntry.file(function(file) { resolve(file); });
+ };
+ var blob = new Blob([data], {type: 'text/plain'});
+ fileWriter.write(blob);
+ });
+ }, function(e) { reject(e); });
+ }, function(e) { reject(e); });
+ });
+}
+
+function xhr_send(url_base, method, data, with_credentials) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ resolve(JSON.parse(xhr.response));
+ };
+ xhr.onerror = function() {
+ reject('XHR should succeed.');
+ };
+ xhr.responseType = 'text';
+ if (with_credentials) {
+ xhr.withCredentials = true;
+ }
+ xhr.open(method, url_base + '/dummy?test', true);
+ xhr.send(data);
+ });
+}
+
+function string_test() {
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'POST', 'test string', false)
+ .then(function(response) {
+ assert_equals(response.method, 'POST');
+ assert_equals(response.body, 'test string');
+ });
+}
+
+function blob_test() {
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'POST', new Blob(['test blob']),
+ false)
+ .then(function(response) {
+ assert_equals(response.method, 'POST');
+ assert_equals(response.body, 'test blob');
+ });
+}
+
+function custom_method_test() {
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'XXX', 'test string xxx', false)
+ .then(function(response) {
+ assert_equals(response.method, 'XXX');
+ assert_equals(response.body, 'test string xxx');
+ });
+}
+
+function options_method_test() {
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'OPTIONS', 'test string xxx', false)
+ .then(function(response) {
+ assert_equals(response.method, 'OPTIONS');
+ assert_equals(response.body, 'test string xxx');
+ });
+}
+
+function form_data_test() {
+ var formData = new FormData();
+ formData.append('sample string', '1234567890');
+ formData.append('sample blob', new Blob(['blob content']));
+ formData.append('sample file', new File(['file content'], 'file.dat'));
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'POST', formData, false)
+ .then(function(response) {
+ assert_equals(response.method, 'POST');
+ var boundary = get_boundary(response.headers);
+ var expected_body =
+ '--' + boundary + '\r\n' +
+ 'Content-Disposition: form-data; name="sample string"\r\n' +
+ '\r\n' +
+ '1234567890\r\n' +
+ '--' + boundary + '\r\n' +
+ 'Content-Disposition: form-data; name="sample blob"; ' +
+ 'filename="blob"\r\n' +
+ 'Content-Type: application/octet-stream\r\n' +
+ '\r\n' +
+ 'blob content\r\n' +
+ '--' + boundary + '\r\n' +
+ 'Content-Disposition: form-data; name="sample file"; ' +
+ 'filename="file.dat"\r\n' +
+ 'Content-Type: application/octet-stream\r\n' +
+ '\r\n' +
+ 'file content\r\n' +
+ '--' + boundary + '--\r\n';
+ assert_equals(response.body, expected_body);
+ });
+}
+
+function mode_credentials_test() {
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'GET', '', false)
+ .then(function(response){
+ assert_equals(response.mode, 'cors');
+ assert_equals(response.credentials, 'same-origin');
+ return xhr_send(host_info['HTTPS_ORIGIN'], 'GET', '', true);
+ })
+ .then(function(response){
+ assert_equals(response.mode, 'cors');
+ assert_equals(response.credentials, 'include');
+ return xhr_send(host_info['HTTPS_REMOTE_ORIGIN'], 'GET', '', false);
+ })
+ .then(function(response){
+ assert_equals(response.mode, 'cors');
+ assert_equals(response.credentials, 'same-origin');
+ return xhr_send(host_info['HTTPS_REMOTE_ORIGIN'], 'GET', '', true);
+ })
+ .then(function(response){
+ assert_equals(response.mode, 'cors');
+ assert_equals(response.credentials, 'include');
+ });
+}
+
+function data_url_test() {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ resolve(xhr.response);
+ };
+ xhr.onerror = function() {
+ reject('XHR should succeed.');
+ };
+ xhr.responseType = 'text';
+ xhr.open('GET', 'data:text/html,Foobar', true);
+ xhr.send();
+ })
+ .then(function(data) {
+ assert_equals(data, 'Foobar');
+ });
+}
+
+window.addEventListener('message', function(evt) {
+ port = evt.ports[0];
+ string_test()
+ .then(blob_test)
+ .then(custom_method_test)
+ .then(options_method_test)
+ .then(form_data_test)
+ .then(mode_credentials_test)
+ .then(data_url_test)
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js
new file mode 100644
index 000000000..91b3abb14
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-request-xhr-worker.js
@@ -0,0 +1,22 @@
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('dummy?test') == -1) {
+ return;
+ }
+ event.respondWith(new Promise(function(resolve) {
+ var headers = [];
+ for (var header of event.request.headers) {
+ headers.push(header);
+ }
+ event.request.text()
+ .then(function(result) {
+ resolve(new Response(JSON.stringify({
+ method: event.request.method,
+ mode: event.request.mode,
+ credentials: event.request.credentials,
+ headers: headers,
+ body: result
+ })));
+ });
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html
new file mode 100644
index 000000000..3391381e3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-iframe.https.html
@@ -0,0 +1,35 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var host_info = get_host_info();
+
+function xhr_send(method, data) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ resolve(xhr);
+ };
+ xhr.onerror = function() {
+ reject('XHR should succeed.');
+ };
+ xhr.responseType = 'text';
+ xhr.open(method, './dummy?test', true);
+ xhr.send(data);
+ });
+}
+
+function coalesce_headers_test() {
+ return xhr_send('POST', 'test string')
+ .then(function(xhr) {
+ window.parent.postMessage({results: xhr.getResponseHeader('foo')},
+ host_info['HTTPS_ORIGIN']);
+ });
+}
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ coalesce_headers_test()
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js
new file mode 100644
index 000000000..465fbc91b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-response-xhr-worker.js
@@ -0,0 +1,12 @@
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('dummy?test') == -1) {
+ return;
+ }
+ event.respondWith(new Promise(function(resolve) {
+ var headers = new Headers;
+ headers.append('foo', 'foo');
+ headers.append('foo', 'bar');
+ resolve(new Response('hello world', {'headers': headers}));
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js
new file mode 100644
index 000000000..9806f2b5b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-rewrite-worker.js
@@ -0,0 +1,149 @@
+function get_query_params(url) {
+ var search = (new URL(url)).search;
+ if (!search) {
+ return {};
+ }
+ var ret = {};
+ var params = search.substring(1).split('&');
+ params.forEach(function(param) {
+ var element = param.split('=');
+ ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+ });
+ return ret;
+}
+
+function get_request_init(base, params) {
+ var init = {};
+ init['method'] = params['method'] || base['method'];
+ init['mode'] = params['mode'] || base['mode'];
+ if (init['mode'] == 'navigate') {
+ init['mode'] = 'same-origin';
+ }
+ init['credentials'] = params['credentials'] || base['credentials'];
+ init['redirect'] = params['redirect-mode'] || base['redirect'];
+ return init;
+}
+
+self.addEventListener('fetch', function(event) {
+ var params = get_query_params(event.request.url);
+ var init = get_request_init(event.request, params);
+ var url = params['url'];
+ if (params['ignore']) {
+ return;
+ }
+ if (params['throw']) {
+ throw new Error('boom');
+ }
+ if (params['reject']) {
+ event.respondWith(new Promise(function(resolve, reject) {
+ reject();
+ }));
+ return;
+ }
+ if (params['resolve-null']) {
+ event.respondWith(new Promise(function(resolve) {
+ resolve(null);
+ }));
+ return;
+ }
+ if (params['generate-png']) {
+ var binary = atob(
+ 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAA' +
+ 'RnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAhSURBVDhPY3wro/Kf' +
+ 'gQLABKXJBqMGjBoAAqMGDLwBDAwAEsoCTFWunmQAAAAASUVORK5CYII=');
+ var array = new Uint8Array(binary.length);
+ for(var i = 0; i < binary.length; i++) {
+ array[i] = binary.charCodeAt(i);
+ };
+ event.respondWith(new Response(new Blob([array], {type: 'image/png'})));
+ return;
+ }
+ if (params['check-ua-header']) {
+ var ua = event.request.headers.get('User-Agent');
+ if (ua) {
+ // We have a user agent!
+ event.respondWith(new Response(new Blob([ua])));
+ } else {
+ // We don't have a user-agent!
+ event.respondWith(new Response(new Blob(["NO_UA"])));
+ }
+ return;
+ }
+ if (params['check-accept-header']) {
+ var accept = event.request.headers.get('Accept');
+ if (accept) {
+ event.respondWith(new Response(accept));
+ } else {
+ event.respondWith(new Response('NO_ACCEPT'));
+ }
+ return;
+ }
+ event.respondWith(new Promise(function(resolve, reject) {
+ var request = event.request;
+ if (url) {
+ request = new Request(url, init);
+ }
+ fetch(request).then(function(response) {
+ var expectedType = params['expected_type'];
+ if (expectedType && response.type !== expectedType) {
+ // Resolve a JSON object with a failure instead of rejecting
+ // in order to distinguish this from a NetworkError, which
+ // may be expected even if the type is correct.
+ resolve(new Response(JSON.stringify({
+ result: 'failure',
+ detail: 'got ' + response.type + ' Response.type instead of ' +
+ expectedType
+ })));
+ }
+
+ var expectedRedirected = params['expected_redirected'];
+ if (typeof expectedRedirected !== 'undefined') {
+ var expected_redirected = (expectedRedirected === 'true');
+ if(response.redirected !== expected_redirected) {
+ // This is simply determining how to pass an error to the outer
+ // test case(fetch-request-redirect.https.html).
+ var execptedResolves = params['expected_resolves'];
+ if (execptedResolves === 'true') {
+ // Reject a JSON object with a failure since promise is expected
+ // to be resolved.
+ reject(new Response(JSON.stringify({
+ result: 'failure',
+ detail: 'got '+ response.redirected +
+ ' Response.redirected instead of ' +
+ expectedRedirected
+ })));
+ } else {
+ // Resolve a JSON object with a failure since promise is
+ // expected to be rejected.
+ resolve(new Response(JSON.stringify({
+ result: 'failure',
+ detail: 'got '+ response.redirected +
+ ' Response.redirected instead of ' +
+ expectedRedirected
+ })));
+ }
+ }
+ }
+
+ if (params['cache']) {
+ var cacheName = "cached-fetches-" + performance.now() + "-" +
+ event.request.url;
+ var cache;
+ var cachedResponse;
+ return self.caches.open(cacheName).then(function(opened) {
+ cache = opened;
+ return cache.put(request, response);
+ }).then(function() {
+ return cache.match(request);
+ }).then(function(cached) {
+ cachedResponse = cached;
+ return self.caches.delete(cacheName);
+ }).then(function() {
+ resolve(cachedResponse);
+ });
+ } else {
+ resolve(response);
+ }
+ }, reject)
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js
new file mode 100644
index 000000000..66f3e5936
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/fetch-waits-for-activate-worker.js
@@ -0,0 +1,17 @@
+var activatePromiseResolve;
+
+addEventListener('activate', function(evt) {
+ evt.waitUntil(new Promise(function(resolve) {
+ activatePromiseResolve = resolve;
+ }));
+});
+
+addEventListener('message', function(evt) {
+ if (typeof activatePromiseResolve === 'function') {
+ activatePromiseResolve();
+ }
+});
+
+addEventListener('fetch', function(evt) {
+ evt.respondWith(new Response('Hello world'));
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html b/testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html
new file mode 100644
index 000000000..7fc35f189
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/frame-for-getregistrations.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<title>Service Worker: frame for getRegistrations()</title>
+<script>
+var scope = 'scope-for-getregistrations';
+var script = 'empty-worker.js';
+var registration;
+
+navigator.serviceWorker.register(script, { scope: scope })
+ .then(function(r) { registration = r; window.parent.postMessage('ready', '*'); })
+
+self.onmessage = function(e) {
+ if (e.data == 'unregister') {
+ registration.unregister()
+ .then(function() {
+ e.ports[0].postMessage('unregistered');
+ });
+ }
+};
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js
new file mode 100644
index 000000000..b64334df6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/get-host-info.sub.js
@@ -0,0 +1,26 @@
+function get_host_info() {
+ var ORIGINAL_HOST = '127.0.0.1';
+ var REMOTE_HOST = 'localhost';
+ var UNAUTHENTICATED_HOST = 'example.test';
+ var HTTP_PORT = 8000;
+ var HTTPS_PORT = 8443;
+ try {
+ // In W3C test, we can get the hostname and port number in config.json
+ // using wptserve's built-in pipe.
+ // http://wptserve.readthedocs.org/en/latest/pipes.html#built-in-pipes
+ HTTP_PORT = eval('{{ports[http][0]}}');
+ HTTPS_PORT = eval('{{ports[https][0]}}');
+ ORIGINAL_HOST = eval('\'{{host}}\'');
+ REMOTE_HOST = 'www1.' + ORIGINAL_HOST;
+ } catch (e) {
+ }
+ return {
+ HTTP_ORIGIN: 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT,
+ HTTPS_ORIGIN: 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT,
+ HTTPS_ORIGIN_WITH_CREDS: 'https://foo:bar@' + ORIGINAL_HOST + ':' + HTTPS_PORT,
+ HTTP_REMOTE_ORIGIN: 'http://' + REMOTE_HOST + ':' + HTTP_PORT,
+ HTTPS_REMOTE_ORIGIN: 'https://' + REMOTE_HOST + ':' + HTTPS_PORT,
+ HTTPS_REMOTE_ORIGIN_WITH_CREDS: 'https://foo:bar@' + REMOTE_HOST + ':' + HTTPS_PORT,
+ UNAUTHENTICATED_ORIGIN: 'http://' + UNAUTHENTICATED_HOST + ':' + HTTP_PORT
+ };
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js
new file mode 100644
index 000000000..ef89550b3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/indexeddb-worker.js
@@ -0,0 +1,26 @@
+var port;
+self.addEventListener('message', function(e) {
+ var message = e.data;
+ if ('port' in message)
+ doIndexedDBTest(message.port);
+ });
+
+function doIndexedDBTest(port) {
+ var delete_request = indexedDB.deleteDatabase('db');
+ delete_request.onsuccess = function() {
+ var open_request = indexedDB.open('db');
+ open_request.onupgradeneeded = function() {
+ var db = open_request.result;
+ db.createObjectStore('store');
+ };
+ open_request.onsuccess = function() {
+ var db = open_request.result;
+ var tx = db.transaction('store', 'readwrite');
+ var store = tx.objectStore('store');
+ store.put('value', 'key');
+ tx.oncomplete = function() {
+ port.postMessage('done');
+ };
+ };
+ };
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js
new file mode 100644
index 000000000..d729afa09
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/install-event-type-worker.js
@@ -0,0 +1,8 @@
+importScripts('worker-testharness.js');
+
+self.oninstall = function(event) {
+ assert_true(event instanceof ExtendableEvent);
+ assert_equals(event.type, 'install');
+ assert_false(event.cancelable);
+ assert_false(event.bubbles);
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js
new file mode 100644
index 000000000..e5ed36fce
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces-worker.sub.js
@@ -0,0 +1,107 @@
+importScripts('interfaces.js');
+importScripts('worker-testharness.js');
+importScripts('../resources/testharness-helpers.js');
+
+var EVENT_HANDLER = 'object';
+
+test(function() {
+ verify_interface('ServiceWorkerGlobalScope',
+ self,
+ {
+ clients: 'object',
+ registration: 'object',
+ skipWaiting: 'function',
+
+ onactivate: EVENT_HANDLER,
+ onfetch: EVENT_HANDLER,
+ oninstall: EVENT_HANDLER,
+ onmessage: EVENT_HANDLER
+ });
+ }, 'ServiceWorkerGlobalScope');
+
+test(function() {
+ verify_interface('Clients',
+ self.clients,
+ {
+ claim: 'function',
+ matchAll: 'function'
+ });
+ }, 'Clients');
+
+test(function() {
+ verify_interface('Client');
+ // FIXME: Get an instance and test it, or ensure property exists on
+ // prototype.
+ }, 'Client');
+
+test(function() {
+ verify_interface('WindowClient');
+ // FIXME: Get an instance and test it, or ensure property exists on
+ // prototype.
+ }, 'WindowClient');
+
+test(function() {
+ verify_interface('CacheStorage',
+ self.caches,
+ {
+ match: 'function',
+ has: 'function',
+ open: 'function',
+ delete: 'function',
+ keys: 'function'
+ });
+ }, 'CacheStorage');
+
+promise_test(function(t) {
+ return create_temporary_cache(t)
+ .then(function(cache) {
+ verify_interface('Cache',
+ cache,
+ {
+ match: 'function',
+ matchAll: 'function',
+ add: 'function',
+ addAll: 'function',
+ put: 'function',
+ delete: 'function',
+ keys: 'function'
+ });
+ });
+ }, 'Cache');
+
+test(function() {
+ var req = new Request('http://{{host}}/',
+ {method: 'POST',
+ headers: [['Content-Type', 'Text/Html']]});
+ assert_equals(
+ new ExtendableEvent('ExtendableEvent').type,
+ 'ExtendableEvent', 'Type of ExtendableEvent should be ExtendableEvent');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req}).type,
+ 'FetchEvent', 'Type of FetchEvent should be FetchEvent');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req}).cancelable,
+ false, 'Default FetchEvent.cancelable should be false');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req}).bubbles,
+ false, 'Default FetchEvent.bubbles should be false');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req}).clientId,
+ null, 'Default FetchEvent.clientId should be null');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req}).isReload,
+ false, 'Default FetchEvent.isReload should be false');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req, cancelable: false}).cancelable,
+ false, 'FetchEvent.cancelable should be false');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req, clientId : 'test-client-id'}).clientId, 'test-client-id',
+ 'FetchEvent.clientId with option {clientId : "test-client-id"} should be "test-client-id"');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request: req, isReload : true}).isReload, true,
+ 'FetchEvent.isReload with option {isReload : true} should be true');
+ assert_equals(
+ new FetchEvent('FetchEvent', {request : req, isReload : true}).request.url,
+ 'http://{{host}}/',
+ 'FetchEvent.request.url should return the value it was initialized to');
+ }, 'Event constructors');
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js
new file mode 100644
index 000000000..eb00df65f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/interfaces.js
@@ -0,0 +1,15 @@
+function verify_interface(name, instance, attributes) {
+ assert_true(name in self,
+ name + ' should be an defined type');
+ if (instance) {
+ assert_true(instance instanceof self[name],
+ instance + ' should be an instance of ' + name);
+ Object.keys(attributes || {}).forEach(function(attribute) {
+ var type = attributes[attribute];
+ assert_true(attribute in instance,
+ attribute + ' should be an attribute of ' + name);
+ assert_equals(typeof instance[attribute], type,
+ attribute + ' should be of type ' + type);
+ });
+ }
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html
new file mode 100644
index 000000000..f111bd924
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-iframe.https.html
@@ -0,0 +1,29 @@
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+
+function xhr_send(method, data) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ if (xhr.getResponseHeader('Content-Type') !== null) {
+ reject('Content-Type must be null.');
+ }
+ resolve();
+ };
+ xhr.onerror = function() {
+ reject('XHR must succeed.');
+ };
+ xhr.responseType = 'text';
+ xhr.open(method, './dummy?test', true);
+ xhr.send(data);
+ });
+}
+
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ xhr_send('POST', 'test string')
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
new file mode 100644
index 000000000..93f496ef4
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
@@ -0,0 +1,10 @@
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('dummy?test') == -1) {
+ return;
+ }
+ event.respondWith(new Promise(function(resolve) {
+ // null byte in blob type
+ resolve(new Response(new Blob([],{type: 'a\0b'})));
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
new file mode 100644
index 000000000..c91250a9d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding-with-flush.py
@@ -0,0 +1,11 @@
+import time
+def main(request, response):
+ response.headers.set("Content-Type", "application/javascript")
+ response.headers.set("Transfer-encoding", "chunked")
+ response.write_status_headers()
+
+ time.sleep(1)
+ response.explicit_flush = True
+
+ response.writer.write("XX\r\n\r\n")
+ response.writer.flush()
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
new file mode 100644
index 000000000..ae2c1f21b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-chunked-encoding.py
@@ -0,0 +1,2 @@
+def main(request, response):
+ return [("Content-Type", "application/javascript"), ("Transfer-encoding", "chunked")], "XX\r\n\r\n"
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html
new file mode 100644
index 000000000..19f302c35
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-iframe.https.html
@@ -0,0 +1,26 @@
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+
+function xhr_send(method, data) {
+ return new Promise(function(resolve, reject) {
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() {
+ reject('XHR must fail.');
+ };
+ xhr.onerror = function() {
+ resolve();
+ };
+ xhr.responseType = 'text';
+ xhr.open(method, './dummy?test', true);
+ xhr.send(data);
+ });
+}
+
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ xhr_send('POST', 'test string')
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js
new file mode 100644
index 000000000..31e7f29d0
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/invalid-header-worker.js
@@ -0,0 +1,12 @@
+self.addEventListener('fetch', function(event) {
+ var url = event.request.url;
+ if (url.indexOf('dummy?test') == -1) {
+ return;
+ }
+ event.respondWith(new Promise(function(resolve) {
+ var headers = new Headers;
+ headers.append('foo', 'foo');
+ headers.append('foo', 'b\0r'); // header value with a null byte
+ resolve(new Response('hello world', {'headers': headers}));
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js
new file mode 100644
index 000000000..2c80f25a3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/load_worker.js
@@ -0,0 +1,29 @@
+self.onmessage = function (evt) {
+ if (evt.data == "xhr") {
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "synthesized-response.txt", true);
+ xhr.responseType = "text";
+ xhr.send();
+ xhr.onload = function (evt) {
+ postMessage(xhr.responseText);
+ };
+ xhr.onerror = function() {
+ postMessage("XHR failed!");
+ };
+ } else if (evt.data == "fetch") {
+ fetch("synthesized-response.txt")
+ .then(function(response) {
+ return response.text();
+ })
+ .then(function(data) {
+ postMessage(data);
+ })
+ .catch(function(error) {
+ postMessage("Fetch failed!");
+ });
+ } else if (evt.data == "importScripts") {
+ importScripts("synthesized-response.js");
+ } else {
+ throw "Unexpected message! " + evt.data;
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/loaded.html b/testing/web-platform/tests/service-workers/service-worker/resources/loaded.html
new file mode 100644
index 000000000..0cabce69f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/loaded.html
@@ -0,0 +1,9 @@
+<script>
+addEventListener('load', function() {
+ opener.postMessage({ type: 'LOADED' }, '*');
+});
+
+addEventListener('pageshow', function() {
+ opener.postMessage({ type: 'PAGESHOW' }, '*');
+});
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py
new file mode 100644
index 000000000..501521ff3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/malformed-worker.py
@@ -0,0 +1,10 @@
+def main(request, response):
+ headers = [("Content-Type", "application/javascript")]
+
+ body = {'parse-error': 'var foo = function() {;',
+ 'undefined-error': 'foo.bar = 42;',
+ 'uncaught-exception': 'throw new DOMException("AbortError");',
+ 'caught-exception': 'try { throw new Error; } catch(e) {}',
+ 'import-malformed-script': 'importScripts("malformed-worker.py?parse-error");',
+ 'import-no-such-script': 'importScripts("no-such-script.js");'}[request.url_parts.query]
+ return headers, body
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py
new file mode 100644
index 000000000..a16684de5
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/mime-type-worker.py
@@ -0,0 +1,4 @@
+def main(request, response):
+ if 'mime' in request.GET:
+ return [('Content-Type', request.GET['mime'])], ""
+ return [], ""
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/mint-new-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/mint-new-worker.py
new file mode 100644
index 000000000..cbe70304d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/mint-new-worker.py
@@ -0,0 +1,25 @@
+import time
+
+body = '''
+onactivate = (e) => e.waitUntil(clients.claim());
+var resolve_wait_until;
+var wait_until = new Promise(resolve => {
+ resolve_wait_until = resolve;
+ });
+onmessage = (e) => {
+ if (e.data == 'wait')
+ e.waitUntil(wait_until);
+ if (e.data == 'go')
+ resolve_wait_until();
+ };'''
+
+def main(request, response):
+ headers = [('Cache-Control', 'no-cache, must-revalidate'),
+ ('Pragma', 'no-cache'),
+ ('Content-Type', 'application/javascript')]
+
+ skipWaiting = ''
+ if 'skip-waiting' in request.GET:
+ skipWaiting = 'skipWaiting();'
+
+ return headers, '/* %s %s */ %s %s' % (time.time(), time.clock(), skipWaiting, body)
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigate-window-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/navigate-window-worker.js
new file mode 100644
index 000000000..f9617439f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigate-window-worker.js
@@ -0,0 +1,21 @@
+addEventListener('message', function(evt) {
+ if (evt.data.type === 'GET_CLIENTS') {
+ clients.matchAll(evt.data.opts).then(function(clientList) {
+ var resultList = clientList.map(function(c) {
+ return { url: c.url, frameType: c.frameType, id: c.id };
+ });
+ evt.source.postMessage({ type: 'success', detail: resultList });
+ }).catch(function(err) {
+ evt.source.postMessage({
+ type: 'failure',
+ detail: 'matchAll() rejected with "' + err + '"'
+ });
+ });
+ return;
+ }
+
+ evt.source.postMessage({
+ type: 'failure',
+ detail: 'Unexpected message type "' + evt.data.type + '"'
+ });
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
new file mode 100644
index 000000000..c1441ba68
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-other-origin.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<script src="get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js"></script>
+<script>
+var host_info = get_host_info();
+var SCOPE = 'navigation-redirect-scope1.py';
+var SCRIPT = 'navigation-redirect-worker.js';
+
+var registration;
+var worker;
+var wait_for_worker_promise = navigator.serviceWorker.getRegistration(SCOPE)
+ .then(function(reg) {
+ if (reg)
+ return reg.unregister();
+ })
+ .then(function() {
+ return navigator.serviceWorker.register(SCRIPT, {scope: SCOPE});
+ })
+ .then(function(reg) {
+ registration = reg;
+ worker = reg.installing;
+ return new Promise(function(resolve) {
+ worker.addEventListener('statechange', function() {
+ if (worker.state == 'activated')
+ resolve();
+ });
+ });
+ });
+
+function send_result(message_id, result) {
+ window.parent.postMessage(
+ {id: message_id, result: result},
+ host_info['HTTPS_ORIGIN']);
+}
+
+function get_intercepted_urls(worker) {
+ return new Promise(function(resolve) {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function(msg) { resolve(msg.data.urls); };
+ worker.postMessage({port: channel.port2}, [channel.port2]);
+ });
+}
+
+window.addEventListener('message', on_message, false);
+
+function on_message(e) {
+ if (e.origin != host_info['HTTPS_ORIGIN']) {
+ console.error('invalid origin: ' + e.origin);
+ return;
+ }
+ if (e.data.message == 'wait_for_worker') {
+ wait_for_worker_promise.then(function() { send_result(e.data.id, 'ok'); });
+ } else if (e.data.message == 'get_intercepted_urls') {
+ get_intercepted_urls(worker)
+ .then(function(urls) {
+ send_result(e.data.id, urls);
+ });
+ } else if (e.data.message == 'unregister') {
+ registration.unregister()
+ .then(function() {
+ send_result(e.data.id, 'ok');
+ });
+ }
+}
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
new file mode 100644
index 000000000..4b40762d8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-out-scope.py
@@ -0,0 +1,15 @@
+def main(request, response):
+ if "url" in request.GET:
+ headers = [("Location", request.GET["url"])]
+ return 302, headers, ''
+
+ return [], '''
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage(
+ {
+ id: 'last_url',
+ result: location.href
+ }, '*');
+</script>
+'''
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
new file mode 100644
index 000000000..4b40762d8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope1.py
@@ -0,0 +1,15 @@
+def main(request, response):
+ if "url" in request.GET:
+ headers = [("Location", request.GET["url"])]
+ return 302, headers, ''
+
+ return [], '''
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage(
+ {
+ id: 'last_url',
+ result: location.href
+ }, '*');
+</script>
+'''
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
new file mode 100644
index 000000000..4b40762d8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-scope2.py
@@ -0,0 +1,15 @@
+def main(request, response):
+ if "url" in request.GET:
+ headers = [("Location", request.GET["url"])]
+ return 302, headers, ''
+
+ return [], '''
+<!DOCTYPE html>
+<script>
+ window.parent.postMessage(
+ {
+ id: 'last_url',
+ result: location.href
+ }, '*');
+</script>
+'''
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js
new file mode 100644
index 000000000..cb15b3ff1
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/navigation-redirect-worker.js
@@ -0,0 +1,75 @@
+// We store an empty response for each fetch event request we see
+// in this Cache object so we can get the list of urls in the
+// message event.
+var cacheName = 'urls-' + self.registration.scope;
+
+var waitUntilPromiseList = [];
+
+self.addEventListener('message', function(event) {
+ var urls;
+ event.waitUntil(Promise.all(waitUntilPromiseList).then(function() {
+ waitUntilPromiseList = [];
+ return caches.open(cacheName);
+ }).then(function(cache) {
+ return cache.keys();
+ }).then(function(requestList) {
+ urls = requestList.map(function(request) { return request.url; });
+ return caches.delete(cacheName);
+ }).then(function() {
+ event.data.port.postMessage({urls: urls});
+ }));
+ });
+
+function get_query_params(url) {
+ var search = (new URL(url)).search;
+ if (!search) {
+ return {};
+ }
+ var ret = {};
+ var params = search.substring(1).split('&');
+ params.forEach(function(param) {
+ var element = param.split('=');
+ ret[decodeURIComponent(element[0])] = decodeURIComponent(element[1]);
+ });
+ return ret;
+}
+
+self.addEventListener('fetch', function(event) {
+ var waitUntilPromise = caches.open(cacheName).then(function(cache) {
+ return cache.put(event.request, new Response());
+ });
+ event.waitUntil(waitUntilPromise);
+
+ var params = get_query_params(event.request.url);
+ if (!params['sw']) {
+ // To avoid races, add the waitUntil() promise to our global list.
+ // If we get a message event before we finish here, it will wait
+ // these promises to complete before proceeding to read from the
+ // cache.
+ waitUntilPromiseList.push(waitUntilPromise);
+ return;
+ }
+
+ event.respondWith(waitUntilPromise.then(function() {
+ if (params['sw'] == 'gen') {
+ return Response.redirect(params['url']);
+ } else if (params['sw'] == 'fetch') {
+ return fetch(event.request);
+ } else if (params['sw'] == 'opaque') {
+ return fetch(new Request(event.request.url, {redirect: 'manual'}));
+ } else if (params['sw'] == 'opaqueThroughCache') {
+ var url = event.request.url;
+ var cache;
+ return caches.delete(url)
+ .then(function() { return self.caches.open(url); })
+ .then(function(c) {
+ cache = c;
+ return fetch(new Request(url, {redirect: 'manual'}));
+ })
+ .then(function(res) { return cache.put(event.request, res); })
+ .then(function() { return cache.match(url); });
+ }
+
+ // unexpected... trigger an interception failure
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js
new file mode 100644
index 000000000..7c97014fd
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-from-nested-event-worker.js
@@ -0,0 +1,13 @@
+var max_nesting_level = 8;
+
+self.addEventListener('message', function(event) {
+ var level = event.data;
+ if (level < max_nesting_level)
+ dispatchEvent(new MessageEvent('message', { data: level + 1 }));
+ throw Error('error at level ' + level);
+ });
+
+self.addEventListener('activate', function(event) {
+ dispatchEvent(new MessageEvent('message', { data: 1 }));
+ });
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js
new file mode 100644
index 000000000..0bd9d318b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-cancel-worker.js
@@ -0,0 +1,3 @@
+self.onerror = function(event) { return true; };
+
+self.addEventListener('activate', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js
new file mode 100644
index 000000000..d56c95113
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-then-prevent-default-worker.js
@@ -0,0 +1,7 @@
+// Ensure we can handle multiple error handlers. One error handler
+// calling preventDefault should cause the event to be treated as
+// handled.
+self.addEventListener('error', function(event) {});
+self.addEventListener('error', function(event) { event.preventDefault(); });
+self.addEventListener('error', function(event) {});
+self.addEventListener('activate', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js
new file mode 100644
index 000000000..eb12ae862
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-with-empty-onerror-worker.js
@@ -0,0 +1,2 @@
+self.addEventListener('error', function(event) {});
+self.addEventListener('activate', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js
new file mode 100644
index 000000000..1e88ac5c4
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/onactivate-throw-error-worker.js
@@ -0,0 +1,7 @@
+// Ensure we can handle multiple activate handlers. One handler throwing an
+// error should cause the event dispatch to be treated as having unhandled
+// errors.
+self.addEventListener('activate', function(event) {});
+self.addEventListener('activate', function(event) {});
+self.addEventListener('activate', function(event) { throw new Error(); });
+self.addEventListener('activate', function(event) {});
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js
new file mode 100644
index 000000000..6729ab61a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-from-nested-event-worker.js
@@ -0,0 +1,12 @@
+var max_nesting_level = 8;
+
+self.addEventListener('message', function(event) {
+ var level = event.data;
+ if (level < max_nesting_level)
+ dispatchEvent(new MessageEvent('message', { data: level + 1 }));
+ throw Error('error at level ' + level);
+ });
+
+self.addEventListener('install', function(event) {
+ dispatchEvent(new MessageEvent('message', { data: 1 }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js
new file mode 100644
index 000000000..c2c499ab1
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-cancel-worker.js
@@ -0,0 +1,3 @@
+self.onerror = function(event) { return true; };
+
+self.addEventListener('install', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js
new file mode 100644
index 000000000..7667c2781
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-then-prevent-default-worker.js
@@ -0,0 +1,7 @@
+// Ensure we can handle multiple error handlers. One error handler
+// calling preventDefault should cause the event to be treated as
+// handled.
+self.addEventListener('error', function(event) {});
+self.addEventListener('error', function(event) { event.preventDefault(); });
+self.addEventListener('error', function(event) {});
+self.addEventListener('install', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js
new file mode 100644
index 000000000..8f56d1bf1
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-with-empty-onerror-worker.js
@@ -0,0 +1,2 @@
+self.addEventListener('error', function(event) {});
+self.addEventListener('install', function(event) { throw new Error(); });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js
new file mode 100644
index 000000000..cc2f6d7e5
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/oninstall-throw-error-worker.js
@@ -0,0 +1,7 @@
+// Ensure we can handle multiple install handlers. One handler throwing an
+// error should cause the event dispatch to be treated as having unhandled
+// errors.
+self.addEventListener('install', function(event) {});
+self.addEventListener('install', function(event) {});
+self.addEventListener('install', function(event) { throw new Error(); });
+self.addEventListener('install', function(event) {});
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/other.html b/testing/web-platform/tests/service-workers/service-worker/resources/other.html
new file mode 100644
index 000000000..b9f350438
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/other.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>Other</title>
+Here's an other html file.
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js b/testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js
new file mode 100644
index 000000000..835046d47
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/override_assert_object_equals.js
@@ -0,0 +1,58 @@
+// .body attribute of Request and Response object are experimental feture. It is
+// enabled when --enable-experimental-web-platform-features flag is set.
+// Touching this attribute can change the behavior of the objects. To avoid
+// touching it while comparing the objects in LayoutTest, we overwrite
+// assert_object_equals method.
+
+(function() {
+ var original_assert_object_equals = self.assert_object_equals;
+ function _brand(object) {
+ return Object.prototype.toString.call(object).match(/^\[object (.*)\]$/)[1];
+ }
+ var assert_request_equals = function(actual, expected, prefix) {
+ if (typeof actual !== 'object') {
+ assert_equals(actual, expected, prefix);
+ return;
+ }
+ assert_true(actual instanceof Request, prefix);
+ assert_true(expected instanceof Request, prefix);
+ assert_equals(actual.bodyUsed, expected.bodyUsed, prefix + '.bodyUsed');
+ assert_equals(actual.method, expected.method, prefix + '.method');
+ assert_equals(actual.url, expected.url, prefix + '.url');
+ original_assert_object_equals(actual.headers, expected.headers,
+ prefix + '.headers');
+ assert_equals(actual.context, expected.context, prefix + '.context');
+ assert_equals(actual.referrer, expected.referrer, prefix + '.referrer');
+ assert_equals(actual.mode, expected.mode, prefix + '.mode');
+ assert_equals(actual.credentials, expected.credentials,
+ prefix + '.credentials');
+ assert_equals(actual.cache, expected.cache, prefix + '.cache');
+ };
+ var assert_response_equals = function(actual, expected, prefix) {
+ if (typeof actual !== 'object') {
+ assert_equals(actual, expected, prefix);
+ return;
+ }
+ assert_true(actual instanceof Response, prefix);
+ assert_true(expected instanceof Response, prefix);
+ assert_equals(actual.bodyUsed, expected.bodyUsed, prefix + '.bodyUsed');
+ assert_equals(actual.type, expected.type, prefix + '.type');
+ assert_equals(actual.url, expected.url, prefix + '.url');
+ assert_equals(actual.status, expected.status, prefix + '.status');
+ assert_equals(actual.statusText, expected.statusText,
+ prefix + '.statusText');
+ original_assert_object_equals(actual.headers, expected.headers,
+ prefix + '.headers');
+ };
+ var assert_object_equals = function(actual, expected, description) {
+ var prefix = (description ? description + ': ' : '') + _brand(expected);
+ if (expected instanceof Request) {
+ assert_request_equals(actual, expected, prefix);
+ } else if (expected instanceof Response) {
+ assert_response_equals(actual, expected, prefix);
+ } else {
+ original_assert_object_equals(actual, expected, description);
+ }
+ };
+ self.assert_object_equals = assert_object_equals;
+})();
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/performance-timeline-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/performance-timeline-worker.js
new file mode 100644
index 000000000..6f7df75e9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/performance-timeline-worker.js
@@ -0,0 +1,58 @@
+importScripts('/resources/testharness.js');
+
+promise_test(function(test) {
+ var durationMsec = 100;
+ // There are limits to our accuracy here. Timers may fire up to a
+ // millisecond early due to platform-dependent rounding. In addition
+ // the performance API introduces some rounding as well to prevent
+ // timing attacks.
+ var accuracy = 1.5;
+ return new Promise(function(resolve) {
+ performance.mark('startMark');
+ setTimeout(resolve, durationMsec);
+ }).then(function() {
+ performance.mark('endMark');
+ performance.measure('measure', 'startMark', 'endMark');
+ var startMark = performance.getEntriesByName('startMark')[0];
+ var endMark = performance.getEntriesByName('endMark')[0];
+ var measure = performance.getEntriesByType('measure')[0];
+ assert_equals(measure.startTime, startMark.startTime);
+ assert_approx_equals(endMark.startTime - startMark.startTime,
+ measure.duration, 0.001);
+ assert_greater_than(measure.duration, durationMsec - accuracy);
+ assert_equals(performance.getEntriesByType('mark').length, 2);
+ assert_equals(performance.getEntriesByType('measure').length, 1);
+ performance.clearMarks('startMark');
+ performance.clearMeasures('measure');
+ assert_equals(performance.getEntriesByType('mark').length, 1);
+ assert_equals(performance.getEntriesByType('measure').length, 0);
+ });
+ }, 'User Timing');
+
+promise_test(function(test) {
+ return fetch('dummy.txt')
+ .then(function(resp) {
+ return resp.text();
+ })
+ .then(function(text) {
+ var expectedResources = ['testharness.js', 'dummy.txt'];
+ assert_equals(performance.getEntriesByType('resource').length, expectedResources.length);
+ for (var i = 0; i < expectedResources.length; i++) {
+ var entry = performance.getEntriesByType('resource')[i];
+ assert_true(entry.name.endsWith(expectedResources[i]));
+ assert_equals(entry.workerStart, 0);
+ assert_greater_than(entry.startTime, 0);
+ assert_greater_than(entry.responseEnd, entry.startTime);
+ }
+ return new Promise(function(resolve) {
+ performance.onresourcetimingbufferfull = resolve;
+ performance.setResourceTimingBufferSize(expectedResources.length);
+ });
+ })
+ .then(function() {
+ performance.clearResourceTimings();
+ assert_equals(performance.getEntriesByType('resource').length, 0);
+ })
+ }, 'Resource Timing');
+
+done();
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js
new file mode 100644
index 000000000..3475321f4
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-msgport-to-client-worker.js
@@ -0,0 +1,20 @@
+self.onmessage = function(e) {
+ self.clients.matchAll().then(function(clients) {
+ clients.forEach(function(client) {
+ var messageChannel = new MessageChannel();
+ messageChannel.port1.onmessage =
+ onMessageViaMessagePort.bind(null, client);
+ client.postMessage({port: messageChannel.port2},
+ [messageChannel.port2]);
+ });
+ });
+};
+
+function onMessageViaMessagePort(client, e) {
+ var message = e.data;
+ if ('value' in message) {
+ client.postMessage({ack: 'Acking value: ' + message.value});
+ } else if ('done' in message) {
+ client.postMessage({done: true});
+ }
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js
new file mode 100644
index 000000000..290a4a9b3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-to-client-worker.js
@@ -0,0 +1,10 @@
+self.onmessage = function(e) {
+ self.clients.matchAll().then(function(clients) {
+ clients.forEach(function(client) {
+ client.postMessage('Sending message via clients');
+ if (!Array.isArray(clients))
+ client.postMessage('clients is not an array');
+ client.postMessage('quit');
+ });
+ });
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js
new file mode 100644
index 000000000..858cf0426
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/postmessage-worker.js
@@ -0,0 +1,19 @@
+var port;
+
+// Exercise the 'onmessage' handler:
+self.onmessage = function(e) {
+ var message = e.data;
+ if ('port' in message) {
+ port = message.port;
+ }
+};
+
+// And an event listener:
+self.addEventListener('message', function(e) {
+ var message = e.data;
+ if ('value' in message) {
+ port.postMessage('Acking value: ' + message.value);
+ } else if ('done' in message) {
+ port.postMessage('quit');
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/redirect.py b/testing/web-platform/tests/service-workers/service-worker/resources/redirect.py
new file mode 100644
index 000000000..20521b00c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/redirect.py
@@ -0,0 +1,25 @@
+def main(request, response):
+ if 'Status' in request.GET:
+ status = int(request.GET["Status"])
+ else:
+ status = 302
+
+ headers = []
+
+ url = request.GET['Redirect']
+ headers.append(("Location", url))
+
+ if "ACAOrigin" in request.GET:
+ for item in request.GET["ACAOrigin"].split(","):
+ headers.append(("Access-Control-Allow-Origin", item))
+
+ for suffix in ["Headers", "Methods", "Credentials"]:
+ query = "ACA%s" % suffix
+ header = "Access-Control-Allow-%s" % suffix
+ if query in request.GET:
+ headers.append((header, request.GET[query]))
+
+ if "ACEHeaders" in request.GET:
+ headers.append(("Access-Control-Expose-Headers", request.GET["ACEHeaders"]))
+
+ return status, headers, ""
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
new file mode 100644
index 000000000..491262e9d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/referer-iframe.html
@@ -0,0 +1,39 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js"></script>
+<script>
+function check_referer(url, expected_referer) {
+ return fetch(url)
+ .then(function(res) { return res.json(); })
+ .then(function(headers) {
+ if (headers['referer'] === expected_referer) {
+ return Promise.resolve();
+ } else {
+ return Promise.reject('Referer for ' + url + ' must be ' +
+ expected_referer + ' but got ' +
+ headers['referer']);
+ }
+ });
+}
+
+window.addEventListener('message', function(evt) {
+ var host_info = get_host_info();
+ var port = evt.ports[0];
+ check_referer('request-headers.py?ignore=true',
+ host_info['HTTPS_ORIGIN'] +
+ base_path() + 'referer-iframe.html')
+ .then(function() {
+ return check_referer(
+ 'request-headers.py',
+ host_info['HTTPS_ORIGIN'] +
+ base_path() + 'referer-iframe.html');
+ })
+ .then(function() {
+ return check_referer(
+ 'request-headers.py?url=request-headers.py',
+ host_info['HTTPS_ORIGIN'] +
+ base_path() + 'fetch-rewrite-worker.js');
+ })
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html
new file mode 100644
index 000000000..ed743ea05
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/register-closed-window-iframe.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<script>
+window.addEventListener('message', function(evt) {
+ if (evt.data === 'START') {
+ var w = window.open('./');
+ var sw = w.navigator.serviceWorker;
+ w.close();
+ w = null;
+ sw.register('doesntmatter.js');
+ parent.postMessage('OK', '*');
+ }
+});
+</script>
+</head>
+</html>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js
new file mode 100644
index 000000000..44d1d2774
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/registration-worker.js
@@ -0,0 +1 @@
+// empty for now
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js
new file mode 100644
index 000000000..41f07fd5d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/reject-install-worker.js
@@ -0,0 +1,3 @@
+self.oninstall = function(event) {
+ event.waitUntil(Promise.reject());
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js
new file mode 100644
index 000000000..323c7f243
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/request-end-to-end-worker.js
@@ -0,0 +1,32 @@
+var port = undefined;
+
+onmessage = function(e) {
+ var message = e.data;
+ if (typeof message === 'object' && 'port' in message) {
+ port = message.port;
+ }
+};
+
+onfetch = function(e) {
+ var headers = {};
+ var errorNameWhileAppendingHeader;
+ for (var header of e.request.headers) {
+ var key = header[0], value = header[1];
+ headers[key] = value;
+ }
+ var errorNameWhileAddingHeader = '';
+ try {
+ e.request.headers.append('Test-Header', 'TestValue');
+ } catch (e) {
+ errorNameWhileAppendingHeader = e.name;
+ }
+ port.postMessage({
+ url: e.request.url,
+ mode: e.request.mode,
+ method: e.request.method,
+ referrer: e.request.referrer,
+ headers: headers,
+ headerSize: e.request.headers.size,
+ errorNameWhileAppendingHeader: errorNameWhileAppendingHeader
+ });
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/request-headers.py b/testing/web-platform/tests/service-workers/service-worker/resources/request-headers.py
new file mode 100644
index 000000000..29897f4ec
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/request-headers.py
@@ -0,0 +1,6 @@
+import json
+
+def main(request, response):
+ data = {key:request.headers[key] for key,value in request.headers.iteritems()}
+
+ return [("Content-Type", "application/json")], json.dumps(data)
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html
new file mode 100644
index 000000000..2af679369
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script src="empty.js"></script>
+<script src="dummy.js"></script>
+<script src="redirect.py?Redirect=empty.js"></script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js
new file mode 100644
index 000000000..481a6536a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/resource-timing-worker.js
@@ -0,0 +1,5 @@
+self.addEventListener('fetch', function(event) {
+ if (event.request.url.indexOf('dummy.js') != -1) {
+ event.respondWith(new Response());
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/service-worker-csp-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/service-worker-csp-worker.py
new file mode 100644
index 000000000..4e5c6f3b6
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/service-worker-csp-worker.py
@@ -0,0 +1,153 @@
+bodyDefault = '''
+importScripts('worker-testharness.js');
+importScripts('test-helpers.sub.js');
+importScripts('../resources/get-host-info.sub.js');
+
+var host_info = get_host_info();
+
+test(function() {
+ var import_script_failed = false;
+ try {
+ importScripts(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'empty.js');
+ } catch(e) {
+ import_script_failed = true;
+ }
+ assert_true(import_script_failed,
+ 'Importing the other origins script should fail.');
+ }, 'importScripts test for default-src');
+
+async_test(function(t) {
+ fetch(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?ACAOrigin=*',
+ {mode: 'cors'})
+ .then(function(response){
+ assert_unreached('fetch should fail.');
+ }, function(){
+ t.done();
+ })
+ .catch(unreached_rejection(t));
+ }, 'Fetch test for default-src');
+
+async_test(function(t) {
+ var REDIRECT_URL = host_info.HTTPS_ORIGIN +
+ base_path() + 'redirect.py?Redirect=';
+ var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?'
+ fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+ {mode: 'cors'})
+ .then(function(response){
+ assert_unreached('Redirected fetch should fail.');
+ }, function(){
+ t.done();
+ })
+ .catch(unreached_rejection(t));
+ }, 'Redirected fetch test for default-src');'''
+
+bodyScript = '''
+importScripts('worker-testharness.js');
+importScripts('test-helpers.sub.js');
+importScripts('../resources/get-host-info.sub.js');
+
+var host_info = get_host_info();
+
+test(function() {
+ var import_script_failed = false;
+ try {
+ importScripts(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'empty.js');
+ } catch(e) {
+ import_script_failed = true;
+ }
+ assert_true(import_script_failed,
+ 'Importing the other origins script should fail.');
+ }, 'importScripts test for script-src');
+
+async_test(function(t) {
+ fetch(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?ACAOrigin=*',
+ {mode: 'cors'})
+ .then(function(response){
+ t.done();
+ }, function(){
+ assert_unreached('fetch should not fail.');
+ })
+ .catch(unreached_rejection(t));
+ }, 'Fetch test for script-src');
+
+async_test(function(t) {
+ var REDIRECT_URL = host_info.HTTPS_ORIGIN +
+ base_path() + 'redirect.py?Redirect=';
+ var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?'
+ fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+ {mode: 'cors'})
+ .then(function(response){
+ t.done();
+ }, function(){
+ assert_unreached('Redirected fetch should not fail.');
+ })
+ .catch(unreached_rejection(t));
+ }, 'Redirected fetch test for script-src');'''
+
+bodyConnect = '''
+importScripts('worker-testharness.js');
+importScripts('test-helpers.sub.js');
+importScripts('../resources/get-host-info.sub.js');
+
+var host_info = get_host_info();
+
+test(function() {
+ var import_script_failed = false;
+ try {
+ importScripts(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'empty.js');
+ } catch(e) {
+ import_script_failed = true;
+ }
+ assert_false(import_script_failed,
+ 'Importing the other origins script should not fail.');
+ }, 'importScripts test for connect-src');
+
+async_test(function(t) {
+ fetch(host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?ACAOrigin=*',
+ {mode: 'cors'})
+ .then(function(response){
+ assert_unreached('fetch should fail.');
+ }, function(){
+ t.done();
+ })
+ .catch(unreached_rejection(t));
+ }, 'Fetch test for connect-src');
+
+async_test(function(t) {
+ var REDIRECT_URL = host_info.HTTPS_ORIGIN +
+ base_path() + 'redirect.py?Redirect=';
+ var OTHER_BASE_URL = host_info.HTTPS_REMOTE_ORIGIN +
+ base_path() + 'fetch-access-control.py?'
+ fetch(REDIRECT_URL + encodeURIComponent(OTHER_BASE_URL + 'ACAOrigin=*'),
+ {mode: 'cors'})
+ .then(function(response){
+ assert_unreached('Redirected fetch should fail.');
+ }, function(){
+ t.done();
+ })
+ .catch(unreached_rejection(t));
+ }, 'Redirected fetch test for connect-src');'''
+
+def main(request, response):
+ headers = []
+ headers.append(('Content-Type', 'application/javascript'))
+ directive = request.GET['directive']
+ body = 'ERROR: Unknown directive'
+ if directive == 'default':
+ headers.append(('Content-Security-Policy', "default-src 'self'"))
+ body = bodyDefault
+ elif directive == 'script':
+ headers.append(('Content-Security-Policy', "script-src 'self'"))
+ body = bodyScript
+ elif directive == 'connect':
+ headers.append(('Content-Security-Policy', "connect-src 'self'"))
+ body = bodyConnect
+ return headers, body
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js
new file mode 100644
index 000000000..1ccc2fe3b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-controlled.js
@@ -0,0 +1,8 @@
+onconnect = function(e) {
+ var port = e.ports[0];
+ var xhr = new XMLHttpRequest();
+ xhr.onload = function() { port.postMessage(this.responseText); };
+ xhr.onerror = function(e) { port.postMessage(e); };
+ xhr.open('GET', 'dummy.txt?simple', true);
+ xhr.send();
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js
new file mode 100644
index 000000000..7c554bd74
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/shared-worker-import.js
@@ -0,0 +1 @@
+importScripts('import-dummy-shared-worker.js');
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/silence.oga b/testing/web-platform/tests/service-workers/service-worker/resources/silence.oga
new file mode 100644
index 000000000..af5918804
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/silence.oga
Binary files differ
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js
new file mode 100644
index 000000000..f8b5f8c5c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/simple-intercept-worker.js
@@ -0,0 +1,5 @@
+self.onfetch = function(event) {
+ if (event.request.url.indexOf('simple') != -1)
+ event.respondWith(
+ new Response(new Blob(['intercepted by service worker'])));
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/simple.html b/testing/web-platform/tests/service-workers/service-worker/resources/simple.html
new file mode 100644
index 000000000..0c3e3e787
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/simple.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<title>Simple</title>
+Here's a simple html file.
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/simple.txt b/testing/web-platform/tests/service-workers/service-worker/resources/simple.txt
new file mode 100644
index 000000000..9e3cb91fb
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/simple.txt
@@ -0,0 +1 @@
+a simple text file
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
new file mode 100644
index 000000000..bf582c770
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-installed-worker.js
@@ -0,0 +1,24 @@
+self.state = 'starting';
+
+self.addEventListener('install', function() {
+ self.state = 'installing';
+ });
+
+self.addEventListener('message', function(event) {
+ var port = event.data.port;
+ if (self.state !== 'installing') {
+ port.postMessage('FAIL: Worker should be waiting in installed state');
+ return;
+ }
+ self.skipWaiting()
+ .then(function(result) {
+ if (result !== undefined) {
+ port.postMessage('FAIL: Promise should be resolved with undefined');
+ return;
+ }
+ port.postMessage('PASS');
+ })
+ .catch(function(e) {
+ port.postMessage('FAIL: unexpected exception: ' + e);
+ });
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js
new file mode 100644
index 000000000..3fc1d1e23
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/skip-waiting-worker.js
@@ -0,0 +1,21 @@
+importScripts('worker-testharness.js');
+
+promise_test(function() {
+ return skipWaiting()
+ .then(function(result) {
+ assert_equals(result, undefined,
+ 'Promise should be resolved with undefined');
+ })
+ .then(function() {
+ var promises = [];
+ for (var i = 0; i < 8; ++i)
+ promises.push(self.skipWaiting());
+ return Promise.all(promises);
+ })
+ .then(function(results) {
+ results.forEach(function(r) {
+ assert_equals(r, undefined,
+ 'Promises should be resolved with undefined');
+ });
+ });
+ }, 'skipWaiting');
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/square.png b/testing/web-platform/tests/service-workers/service-worker/resources/square.png
new file mode 100644
index 000000000..01c9666a8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/square.png
Binary files differ
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/success.py b/testing/web-platform/tests/service-workers/service-worker/resources/success.py
new file mode 100644
index 000000000..bcbb487d2
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/success.py
@@ -0,0 +1,8 @@
+def main(request, response):
+ headers = []
+
+ if "ACAOrigin" in request.GET:
+ for item in request.GET["ACAOrigin"].split(","):
+ headers.append(("Access-Control-Allow-Origin", item))
+
+ return headers, "{ \"result\": \"success\" }"
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js b/testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js
new file mode 100644
index 000000000..b0ffbd406
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/test-helpers.sub.js
@@ -0,0 +1,227 @@
+// Adapter for testharness.js-style tests with Service Workers
+
+function service_worker_unregister_and_register(test, url, scope) {
+ if (!scope || scope.length == 0)
+ return Promise.reject(new Error('tests must define a scope'));
+
+ var options = { scope: scope };
+ return service_worker_unregister(test, scope)
+ .then(function() {
+ return navigator.serviceWorker.register(url, options);
+ })
+ .catch(unreached_rejection(test,
+ 'unregister and register should not fail'));
+}
+
+// This unregisters the registration that precisely matches scope. Use this
+// when unregistering by scope. If no registration is found, it just resolves.
+function service_worker_unregister(test, scope) {
+ var absoluteScope = (new URL(scope, window.location).href);
+ return navigator.serviceWorker.getRegistration(scope)
+ .then(function(registration) {
+ if (registration && registration.scope === absoluteScope)
+ return registration.unregister();
+ })
+ .catch(unreached_rejection(test, 'unregister should not fail'));
+}
+
+function service_worker_unregister_and_done(test, scope) {
+ return service_worker_unregister(test, scope)
+ .then(test.done.bind(test));
+}
+
+function unreached_fulfillment(test, prefix) {
+ return test.step_func(function(result) {
+ var error_prefix = prefix || 'unexpected fulfillment';
+ assert_unreached(error_prefix + ': ' + result);
+ });
+}
+
+// Rejection-specific helper that provides more details
+function unreached_rejection(test, prefix) {
+ return test.step_func(function(error) {
+ var reason = error.message || error.name || error;
+ var error_prefix = prefix || 'unexpected rejection';
+ assert_unreached(error_prefix + ': ' + reason);
+ });
+}
+
+// Adds an iframe to the document and returns a promise that resolves to the
+// iframe when it finishes loading. The caller is responsible for removing the
+// iframe later if needed.
+function with_iframe(url) {
+ return new Promise(function(resolve) {
+ var frame = document.createElement('iframe');
+ frame.src = url;
+ frame.onload = function() { resolve(frame); };
+ document.body.appendChild(frame);
+ });
+}
+
+function normalizeURL(url) {
+ return new URL(url, self.location).toString().replace(/#.*$/, '');
+}
+
+function wait_for_update(test, registration) {
+ if (!registration || registration.unregister == undefined) {
+ return Promise.reject(new Error(
+ 'wait_for_update must be passed a ServiceWorkerRegistration'));
+ }
+
+ return new Promise(test.step_func(function(resolve) {
+ registration.addEventListener('updatefound', test.step_func(function() {
+ resolve(registration.installing);
+ }));
+ }));
+}
+
+function wait_for_state(test, worker, state) {
+ if (!worker || worker.state == undefined) {
+ return Promise.reject(new Error(
+ 'wait_for_state must be passed a ServiceWorker'));
+ }
+ if (worker.state === state)
+ return Promise.resolve(state);
+
+ if (state === 'installing') {
+ switch (worker.state) {
+ case 'installed':
+ case 'activating':
+ case 'activated':
+ case 'redundant':
+ return Promise.reject(new Error(
+ 'worker is ' + worker.state + ' but waiting for ' + state));
+ }
+ }
+
+ if (state === 'installed') {
+ switch (worker.state) {
+ case 'activating':
+ case 'activated':
+ case 'redundant':
+ return Promise.reject(new Error(
+ 'worker is ' + worker.state + ' but waiting for ' + state));
+ }
+ }
+
+ if (state === 'activating') {
+ switch (worker.state) {
+ case 'activated':
+ case 'redundant':
+ return Promise.reject(new Error(
+ 'worker is ' + worker.state + ' but waiting for ' + state));
+ }
+ }
+
+ if (state === 'activated') {
+ switch (worker.state) {
+ case 'redundant':
+ return Promise.reject(new Error(
+ 'worker is ' + worker.state + ' but waiting for ' + state));
+ }
+ }
+
+ return new Promise(test.step_func(function(resolve) {
+ worker.addEventListener('statechange', test.step_func(function() {
+ if (worker.state === state)
+ resolve(state);
+ }));
+ }));
+}
+
+// Declare a test that runs entirely in the ServiceWorkerGlobalScope. The |url|
+// is the service worker script URL. This function:
+// - Instantiates a new test with the description specified in |description|.
+// The test will succeed if the specified service worker can be successfully
+// registered and installed.
+// - Creates a new ServiceWorker registration with a scope unique to the current
+// document URL. Note that this doesn't allow more than one
+// service_worker_test() to be run from the same document.
+// - Waits for the new worker to begin installing.
+// - Imports tests results from tests running inside the ServiceWorker.
+function service_worker_test(url, description) {
+ // If the document URL is https://example.com/document and the script URL is
+ // https://example.com/script/worker.js, then the scope would be
+ // https://example.com/script/scope/document.
+ var scope = new URL('scope' + window.location.pathname,
+ new URL(url, window.location)).toString();
+ promise_test(function(test) {
+ return service_worker_unregister_and_register(test, url, scope)
+ .then(function(registration) {
+ add_completion_callback(function() {
+ registration.unregister();
+ });
+ return wait_for_update(test, registration)
+ .then(function(worker) {
+ return fetch_tests_from_worker(worker);
+ });
+ });
+ }, description);
+}
+
+function base_path() {
+ return location.pathname.replace(/\/[^\/]*$/, '/');
+}
+
+function test_login(test, origin, username, password, cookie) {
+ return new Promise(function(resolve, reject) {
+ with_iframe(
+ origin + base_path() +
+ 'resources/fetch-access-control-login.html')
+ .then(test.step_func(function(frame) {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = test.step_func(function() {
+ frame.remove();
+ resolve();
+ });
+ frame.contentWindow.postMessage(
+ {username: username, password: password, cookie: cookie},
+ origin, [channel.port2]);
+ }));
+ });
+}
+
+function test_websocket(test, frame, url) {
+ return new Promise(function(resolve, reject) {
+ var ws = new frame.contentWindow.WebSocket(url, ['echo', 'chat']);
+ var openCalled = false;
+ ws.addEventListener('open', test.step_func(function(e) {
+ assert_equals(ws.readyState, 1, "The WebSocket should be open");
+ openCalled = true;
+ ws.close();
+ }), true);
+
+ ws.addEventListener('close', test.step_func(function(e) {
+ assert_true(openCalled, "The WebSocket should be closed after being opened");
+ resolve();
+ }), true);
+
+ ws.addEventListener('error', reject);
+ });
+}
+
+function login(test) {
+ return test_login(test, 'http://{{domains[www1]}}:{{ports[http][0]}}',
+ 'username1', 'password1', 'cookie1')
+ .then(function() {
+ return test_login(test, 'http://{{host}}:{{ports[http][0]}}',
+ 'username2', 'password2', 'cookie2');
+ });
+}
+
+function login_https(test) {
+ return test_login(test, 'https://{{domains[www1]}}:{{ports[https][0]}}',
+ 'username1s', 'password1s', 'cookie1')
+ .then(function() {
+ return test_login(test, 'https://{{host}}:{{ports[https][0]}}',
+ 'username2s', 'password2s', 'cookie2');
+ });
+}
+
+function websocket(test, frame) {
+ return test_websocket(test, frame, get_websocket_url());
+}
+
+function get_websocket_url() {
+ return 'wss://{{host}}:{{ports[wss][0]}}/echo';
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js b/testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js
new file mode 100644
index 000000000..4d7af1ff9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/testharness-helpers.js
@@ -0,0 +1,163 @@
+/*
+ * testharness-helpers contains various useful extensions to testharness.js to
+ * allow them to be used across multiple tests before they have been
+ * upstreamed. This file is intended to be usable from both document and worker
+ * environments, so code should for example not rely on the DOM.
+ */
+
+// Returns a promise that fulfills after the provided |promise| is fulfilled.
+// The |test| succeeds only if |promise| rejects with an exception matching
+// |code|. Accepted values for |code| follow those accepted for assert_throws().
+// The optional |description| describes the test being performed.
+//
+// E.g.:
+// assert_promise_rejects(
+// new Promise(...), // something that should throw an exception.
+// 'NotFoundError',
+// 'Should throw NotFoundError.');
+//
+// assert_promise_rejects(
+// new Promise(...),
+// new TypeError(),
+// 'Should throw TypeError');
+function assert_promise_rejects(promise, code, description) {
+ return promise.then(
+ function() {
+ throw 'assert_promise_rejects: ' + description + ' Promise did not reject.';
+ },
+ function(e) {
+ if (code !== undefined) {
+ assert_throws(code, function() { throw e; }, description);
+ }
+ });
+}
+
+// Asserts that two objects |actual| and |expected| are weakly equal under the
+// following definition:
+//
+// |a| and |b| are weakly equal if any of the following are true:
+// 1. If |a| is not an 'object', and |a| === |b|.
+// 2. If |a| is an 'object', and all of the following are true:
+// 2.1 |a.p| is weakly equal to |b.p| for all own properties |p| of |a|.
+// 2.2 Every own property of |b| is an own property of |a|.
+//
+// This is a replacement for the the version of assert_object_equals() in
+// testharness.js. The latter doesn't handle own properties correctly. I.e. if
+// |a.p| is not an own property, it still requires that |b.p| be an own
+// property.
+//
+// Note that |actual| must not contain cyclic references.
+self.assert_object_equals = function(actual, expected, description) {
+ var object_stack = [];
+
+ function _is_equal(actual, expected, prefix) {
+ if (typeof actual !== 'object') {
+ assert_equals(actual, expected, prefix);
+ return;
+ }
+ assert_true(typeof expected === 'object', prefix);
+ assert_equals(object_stack.indexOf(actual), -1,
+ prefix + ' must not contain cyclic references.');
+
+ object_stack.push(actual);
+
+ Object.getOwnPropertyNames(expected).forEach(function(property) {
+ assert_own_property(actual, property, prefix);
+ _is_equal(actual[property], expected[property],
+ prefix + '.' + property);
+ });
+ Object.getOwnPropertyNames(actual).forEach(function(property) {
+ assert_own_property(expected, property, prefix);
+ });
+
+ object_stack.pop();
+ }
+
+ function _brand(object) {
+ return Object.prototype.toString.call(object).match(/^\[object (.*)\]$/)[1];
+ }
+
+ _is_equal(actual, expected,
+ (description ? description + ': ' : '') + _brand(expected));
+};
+
+// Equivalent to assert_in_array, but uses a weaker equivalence relation
+// (assert_object_equals) than '==='.
+function assert_object_in_array(actual, expected_array, description) {
+ assert_true(expected_array.some(function(element) {
+ try {
+ assert_object_equals(actual, element);
+ return true;
+ } catch (e) {
+ return false;
+ }
+ }), description);
+}
+
+// Assert that the two arrays |actual| and |expected| contain the same set of
+// elements as determined by assert_object_equals. The order is not significant.
+//
+// |expected| is assumed to not contain any duplicates as determined by
+// assert_object_equals().
+function assert_array_equivalent(actual, expected, description) {
+ assert_true(Array.isArray(actual), description);
+ assert_equals(actual.length, expected.length, description);
+ expected.forEach(function(expected_element) {
+ // assert_in_array treats the first argument as being 'actual', and the
+ // second as being 'expected array'. We are switching them around because
+ // we want to be resilient against the |actual| array containing
+ // duplicates.
+ assert_object_in_array(expected_element, actual, description);
+ });
+}
+
+// Asserts that two arrays |actual| and |expected| contain the same set of
+// elements as determined by assert_object_equals(). The corresponding elements
+// must occupy corresponding indices in their respective arrays.
+function assert_array_objects_equals(actual, expected, description) {
+ assert_true(Array.isArray(actual), description);
+ assert_equals(actual.length, expected.length, description);
+ actual.forEach(function(value, index) {
+ assert_object_equals(value, expected[index],
+ description + ' : object[' + index + ']');
+ });
+}
+
+// Asserts that |object| that is an instance of some interface has the attribute
+// |attribute_name| following the conditions specified by WebIDL, but it's
+// acceptable that the attribute |attribute_name| is an own property of the
+// object because we're in the middle of moving the attribute to a prototype
+// chain. Once we complete the transition to prototype chains,
+// assert_will_be_idl_attribute must be replaced with assert_idl_attribute
+// defined in testharness.js.
+//
+// FIXME: Remove assert_will_be_idl_attribute once we complete the transition
+// of moving the DOM attributes to prototype chains. (http://crbug.com/43394)
+function assert_will_be_idl_attribute(object, attribute_name, description) {
+ assert_true(typeof object === "object", description);
+
+ assert_true("hasOwnProperty" in object, description);
+
+ // Do not test if |attribute_name| is not an own property because
+ // |attribute_name| is in the middle of the transition to a prototype
+ // chain. (http://crbug.com/43394)
+
+ assert_true(attribute_name in object, description);
+}
+
+// Stringifies a DOM object. This function stringifies not only own properties
+// but also DOM attributes which are on a prototype chain. Note that
+// JSON.stringify only stringifies own properties.
+function stringifyDOMObject(object)
+{
+ function deepCopy(src) {
+ if (typeof src != "object")
+ return src;
+ var dst = Array.isArray(src) ? [] : {};
+ for (var property in src) {
+ dst[property] = deepCopy(src[property]);
+ }
+ return dst;
+ }
+ return JSON.stringify(deepCopy(object));
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html b/testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html
new file mode 100644
index 000000000..18a95ee89
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/unregister-controller-page.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<script>
+function fetch_url(url) {
+ return new Promise(function(resolve, reject) {
+ var request = new XMLHttpRequest();
+ request.addEventListener('load', function(event) {
+ if (request.status == 200)
+ resolve(request.response);
+ else
+ reject(Error(request.statusText));
+ });
+ request.open('GET', url);
+ request.send();
+ });
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py
new file mode 100644
index 000000000..0f09b7e32
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/update-nocookie-worker.py
@@ -0,0 +1,15 @@
+import time
+
+def main(request, response):
+ # no-cache itself to ensure the user agent finds a new version for each update.
+ headers = [('Cache-Control', 'no-cache, must-revalidate'),
+ ('Pragma', 'no-cache')]
+
+ # Set a normal mimetype.
+ content_type = 'application/javascript'
+
+ headers.append(('Content-Type', content_type))
+ # Return a different script for each access. Use .time() and .clock() for
+ # best time resolution across different platforms.
+ return headers, '// %s %s' % (time.time(), time.clock())
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/update-recovery-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-recovery-worker.py
new file mode 100644
index 000000000..8aaa5ca93
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/update-recovery-worker.py
@@ -0,0 +1,25 @@
+def main(request, response):
+ # Set mode to 'init' for initial fetch.
+ mode = 'init'
+ if 'update-recovery-mode' in request.cookies:
+ mode = request.cookies['update-recovery-mode'].value
+
+ # no-cache itself to ensure the user agent finds a new version for each update.
+ headers = [('Cache-Control', 'no-cache, must-revalidate'),
+ ('Pragma', 'no-cache')]
+
+ extra_body = ''
+
+ if mode == 'init':
+ # Install a bad service worker that will break the controlled
+ # document navigation.
+ response.set_cookie('update-recovery-mode', 'bad')
+ extra_body = "addEventListener('fetch', function(e) { e.respondWith(Promise.reject()); });"
+ elif mode == 'bad':
+ # When the update tries to pull the script again, update to
+ # a worker service worker that does not break document
+ # navigation. Serve the same script from then on.
+ response.delete_cookie('update-recovery-mode')
+
+ headers.append(('Content-Type', 'application/javascript'))
+ return headers, '%s' % (extra_body)
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py b/testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py
new file mode 100644
index 000000000..bc9b32ad3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/update-worker.py
@@ -0,0 +1,46 @@
+import time
+
+def main(request, response):
+ # Set mode to 'init' for initial fetch.
+ mode = 'init'
+ if 'mode' in request.cookies:
+ mode = request.cookies['mode'].value
+
+ # no-cache itself to ensure the user agent finds a new version for each update.
+ headers = [('Cache-Control', 'no-cache, must-revalidate'),
+ ('Pragma', 'no-cache')]
+
+ content_type = ''
+ extra_body = ''
+
+ if mode == 'init':
+ # Set a normal mimetype.
+ # Set cookie value to 'normal' so the next fetch will work in 'normal' mode.
+ content_type = 'application/javascript'
+ response.set_cookie('mode', 'normal')
+ elif mode == 'normal':
+ # Set a normal mimetype.
+ # Set cookie value to 'error' so the next fetch will work in 'error' mode.
+ content_type = 'application/javascript'
+ response.set_cookie('mode', 'error');
+ elif mode == 'error':
+ # Set a disallowed mimetype.
+ # Set cookie value to 'syntax-error' so the next fetch will work in 'syntax-error' mode.
+ content_type = 'text/html'
+ response.set_cookie('mode', 'syntax-error');
+ elif mode == 'syntax-error':
+ # Set cookie value to 'throw-install' so the next fetch will work in 'throw-install' mode.
+ content_type = 'application/javascript'
+ response.set_cookie('mode', 'throw-install');
+ extra_body = 'badsyntax(isbad;'
+ elif mode == 'throw-install':
+ # Unset and delete cookie to clean up the test setting.
+ content_type = 'application/javascript'
+ response.delete_cookie('mode')
+ extra_body = "addEventListener('install', function(e) { throw new Error('boom'); });"
+
+ headers.append(('Content-Type', content_type))
+ # Return a different script for each access. Use .time() and .clock() for
+ # best time resolution across different platforms.
+ return headers, '/* %s %s */ %s' % (time.time(), time.clock(), extra_body)
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html
new file mode 100644
index 000000000..9d4c98272
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/update/update-after-oneday.https.html
@@ -0,0 +1,8 @@
+<body>
+<script>
+function load_image(url) {
+ var img = document.createElement('img');
+ img.src = url;
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js b/testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js
new file mode 100644
index 000000000..af85a73ad
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/wait-forever-in-install-worker.js
@@ -0,0 +1,12 @@
+var waitUntilResolve;
+self.addEventListener('install', function(event) {
+ event.waitUntil(new Promise(function(resolve) {
+ waitUntilResolve = resolve;
+ }));
+ });
+
+self.addEventListener('message', function(event) {
+ if (event.data === 'STOP_WAITING') {
+ waitUntilResolve();
+ }
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/websocket.js b/testing/web-platform/tests/service-workers/service-worker/resources/websocket.js
new file mode 100644
index 000000000..fc6abd283
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/websocket.js
@@ -0,0 +1,7 @@
+self.urls = [];
+self.addEventListener('fetch', function(event) {
+ self.urls.push(event.request.url);
+ });
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage({urls: self.urls});
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html
new file mode 100644
index 000000000..12a461ea5
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/worker-interception-iframe.https.html
@@ -0,0 +1,39 @@
+<script src="../resources/get-host-info.sub.js"></script>
+<script src="test-helpers.sub.js?pipe=sub"></script>
+<script>
+var host_info = get_host_info();
+
+function boilerplate_test(msg) {
+ return new Promise(function(resolve, reject) {
+ var worker = new Worker("load_worker.js");
+ worker.onmessage = function(e) { resolve(e.data) };
+ worker.onerror = function(e) { reject(e) };
+ worker.postMessage(msg);
+ })
+ .then(function(data) {
+ window.parent.postMessage({results: data}, host_info['HTTPS_ORIGIN']);
+ });
+}
+
+function xhr_test() {
+ return boilerplate_test("xhr");
+}
+
+function fetch_test() {
+ return boilerplate_test("fetch");
+}
+
+function importScripts_test() {
+ return boilerplate_test("importScripts");
+}
+
+window.addEventListener('message', function(evt) {
+ var port = evt.ports[0];
+ xhr_test()
+ .then(fetch_test)
+ .then(importScripts_test)
+ .then(function() { port.postMessage({results: 'finish'}); })
+ .catch(function(e) { port.postMessage({results: 'failure:' + e}); });
+ });
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js b/testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js
new file mode 100644
index 000000000..960c6328c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/worker-load-interceptor.js
@@ -0,0 +1,13 @@
+importScripts('get-host-info.sub.js');
+
+var response_text = "This load was successfully intercepted.";
+var response_script = "postMessage(\"This load was successfully intercepted.\");";
+
+self.onfetch = function(event) {
+ var url = event.request.url;
+ if (url.indexOf("synthesized-response.txt") != -1) {
+ event.respondWith(new Response(response_text));
+ } else if (url.indexOf("synthesized-response.js") != -1) {
+ event.respondWith(new Response(response_script));
+ }
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js b/testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js
new file mode 100644
index 000000000..fdf5868e3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/worker-testharness.js
@@ -0,0 +1,49 @@
+/*
+ * worker-test-harness should be considered a temporary polyfill around
+ * testharness.js for supporting Service Worker based tests. It should not be
+ * necessary once the test harness is able to drive worker based tests natively.
+ * See https://github.com/w3c/testharness.js/pull/82 for status of effort to
+ * update upstream testharness.js. Once the upstreaming is complete, tests that
+ * reference worker-test-harness should be updated to directly import
+ * testharness.js.
+ */
+
+importScripts('/resources/testharness.js');
+
+(function() {
+ var next_cache_index = 1;
+
+ // Returns a promise that resolves to a newly created Cache object. The
+ // returned Cache will be destroyed when |test| completes.
+ function create_temporary_cache(test) {
+ var uniquifier = String(++next_cache_index);
+ var cache_name = self.location.pathname + '/' + uniquifier;
+
+ test.add_cleanup(function() {
+ self.caches.delete(cache_name);
+ });
+
+ return self.caches.delete(cache_name)
+ .then(function() {
+ return self.caches.open(cache_name);
+ });
+ }
+
+ self.create_temporary_cache = create_temporary_cache;
+})();
+
+// Runs |test_function| with a temporary unique Cache passed in as the only
+// argument. The function is run as a part of Promise chain owned by
+// promise_test(). As such, it is expected to behave in a manner identical (with
+// the exception of the argument) to a function passed into promise_test().
+//
+// E.g.:
+// cache_test(function(cache) {
+// // Do something with |cache|, which is a Cache object.
+// }, "Some Cache test");
+function cache_test(test_function, description) {
+ promise_test(function(test) {
+ return create_temporary_cache(test)
+ .then(test_function);
+ }, description);
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/resources/xhr.js b/testing/web-platform/tests/service-workers/service-worker/resources/xhr.js
new file mode 100644
index 000000000..387c4a48e
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/xhr.js
@@ -0,0 +1,6 @@
+self.addEventListener('activate', function(event) {
+ event.waitUntil(clients.claim());
+ });
+self.addEventListener('message', function(event) {
+ event.data.port.postMessage({xhr: !!("XMLHttpRequest" in self)});
+ });