| <!DOCTYPE html> |
| <title>Service Worker: intercepting Worker script loads</title> |
| <meta name="timeout" content="long"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="resources/test-helpers.sub.js"></script> |
| <body> |
| <script> |
| |
| // ========== Worker main resource interception tests ========== |
| |
| async function setup_service_worker(t, service_worker_url, scope) { |
| const r = await service_worker_unregister_and_register( |
| t, service_worker_url, scope); |
| t.add_cleanup(() => service_worker_unregister(t, scope)); |
| await wait_for_state(t, r.installing, 'activated'); |
| return r.active; |
| } |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-synthesized-worker.js?dedicated'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| const serviceWorker = await setup_service_worker(t, service_worker_url, scope); |
| |
| const channels = new MessageChannel(); |
| serviceWorker.postMessage({port: channels.port1}, [channels.port1]); |
| |
| const clientId = await new Promise(resolve => channels.port2.onmessage = (e) => resolve(e.data.id)); |
| |
| const resultPromise = new Promise(resolve => channels.port2.onmessage = (e) => resolve(e.data)); |
| |
| const w = new Worker(worker_url); |
| const data = await new Promise((resolve, reject) => { |
| w.onmessage = e => resolve(e.data); |
| w.onerror = e => reject(e.message); |
| }); |
| assert_equals(data, 'worker loading intercepted by service worker'); |
| |
| const results = await resultPromise; |
| assert_equals(results.clientId, clientId); |
| assert_true(!!results.resultingClientId.length); |
| |
| channels.port2.postMessage("done"); |
| }, `Verify a dedicated worker script request gets correct client Ids`); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-synthesized-worker.js?dedicated'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new Worker(worker_url); |
| const data = await new Promise((resolve, reject) => { |
| w.onmessage = e => resolve(e.data); |
| w.onerror = e => reject(e.message); |
| }); |
| assert_equals(data, 'worker loading intercepted by service worker'); |
| }, `Verify a dedicated worker script request issued from a uncontrolled ` + |
| `document is intercepted by worker's own service worker.`); |
| |
| promise_test(async t => { |
| const frame_url = 'resources/create-out-of-scope-worker.html'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = frame_url; |
| |
| const registration = await service_worker_unregister_and_register( |
| t, service_worker_url, scope); |
| t.add_cleanup(() => service_worker_unregister(t, scope)); |
| await wait_for_state(t, registration.installing, 'activated'); |
| |
| const frame = await with_iframe(frame_url); |
| t.add_cleanup(_ => frame.remove()); |
| |
| assert_equals( |
| frame.contentWindow.navigator.serviceWorker.controller.scriptURL, |
| get_newest_worker(registration).scriptURL, |
| 'the frame should be controlled by a service worker' |
| ); |
| |
| const result = await frame.contentWindow.getWorkerPromise(); |
| |
| assert_equals(result, |
| 'worker loading was not intercepted by service worker'); |
| }, `Verify an out-of-scope dedicated worker script request issued from a ` + |
| `controlled document should not be intercepted by document's service ` + |
| `worker.`); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-synthesized-worker.js?shared'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new SharedWorker(worker_url); |
| const data = await new Promise((resolve, reject) => { |
| w.port.onmessage = e => resolve(e.data); |
| w.onerror = e => reject(e.message); |
| }); |
| assert_equals(data, 'worker loading intercepted by service worker'); |
| }, `Verify a shared worker script request issued from a uncontrolled ` + |
| `document is intercepted by worker's own service worker.`); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-same-origin-worker.js?dedicated'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new Worker(worker_url); |
| const data = await new Promise((resolve, reject) => { |
| w.onmessage = e => resolve(e.data); |
| w.onerror = e => reject(e.message); |
| }); |
| assert_equals(data, 'dedicated worker script loaded'); |
| }, 'Verify a same-origin worker script served by a service worker succeeds ' + |
| 'in starting a dedicated worker.'); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-same-origin-worker.js?shared'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new SharedWorker(worker_url); |
| const data = await new Promise((resolve, reject) => { |
| w.port.onmessage = e => resolve(e.data); |
| w.onerror = e => reject(e.message); |
| }); |
| assert_equals(data, 'shared worker script loaded'); |
| }, 'Verify a same-origin worker script served by a service worker succeeds ' + |
| 'in starting a shared worker.'); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-cors-worker.js?dedicated'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new Worker(worker_url); |
| const watcher = new EventWatcher(t, w, ['message', 'error']); |
| await watcher.wait_for('error'); |
| }, 'Verify a cors worker script served by a service worker fails dedicated ' + |
| 'worker start.'); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-cors-worker.js?shared'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new SharedWorker(worker_url); |
| const watcher = new EventWatcher(t, w, ['message', 'error']); |
| await watcher.wait_for('error'); |
| }, 'Verify a cors worker script served by a service worker fails shared ' + |
| 'worker start.'); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-no-cors-worker.js?dedicated'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new Worker(worker_url); |
| const watcher = new EventWatcher(t, w, ['message', 'error']); |
| await watcher.wait_for('error'); |
| }, 'Verify a no-cors cross-origin worker script served by a service worker ' + |
| 'fails dedicated worker start.'); |
| |
| promise_test(async t => { |
| const worker_url = 'resources/sample-no-cors-worker.js?shared'; |
| const service_worker_url = 'resources/sample-worker-interceptor.js'; |
| const scope = worker_url; |
| |
| await setup_service_worker(t, service_worker_url, scope); |
| const w = new SharedWorker(worker_url); |
| const watcher = new EventWatcher(t, w, ['message', 'error']); |
| await watcher.wait_for('error'); |
| }, 'Verify a no-cors cross-origin worker script served by a service worker ' + |
| 'fails shared worker start.'); |
| |
| // ========== Worker subresource interception tests ========== |
| |
| const scope_for_subresource_interception = 'resources/load_worker.js'; |
| |
| promise_test(async t => { |
| const service_worker_url = 'resources/worker-load-interceptor.js'; |
| const r = await service_worker_unregister_and_register( |
| t, service_worker_url, scope_for_subresource_interception); |
| await wait_for_state(t, r.installing, 'activated'); |
| }, 'Register a service worker for worker subresource interception tests.'); |
| |
| // Do not call this function multiple times without waiting for the promise |
| // resolution because this sets new event handlers on |worker|. |
| // TODO(nhiroki): To isolate multiple function calls, use MessagePort instead of |
| // worker's onmessage event handler. |
| async function request_on_worker(worker, resource_type) { |
| const data = await new Promise((resolve, reject) => { |
| if (worker instanceof Worker) { |
| worker.onmessage = e => resolve(e.data); |
| worker.onerror = e => reject(e); |
| worker.postMessage(resource_type); |
| } else if (worker instanceof SharedWorker) { |
| worker.port.onmessage = e => resolve(e.data); |
| worker.onerror = e => reject(e); |
| worker.port.postMessage(resource_type); |
| } else { |
| reject('Unexpected worker type!'); |
| } |
| }); |
| assert_equals(data, 'This load was successfully intercepted.'); |
| } |
| |
| async function subresource_test(worker) { |
| await request_on_worker(worker, 'xhr'); |
| await request_on_worker(worker, 'fetch'); |
| await request_on_worker(worker, 'importScripts'); |
| } |
| |
| promise_test(async t => { |
| await subresource_test(new Worker('resources/load_worker.js')); |
| }, 'Requests on a dedicated worker controlled by a service worker.'); |
| |
| promise_test(async t => { |
| await subresource_test(new SharedWorker('resources/load_worker.js')); |
| }, 'Requests on a shared worker controlled by a service worker.'); |
| |
| promise_test(async t => { |
| await subresource_test(new Worker('resources/nested_load_worker.js')); |
| }, 'Requests on a dedicated worker nested in a dedicated worker and ' + |
| 'controlled by a service worker'); |
| |
| promise_test(async t => { |
| await subresource_test(new SharedWorker('resources/nested_load_worker.js')); |
| }, 'Requests on a dedicated worker nested in a shared worker and controlled ' + |
| 'by a service worker'); |
| |
| promise_test(async t => { |
| await service_worker_unregister(t, scope_for_subresource_interception); |
| }, 'Unregister a service worker for subresource interception tests.'); |
| |
| </script> |
| </body> |