<!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>