summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/service-workers/service-worker/tentative
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/service-workers/service-worker/tentative
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/service-workers/service-worker/tentative')
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/README.md4
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.css3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.html5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.js1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.py11
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.txt1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/imported-sw.js13
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text.headers1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text.headers1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js76
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple-test-for-condition-main-resource.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.csv1
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-helpers.sub.js76
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-no-fetch-handler-sw.js35
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-race-network-and-fetch-handler-sw.js57
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js45
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.sub.js29
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/test-helpers.sub.js303
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-fetch-event.https.html39
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-invalid-rules.https.html29
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html69
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-multiple-router-registrations.https.html57
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-no-fetch-handler.https.html48
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-race-network-and-fetch-handler.https.html91
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-destination.https.html77
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-method.https.html59
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html154
30 files changed, 1293 insertions, 0 deletions
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/README.md b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/README.md
new file mode 100644
index 0000000000..5429b61d40
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/README.md
@@ -0,0 +1,4 @@
+A test suite for the ServiceWorker Static Routing API.
+
+WICG proposal: https://github.com/WICG/proposals/issues/102
+Specification PR: https://github.com/w3c/ServiceWorker/pull/1686
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.css b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.css
new file mode 100644
index 0000000000..c812213f53
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.css
@@ -0,0 +1,3 @@
+body {
+ width: 100%;
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.html
new file mode 100644
index 0000000000..e98d70207a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<title>Direct</title>
+<body>
+ Here's a direct html from network.
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.js
new file mode 100644
index 0000000000..ed6f0e5f98
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.js
@@ -0,0 +1 @@
+window.router_condition_request_destination_script = true;
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.py b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.py
new file mode 100644
index 0000000000..d30d41b44e
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.py
@@ -0,0 +1,11 @@
+import os
+import time
+
+def main(request, response):
+ if 'server_slow' in request.url_parts.query:
+ time.sleep(0.2)
+ if 'server_no_content' in request.url_parts.query:
+ return 204, [(b'Content-Type', b'text/plain')], u'Network with %s request' % request.method
+ if 'server_not_found' in request.url_parts.query:
+ return 404, [(b'Content-Type', b'text/plain')], u'Not Found'
+ return 200, [(b'Content-Type', b'text/plain')], u'Network with %s request' % request.method
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.txt b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.txt
new file mode 100644
index 0000000000..f3d9861c13
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/direct.txt
@@ -0,0 +1 @@
+Network
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/imported-sw.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/imported-sw.js
new file mode 100644
index 0000000000..04a894d77f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/imported-sw.js
@@ -0,0 +1,13 @@
+'use strict';
+
+import {routerRules} from './router-rules.js';
+
+const params = new URLSearchParams(location.search);
+const key = params.get('imported-sw-router-key');
+
+if (key) {
+ self.addEventListener('install', async e => {
+ await e.addRoutes(routerRules[key]);
+ self.skipWaiting();
+ });
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text
new file mode 100644
index 0000000000..f3d9861c13
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text
@@ -0,0 +1 @@
+Network
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text.headers b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct1.text.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text
new file mode 100644
index 0000000000..f3d9861c13
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text
@@ -0,0 +1 @@
+Network
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text.headers b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text.headers
new file mode 100644
index 0000000000..156209f9c8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/or-test/direct2.text.headers
@@ -0,0 +1 @@
+Content-Type: text/html
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js
new file mode 100644
index 0000000000..4e6f8bb955
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js
@@ -0,0 +1,76 @@
+const routerRules = {
+ 'condition-urlpattern-constructed-source-network': [{
+ condition: {urlPattern: new URLPattern({pathname: '/**/direct.txt'})},
+ source: 'network'
+ }],
+ 'condition-urlpattern-urlpatterninit-source-network': [
+ {condition: {urlPattern: {pathname: '/**/direct.txt'}}, source: 'network'},
+ ],
+ 'condition-urlpattern-string-source-network': [
+ {condition: {urlPattern: '/**/direct.txt'}, source: 'network'},
+ ],
+ 'condition-urlpattern-string-source-cache': [
+ {condition: {urlPattern: '/**/cache.txt'}, source: 'cache'},
+ ],
+ 'condition-urlpattern-constructed-ignore-case-source-network': [{
+ condition: {
+ urlPattern:
+ new URLPattern({pathname: '/**/DiReCT.TxT'}, {ignoreCase: true})
+ },
+ source: 'network'
+ }],
+ 'condition-urlpattern-constructed-respect-case-source-network': [{
+ condition: {urlPattern: new URLPattern({pathname: '/**/DiReCT.TxT'})},
+ source: 'network'
+ }],
+ 'condition-request-source-network':
+ [{condition: {requestMode: 'no-cors'}, source: 'network'}],
+ 'condition-request-navigate-source-cache':
+ [{condition: {requestMode: 'navigate'}, source: 'cache'}],
+ 'condition-request-method-get-network':
+ [{condition: {requestMethod: 'GET'}, source: 'network'}],
+ 'condition-request-method-post-network':
+ [{condition: {requestMethod: 'POST'}, source: 'network'}],
+ 'condition-request-method-put-network':
+ [{condition: {requestMethod: 'PUT'}, source: 'network'}],
+ 'condition-request-method-delete-network':
+ [{condition: {requestMethod: 'DELETE'}, source: 'network'}],
+ 'condition-invalid-request-method': [{
+ condition: {requestMethod: String.fromCodePoint(0x3042)},
+ source: 'network'
+ }],
+ 'condition-request-destination-script-network':
+ [{condition: {requestDestination: 'script'}, source: 'network'}],
+ 'condition-or-source-network': [{
+ condition: {
+ or: [
+ {
+ or: [{urlPattern: '/**/or-test/direct1.*??*'}],
+ },
+ {urlPattern: '/**/or-test/direct2.*??*'}
+ ]
+ },
+ source: 'network'
+ }],
+ 'condition-request-source-fetch-event':
+ [{condition: {requestMode: 'no-cors'}, source: 'fetch-event'}],
+ 'condition-urlpattern-string-source-fetch-event':
+ [{condition: {urlPattern: '/**/*'}, source: 'fetch-event'}],
+ 'multiple-router-rules': [
+ {
+ condition: {
+ urlPattern: '/**/direct.txt',
+ },
+ source: 'network'
+ },
+ {condition: {urlPattern: '/**/direct.html'}, source: 'network'}
+ ],
+ 'condition-urlpattern-string-source-race-network-and-fetch-handler': [
+ {
+ condition: {urlPattern: '/**/direct.py'},
+ source: 'race-network-and-fetch-handler'
+ },
+ ],
+};
+
+export {routerRules};
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple-test-for-condition-main-resource.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple-test-for-condition-main-resource.html
new file mode 100644
index 0000000000..0c3e3e7870
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple-test-for-condition-main-resource.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/tentative/static-router/resources/simple.csv b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.csv
new file mode 100644
index 0000000000..ecbef786e7
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.csv
@@ -0,0 +1 @@
+matched,with,non-url,conditions
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/simple.html
new file mode 100644
index 0000000000..0c3e3e7870
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/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/tentative/static-router/resources/static-router-helpers.sub.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-helpers.sub.js
new file mode 100644
index 0000000000..cf34e98635
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-helpers.sub.js
@@ -0,0 +1,76 @@
+// Helper functions for ServiceWorker static routing API.
+//
+// test-helpers.sub.js must be loaded before using this.
+
+// Get a dictionary of information recorded inside ServiceWorker.
+// It includes:
+// - request URL and mode.
+// - errors.
+//
+// See: static-router-sw.js for details.
+const get_info_from_worker =
+ async worker => {
+ const promise = new Promise(function(resolve) {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function(msg) { resolve(msg); };
+ worker.postMessage({port: channel.port2}, [channel.port2]);
+ });
+ const message = await promise;
+
+ return message.data;
+}
+
+// Reset information stored inside ServiceWorker.
+const reset_info_in_worker =
+ async worker => {
+ const promise = new Promise(function(resolve) {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = function(msg) { resolve(msg); };
+ worker.postMessage({port: channel.port2, reset: true}, [channel.port2]);
+ });
+ await promise;
+}
+
+// Register the ServiceWorker and wait until activated.
+// {ruleKey} represents the key of routerRules defined in router-rules.js.
+// {swScript} represents the service worker source URL.
+const registerAndActivate = async (test, ruleKey, swScript) => {
+ if (!swScript) {
+ swScript = 'resources/static-router-sw.js'
+ }
+ const swURL = `${swScript}?key=${ruleKey}`;
+ const swScope = 'resources/';
+ const reg = await service_worker_unregister_and_register(
+ test, swURL, swScope, { type: 'module' });
+ add_completion_callback(() => reg.unregister());
+ const worker = reg.installing;
+ await wait_for_state(test, worker, 'activated');
+
+ return worker;
+};
+
+// Create iframe with the given url. This automatically removes the iframe in a
+// cleanup.
+const createIframe = async (t, url) => {
+ const iframe = await with_iframe(url);
+ t.add_cleanup(() => iframe.remove());
+
+ return iframe;
+};
+
+// Register a service worker, then create an iframe at url.
+function iframeTest(url, ruleKey, callback, name) {
+ return promise_test(async t => {
+ const worker = await registerAndActivate(t, ruleKey);
+ const iframe = await createIframe(t, url);
+ await callback(t, iframe.contentWindow, worker);
+ }, name);
+};
+
+function randomString() {
+ let result = "";
+ for (let i = 0; i < 5; i++) {
+ result += String.fromCharCode(97 + Math.floor(Math.random() * 26));
+ }
+ return result;
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-no-fetch-handler-sw.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-no-fetch-handler-sw.js
new file mode 100644
index 0000000000..1ba5fd7d46
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-no-fetch-handler-sw.js
@@ -0,0 +1,35 @@
+'use strict';
+
+import {routerRules} from './router-rules.js';
+import {
+ recordError,
+ getRecords,
+ resetRecords } from './static-router-sw.sub.js';
+
+self.addEventListener('install', async e => {
+ e.waitUntil(caches.open('v1').then(
+ cache => {cache.put('cache.txt', new Response('From cache'))}));
+
+ const params = new URLSearchParams(location.search);
+ const key = params.get('key');
+ try {
+ await e.addRoutes(routerRules[key]);
+ } catch (e) {
+ recordError(e);
+ }
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', e => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('message', function(event) {
+ if (event.data.reset) {
+ resetRecords();
+ }
+ if (event.data.port) {
+ const {requests, errors} = getRecords();
+ event.data.port.postMessage({requests, errors});
+ }
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-race-network-and-fetch-handler-sw.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-race-network-and-fetch-handler-sw.js
new file mode 100644
index 0000000000..468116eea8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-race-network-and-fetch-handler-sw.js
@@ -0,0 +1,57 @@
+'use strict';
+
+import {routerRules} from './router-rules.js';
+import {
+ recordRequest,
+ recordError,
+ getRecords,
+ resetRecords } from './static-router-sw.sub.js';
+
+import './imported-sw.js';
+
+self.addEventListener('install', async e => {
+ e.waitUntil(caches.open('v1').then(
+ cache => {cache.put('cache.txt', new Response('From cache'))}));
+
+ const params = new URLSearchParams(location.search);
+ const key = params.get('key');
+ try {
+ await e.addRoutes(routerRules[key]);
+ } catch (e) {
+ recordError(e);
+ }
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', e => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('fetch', function(event) {
+ recordRequest(event.request);
+ const url = new URL(event.request.url);
+
+
+ // Force slow response
+ if (url.searchParams.has('sw_slow')) {
+ const start = Date.now();
+ while (true) {
+ if (Date.now() - start > 200) {
+ break;
+ }
+ }
+ }
+
+ const nonce = url.searchParams.get('nonce');
+ event.respondWith(new Response(nonce));
+});
+
+self.addEventListener('message', function(event) {
+ if (event.data.reset) {
+ resetRecords();
+ }
+ if (event.data.port) {
+ const {requests, errors} = getRecords();
+ event.data.port.postMessage({requests, errors});
+ }
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js
new file mode 100644
index 0000000000..07409ec42c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js
@@ -0,0 +1,45 @@
+'use strict';
+
+import {routerRules} from './router-rules.js';
+import {
+ recordRequest,
+ recordError,
+ getRecords,
+ resetRecords } from './static-router-sw.sub.js';
+
+import './imported-sw.js';
+
+self.addEventListener('install', async e => {
+ e.waitUntil(caches.open('v1').then(
+ cache => {cache.put('cache.txt', new Response('From cache'))}));
+
+ const params = new URLSearchParams(location.search);
+ const key = params.get('key');
+ try {
+ await e.addRoutes(routerRules[key]);
+ } catch (e) {
+ recordError(e);
+ }
+ self.skipWaiting();
+});
+
+self.addEventListener('activate', e => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('fetch', function(event) {
+ recordRequest(event.request);
+ const url = new URL(event.request.url);
+ const nonce = url.searchParams.get('nonce');
+ event.respondWith(new Response(nonce));
+});
+
+self.addEventListener('message', function(event) {
+ if (event.data.reset) {
+ resetRecords();
+ }
+ if (event.data.port) {
+ const {requests, errors} = getRecords();
+ event.data.port.postMessage({requests, errors});
+ }
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.sub.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.sub.js
new file mode 100644
index 0000000000..04f9c5533a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.sub.js
@@ -0,0 +1,29 @@
+let requests = [];
+let errors = [];
+
+const recordRequest = req => {
+ requests.push({url: req.url, mode: req.mode, destination: req.destination});
+};
+
+const recordError = (error) => {
+ errors.push(error);
+};
+
+const getRecords = () => {
+ return {
+ requests,
+ errors
+ };
+}
+
+const resetRecords = () => {
+ requests = [];
+ errors = [];
+}
+
+export {
+ recordRequest,
+ recordError,
+ getRecords,
+ resetRecords
+};
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/test-helpers.sub.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/test-helpers.sub.js
new file mode 100644
index 0000000000..64a7f7d24f
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/test-helpers.sub.js
@@ -0,0 +1,303 @@
+// Copied from
+// service-workers/service-worker/resources/testharness-helpers.js to be used under tentative.
+
+// Adapter for testharness.js-style tests with Service Workers
+
+/**
+ * @param options an object that represents RegistrationOptions except for scope.
+ * @param options.type a WorkerType.
+ * @param options.updateViaCache a ServiceWorkerUpdateViaCache.
+ * @see https://w3c.github.io/ServiceWorker/#dictdef-registrationoptions
+ */
+function service_worker_unregister_and_register(test, url, scope, options) {
+ if (!scope || scope.length == 0)
+ return Promise.reject(new Error('tests must define a scope'));
+
+ if (options && options.scope)
+ return Promise.reject(new Error('scope must not be passed in options'));
+
+ options = Object.assign({ scope: scope }, options);
+ 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.
+ *
+ * @param {string} url
+ * @returns {HTMLIFrameElement}
+ */
+function with_iframe(url) {
+ return new Promise(function(resolve) {
+ var frame = document.createElement('iframe');
+ frame.className = 'test-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) {
+ var handler = test.step_func(function() {
+ registration.removeEventListener('updatefound', handler);
+ resolve(registration.installing);
+ });
+ registration.addEventListener('updatefound', handler);
+ }));
+}
+
+// Return true if |state_a| is more advanced than |state_b|.
+function is_state_advanced(state_a, state_b) {
+ if (state_b === 'installing') {
+ switch (state_a) {
+ case 'installed':
+ case 'activating':
+ case 'activated':
+ case 'redundant':
+ return true;
+ }
+ }
+
+ if (state_b === 'installed') {
+ switch (state_a) {
+ case 'activating':
+ case 'activated':
+ case 'redundant':
+ return true;
+ }
+ }
+
+ if (state_b === 'activating') {
+ switch (state_a) {
+ case 'activated':
+ case 'redundant':
+ return true;
+ }
+ }
+
+ if (state_b === 'activated') {
+ switch (state_a) {
+ case 'redundant':
+ return true;
+ }
+ }
+ return false;
+}
+
+function wait_for_state(test, worker, state) {
+ if (!worker || worker.state == undefined) {
+ return Promise.reject(new Error(
+ 'wait_for_state needs a ServiceWorker object to be passed.'));
+ }
+ if (worker.state === state)
+ return Promise.resolve(state);
+
+ if (is_state_advanced(worker.state, state)) {
+ return Promise.reject(new Error(
+ `Waiting for ${state} but the worker is already ${worker.state}.`));
+ }
+ return new Promise(test.step_func(function(resolve, reject) {
+ worker.addEventListener('statechange', test.step_func(function() {
+ if (worker.state === state)
+ resolve(state);
+
+ if (is_state_advanced(worker.state, state)) {
+ reject(new Error(
+ `The state of the worker becomes ${worker.state} while waiting` +
+ `for ${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_https(test) {
+ var host_info = get_host_info();
+ return test_login(test, host_info.HTTPS_REMOTE_ORIGIN,
+ 'username1s', 'password1s', 'cookie1')
+ .then(function() {
+ return test_login(test, host_info.HTTPS_ORIGIN,
+ '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';
+}
+
+// The navigator.serviceWorker.register() method guarantees that the newly
+// installing worker is available as registration.installing when its promise
+// resolves. However some tests test installation using a <link> element where
+// it is possible for the installing worker to have already become the waiting
+// or active worker. So this method is used to get the newest worker when these
+// tests need access to the ServiceWorker itself.
+function get_newest_worker(registration) {
+ if (registration.installing)
+ return registration.installing;
+ if (registration.waiting)
+ return registration.waiting;
+ if (registration.active)
+ return registration.active;
+}
+
+function register_using_link(script, options) {
+ var scope = options.scope;
+ var link = document.createElement('link');
+ link.setAttribute('rel', 'serviceworker');
+ link.setAttribute('href', script);
+ link.setAttribute('scope', scope);
+ document.getElementsByTagName('head')[0].appendChild(link);
+ return new Promise(function(resolve, reject) {
+ link.onload = resolve;
+ link.onerror = reject;
+ })
+ .then(() => navigator.serviceWorker.getRegistration(scope));
+}
+
+function with_sandboxed_iframe(url, sandbox) {
+ return new Promise(function(resolve) {
+ var frame = document.createElement('iframe');
+ frame.sandbox = sandbox;
+ frame.src = url;
+ frame.onload = function() { resolve(frame); };
+ document.body.appendChild(frame);
+ });
+}
+
+// Registers, waits for activation, then unregisters on a sample scope.
+//
+// This can be used to wait for a period of time needed to register,
+// activate, and then unregister a service worker. When checking that
+// certain behavior does *NOT* happen, this is preferable to using an
+// arbitrary delay.
+async function wait_for_activation_on_sample_scope(t, window_or_workerglobalscope) {
+ const script = '/service-workers/service-worker/resources/empty-worker.js';
+ const scope = 'resources/there/is/no/there/there?' + Date.now();
+ let registration = await window_or_workerglobalscope.navigator.serviceWorker.register(script, { scope });
+ await wait_for_state(t, registration.installing, 'activated');
+ await registration.unregister();
+}
+
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-fetch-event.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-fetch-event.https.html
new file mode 100644
index 0000000000..167e3af009
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-fetch-event.https.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are evaluated with the fetch-event source.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const ROUTER_KEY = 'condition-urlpattern-string-source-fetch-event';
+const REGISTERED_ROUTE = 'resources/simple.html';
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, ROUTER_KEY);
+ const rnd = randomString();
+ const iframe = await createIframe(t, `${REGISTERED_ROUTE}?nonce=${rnd}`);
+ const {errors, requests} = await get_info_from_worker(worker);
+
+ assert_equals(errors.length, 0);
+ assert_equals(requests.length, 1);
+ assert_equals(iframe.contentWindow.document.body.innerText, rnd);
+}, 'Main resource matched the rule with fetch-event source');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_KEY, async (t, iwin, worker) => {
+ const rnd = randomString();
+ const response = await iwin.fetch(`?nonce=${rnd}`);
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), rnd);
+ const {requests} = await get_info_from_worker(worker);
+ // Main resource request + subreosurce request = 2.
+ assert_equals(requests.length, 2);
+}, 'Subresource load matched the rule fetch-event source');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-invalid-rules.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-invalid-rules.https.html
new file mode 100644
index 0000000000..4ed4ad4b3c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-invalid-rules.https.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: registration of invalid rules should raise.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+
+const ROUTER_RULE_KEY_INVALID_REQUEST_METHOD =
+ 'condition-invalid-request-method';
+const ROUTER_RULE_KEY_SOURCE_FETCH_EVENT =
+ 'condition-request-source-fetch-event';
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, ROUTER_RULE_KEY_INVALID_REQUEST_METHOD);
+ t.add_cleanup(() => {reset_info_in_worker(worker)});
+ const {errors} = await get_info_from_worker(worker);
+ assert_equals(errors.length, 1);
+}, 'addRoutes should raise for invalid request method.');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html
new file mode 100644
index 0000000000..fc93a4f7c9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: simply skip fetch handler for main resource if pattern matches
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script src="resources/static-router-helpers.sub.js"></script>
+<body>
+<script>
+const ROUTER_RULE_KEY = 'condition-urlpattern-constructed-source-network';
+const ROUTER_RULE_KEY_IGNORE_CASE =
+ 'condition-urlpattern-constructed-ignore-case-source-network';
+const ROUTER_RULE_KEY_RESPECT_CASE =
+ 'condition-urlpattern-constructed-respect-case-source-network';
+const ROUTER_RULE_KEY_URLPATTERN_CACHE =
+ 'condition-urlpattern-string-source-cache';
+const ROUTER_RULE_KEY_REQUEST_CACHE = 'condition-request-navigate-source-cache';
+const REGISTERED_ROUTE = 'resources/direct.txt';
+const CACHED_ROUTE = 'resources/cache.txt';
+const NON_REGISTERED_ROUTE = 'resources/simple.html';
+const host_info = get_host_info();
+const path = new URL(".", window.location).pathname;
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+ assert_equals(iwin.document.body.innerText, "Network\n");
+}, 'Main resource load matched with the condition');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY_IGNORE_CASE, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+ assert_equals(iwin.document.body.innerText, "Network\n");
+}, 'Main resource load matched with the ignore case condition');
+
+iframeTest(REGISTERED_ROUTE, ROUTER_RULE_KEY_RESPECT_CASE, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+}, 'Main resource load matched without the ignore case condition');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+ assert_equals(
+ requests[0].url,
+ `${host_info['HTTPS_ORIGIN']}${path}${NON_REGISTERED_ROUTE}`);
+ assert_equals(requests[0].mode, 'navigate');
+}, 'Main resource load not matched with the condition');
+
+iframeTest(CACHED_ROUTE, ROUTER_RULE_KEY_URLPATTERN_CACHE, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+ assert_equals(iwin.document.body.innerText, "From cache");
+}, 'Main resource load matched with the cache source');
+
+iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY_REQUEST_CACHE, async (t, iwin, worker) => {
+ const {requests} = await get_info_from_worker(worker);
+ // When the request matched to the rule with the "cache" source but failed to
+ // get the cache entry, the fetch handler is not involved and the network
+ // fallback is triggered instead.
+ assert_equals(requests.length, 0);
+ assert_equals(iwin.document.body.innerText, "Here's a simple html file.");
+}, 'Main resource fallback to the network when there is no cache entry');
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-multiple-router-registrations.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-multiple-router-registrations.https.html
new file mode 100644
index 0000000000..dd7ff1e4e9
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-multiple-router-registrations.https.html
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are registered multiple times.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const ROUTER_KEY_MULTIPLE_RULES = 'multiple-router-rules';
+const ROUTER_KEY_1 = 'condition-urlpattern-string-source-network';
+const ROUTER_KEY_2 = 'condition-request-method-post-network';
+
+const REGISTERED_ROUTE_1 = 'resources/direct.txt';
+const REGISTERED_ROUTE_2 = 'resources/direct.html';
+const REGISTERED_ROUTE_3 = 'direct.py';
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, ROUTER_KEY_MULTIPLE_RULES);
+
+ // Matched with the first rule.
+ const iframe = await createIframe(t, REGISTERED_ROUTE_1);
+ assert_equals(iframe.contentWindow.document.body.innerText, "Network\n");
+
+ // Matched with the second rule.
+ const second_iframe = await createIframe(t, REGISTERED_ROUTE_2);
+ assert_equals(second_iframe.contentWindow.document.body.innerText, "Here's a direct html from network.");
+
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+}, 'Main reosurce load matched with the service worker having multiple rules');
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t,
+ `${ROUTER_KEY_1}&imported-sw-router-key=${ROUTER_KEY_2}`);
+
+ // Matched with the first rule.
+ const iframe = await createIframe(t, REGISTERED_ROUTE_1);
+ assert_equals(iframe.contentWindow.document.body.innerText, "Network\n");
+ let info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 0);
+
+ // Matched with the second rule, which is registered in the service worker
+ // loaded via `import` by the main script.
+ const response = await iframe.contentWindow.fetch(REGISTERED_ROUTE_3, {method: 'POST'});
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), "Network with POST request");
+ info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 0);
+}, 'Resource load matched with the rule registered in the imported service worker');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-no-fetch-handler.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-no-fetch-handler.https.html
new file mode 100644
index 0000000000..e98dd08a90
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-no-fetch-handler.https.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are evaluated when there is no fetch handler.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const RULE_KEY_SOURCE_CACHE = 'condition-urlpattern-string-source-cache';
+const RULE_KEY_SOURCE_FETCH_EVENT =
+ 'condition-request-source-fetch-event';
+const SW_SRC = 'resources/static-router-no-fetch-handler-sw.js';
+const CACHED_FILE = 'cache.txt';
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, RULE_KEY_SOURCE_CACHE, SW_SRC);
+
+ // Matched with the main reosurce load.
+ const {contentWindow} = await createIframe(t, `resources/${CACHED_FILE}`);
+ assert_equals(contentWindow.document.body.innerText, "From cache");
+
+ // Matched with the subreosurce load.
+ const response = await contentWindow.fetch(CACHED_FILE);
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), "From cache");
+
+ // Both requests are served from cache.
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+}, 'The router rule is evaluated without fetch handlers in service worker');
+
+promise_test(async t => {
+ const worker =
+ await registerAndActivate(t, RULE_KEY_SOURCE_FETCH_EVENT, SW_SRC);
+ t.add_cleanup(() => {reset_info_in_worker(worker)});
+
+ const {errors} = await get_info_from_worker(worker);
+ assert_equals(errors.length, 1);
+}, 'addRoutes should raise if the fetch-event source is used without onfetch')
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-race-network-and-fetch-handler.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-race-network-and-fetch-handler.https.html
new file mode 100644
index 0000000000..0dad64ad99
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-race-network-and-fetch-handler.https.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are evaluated with the request desitnation condition.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const ROUTER_KEY = 'condition-urlpattern-string-source-race-network-and-fetch-handler';
+const SW_SRC = 'resources/static-router-race-network-and-fetch-handler-sw.js';
+const FRAME_SRC = 'resources/direct.py';
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, `${FRAME_SRC}?nonce=${rnd}&server_slow`);
+ // Expect the response from the fetch handler.
+ assert_equals(iframe.contentWindow.document.body.innerText, rnd);
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+}, 'Main resource load matched the rule with race-network-and-fetch-handler source, and the fetch handler response is faster than the server response');
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, `${FRAME_SRC}?nonce=${rnd}&sw_slow`);
+ // Expect the response from the netowrk request.
+ assert_equals(iframe.contentWindow.document.body.innerText, "Network with GET request");
+ // Ensure the fetch handler is also executed.
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+}, 'Main resource load matched the rule with race-network-and-fetch-handler source, and the server reseponse is faster than the fetch handler');
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, FRAME_SRC);
+ // Expect the response from the fetch handler.
+ const response = await iframe.contentWindow.fetch(`?nonce=${rnd}&server_slow`);
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), rnd);
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 2);
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the fetch handler response is faster than the server response');
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, FRAME_SRC);
+ // Expect the response from the network request.
+ const response = await iframe.contentWindow.fetch(`?nonce=${rnd}&sw_slow`);
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), "Network with GET request");
+ // Ensure the fetch handler is also executed.
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 2);
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the server reseponse is faster than the fetch handler');
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, FRAME_SRC);
+ // Expect the response from the network request.
+ const response = await iframe.contentWindow.fetch(`?nonce=${rnd}&sw_slow&server_no_content`);
+ assert_equals(response.status, 204);
+ // Ensure the fetch handler is also executed.
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 2);
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the server reseponse with 204 response is faster than the fetch handler');
+
+
+promise_test(async t => {
+ const rnd = randomString();
+ const worker = await registerAndActivate(t, ROUTER_KEY, SW_SRC);
+ const iframe = await createIframe(t, FRAME_SRC);
+ const response = await iframe.contentWindow.fetch(`?nonce=${rnd}&sw_slow&server_not_found`);
+ // Expect the response from the network request was faster, but the result was 404.
+ // So, the fetch handler result will be used instead.
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), rnd);
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 2);
+}, 'Subresource load matched the rule with race-network-and-fetch-handler source, and the server reseponse is faster than the fetch handler, but not found');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-destination.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-destination.https.html
new file mode 100644
index 0000000000..55ad6da31a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-destination.https.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are evaluated with the request desitnation condition.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const ROUTER_KEY_DESTINATION_SCRIPT = 'condition-request-destination-script-network';
+const FRAME_SRC = 'resources/direct.html';
+const SCRIPT_SRC = 'direct.js';
+const STYLE_SRC = 'direct.txt';
+
+const appendScript = async (iwin, src) => {
+ const promise = new Promise(resolve => {
+ const script = iwin.document.createElement('script');
+ script.src = src;
+ iwin.document.body.appendChild(script);
+ script.onload = () => {
+ resolve(script);
+ };
+ });
+
+ return promise;
+};
+
+const appendCSS = async (iwin, src) => {
+ const promise = new Promise(resolve => {
+ const link = iwin.document.createElement('link');
+ link.rel = 'stylesheet';
+ link.href = src;
+ iwin.document.head.appendChild(link);
+ link.onload = () => {
+ resolve(link);
+ };
+ });
+
+ return promise;
+};
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, ROUTER_KEY_DESTINATION_SCRIPT);
+ const iframe = await createIframe(t, FRAME_SRC);
+
+ let info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 1);
+ assert_equals(info.requests[0].destination, 'iframe');
+
+ // JavaScript is not handled by fetch handler.
+ await appendScript(iframe.contentWindow, SCRIPT_SRC);
+ assert_equals(iframe.contentWindow.router_condition_request_destination_script, true);
+ info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 1);
+}, 'Subreosurce load matched with the requestMethod script condition');
+
+promise_test(async t => {
+ const worker = await registerAndActivate(t, ROUTER_KEY_DESTINATION_SCRIPT);
+ const iframe = await createIframe(t, FRAME_SRC);
+
+ let info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 1);
+ assert_equals(info.requests[0].destination, 'iframe');
+
+ // Stylesheet is not handled by fetch handler.
+ await appendCSS(iframe.contentWindow, STYLE_SRC);
+ info = await get_info_from_worker(worker);
+ assert_equals(info.requests.length, 2);
+ assert_equals(info.requests[1].destination, 'style');
+}, 'Subreosurce load not matched with the requestMethod script condition');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-method.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-method.https.html
new file mode 100644
index 0000000000..8e219532a3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-request-method.https.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>
+ Static Router: routers are evaluated with the request method condition.
+</title>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js">
+</script>
+<script src="resources/static-router-helpers.sub.js">
+</script>
+<body>
+<script>
+const ROUTER_KEY_MEHOD_GET = 'condition-request-method-get-network';
+const ROUTER_KEY_MEHOD_POST = 'condition-request-method-post-network';
+const ROUTER_KEY_MEHOD_PUT = 'condition-request-method-put-network';
+const ROUTER_KEY_MEHOD_DELETE = 'condition-request-method-delete-network';
+const REQUET_SRC = 'resources/direct.py';
+
+iframeTest(REQUET_SRC, ROUTER_KEY_MEHOD_GET, async (t, iwin, worker) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network with GET request");
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+}, 'Subresource load matched with the requestMethod GET condition');
+
+iframeTest(REQUET_SRC, ROUTER_KEY_MEHOD_POST, async (t, iwin, worker) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd, {method: 'POST'});
+ assert_equals(await response.text(), "Network with POST request");
+ const {requests} = await get_info_from_worker(worker);
+ // The navigation request is the only request handled by fetch handler.
+ assert_equals(requests.length, 1);
+ assert_equals(requests[0].mode, 'navigate');
+}, 'Subresource load matched with the requestMethod POST condition');
+
+iframeTest(REQUET_SRC, ROUTER_KEY_MEHOD_PUT, async (t, iwin, worker) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd, {method: 'PUT'});
+ assert_equals(await response.text(), "Network with PUT request");
+ const {requests} = await get_info_from_worker(worker);
+ // The navigation request is the only request handled by fetch handler.
+ assert_equals(requests.length, 1);
+ assert_equals(requests[0].mode, 'navigate');
+}, 'Subresource load matched with the requestMethod PUT condition');
+
+iframeTest(REQUET_SRC, ROUTER_KEY_MEHOD_DELETE, async (t, iwin, worker) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd, {method: 'DELETE'});
+ assert_equals(await response.text(), "Network with DELETE request");
+ const {requests} = await get_info_from_worker(worker);
+ // The navigation request is the only request handled by fetch handler.
+ assert_equals(requests.length, 1);
+ assert_equals(requests[0].mode, 'navigate');
+}, 'Subresource load matched with the requestMethod DELETE condition');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html
new file mode 100644
index 0000000000..3f7902a872
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html
@@ -0,0 +1,154 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Static Router: simply skip fetch handler if pattern matches</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/static-router-helpers.sub.js"></script>
+<body>
+<script>
+const SCRIPT = 'resources/static-router-sw.js';
+const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED =
+ 'condition-urlpattern-constructed-source-network';
+const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_IGNORE_CASE =
+ 'condition-urlpattern-constructed-ignore-case-source-network';
+const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_RESPECT_CASE =
+ 'condition-urlpattern-constructed-respect-case-source-network';
+const ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT =
+ 'condition-urlpattern-urlpatterninit-source-network';
+const ROUTER_RULE_KEY_URL_PATTERN_STRING =
+ 'condition-urlpattern-string-source-network';
+const ROUTER_RULE_KEY_REQUEST = 'condition-request-source-network'
+const ROUTER_RULE_KEY_URL_PATTERN_STRING_CACHE =
+ 'condition-urlpattern-string-source-cache';
+const ROUTER_RULE_KEY_OR = 'condition-or-source-network'
+const SCOPE = 'resources/';
+const HTML_FILE = 'resources/simple.html';
+const TXT_FILE = 'resources/direct.txt';
+const CSV_FILE = 'resources/simple.csv';
+// Warning: please prepare the corresponding `*.text.headers` files, otherwise
+// iframeTest() fails to load the following files due to MIME mismatches.
+const OR_TEST_FILES = [
+ 'resources/or-test/direct1.text',
+ 'resources/or-test/direct2.text',
+ 'resources/or-test/does-not-exist.text',
+];
+
+iframeTest(HTML_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), rnd);
+}, 'Subresource load not matched with URLPattern condition');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with URLPattern condition');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED, async (t, iwin, worker) => {
+ const rnd = randomString();
+ // Confirm that the given URLPatternInit has a wildcard pattern for the
+ // hostname. Also, if |urlPattern| is a consutructed URLPattern object,
+ // baseURL won't be set while adding router rules, thus it matches the cross
+ // origin request as far as other components matches. So expecting the direct
+ // network request and the fetch handler doesn't capture the response.
+ // The response is going to be a opaque.
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const response = await iwin.fetch(
+ `${origin}/${TXT_FILE}?nonce=${rnd}`, {mode: 'no-cors'});
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 0);
+ assert_equals(response.type, 'opaque');
+}, 'Subresource cross origin load matched with URLPattern condition via constructed object');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_IGNORE_CASE, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with ignoreCase URLPattern condition');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_RESPECT_CASE, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), rnd);
+}, 'Subresource load matched without ignoreCase URLPattern condition');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with URLPattern condition via URLPatternInit');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT, async (t, iwin, worker) => {
+ // The SW script URL is added as a baseURL when |urlPattern| is passed via
+ // URLPatternInit, and there is not |baseURL| in it. Cross origin request will
+ // go through the fetch handler because |baseURL| info complements hostname
+ // with the hostname of the SW script.
+ const rnd = randomString();
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const response = await iwin.fetch(`${origin}/${TXT_FILE}?nonce=${rnd}`);
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+ assert_equals(await response.text(), rnd);
+}, 'Subresource cross origin load not matched with URLPattern condition via URLPatternInit');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_STRING, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with URLPattern condition via string');
+
+iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_STRING, async (t, iwin, worker) => {
+ // The SW script URL is added as a baseURL when |urlPattern| is passed via
+ // string, and there is not |baseURL| in it. Cross origin request will go
+ // through the fetch handler because |baseURL| info complements hostname with
+ // the hostname of the SW script.
+ const rnd = randomString();
+ const origin = get_host_info().HTTPS_REMOTE_ORIGIN;
+ const response = await iwin.fetch(`${origin}/${TXT_FILE}?nonce=${rnd}`);
+ const {requests} = await get_info_from_worker(worker);
+ assert_equals(requests.length, 1);
+ assert_equals(await response.text(), rnd);
+}, 'Subresource cross origin load not matched with URLPattern condition via string');
+
+iframeTest(CSV_FILE, ROUTER_RULE_KEY_REQUEST, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd, { mode: 'no-cors' });
+ assert_equals(await response.text(), "matched,with,non-url,conditions\n");
+}, 'Subresource load matched with RequestMode condition');
+
+iframeTest(OR_TEST_FILES[0], ROUTER_RULE_KEY_OR, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with the nested `or` condition');
+
+iframeTest(OR_TEST_FILES[1], ROUTER_RULE_KEY_OR, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), "Network\n");
+}, 'Subresource load matched with the next `or` condition');
+
+iframeTest(OR_TEST_FILES[2], ROUTER_RULE_KEY_OR, async (t, iwin) => {
+ const rnd = randomString();
+ const response = await iwin.fetch('?nonce=' + rnd);
+ assert_equals(await response.text(), rnd);
+}, 'Subresource load not matched with `or` condition');
+
+iframeTest(HTML_FILE, ROUTER_RULE_KEY_URL_PATTERN_STRING_CACHE, async (t, iwin) => {
+ // No need to set `resources/` because the request is dispatched from iframe.
+ const CACHED_FILE = 'cache.txt';
+ const response = await iwin.fetch(CACHED_FILE);
+ assert_equals(response.status, 200);
+ assert_equals(await response.text(), "From cache");
+
+ // This doesn't match because the cache key is wrong.
+ const rnd = randomString();
+ const response_with_param = await iwin.fetch(`${CACHED_FILE}?nonce=${rnd}`);
+ assert_equals(response_with_param.status, 404);
+}, 'Subresource load matched with the cache source rule');
+
+</script>
+</body>