summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/service-workers/service-worker/navigation-preload
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/navigation-preload
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/navigation-preload')
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html42
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/chunked-encoding.https.html25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html25
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/get-state.https.html217
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/navigationPreload.https.html20
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/redirect.https.html93
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/request-headers.https.html41
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resource-timing.https.html92
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-scope.asis6
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-worker.js11
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-scope.py19
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-worker.js8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/cookie.py20
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-scope.html0
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-worker.js15
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/get-state-worker.js21
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/helpers.js5
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/navigation-preload-worker.js3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-redirected.html3
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-scope.py38
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-worker.js35
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-scope.py14
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-worker.js10
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-scope.py19
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-worker.js37
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-iframe.html10
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-sw-helper.html34
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-worker.js8
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/wait-for-activate-worker.js40
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-cookies.https.html61
-rw-r--r--testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-iframe.https.html67
31 files changed, 1039 insertions, 0 deletions
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html
new file mode 100644
index 0000000000..ec74282ac3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/broken-chunked-encoding.https.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload with chunked encoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+promise_test(t => {
+ var script = 'resources/broken-chunked-encoding-worker.js';
+ var scope = 'resources/broken-chunked-encoding-scope.asis';
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope))
+ .then(frame => {
+ assert_equals(
+ frame.contentDocument.body.textContent,
+ 'PASS: preloadResponse resolved');
+ });
+ }, 'FetchEvent#preloadResponse resolves even if the body is sent with broken chunked encoding.');
+
+promise_test(t => {
+ var script = 'resources/broken-chunked-encoding-worker.js';
+ var scope = 'resources/chunked-encoding-scope.py?use_broken_body';
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope))
+ .then(frame => {
+ assert_equals(
+ frame.contentDocument.body.textContent,
+ 'PASS: preloadResponse resolved');
+ });
+ }, 'FetchEvent#preloadResponse resolves even if the body is sent with broken chunked encoding with some delays');
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/chunked-encoding.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/chunked-encoding.https.html
new file mode 100644
index 0000000000..830ce32cea
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/chunked-encoding.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload with chunked encoding</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+promise_test(t => {
+ var script = 'resources/chunked-encoding-worker.js';
+ var scope = 'resources/chunked-encoding-scope.py';
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope))
+ .then(frame => {
+ assert_equals(
+ frame.contentDocument.body.textContent,
+ '0123456789');
+ });
+ }, 'Navigation Preload must work with chunked encoding.');
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html
new file mode 100644
index 0000000000..7e8aacdd36
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/empty-preload-response-body.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload empty response body</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+promise_test(t => {
+ var script = 'resources/empty-preload-response-body-worker.js';
+ var scope = 'resources/empty-preload-response-body-scope.html';
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope))
+ .then(frame => {
+ assert_equals(
+ frame.contentDocument.body.textContent,
+ '[]');
+ });
+ }, 'Navigation Preload empty response body.');
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/get-state.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/get-state.https.html
new file mode 100644
index 0000000000..08e2f4976c
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/get-state.https.html
@@ -0,0 +1,217 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>NavigationPreloadManager.getState</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script src="resources/helpers.js"></script>
+<body>
+<script>
+function post_and_wait_for_reply(worker, message) {
+ return new Promise(resolve => {
+ navigator.serviceWorker.onmessage = e => { resolve(e.data); };
+ worker.postMessage(message);
+ });
+}
+
+promise_test(t => {
+ const scope = '../resources/get-state';
+ const script = '../resources/empty-worker.js';
+ var np;
+
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(r => {
+ np = r.navigationPreload;
+ add_completion_callback(() => r.unregister());
+ return wait_for_state(t, r.installing, 'activated');
+ })
+ .then(() => np.getState())
+ .then(state => {
+ expect_navigation_preload_state(state, false, 'true', 'default state');
+ return np.enable();
+ })
+ .then(result => {
+ assert_equals(result, undefined,
+ 'enable() should resolve to undefined');
+ return np.getState();
+ })
+ .then(state => {
+ expect_navigation_preload_state(state, true, 'true',
+ 'state after enable()');
+ return np.disable();
+ })
+ .then(result => {
+ assert_equals(result, undefined,
+ 'disable() should resolve to undefined');
+ return np.getState();
+ })
+ .then(state => {
+ expect_navigation_preload_state(state, false, 'true',
+ 'state after disable()');
+ return np.setHeaderValue('dreams that cannot be');
+ })
+ .then(result => {
+ assert_equals(result, undefined,
+ 'setHeaderValue() should resolve to undefined');
+ return np.getState();
+ })
+ .then(state => {
+ expect_navigation_preload_state(state, false, 'dreams that cannot be',
+ 'state after setHeaderValue()');
+ return np.setHeaderValue('').then(() => np.getState());
+ })
+ .then(state => {
+ expect_navigation_preload_state(state, false, '',
+ 'after setHeaderValue to empty string');
+ return np.setHeaderValue(null).then(() => np.getState());
+ })
+ .then(state => {
+ expect_navigation_preload_state(state, false, 'null',
+ 'after setHeaderValue to null');
+ return promise_rejects_js(t,
+ TypeError,
+ np.setHeaderValue('what\uDC00\uD800this'),
+ 'setHeaderValue() should throw if passed surrogates');
+ })
+ .then(() => {
+ return promise_rejects_js(t,
+ TypeError,
+ np.setHeaderValue('zer\0o'),
+ 'setHeaderValue() should throw if passed \\0');
+ })
+ .then(() => {
+ return promise_rejects_js(t,
+ TypeError,
+ np.setHeaderValue('\rcarriage'),
+ 'setHeaderValue() should throw if passed \\r');
+ })
+ .then(() => {
+ return promise_rejects_js(t,
+ TypeError,
+ np.setHeaderValue('newline\n'),
+ 'setHeaderValue() should throw if passed \\n');
+ })
+ .then(() => {
+ return promise_rejects_js(t,
+ TypeError,
+ np.setHeaderValue(),
+ 'setHeaderValue() should throw if passed undefined');
+ })
+ .then(() => np.enable().then(() => np.getState()))
+ .then(state => {
+ expect_navigation_preload_state(state, true, 'null',
+ 'enable() should not change header');
+ });
+ }, 'getState');
+
+// This test sends commands to a worker to call enable()/disable()/getState().
+// It checks the results from the worker and verifies that they match the
+// navigation preload state accessible from the page.
+promise_test(t => {
+ const scope = 'resources/get-state-worker';
+ const script = 'resources/get-state-worker.js';
+ var worker;
+ var registration;
+
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(r => {
+ registration = r;
+ add_completion_callback(() => registration.unregister());
+ worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(() => {
+ // Call getState().
+ return post_and_wait_for_reply(worker, 'getState');
+ })
+ .then(data => {
+ return Promise.all([data, registration.navigationPreload.getState()]);
+ })
+ .then(states => {
+ expect_navigation_preload_state(states[0], false, 'true',
+ 'default state (from worker)');
+ expect_navigation_preload_state(states[1], false, 'true',
+ 'default state (from page)');
+ // Call enable() and then getState().
+ return post_and_wait_for_reply(worker, 'enable');
+ })
+ .then(data => {
+ assert_equals(data, undefined, 'enable() should resolve to undefined');
+ return Promise.all([
+ post_and_wait_for_reply(worker, 'getState'),
+ registration.navigationPreload.getState()
+ ]);
+ })
+ .then(states => {
+ expect_navigation_preload_state(states[0], true, 'true',
+ 'state after enable() (from worker)');
+ expect_navigation_preload_state(states[1], true, 'true',
+ 'state after enable() (from page)');
+ // Call disable() and then getState().
+ return post_and_wait_for_reply(worker, 'disable');
+ })
+ .then(data => {
+ assert_equals(data, undefined,
+ '.disable() should resolve to undefined');
+ return Promise.all([
+ post_and_wait_for_reply(worker, 'getState'),
+ registration.navigationPreload.getState()
+ ]);
+ })
+ .then(states => {
+ expect_navigation_preload_state(states[0], false, 'true',
+ 'state after disable() (from worker)');
+ expect_navigation_preload_state(states[1], false, 'true',
+ 'state after disable() (from page)');
+ return post_and_wait_for_reply(worker, 'setHeaderValue');
+ })
+ .then(data => {
+ assert_equals(data, undefined,
+ '.setHeaderValue() should resolve to undefined');
+ return Promise.all([
+ post_and_wait_for_reply(worker, 'getState'),
+ registration.navigationPreload.getState()]);
+ })
+ .then(states => {
+ expect_navigation_preload_state(
+ states[0], false, 'insightful',
+ 'state after setHeaderValue() (from worker)');
+ expect_navigation_preload_state(
+ states[1], false, 'insightful',
+ 'state after setHeaderValue() (from page)');
+ });
+ }, 'getState from a worker');
+
+// This tests navigation preload API when there is no active worker. It calls
+// the API from the main page and then from the worker itself.
+promise_test(t => {
+ const scope = 'resources/wait-for-activate-worker';
+ const script = 'resources/wait-for-activate-worker.js';
+ var registration;
+ var np;
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(r => {
+ registration = r;
+ np = registration.navigationPreload;
+ add_completion_callback(() => registration.unregister());
+ return Promise.all([
+ promise_rejects_dom(
+ t, 'InvalidStateError', np.enable(),
+ 'enable should reject if there is no active worker'),
+ promise_rejects_dom(
+ t, 'InvalidStateError', np.disable(),
+ 'disable should reject if there is no active worker'),
+ promise_rejects_dom(
+ t, 'InvalidStateError', np.setHeaderValue('umm'),
+ 'setHeaderValue should reject if there is no active worker')]);
+ })
+ .then(() => np.getState())
+ .then(state => {
+ expect_navigation_preload_state(state, false, 'true',
+ 'state before activation');
+ return post_and_wait_for_reply(registration.installing, 'ping');
+ })
+ .then(result => assert_equals(result, 'PASS'));
+ }, 'no active worker');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/navigationPreload.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/navigationPreload.https.html
new file mode 100644
index 0000000000..392e5c14dc
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/navigationPreload.https.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>ServiceWorker: navigator.serviceWorker.navigationPreload</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script src="resources/helpers.js"></script>
+<script>
+promise_test(async t => {
+ const SCRIPT = '../resources/empty-worker.js';
+ const SCOPE = '../resources/navigationpreload';
+ const registration =
+ await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
+ const navigationPreload = registration.navigationPreload;
+ assert_true(navigationPreload instanceof NavigationPreloadManager,
+ 'ServiceWorkerRegistration.navigationPreload');
+ await registration.unregister();
+}, "The navigationPreload attribute must return service worker " +
+ "registration's NavigationPreloadManager object.");
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/redirect.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/redirect.https.html
new file mode 100644
index 0000000000..5970f053e3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/redirect.https.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload redirect response</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+
+function check_opaqueredirect(response_info, scope) {
+ assert_equals(response_info.type, 'opaqueredirect');
+ assert_equals(response_info.url, '' + new URL(scope, location));
+ assert_equals(response_info.status, 0);
+ assert_equals(response_info.ok, false);
+ assert_equals(response_info.statusText, '');
+ assert_equals(response_info.headers.length, 0);
+}
+
+function redirect_response_test(t, scope, expected_body, expected_urls) {
+ var script = 'resources/redirect-worker.js';
+ var registration;
+ var message_resolvers = [];
+ function wait_for_message(count) {
+ var promises = [];
+ message_resolvers = [];
+ for (var i = 0; i < count; ++i) {
+ promises.push(new Promise(resolve => message_resolvers.push(resolve)));
+ }
+ return promises;
+ }
+ function on_message(e) {
+ var resolve = message_resolvers.shift();
+ if (resolve)
+ resolve(e.data);
+ }
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(reg => {
+ registration = reg;
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope + '&base'))
+ .then(frame => {
+ assert_equals(frame.contentDocument.body.textContent, 'OK');
+ frame.contentWindow.navigator.serviceWorker.onmessage = on_message;
+ return Promise.all(wait_for_message(expected_urls.length)
+ .concat(with_iframe(scope)));
+ })
+ .then(results => {
+ var frame = results[expected_urls.length];
+ assert_equals(frame.contentDocument.body.textContent, expected_body);
+ for (var i = 0; i < expected_urls.length; ++i) {
+ check_opaqueredirect(results[i], expected_urls[i]);
+ }
+ frame.remove();
+ return registration.unregister();
+ });
+}
+
+promise_test(t => {
+ return redirect_response_test(
+ t,
+ 'resources/redirect-scope.py?type=normal',
+ 'redirected\n',
+ ['resources/redirect-scope.py?type=normal']);
+ }, 'Navigation Preload redirect response.');
+
+promise_test(t => {
+ return redirect_response_test(
+ t,
+ 'resources/redirect-scope.py?type=no-location',
+ '',
+ ['resources/redirect-scope.py?type=no-location']);
+ }, 'Navigation Preload no-location redirect response.');
+
+promise_test(t => {
+ return redirect_response_test(
+ t,
+ 'resources/redirect-scope.py?type=no-location-with-body',
+ 'BODY',
+ ['resources/redirect-scope.py?type=no-location-with-body']);
+ }, 'Navigation Preload no-location redirect response with body.');
+
+promise_test(t => {
+ return redirect_response_test(
+ t,
+ 'resources/redirect-scope.py?type=redirect-to-scope',
+ 'redirected\n',
+ ['resources/redirect-scope.py?type=redirect-to-scope',
+ 'resources/redirect-scope.py?type=redirect-to-scope2',
+ 'resources/redirect-scope.py?type=redirect-to-scope3',]);
+ }, 'Navigation Preload redirect to the same scope.');
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/request-headers.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/request-headers.https.html
new file mode 100644
index 0000000000..0964201021
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/request-headers.https.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload request headers</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+promise_test(t => {
+ var script = 'resources/request-headers-worker.js';
+ var scope = 'resources/request-headers-scope.py';
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(registration => {
+ add_completion_callback(_ => registration.unregister());
+ var worker = registration.installing;
+ return wait_for_state(t, worker, 'activated');
+ })
+ .then(_ => with_iframe(scope))
+ .then(frame => {
+ var headers = JSON.parse(frame.contentDocument.body.textContent);
+ assert_true(
+ 'SERVICE-WORKER-NAVIGATION-PRELOAD' in headers,
+ 'The Navigation Preload request must specify a ' +
+ '"Service-Worker-Navigation-Preload" header.');
+ assert_array_equals(
+ headers['SERVICE-WORKER-NAVIGATION-PRELOAD'],
+ ['hello'],
+ 'The Navigation Preload request must specify the correct value ' +
+ 'for the "Service-Worker-Navigation-Preload" header.');
+ assert_true(
+ 'UPGRADE-INSECURE-REQUESTS' in headers,
+ 'The Navigation Preload request must specify an ' +
+ '"Upgrade-Insecure-Requests" header.');
+ assert_array_equals(
+ headers['UPGRADE-INSECURE-REQUESTS'],
+ ['1'],
+ 'The Navigation Preload request must specify the correct value ' +
+ 'for the "Upgrade-Insecure-Requests" header.');
+ });
+ }, 'Navigation Preload request headers.');
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resource-timing.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resource-timing.https.html
new file mode 100644
index 0000000000..468a1f51e8
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resource-timing.https.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload Resource Timing</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<script>
+
+function check_timing_entry(entry, url, decodedBodySize, encodedBodySize) {
+ assert_equals(entry.name, url, 'The entry name of '+ url);
+
+ assert_equals(
+ entry.entryType, 'resource',
+ 'The entryType of preload response timing entry must be "resource' +
+ '" :' + url);
+ assert_equals(
+ entry.initiatorType, 'navigation',
+ 'The initiatorType of preload response timing entry must be ' +
+ '"navigation":' + url);
+
+ // If the server returns the redirect response, |decodedBodySize| is null and
+ // |entry.decodedBodySize| should be 0. Otherwise |entry.decodedBodySize| must
+ // same as |decodedBodySize|
+ assert_equals(
+ entry.decodedBodySize, Number(decodedBodySize),
+ 'decodedBodySize must same as the decoded size in the server:' + url);
+
+ // If the server returns the redirect response, |encodedBodySize| is null and
+ // |entry.encodedBodySize| should be 0. Otherwise |entry.encodedBodySize| must
+ // same as |encodedBodySize|
+ assert_equals(
+ entry.encodedBodySize, Number(encodedBodySize),
+ 'encodedBodySize must same as the encoded size in the server:' + url);
+
+ assert_greater_than(
+ entry.transferSize, entry.decodedBodySize,
+ 'transferSize must greater then encodedBodySize.');
+
+ assert_greater_than(entry.startTime, 0, 'startTime of ' + url);
+ assert_greater_than_equal(entry.fetchStart, entry.startTime,
+ 'fetchStart >= startTime of ' + url);
+ assert_greater_than_equal(entry.domainLookupStart, entry.fetchStart,
+ 'domainLookupStart >= fetchStart of ' + url);
+ assert_greater_than_equal(entry.domainLookupEnd, entry.domainLookupStart,
+ 'domainLookupEnd >= domainLookupStart of ' + url);
+ assert_greater_than_equal(entry.connectStart, entry.domainLookupEnd,
+ 'connectStart >= domainLookupEnd of ' + url);
+ assert_greater_than_equal(entry.connectEnd, entry.connectStart,
+ 'connectEnd >= connectStart of ' + url);
+ assert_greater_than_equal(entry.requestStart, entry.connectEnd,
+ 'requestStart >= connectEnd of ' + url);
+ assert_greater_than_equal(entry.responseStart, entry.requestStart,
+ 'domainLookupStart >= requestStart of ' + url);
+ assert_greater_than_equal(entry.responseEnd, entry.responseStart,
+ 'responseEnd >= responseStart of ' + url);
+ assert_greater_than(entry.duration, 0, 'duration of ' + url);
+}
+
+promise_test(t => {
+ var script = 'resources/resource-timing-worker.js';
+ var scope = 'resources/resource-timing-scope.py';
+ var registration;
+ var frames = [];
+ return service_worker_unregister_and_register(t, script, scope)
+ .then(reg => {
+ registration = reg;
+ add_completion_callback(_ => registration.unregister());
+ return wait_for_state(t, registration.installing, 'activated');
+ })
+ .then(_ => with_iframe(scope + '?type=normal'))
+ .then(frame => {
+ frames.push(frame);
+ return with_iframe(scope + '?type=redirect');
+ })
+ .then(frame => {
+ frames.push(frame);
+ frames.forEach(frame => {
+ var result = JSON.parse(frame.contentDocument.body.textContent);
+ assert_equals(
+ result.timingEntries.length, 1,
+ 'performance.getEntriesByName() must returns one ' +
+ 'PerformanceResourceTiming entry for the navigation preload.');
+ var entry = result.timingEntries[0];
+ check_timing_entry(entry, frame.src, result.decodedBodySize,
+ result.encodedBodySize);
+ frame.remove();
+ });
+ return registration.unregister();
+ });
+ }, 'Navigation Preload Resource Timing.');
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-scope.asis b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-scope.asis
new file mode 100644
index 0000000000..2a719536fb
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-scope.asis
@@ -0,0 +1,6 @@
+HTTP/1.1 200 OK
+Content-type: text/html; charset=UTF-8
+Transfer-encoding: chunked
+
+hello
+world
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-worker.js
new file mode 100644
index 0000000000..7a453e4055
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/broken-chunked-encoding-worker.js
@@ -0,0 +1,11 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ self.registration.navigationPreload.enable());
+ });
+
+self.addEventListener('fetch', event => {
+ event.respondWith(event.preloadResponse
+ .then(
+ _ => new Response('PASS: preloadResponse resolved'),
+ _ => new Response('FAIL: preloadResponse rejected')));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-scope.py b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-scope.py
new file mode 100644
index 0000000000..659c4d8cdf
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-scope.py
@@ -0,0 +1,19 @@
+import time
+
+def main(request, response):
+ use_broken_body = b'use_broken_body' in request.GET
+
+ response.add_required_headers = False
+ response.writer.write_status(200)
+ response.writer.write_header(b"Content-type", b"text/html; charset=UTF-8")
+ response.writer.write_header(b"Transfer-encoding", b"chunked")
+ response.writer.end_headers()
+
+ for idx in range(10):
+ if use_broken_body:
+ response.writer.write(u"%s\n%s\n" % (len(str(idx)), idx))
+ else:
+ response.writer.write(u"%s\r\n%s\r\n" % (len(str(idx)), idx))
+ time.sleep(0.001)
+
+ response.writer.write(u"0\r\n\r\n")
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-worker.js
new file mode 100644
index 0000000000..f30e5ed274
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/chunked-encoding-worker.js
@@ -0,0 +1,8 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ self.registration.navigationPreload.enable());
+ });
+
+self.addEventListener('fetch', event => {
+ event.respondWith(event.preloadResponse);
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/cookie.py b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/cookie.py
new file mode 100644
index 0000000000..30a1dd498a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/cookie.py
@@ -0,0 +1,20 @@
+def main(request, response):
+ """
+ Returns a response with a Set-Cookie header based on the query params.
+ The body will be "1" if the cookie is present in the request and `drop` parameter is "0",
+ otherwise the body will be "0".
+ """
+ same_site = request.GET.first(b"same-site")
+ cookie_name = request.GET.first(b"cookie-name")
+ drop = request.GET.first(b"drop")
+ cookie_in_request = b"0"
+ cookie = b"%s=1; Secure; SameSite=%s" % (cookie_name, same_site)
+
+ if drop == b"1":
+ cookie += b"; Max-Age=0"
+
+ if request.cookies.get(cookie_name):
+ cookie_in_request = request.cookies[cookie_name].value
+
+ headers = [(b'Content-Type', b'text/html'), (b'Set-Cookie', cookie)]
+ return (200, headers, cookie_in_request)
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-scope.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-scope.html
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-scope.html
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-worker.js
new file mode 100644
index 0000000000..48c14b7916
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/empty-preload-response-body-worker.js
@@ -0,0 +1,15 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ self.registration.navigationPreload.enable());
+ });
+
+self.addEventListener('fetch', event => {
+ event.respondWith(
+ event.preloadResponse
+ .then(res => res.text())
+ .then(text => {
+ return new Response(
+ '<body>[' + text + ']</body>',
+ {headers: [['content-type', 'text/html']]});
+ }));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/get-state-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/get-state-worker.js
new file mode 100644
index 0000000000..a14ffb4faa
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/get-state-worker.js
@@ -0,0 +1,21 @@
+// This worker listens for commands from the page and messages back
+// the result.
+
+function handle(message) {
+ const np = self.registration.navigationPreload;
+ switch (message) {
+ case 'getState':
+ return np.getState();
+ case 'enable':
+ return np.enable();
+ case 'disable':
+ return np.disable();
+ case 'setHeaderValue':
+ return np.setHeaderValue('insightful');
+ }
+ return Promise.reject('bad message');
+}
+
+self.addEventListener('message', e => {
+ e.waitUntil(handle(e.data).then(result => e.source.postMessage(result)));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/helpers.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/helpers.js
new file mode 100644
index 0000000000..86f0c0916e
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/helpers.js
@@ -0,0 +1,5 @@
+function expect_navigation_preload_state(state, enabled, header, desc) {
+ assert_equals(Object.keys(state).length, 2, desc + ': # of keys');
+ assert_equals(state.enabled, enabled, desc + ': enabled');
+ assert_equals(state.headerValue, header, desc + ': header');
+}
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/navigation-preload-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/navigation-preload-worker.js
new file mode 100644
index 0000000000..6e1ab23290
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/navigation-preload-worker.js
@@ -0,0 +1,3 @@
+self.addEventListener('fetch', event => {
+ event.respondWith(event.preloadResponse);
+});
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-redirected.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-redirected.html
new file mode 100644
index 0000000000..f9bfce5e89
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-redirected.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<body>redirected</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-scope.py b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-scope.py
new file mode 100644
index 0000000000..84a97e594b
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-scope.py
@@ -0,0 +1,38 @@
+def main(request, response):
+ if b"base" in request.GET:
+ return [(b"Content-Type", b"text/html")], b"OK"
+ type = request.GET.first(b"type")
+
+ if type == b"normal":
+ response.status = 302
+ response.headers.append(b"Location", b"redirect-redirected.html")
+ response.headers.append(b"Custom-Header", b"hello")
+ return b""
+
+ if type == b"no-location":
+ response.status = 302
+ response.headers.append(b"Content-Type", b"text/html")
+ response.headers.append(b"Custom-Header", b"hello")
+ return b""
+
+ if type == b"no-location-with-body":
+ response.status = 302
+ response.headers.append(b"Content-Type", b"text/html")
+ response.headers.append(b"Custom-Header", b"hello")
+ return b"<body>BODY</body>"
+
+ if type == b"redirect-to-scope":
+ response.status = 302
+ response.headers.append(b"Location",
+ b"redirect-scope.py?type=redirect-to-scope2")
+ return b""
+ if type == b"redirect-to-scope2":
+ response.status = 302
+ response.headers.append(b"Location",
+ b"redirect-scope.py?type=redirect-to-scope3")
+ return b""
+ if type == b"redirect-to-scope3":
+ response.status = 302
+ response.headers.append(b"Location", b"redirect-redirected.html")
+ response.headers.append(b"Custom-Header", b"hello")
+ return b""
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-worker.js
new file mode 100644
index 0000000000..1b55f2ef0d
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/redirect-worker.js
@@ -0,0 +1,35 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ self.registration.navigationPreload.enable());
+ });
+
+function get_response_info(r) {
+ var info = {
+ type: r.type,
+ url: r.url,
+ status: r.status,
+ ok: r.ok,
+ statusText: r.statusText,
+ headers: []
+ };
+ r.headers.forEach((value, name) => { info.headers.push([value, name]); });
+ return info;
+}
+
+function post_to_page(data) {
+ return self.clients.matchAll()
+ .then(clients => clients.forEach(client => client.postMessage(data)));
+}
+
+self.addEventListener('fetch', event => {
+ event.respondWith(
+ event.preloadResponse
+ .then(
+ res => {
+ if (res.url.includes("base")) {
+ return res;
+ }
+ return post_to_page(get_response_info(res)).then(_ => res);
+ },
+ err => new Response(err.toString())));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-scope.py b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-scope.py
new file mode 100644
index 0000000000..5bab5b01f3
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-scope.py
@@ -0,0 +1,14 @@
+import json
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ normalized = dict()
+
+ for key, values in dict(request.headers).items():
+ values = [isomorphic_decode(value) for value in values]
+ normalized[isomorphic_decode(key.upper())] = values
+
+ response.headers.append(b"Content-Type", b"text/html")
+
+ return json.dumps(normalized)
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-worker.js
new file mode 100644
index 0000000000..1006cf2791
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/request-headers-worker.js
@@ -0,0 +1,10 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ Promise.all[
+ self.registration.navigationPreload.enable(),
+ self.registration.navigationPreload.setHeaderValue('hello')]);
+ });
+
+self.addEventListener('fetch', event => {
+ event.respondWith(event.preloadResponse);
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-scope.py b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-scope.py
new file mode 100644
index 0000000000..856f9dbc2a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-scope.py
@@ -0,0 +1,19 @@
+import zlib
+
+def main(request, response):
+ type = request.GET.first(b"type")
+
+ if type == "normal":
+ content = b"This is Navigation Preload Resource Timing test."
+ output = zlib.compress(content, 9)
+ headers = [(b"Content-type", b"text/plain"),
+ (b"Content-Encoding", b"deflate"),
+ (b"X-Decoded-Body-Size", len(content)),
+ (b"X-Encoded-Body-Size", len(output)),
+ (b"Content-Length", len(output))]
+ return headers, output
+
+ if type == b"redirect":
+ response.status = 302
+ response.headers.append(b"Location", b"redirect-redirected.html")
+ return b""
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-worker.js
new file mode 100644
index 0000000000..fac0d8de2a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/resource-timing-worker.js
@@ -0,0 +1,37 @@
+async function wait_for_performance_entries(url) {
+ let entries = performance.getEntriesByName(url);
+ if (entries.length > 0) {
+ return entries;
+ }
+ return new Promise((resolve) => {
+ new PerformanceObserver((list) => {
+ const entries = list.getEntriesByName(url);
+ if (entries.length > 0) {
+ resolve(entries);
+ }
+ }).observe({ entryTypes: ['resource'] });
+ });
+}
+
+self.addEventListener('activate', event => {
+ event.waitUntil(self.registration.navigationPreload.enable());
+ });
+
+self.addEventListener('fetch', event => {
+ let headers;
+ event.respondWith(
+ event.preloadResponse
+ .then(response => {
+ headers = response.headers;
+ return response.text()
+ })
+ .then(_ => wait_for_performance_entries(event.request.url))
+ .then(entries =>
+ new Response(
+ JSON.stringify({
+ decodedBodySize: headers.get('X-Decoded-Body-Size'),
+ encodedBodySize: headers.get('X-Encoded-Body-Size'),
+ timingEntries: entries
+ }),
+ {headers: {'Content-Type': 'text/html'}})));
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-iframe.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-iframe.html
new file mode 100644
index 0000000000..a28b61261e
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-iframe.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<body>samesite</body>
+<script>
+onmessage = (e) => {
+ if (e.data === "GetBody") {
+ parent.postMessage("samesite", '*');
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-sw-helper.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-sw-helper.html
new file mode 100644
index 0000000000..51fdc9ec74
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-sw-helper.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload Same Site SW registrator</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../../resources/test-helpers.sub.js"></script>
+<script>
+
+/**
+ * This is a helper file to register/unregister service worker in a same-site
+ * iframe.
+ **/
+
+async function messageToParent(msg) {
+ parent.postMessage(msg, '*');
+}
+
+onmessage = async (e) => {
+ // t is a , but the helper function needs a test object.
+ let t = {
+ step_func: (func) => func,
+ };
+ if (e.data === "Register") {
+ let reg = await service_worker_unregister_and_register(t, "samesite-worker.js", ".");
+ let worker = reg.installing;
+ await wait_for_state(t, worker, 'activated');
+ await messageToParent("SW Registered");
+ } else if (e.data == "Unregister") {
+ await service_worker_unregister(t, ".");
+ await messageToParent("SW Unregistered");
+ }
+}
+
+</script>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-worker.js
new file mode 100644
index 0000000000..f30e5ed274
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/samesite-worker.js
@@ -0,0 +1,8 @@
+self.addEventListener('activate', event => {
+ event.waitUntil(
+ self.registration.navigationPreload.enable());
+ });
+
+self.addEventListener('fetch', event => {
+ event.respondWith(event.preloadResponse);
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/wait-for-activate-worker.js b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/wait-for-activate-worker.js
new file mode 100644
index 0000000000..87791d2e48
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/resources/wait-for-activate-worker.js
@@ -0,0 +1,40 @@
+// This worker remains in the installing phase so that the
+// navigation preload API can be tested when there is no
+// active worker.
+importScripts('/resources/testharness.js');
+importScripts('helpers.js');
+
+function expect_rejection(promise) {
+ return promise.then(
+ () => { return Promise.reject('unexpected fulfillment'); },
+ err => { assert_equals('InvalidStateError', err.name); });
+}
+
+function test_before_activation() {
+ const np = self.registration.navigationPreload;
+ return expect_rejection(np.enable())
+ .then(() => expect_rejection(np.disable()))
+ .then(() => expect_rejection(np.setHeaderValue('hi')))
+ .then(() => np.getState())
+ .then(state => expect_navigation_preload_state(
+ state, false, 'true', 'state should be the default'))
+ .then(() => 'PASS')
+ .catch(err => 'FAIL: ' + err);
+}
+
+var resolve_done_promise;
+var done_promise = new Promise(resolve => { resolve_done_promise = resolve; });
+
+// Run the test once the page messages this worker.
+self.addEventListener('message', e => {
+ e.waitUntil(test_before_activation()
+ .then(result => {
+ e.source.postMessage(result);
+ resolve_done_promise();
+ }));
+ });
+
+// Don't become the active worker until the test is done.
+self.addEventListener('install', e => {
+ e.waitUntil(done_promise);
+ });
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-cookies.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-cookies.https.html
new file mode 100644
index 0000000000..a860d95456
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-cookies.https.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="timeout" content="long">
+<title>Navigation Preload: SameSite cookies</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<body>
+<script>
+const scope = 'resources/cookie.py';
+const script = 'resources/navigation-preload-worker.js';
+
+async function drop_cookie(t, same_site, cookie) {
+ const frame = await with_iframe(scope + '?same-site=' + same_site + '&cookie-name=' + cookie + '&drop=1');
+ t.add_cleanup(() => frame.remove());
+}
+
+async function same_site_cookies_test(t, same_site, cookie) {
+ // Remove the cookie before the first visit.
+ await drop_cookie(t, same_site, cookie);
+
+ {
+ const frame = await with_iframe(scope + '?same-site=' + same_site + '&cookie-name=' + cookie + '&drop=0');
+ t.add_cleanup(() => frame.remove());
+ // The body will be 0 because this is the first visit.
+ assert_equals(frame.contentDocument.body.textContent, '0', 'first visit');
+ }
+
+ {
+ const frame = await with_iframe(scope + '?same-site=' + same_site + '&cookie-name=' + cookie + '&drop=0');
+ t.add_cleanup(() => frame.remove());
+ // The body will be 1 because this is the second visit.
+ assert_equals(frame.contentDocument.body.textContent, '1', 'second visit');
+ }
+
+ // Remove the cookie after the test.
+ t.add_cleanup(() => drop_cookie(t, same_site, cookie));
+}
+
+promise_test(async t => {
+ const registration =
+ await service_worker_unregister_and_register(t, script, scope);
+ promise_test(t => registration.unregister(), 'Unregister a service worker.');
+
+ await wait_for_state(t, registration.installing, 'activated');
+ await registration.navigationPreload.enable();
+}, 'Set up a service worker for navigation preload tests.');
+
+promise_test(async t => {
+ await same_site_cookies_test(t, 'None', 'cookie-key-none');
+}, 'Navigation Preload for same site cookies (None).');
+
+promise_test(async t => {
+ await same_site_cookies_test(t, 'Strict', 'cookie-key-strict');
+}, 'Navigation Preload for same site cookies (Strict).');
+
+promise_test(async t => {
+ await same_site_cookies_test(t, 'Lax', 'cookie-key-lax');
+}, 'Navigation Preload for same site cookies (Lax).');
+</script>
+</body>
diff --git a/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-iframe.https.html b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-iframe.https.html
new file mode 100644
index 0000000000..633da9926a
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/navigation-preload/samesite-iframe.https.html
@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Navigation Preload for same site iframe</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="../resources/test-helpers.sub.js"></script>
+<body></body>
+<script>
+
+const SAME_SITE = get_host_info().HTTPS_REMOTE_ORIGIN;
+const RESOURCES_DIR = "/service-workers/service-worker/navigation-preload/resources/";
+
+/**
+ * This test is used for testing the NavigationPreload works in a same site iframe.
+ * The test scenario is
+ * 1. Create a same site iframe to register service worker and wait for it be activated
+ * 2. Create a same site iframe which be intercepted by the service worker.
+ * 3. Once the iframe is loaded, service worker should set the page through the preload response.
+ * And checking if the iframe's body content is expected.
+ * 4. Unregister the service worker.
+ * 5. remove created iframes.
+ */
+
+promise_test(async (t) => {
+ let resolver;
+ let checkValue = false;
+ window.onmessage = (e) => {
+ if (checkValue) {
+ assert_equals(e.data, "samesite");
+ checkValue = false;
+ }
+ resolver();
+ };
+
+ let helperIframe = document.createElement("iframe");
+ helperIframe.src = SAME_SITE + RESOURCES_DIR + "samesite-sw-helper.html";
+ document.body.appendChild(helperIframe);
+
+ await new Promise(resolve => {
+ resolver = resolve;
+ helperIframe.onload = async () => {
+ helperIframe.contentWindow.postMessage("Register", '*');
+ }
+ });
+
+ let sameSiteIframe = document.createElement("iframe");
+ sameSiteIframe.src = SAME_SITE + RESOURCES_DIR + "samesite-iframe.html";
+ document.body.appendChild(sameSiteIframe);
+ await new Promise(resolve => {
+ resolver = resolve;
+ sameSiteIframe.onload = async() => {
+ checkValue = true;
+ sameSiteIframe.contentWindow.postMessage("GetBody", '*')
+ }
+ });
+
+ await new Promise(resolve => {
+ resolver = resolve;
+ helperIframe.contentWindow.postMessage("Unregister", '*')
+ });
+
+ helperIframe.remove();
+ sameSiteIframe.remove();
+ });
+
+</script>