| <!DOCTYPE html> |
| <title>Service Worker: WindowClient.navigate() tests</title> |
| <meta name="timeout" content="long"> |
| <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> |
| 'use strict'; |
| |
| const SCOPE = 'resources/blank.html'; |
| const SCRIPT_URL = 'resources/windowclient-navigate-worker.js'; |
| const CROSS_ORIGIN_URL = |
| get_host_info()['HTTPS_REMOTE_ORIGIN'] + base_path() + 'resources/blank.html'; |
| |
| navigateTest({ |
| description: 'normal', |
| destUrl: 'blank.html?navigate', |
| expected: normalizeURL(SCOPE) + '?navigate', |
| }); |
| |
| navigateTest({ |
| description: 'blank url', |
| destUrl: '', |
| expected: normalizeURL(SCRIPT_URL) |
| }); |
| |
| navigateTest({ |
| description: 'in scope but not controlled test on installing worker', |
| destUrl: 'blank.html?navigate', |
| expected: 'TypeError', |
| waitState: 'installing', |
| }); |
| |
| navigateTest({ |
| description: 'in scope but not controlled test on active worker', |
| destUrl: 'blank.html?navigate', |
| expected: 'TypeError', |
| controlled: false, |
| }); |
| |
| navigateTest({ |
| description: 'out of scope', |
| srcUrl: '/common/blank.html', |
| destUrl: 'blank.html?navigate', |
| expected: 'TypeError', |
| }); |
| |
| navigateTest({ |
| description: 'cross orgin url', |
| destUrl: CROSS_ORIGIN_URL, |
| expected: null |
| }); |
| |
| navigateTest({ |
| description: 'invalid url (http://[example.com])', |
| destUrl: 'http://[example].com', |
| expected: 'TypeError' |
| }); |
| |
| navigateTest({ |
| description: 'invalid url (view-source://example.com)', |
| destUrl: 'view-source://example.com', |
| expected: 'TypeError' |
| }); |
| |
| navigateTest({ |
| description: 'invalid url (file:///)', |
| destUrl: 'file:///', |
| expected: 'TypeError' |
| }); |
| |
| navigateTest({ |
| description: 'invalid url (about:blank)', |
| destUrl: 'about:blank', |
| expected: 'TypeError' |
| }); |
| |
| navigateTest({ |
| description: 'navigate on a top-level window client', |
| destUrl: 'blank.html?navigate', |
| srcUrl: 'resources/loaded.html', |
| scope: 'resources/loaded.html', |
| expected: normalizeURL(SCOPE) + '?navigate', |
| frameType: 'top-level' |
| }); |
| |
| async function createFrame(t, parameters) { |
| if (parameters.frameType === 'top-level') { |
| // Wait for window.open is completed. |
| await new Promise(resolve => { |
| const win = window.open(parameters.srcUrl); |
| t.add_cleanup(() => win.close()); |
| window.addEventListener('message', (e) => { |
| if (e.data.type === 'LOADED') { |
| resolve(); |
| } |
| }); |
| }); |
| } |
| |
| if (parameters.frameType === 'nested') { |
| const frame = await with_iframe(parameters.srcUrl); |
| t.add_cleanup(() => frame.remove()); |
| } |
| } |
| |
| function navigateTest(overrideParameters) { |
| // default parameters |
| const parameters = { |
| description: null, |
| srcUrl: SCOPE, |
| destUrl: null, |
| expected: null, |
| waitState: 'activated', |
| scope: SCOPE, |
| controlled: true, |
| // `frameType` can be 'nested' for an iframe WindowClient or 'top-level' for |
| // a main frame WindowClient. |
| frameType: 'nested' |
| }; |
| |
| for (const key in overrideParameters) |
| parameters[key] = overrideParameters[key]; |
| |
| promise_test(async function(t) { |
| let pausedLifecyclePort; |
| let scriptUrl = SCRIPT_URL; |
| |
| // For in-scope-but-not-controlled test on installing worker, |
| // if the waitState is "installing", then append the query to scriptUrl. |
| if (parameters.waitState === 'installing') { |
| scriptUrl += '?' + parameters.waitState; |
| |
| navigator.serviceWorker.addEventListener('message', (event) => { |
| if (event.data.port) { |
| pausedLifecyclePort = event.data.port; |
| } |
| }); |
| } |
| |
| t.add_cleanup(() => { |
| // Some tests require that the worker remain in a given lifecycle phase. |
| // "Clean up" logic for these tests requires signaling the worker to |
| // release the hold; this allows the worker to be properly discarded |
| // prior to the execution of additional tests. |
| if (pausedLifecyclePort) { |
| // The value of the posted message is inconsequential. A string is |
| // specified here solely to aid in test debugging. |
| pausedLifecyclePort.postMessage('continue lifecycle'); |
| } |
| }); |
| |
| // Create a frame that is not controlled by a service worker. |
| if (!parameters.controlled) { |
| await createFrame(t, parameters); |
| } |
| |
| const registration = await service_worker_unregister_and_register( |
| t, scriptUrl, parameters.scope); |
| const serviceWorker = registration.installing; |
| await wait_for_state(t, serviceWorker, parameters.waitState); |
| t.add_cleanup(() => registration.unregister()); |
| |
| // Create a frame after a service worker is registered so that the frmae is |
| // controlled by an active service worker. |
| if (parameters.controlled) { |
| await createFrame(t, parameters); |
| } |
| |
| const response = await new Promise(resolve => { |
| const channel = new MessageChannel(); |
| channel.port1.onmessage = t.step_func(resolve); |
| serviceWorker.postMessage({ |
| port: channel.port2, |
| url: parameters.destUrl, |
| clientUrl: new URL(parameters.srcUrl, location).toString(), |
| frameType: parameters.frameType, |
| expected: parameters.expected, |
| description: parameters.description, |
| }, [channel.port2]); |
| }); |
| |
| assert_equals(response.data, null); |
| await fetch_tests_from_worker(serviceWorker); |
| }, parameters.description); |
| } |
| </script> |
| </body> |