clients-get-resultingClientId.https.html (6999B)
1 <!DOCTYPE html> 2 <meta charset="utf-8"> 3 <title>Test clients.get(resultingClientId)</title> 4 <script src="/resources/testharness.js"></script> 5 <script src="/resources/testharnessreport.js"></script> 6 <script src="/common/get-host-info.sub.js"></script> 7 <script src="resources/test-helpers.sub.js"></script> 8 <script> 9 const scope = "resources/"; 10 let worker; 11 12 // Setup. Keep this as the first promise_test. 13 promise_test(async (t) => { 14 const registration = await service_worker_unregister_and_register( 15 t, 'resources/get-resultingClientId-worker.js', 16 scope); 17 worker = registration.installing; 18 await wait_for_state(t, worker, 'activated'); 19 }, 'global setup'); 20 21 // Sends |command| to the worker and returns a promise that resolves to its 22 // response. There should only be one inflight command at a time. 23 async function sendCommand(command) { 24 const saw_message = new Promise((resolve) => { 25 navigator.serviceWorker.onmessage = (event) => { 26 resolve(event.data); 27 }; 28 }); 29 worker.postMessage(command); 30 return saw_message; 31 } 32 33 // Wrapper for 'startTest' command. Tells the worker a test is starting, 34 // so it resets state and keeps itself alive until 'finishTest'. 35 async function startTest(t) { 36 const result = await sendCommand({command: 'startTest'}); 37 assert_equals(result, 'ok', 'startTest'); 38 39 t.add_cleanup(async () => { 40 return finishTest(); 41 }); 42 } 43 44 // Wrapper for 'finishTest' command. 45 async function finishTest() { 46 const result = await sendCommand({command: 'finishTest'}); 47 assert_equals(result, 'ok', 'finishTest'); 48 } 49 50 // Wrapper for 'getResultingClient' command. Tells the worker to return 51 // clients.get(event.resultingClientId) for the navigation that occurs 52 // during this test. 53 // 54 // The return value describes how clients.get() settled. It also includes 55 // |queriedId| which is the id passed to clients.get() (the resultingClientId 56 // in this case). 57 // 58 // Example value: 59 // { 60 // queriedId: 'abc', 61 // promiseState: fulfilled, 62 // promiseValue: client, 63 // client: { 64 // id: 'abc', 65 // url: '//example.com/client' 66 // } 67 // } 68 async function getResultingClient() { 69 return sendCommand({command: 'getResultingClient'}); 70 } 71 72 // Wrapper for 'getClient' command. Tells the worker to return 73 // clients.get(|id|). The return value is as in the getResultingClient() 74 // documentation. 75 async function getClient(id) { 76 return sendCommand({command: 'getClient', id: id}); 77 } 78 79 // Navigates to |url|. Returns the result of clients.get() on the 80 // resultingClientId. 81 async function navigateAndGetResultingClient(t, url) { 82 const resultPromise = getResultingClient(); 83 const frame = await with_iframe(url); 84 t.add_cleanup(() => { 85 frame.remove(); 86 }); 87 const result = await resultPromise; 88 const resultingClientId = result.queriedId; 89 90 // First test clients.get(event.resultingClientId) inside the fetch event. The 91 // behavior of this is subtle due to the use of iframes and about:blank 92 // replacement. The spec probably requires that it resolve to the original 93 // about:blank client, and that later that client should be discarded after 94 // load if the load was to another origin. Implementations might differ. For 95 // now, this test just asserts that the promise resolves. See 96 // https://github.com/w3c/ServiceWorker/issues/1385. 97 assert_equals(result.promiseState, 'fulfilled', 98 'get(event.resultingClientId) in the fetch event should fulfill'); 99 100 // Test clients.get() on the previous resultingClientId again. By this 101 // time the load finished, so it's more straightforward how this promise 102 // should settle. Return the result of this promise. 103 return await getClient(resultingClientId); 104 } 105 106 // Test get(resultingClientId) in the basic same-origin case. 107 promise_test(async (t) => { 108 await startTest(t); 109 110 const url = new URL('resources/empty.html', window.location); 111 const result = await navigateAndGetResultingClient(t, url); 112 assert_equals(result.promiseState, 'fulfilled', 'promiseState'); 113 assert_equals(result.promiseValue, 'client', 'promiseValue'); 114 assert_equals(result.client.url, url.href, 'client.url',); 115 assert_equals(result.client.id, result.queriedId, 'client.id'); 116 }, 'get(resultingClientId) for same-origin document'); 117 118 // Test get(resultingClientId) when the response redirects to another origin. 119 promise_test(async (t) => { 120 await startTest(t); 121 122 // Navigate to a URL that redirects to another origin. 123 const base_url = new URL('.', window.location); 124 const host_info = get_host_info(); 125 const other_origin_url = new URL(base_url.pathname + 'resources/empty.html', 126 host_info['HTTPS_REMOTE_ORIGIN']); 127 const url = new URL('resources/empty.html', window.location); 128 const pipe = `status(302)|header(Location, ${other_origin_url})`; 129 url.searchParams.set('pipe', pipe); 130 131 // The original reserved client should have been discarded on cross-origin 132 // redirect. 133 const result = await navigateAndGetResultingClient(t, url); 134 assert_equals(result.promiseState, 'fulfilled', 'promiseState'); 135 assert_equals(result.promiseValue, 'undefinedValue', 'promiseValue'); 136 }, 'get(resultingClientId) on cross-origin redirect'); 137 138 // Test get(resultingClientId) when the document is sandboxed to a unique 139 // origin using a CSP HTTP response header. 140 promise_test(async (t) => { 141 await startTest(t); 142 143 // Navigate to a URL that has CSP sandboxing set in the HTTP response header. 144 const url = new URL('resources/empty.html', window.location); 145 const pipe = 'header(Content-Security-Policy, sandbox)'; 146 url.searchParams.set('pipe', pipe); 147 148 // The original reserved client should have been discarded upon loading 149 // the sandboxed document. 150 const result = await navigateAndGetResultingClient(t, url); 151 assert_equals(result.promiseState, 'fulfilled', 'promiseState'); 152 assert_equals(result.promiseValue, 'undefinedValue', 'promiseValue'); 153 }, 'get(resultingClientId) for document sandboxed by CSP header'); 154 155 // Test get(resultingClientId) when the document is sandboxed with 156 // allow-same-origin. 157 promise_test(async (t) => { 158 await startTest(t); 159 160 // Navigate to a URL that has CSP sandboxing set in the HTTP response header. 161 const url = new URL('resources/empty.html', window.location); 162 const pipe = 'header(Content-Security-Policy, sandbox allow-same-origin)'; 163 url.searchParams.set('pipe', pipe); 164 165 // The client should be the original reserved client, as it's same-origin. 166 const result = await navigateAndGetResultingClient(t, url); 167 assert_equals(result.promiseState, 'fulfilled', 'promiseState'); 168 assert_equals(result.promiseValue, 'client', 'promiseValue'); 169 assert_equals(result.client.url, url.href, 'client.url',); 170 assert_equals(result.client.id, result.queriedId, 'client.id'); 171 }, 'get(resultingClientId) for document sandboxed by CSP header with allow-same-origin'); 172 173 // Cleanup. Keep this as the last promise_test. 174 promise_test(async (t) => { 175 return service_worker_unregister(t, scope); 176 }, 'global cleanup'); 177 </script>