summaryrefslogtreecommitdiffstats
path: root/test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html
diff options
context:
space:
mode:
Diffstat (limited to 'test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html')
-rw-r--r--test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html177
1 files changed, 177 insertions, 0 deletions
diff --git a/test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html b/test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html
new file mode 100644
index 0000000..3419cf1
--- /dev/null
+++ b/test/wpt/tests/service-workers/service-worker/clients-get-resultingClientId.https.html
@@ -0,0 +1,177 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test clients.get(resultingClientId)</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>
+<script>
+const scope = "resources/";
+let worker;
+
+// Setup. Keep this as the first promise_test.
+promise_test(async (t) => {
+ const registration = await service_worker_unregister_and_register(
+ t, 'resources/get-resultingClientId-worker.js',
+ scope);
+ worker = registration.installing;
+ await wait_for_state(t, worker, 'activated');
+}, 'global setup');
+
+// Sends |command| to the worker and returns a promise that resolves to its
+// response. There should only be one inflight command at a time.
+async function sendCommand(command) {
+ const saw_message = new Promise((resolve) => {
+ navigator.serviceWorker.onmessage = (event) => {
+ resolve(event.data);
+ };
+ });
+ worker.postMessage(command);
+ return saw_message;
+}
+
+// Wrapper for 'startTest' command. Tells the worker a test is starting,
+// so it resets state and keeps itself alive until 'finishTest'.
+async function startTest(t) {
+ const result = await sendCommand({command: 'startTest'});
+ assert_equals(result, 'ok', 'startTest');
+
+ t.add_cleanup(async () => {
+ return finishTest();
+ });
+}
+
+// Wrapper for 'finishTest' command.
+async function finishTest() {
+ const result = await sendCommand({command: 'finishTest'});
+ assert_equals(result, 'ok', 'finishTest');
+}
+
+// Wrapper for 'getResultingClient' command. Tells the worker to return
+// clients.get(event.resultingClientId) for the navigation that occurs
+// during this test.
+//
+// The return value describes how clients.get() settled. It also includes
+// |queriedId| which is the id passed to clients.get() (the resultingClientId
+// in this case).
+//
+// Example value:
+// {
+// queriedId: 'abc',
+// promiseState: fulfilled,
+// promiseValue: client,
+// client: {
+// id: 'abc',
+// url: '//example.com/client'
+// }
+// }
+async function getResultingClient() {
+ return sendCommand({command: 'getResultingClient'});
+}
+
+// Wrapper for 'getClient' command. Tells the worker to return
+// clients.get(|id|). The return value is as in the getResultingClient()
+// documentation.
+async function getClient(id) {
+ return sendCommand({command: 'getClient', id: id});
+}
+
+// Navigates to |url|. Returns the result of clients.get() on the
+// resultingClientId.
+async function navigateAndGetResultingClient(t, url) {
+ const resultPromise = getResultingClient();
+ const frame = await with_iframe(url);
+ t.add_cleanup(() => {
+ frame.remove();
+ });
+ const result = await resultPromise;
+ const resultingClientId = result.queriedId;
+
+ // First test clients.get(event.resultingClientId) inside the fetch event. The
+ // behavior of this is subtle due to the use of iframes and about:blank
+ // replacement. The spec probably requires that it resolve to the original
+ // about:blank client, and that later that client should be discarded after
+ // load if the load was to another origin. Implementations might differ. For
+ // now, this test just asserts that the promise resolves. See
+ // https://github.com/w3c/ServiceWorker/issues/1385.
+ assert_equals(result.promiseState, 'fulfilled',
+ 'get(event.resultingClientId) in the fetch event should fulfill');
+
+ // Test clients.get() on the previous resultingClientId again. By this
+ // time the load finished, so it's more straightforward how this promise
+ // should settle. Return the result of this promise.
+ return await getClient(resultingClientId);
+}
+
+// Test get(resultingClientId) in the basic same-origin case.
+promise_test(async (t) => {
+ await startTest(t);
+
+ const url = new URL('resources/empty.html', window.location);
+ const result = await navigateAndGetResultingClient(t, url);
+ assert_equals(result.promiseState, 'fulfilled', 'promiseState');
+ assert_equals(result.promiseValue, 'client', 'promiseValue');
+ assert_equals(result.client.url, url.href, 'client.url',);
+ assert_equals(result.client.id, result.queriedId, 'client.id');
+}, 'get(resultingClientId) for same-origin document');
+
+// Test get(resultingClientId) when the response redirects to another origin.
+promise_test(async (t) => {
+ await startTest(t);
+
+ // Navigate to a URL that redirects to another origin.
+ const base_url = new URL('.', window.location);
+ const host_info = get_host_info();
+ const other_origin_url = new URL(base_url.pathname + 'resources/empty.html',
+ host_info['HTTPS_REMOTE_ORIGIN']);
+ const url = new URL('resources/empty.html', window.location);
+ const pipe = `status(302)|header(Location, ${other_origin_url})`;
+ url.searchParams.set('pipe', pipe);
+
+ // The original reserved client should have been discarded on cross-origin
+ // redirect.
+ const result = await navigateAndGetResultingClient(t, url);
+ assert_equals(result.promiseState, 'fulfilled', 'promiseState');
+ assert_equals(result.promiseValue, 'undefinedValue', 'promiseValue');
+}, 'get(resultingClientId) on cross-origin redirect');
+
+// Test get(resultingClientId) when the document is sandboxed to a unique
+// origin using a CSP HTTP response header.
+promise_test(async (t) => {
+ await startTest(t);
+
+ // Navigate to a URL that has CSP sandboxing set in the HTTP response header.
+ const url = new URL('resources/empty.html', window.location);
+ const pipe = 'header(Content-Security-Policy, sandbox)';
+ url.searchParams.set('pipe', pipe);
+
+ // The original reserved client should have been discarded upon loading
+ // the sandboxed document.
+ const result = await navigateAndGetResultingClient(t, url);
+ assert_equals(result.promiseState, 'fulfilled', 'promiseState');
+ assert_equals(result.promiseValue, 'undefinedValue', 'promiseValue');
+}, 'get(resultingClientId) for document sandboxed by CSP header');
+
+// Test get(resultingClientId) when the document is sandboxed with
+// allow-same-origin.
+promise_test(async (t) => {
+ await startTest(t);
+
+ // Navigate to a URL that has CSP sandboxing set in the HTTP response header.
+ const url = new URL('resources/empty.html', window.location);
+ const pipe = 'header(Content-Security-Policy, sandbox allow-same-origin)';
+ url.searchParams.set('pipe', pipe);
+
+ // The client should be the original reserved client, as it's same-origin.
+ const result = await navigateAndGetResultingClient(t, url);
+ assert_equals(result.promiseState, 'fulfilled', 'promiseState');
+ assert_equals(result.promiseValue, 'client', 'promiseValue');
+ assert_equals(result.client.url, url.href, 'client.url',);
+ assert_equals(result.client.id, result.queriedId, 'client.id');
+}, 'get(resultingClientId) for document sandboxed by CSP header with allow-same-origin');
+
+// Cleanup. Keep this as the last promise_test.
+promise_test(async (t) => {
+ return service_worker_unregister(t, scope);
+}, 'global cleanup');
+</script>