| <!DOCTYPE html> |
| <title>Service Worker: Navigation redirection</title> |
| <meta name="timeout" content="long"> |
| <!-- empty variant tests document.location and intercepted URLs --> |
| <meta name="variant" content=""> |
| <!-- client variant tests the Clients API (resultingClientId and Client.url) --> |
| <meta name="variant" content="?client"> |
| |
| <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> |
| <script> |
| const host_info = get_host_info(); |
| |
| // This test registers three Service Workers at SCOPE1, SCOPE2 and |
| // OTHER_ORIGIN_SCOPE. And checks the redirected page's URL and the requests |
| // which are intercepted by Service Worker while loading redirect page. |
| const BASE_URL = host_info['HTTPS_ORIGIN'] + base_path(); |
| const OTHER_BASE_URL = host_info['HTTPS_REMOTE_ORIGIN'] + base_path(); |
| |
| const SCOPE1 = BASE_URL + 'resources/navigation-redirect-scope1.py?'; |
| const SCOPE2 = BASE_URL + 'resources/navigation-redirect-scope2.py?'; |
| const OUT_SCOPE = BASE_URL + 'resources/navigation-redirect-out-scope.py?'; |
| const SCRIPT = 'resources/redirect-worker.js'; |
| |
| const OTHER_ORIGIN_IFRAME_URL = |
| OTHER_BASE_URL + 'resources/navigation-redirect-other-origin.html'; |
| const OTHER_ORIGIN_SCOPE = |
| OTHER_BASE_URL + 'resources/navigation-redirect-scope1.py?'; |
| const OTHER_ORIGIN_OUT_SCOPE = |
| OTHER_BASE_URL + 'resources/navigation-redirect-out-scope.py?'; |
| |
| let registrations; |
| let workers; |
| let other_origin_frame; |
| let message_resolvers = {}; |
| let next_message_id = 0; |
| |
| promise_test(async t => { |
| // In this frame we register a service worker at OTHER_ORIGIN_SCOPE. |
| // And will use this frame to communicate with the worker. |
| other_origin_frame = await with_iframe(OTHER_ORIGIN_IFRAME_URL); |
| |
| // Register same-origin service workers. |
| registrations = await Promise.all([ |
| service_worker_unregister_and_register(t, SCRIPT, SCOPE1), |
| service_worker_unregister_and_register(t, SCRIPT, SCOPE2)]); |
| |
| // Wait for all workers to activate. |
| workers = registrations.map(get_effective_worker); |
| return Promise.all([ |
| wait_for_state(t, workers[0], 'activated'), |
| wait_for_state(t, workers[1], 'activated'), |
| // This promise will resolve when |wait_for_worker_promise| |
| // in OTHER_ORIGIN_IFRAME_URL resolves. |
| send_to_iframe(other_origin_frame, {command: 'wait_for_worker'})]); |
| }, 'initialize global state'); |
| |
| function get_effective_worker(registration) { |
| if (registration.active) |
| return registration.active; |
| if (registration.waiting) |
| return registration.waiting; |
| if (registration.installing) |
| return registration.installing; |
| } |
| |
| async function check_all_intercepted_urls(expected_urls) { |
| const urls = []; |
| urls.push(await get_intercepted_urls(workers[0])); |
| urls.push(await get_intercepted_urls(workers[1])); |
| // Gets the request URLs which are intercepted by OTHER_ORIGIN_SCOPE's |
| // SW. This promise will resolve when get_request_infos() in |
| // OTHER_ORIGIN_IFRAME_URL resolves. |
| const request_infos = await send_to_iframe(other_origin_frame, |
| {command: 'get_request_infos'}); |
| urls.push(request_infos.map(info => { return info.url; })); |
| |
| assert_object_equals(urls, expected_urls, 'Intercepted URLs should match.'); |
| } |
| |
| // Checks |clients| returned from a worker. Only the client matching |
| // |expected_final_client_tag| should be found. Returns true if a client was |
| // found. Note that the final client is not necessarily found by this worker, |
| // if the client is cross-origin. |
| // |
| // |clients| is an object like: |
| // {x: {found: true, id: id1, url: url1}, b: {found: false}} |
| function check_clients(clients, |
| expected_id, |
| expected_url, |
| expected_final_client_tag, |
| worker_name) { |
| let found = false; |
| Object.keys(clients).forEach(key => { |
| const info = clients[key]; |
| if (info.found) { |
| assert_true(!!expected_final_client_tag, |
| `${worker_name} client tag exists`); |
| assert_equals(key, expected_final_client_tag, |
| `${worker_name} client tag matches`); |
| assert_equals(info.id, expected_id, `${worker_name} client id`); |
| assert_equals(info.url, expected_url, `${worker_name} client url`); |
| found = true; |
| } |
| }); |
| return found; |
| } |
| |
| function check_resulting_client_ids(infos, expected_infos, actual_ids, worker) { |
| assert_equals(infos.length, expected_infos.length, |
| `request length for ${worker}`); |
| for (var i = 0; i < infos.length; i++) { |
| const tag = expected_infos[i].resultingClientIdTag; |
| const url = expected_infos[i].url; |
| const actual_id = infos[i].resultingClientId; |
| const expected_id = actual_ids[tag]; |
| assert_equals(typeof(actual_id), 'string', |
| `resultingClientId for ${url} request to ${worker}`); |
| if (expected_id) { |
| assert_equals(actual_id, expected_id, |
| `resultingClientId for ${url} request to ${worker}`); |
| } else { |
| actual_ids[tag] = actual_id; |
| } |
| } |
| } |
| |
| // Creates an iframe and navigates to |url|, which is expected to start a chain |
| // of redirects. |
| // - |expected_last_url| is the expected window.location after the |
| // navigation. |
| // |
| // - |expected_request_infos| is the expected requests that the service workers |
| // were dispatched fetch events for. The format is: |
| // [ |
| // [ |
| // // Requests received by workers[0]. |
| // {url: url1, resultingClientIdTag: 'a'}, |
| // {url: url2, resultingClientIdTag: 'a'} |
| // ], |
| // [ |
| // // Requests received by workers[1]. |
| // {url: url3, resultingClientIdTag: 'a'} |
| // ], |
| // [ |
| // // Requests received by the cross-origin worker. |
| // {url: url4, resultingClientIdTag: 'x'} |
| // {url: url5, resultingClientIdTag: 'x'} |
| // ] |
| // ] |
| // Here, |url| is |event.request.url| and |resultingClientIdTag| represents |
| // |event.resultingClientId|. Since the actual client ids are not known |
| // beforehand, the expectation isn't the literal expected value, but all equal |
| // tags must map to the same actual id. |
| // |
| // - |expected_final_client_tag| is the resultingClientIdTag that is |
| // expected to map to the created client's id. This is null if there |
| // is no such tag, which can happen when the final request was a cross-origin |
| // redirect to out-scope, so no worker received a fetch event whose |
| // resultingClientId is the id of the resulting client. |
| // |
| // In the example above: |
| // - workers[0] receives two requests with the same resultingClientId. |
| // - workers[1] receives one request also with that resultingClientId. |
| // - The cross-origin worker receives two requests with the same |
| // resultingClientId which differs from the previous one. |
| // - Assuming |expected_final_client_tag| is 'x', then the created |
| // client has the id seen by the cross-origin worker above. |
| function redirect_test(url, |
| expected_last_url, |
| expected_request_infos, |
| expected_final_client_tag, |
| test_name) { |
| promise_test(async t => { |
| const frame = await with_iframe(url); |
| t.add_cleanup(() => { frame.remove(); }); |
| |
| // Switch on variant. |
| if (document.location.search == '?client') { |
| return client_variant_test(url, expected_last_url, expected_request_infos, |
| expected_final_client_tag, test_name); |
| } |
| |
| return default_variant_test(url, expected_last_url, expected_request_infos, |
| frame, test_name); |
| }, test_name); |
| } |
| |
| // The default variant tests the request interception chain and |
| // resulting document.location. |
| async function default_variant_test(url, |
| expected_last_url, |
| expected_request_infos, |
| frame, |
| test_name) { |
| const expected_intercepted_urls = expected_request_infos.map( |
| requests_for_worker => { |
| return requests_for_worker.map(info => { |
| return info.url; |
| }); |
| }); |
| await check_all_intercepted_urls(expected_intercepted_urls); |
| const last_url = await send_to_iframe(frame, 'getLocation'); |
| assert_equals(last_url, expected_last_url, 'Last URL should match.'); |
| } |
| |
| // The "client" variant tests the Clients API using resultingClientId. |
| async function client_variant_test(url, |
| expected_last_url, |
| expected_request_infos, |
| expected_final_client_tag, |
| test_name) { |
| // Request infos is an array like: |
| // [ |
| // [{url: url1, resultingClientIdTag: tag1}], |
| // [{url: url2, resultingClientIdTag: tag2}], |
| // [{url: url3: resultingClientIdTag: tag3}] |
| // ] |
| const requestInfos = await get_all_request_infos(); |
| |
| // We check the actual infos against the expected ones, and learn the |
| // actual ids as we go. |
| const actual_ids = {}; |
| check_resulting_client_ids(requestInfos[0], |
| expected_request_infos[0], |
| actual_ids, |
| 'worker0'); |
| check_resulting_client_ids(requestInfos[1], |
| expected_request_infos[1], |
| actual_ids, |
| 'worker1'); |
| check_resulting_client_ids(requestInfos[2], |
| expected_request_infos[2], |
| actual_ids, |
| 'crossOriginWorker'); |
| |
| // Now |actual_ids| maps tag to actual id: |
| // {x: id1, b: id2, c: id3} |
| // Ask each worker to try to resolve the actual ids to clients. |
| // Only |expected_final_client_tag| should resolve to a client. |
| const client_infos = await get_all_clients(actual_ids); |
| |
| // Client infos is an object like: |
| // { |
| // worker0: {x: {found: true, id: id1, url: url1}, b: {found: false}}, |
| // worker1: {x: {found: true, id: id1, url: url1}}, |
| // crossOriginWorker: {x: {found: false}}, {b: {found: false}} |
| // } |
| // |
| // Now check each client info. check_clients() verifies each info: only |
| // |expected_final_client_tag| should ever be found and the found client |
| // should have the expected url and id. A wrinkle is that not all workers |
| // will find the client, if they are cross-origin to the client. This |
| // means check_clients() trivially passes if no clients are found. So |
| // additionally check that at least one worker found the client (|found|), |
| // if that was expected (|expect_found|). |
| let found = false; |
| const expect_found = !!expected_final_client_tag; |
| const expected_id = actual_ids[expected_final_client_tag]; |
| found = check_clients(client_infos.worker0, |
| expected_id, |
| expected_last_url, |
| expected_final_client_tag, |
| 'worker0'); |
| found = check_clients(client_infos.worker1, |
| expected_id, |
| expected_last_url, |
| expected_final_client_tag, |
| 'worker1') || found; |
| found = check_clients(client_infos.crossOriginWorker, |
| expected_id, |
| expected_last_url, |
| expected_final_client_tag, |
| 'crossOriginWorker') || found; |
| assert_equals(found, expect_found, 'client found'); |
| |
| if (!expect_found) { |
| // TODO(falken): Ask the other origin frame if it has a client of the |
| // expected URL. |
| } |
| } |
| |
| window.addEventListener('message', on_message, false); |
| |
| function on_message(e) { |
| if (e.origin != host_info['HTTPS_REMOTE_ORIGIN'] && |
| e.origin != host_info['HTTPS_ORIGIN'] ) { |
| console.error('invalid origin: ' + e.origin); |
| return; |
| } |
| var resolve = message_resolvers[e.data.id]; |
| delete message_resolvers[e.data.id]; |
| resolve(e.data.result); |
| } |
| |
| function send_to_iframe(frame, message) { |
| var message_id = next_message_id++; |
| return new Promise(resolve => { |
| message_resolvers[message_id] = resolve; |
| frame.contentWindow.postMessage( |
| {id: message_id, message}, |
| '*'); |
| }); |
| } |
| |
| async function get_all_clients(actual_ids) { |
| const client_infos = {}; |
| client_infos['worker0'] = await get_clients(workers[0], actual_ids); |
| client_infos['worker1'] = await get_clients(workers[1], actual_ids); |
| client_infos['crossOriginWorker'] = |
| await send_to_iframe(other_origin_frame, |
| {command: 'get_clients', actual_ids}); |
| return client_infos; |
| } |
| |
| function get_clients(worker, actual_ids) { |
| return new Promise(resolve => { |
| var channel = new MessageChannel(); |
| channel.port1.onmessage = (msg) => { |
| resolve(msg.data.clients); |
| }; |
| worker.postMessage({command: 'getClients', actual_ids, port: channel.port2}, |
| [channel.port2]); |
| }); |
| } |
| |
| // Returns an array of the URLs that |worker| received fetch events for: |
| // [url1, url2] |
| async function get_intercepted_urls(worker) { |
| const infos = await get_request_infos(worker); |
| return infos.map(info => { return info.url; }); |
| } |
| |
| // Returns the requests that |worker| received fetch events for. The return |
| // value is an array of format: |
| // [ |
| // {url: url1, resultingClientId: id}, |
| // {url: url2, resultingClientId: id} |
| // ] |
| function get_request_infos(worker) { |
| return new Promise(resolve => { |
| var channel = new MessageChannel(); |
| channel.port1.onmessage = (msg) => { |
| resolve(msg.data.requestInfos); |
| }; |
| worker.postMessage({command: 'getRequestInfos', port: channel.port2}, |
| [channel.port2]); |
| }); |
| } |
| |
| // Returns an array of the requests the workers received fetch events for: |
| // [ |
| // // Requests from workers[0]. |
| // [ |
| // {url: url1, resultingClientIdTag: tag1}, |
| // {url: url2, resultingClientIdTag: tag1} |
| // ], |
| // |
| // // Requests from workers[1]. |
| // [{url: url3, resultingClientIdTag: tag2}], |
| // |
| // // Requests from the cross-origin worker. |
| // [] |
| // ] |
| async function get_all_request_infos() { |
| const request_infos = []; |
| request_infos.push(await get_request_infos(workers[0])); |
| request_infos.push(await get_request_infos(workers[1])); |
| request_infos.push(await send_to_iframe(other_origin_frame, |
| {command: 'get_request_infos'})); |
| return request_infos; |
| } |
| |
| let url; |
| let url1; |
| let url2; |
| |
| // Normal redirect (from out-scope to in-scope). |
| url = SCOPE1; |
| redirect_test( |
| OUT_SCOPE + 'url=' + encodeURIComponent(url), |
| url, |
| [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'Normal redirect to same-origin scope.'); |
| |
| |
| url = SCOPE1 + '#ref'; |
| redirect_test( |
| OUT_SCOPE + 'url=' + encodeURIComponent(SCOPE1) + '#ref', |
| url, |
| [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'Normal redirect to same-origin scope with a hash fragment.'); |
| |
| url = SCOPE1 + '#ref2'; |
| redirect_test( |
| OUT_SCOPE + 'url=' + encodeURIComponent(url) + '#ref', |
| url, |
| [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'Normal redirect to same-origin scope with different hash fragments.'); |
| |
| url = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| OUT_SCOPE + 'url=' + encodeURIComponent(url), |
| url, |
| [[], [], [{url, resultingClientIdTag: 'x'}]], |
| 'x', |
| 'Normal redirect to other-origin scope.'); |
| |
| // SW fallbacked redirect. SW doesn't handle the fetch request. |
| url = SCOPE1 + 'url=' + encodeURIComponent(OUT_SCOPE); |
| redirect_test( |
| url, |
| OUT_SCOPE, |
| [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'SW-fallbacked redirect to same-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1); |
| url2 = SCOPE1; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to same-origin same-scope.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1) + '#ref'; |
| url2 = SCOPE1 + '#ref'; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to same-origin same-scope with a hash fragment.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE1 + '#ref2') + '#ref'; |
| url2 = SCOPE1 + '#ref2'; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to same-origin same-scope with different hash ' + |
| 'fragments.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(SCOPE2); |
| url2 = SCOPE2; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'x'}], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| [] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to same-origin other-scope.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE); |
| url2 = OTHER_ORIGIN_OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'a'}], [], []], |
| null, |
| 'SW-fallbacked redirect to other-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE); |
| url2 = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'a'}], |
| [], |
| [{url: url2, resultingClientIdTag: 'x'}] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to other-origin in-scope.'); |
| |
| |
| url3 = SCOPE1; |
| url2 = OTHER_ORIGIN_SCOPE + 'url=' + encodeURIComponent(url3); |
| url1 = SCOPE1 + 'url=' + encodeURIComponent(url2); |
| redirect_test( |
| url1, |
| url3, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'a'}, |
| {url: url3, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [{url: url2, resultingClientIdTag: 'b'}] |
| ], |
| 'x', |
| 'SW-fallbacked redirect to other-origin and back to same-origin.'); |
| |
| // SW generated redirect. |
| // SW: event.respondWith(Response.redirect(params['url'])); |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE); |
| url2 = OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'SW-generated redirect to same-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE) + '#ref'; |
| url2 = OUT_SCOPE + '#ref'; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'SW-generated redirect to same-origin out-scope with a hash fragment.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OUT_SCOPE + '#ref2') + '#ref'; |
| url2 = OUT_SCOPE + '#ref2'; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'SW-generated redirect to same-origin out-scope with different hash ' + |
| 'fragments.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(SCOPE1); |
| url2 = SCOPE1; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'SW-generated redirect to same-origin same-scope.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(SCOPE2); |
| url2 = SCOPE2; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'x'}], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| [] |
| ], |
| 'x', |
| 'SW-generated redirect to same-origin other-scope.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE); |
| url2 = OTHER_ORIGIN_OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'a'}], [], []], |
| null, |
| 'SW-generated redirect to other-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'sw=gen&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE); |
| url2 = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'a'}], |
| [], |
| [{url: url2, resultingClientIdTag: 'x'}] |
| ], |
| 'x', |
| 'SW-generated redirect to other-origin in-scope.'); |
| |
| |
| // SW fetched redirect. |
| // SW: event.respondWith(fetch(event.request)); |
| url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OUT_SCOPE) |
| url2 = OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'SW-fetched redirect to same-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(SCOPE1); |
| url2 = SCOPE1; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'SW-fetched redirect to same-origin same-scope.'); |
| |
| url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(SCOPE2); |
| url2 = SCOPE2; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'x'}], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| [] |
| ], |
| 'x', |
| 'SW-fetched redirect to same-origin other-scope.'); |
| |
| url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE); |
| url2 = OTHER_ORIGIN_OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'a'}], [], []], |
| null, |
| 'SW-fetched redirect to other-origin out-scope.'); |
| |
| url1 = SCOPE1 + 'sw=fetch&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE); |
| url2 = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'a'}], |
| [], |
| [{url: url2, resultingClientIdTag: 'x'}] |
| ], |
| 'x', |
| 'SW-fetched redirect to other-origin in-scope.'); |
| |
| |
| // Opaque redirect. |
| // SW: event.respondWith(fetch( |
| // new Request(event.request.url, {redirect: 'manual'}))); |
| url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OUT_SCOPE); |
| url2 = OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'Redirect to same-origin out-scope with opaque redirect response.'); |
| |
| url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(SCOPE1); |
| url2 = SCOPE1; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'Redirect to same-origin same-scope with opaque redirect response.'); |
| |
| url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(SCOPE2); |
| url2 = SCOPE2; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'x'}], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| [] |
| ], |
| 'x', |
| 'Redirect to same-origin other-scope with opaque redirect response.'); |
| |
| url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE); |
| url2 = OTHER_ORIGIN_OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'a'}], [], []], |
| null, |
| 'Redirect to other-origin out-scope with opaque redirect response.'); |
| |
| url1 = SCOPE1 + 'sw=manual&url=' + encodeURIComponent(OTHER_ORIGIN_SCOPE); |
| url2 = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'a'}], |
| [], |
| [{url: url2, resultingClientIdTag: 'x'}] |
| ], |
| 'x', |
| 'Redirect to other-origin in-scope with opaque redirect response.'); |
| |
| url= SCOPE1 + 'sw=manual&noLocationRedirect'; |
| redirect_test( |
| url, url, [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'No location redirect response.'); |
| |
| |
| // Opaque redirect passed through Cache. |
| // SW responds with an opaque redirectresponse from the Cache API. |
| url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(OUT_SCOPE); |
| url2 = OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'Redirect to same-origin out-scope with opaque redirect response which ' + |
| 'is passed through Cache.'); |
| |
| url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(SCOPE1); |
| url2 = SCOPE1; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [ |
| {url: url1, resultingClientIdTag: 'x'}, |
| {url: url2, resultingClientIdTag: 'x'} |
| ], |
| [], |
| [] |
| ], |
| 'x', |
| 'Redirect to same-origin same-scope with opaque redirect response which ' + |
| 'is passed through Cache.'); |
| |
| url1 = SCOPE1 + 'sw=manualThroughCache&url=' + encodeURIComponent(SCOPE2); |
| url2 = SCOPE2; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'x'}], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| [] |
| ], |
| 'x', |
| 'Redirect to same-origin other-scope with opaque redirect response which ' + |
| 'is passed through Cache.'); |
| |
| url1 = SCOPE1 + 'sw=manualThroughCache&url=' + |
| encodeURIComponent(OTHER_ORIGIN_OUT_SCOPE); |
| url2 = OTHER_ORIGIN_OUT_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [[{url: url1, resultingClientIdTag: 'a'}], [], []], |
| null, |
| 'Redirect to other-origin out-scope with opaque redirect response which ' + |
| 'is passed through Cache.'); |
| |
| url1 = SCOPE1 + 'sw=manualThroughCache&url=' + |
| encodeURIComponent(OTHER_ORIGIN_SCOPE); |
| url2 = OTHER_ORIGIN_SCOPE; |
| redirect_test( |
| url1, |
| url2, |
| [ |
| [{url: url1, resultingClientIdTag: 'a'}], |
| [], |
| [{url: url2, resultingClientIdTag: 'x'}], |
| ], |
| 'x', |
| 'Redirect to other-origin in-scope with opaque redirect response which ' + |
| 'is passed through Cache.'); |
| |
| url = SCOPE1 + 'sw=manualThroughCache&noLocationRedirect'; |
| redirect_test( |
| url, |
| url, |
| [[{url, resultingClientIdTag: 'x'}], [], []], |
| 'x', |
| 'No location redirect response via Cache.'); |
| |
| // Clean up the test environment. This promise_test() needs to be the last one. |
| promise_test(async t => { |
| registrations.forEach(async registration => { |
| if (registration) |
| await registration.unregister(); |
| }); |
| await send_to_iframe(other_origin_frame, {command: 'unregister'}); |
| other_origin_frame.remove(); |
| }, 'clean up global state'); |
| </script> |
| </body> |