Commit 3d4f72a8 authored by Hiroki Nakagawa's avatar Hiroki Nakagawa Committed by Commit Bot

Worker: Add service worker interception tests for shared workers

One test case for shared workers is failing because it's not allowed to create a
dedicated worker on a shared worker.

Bug: n/a
Change-Id: I4f7139adacdccb0f16d7afedd7eaab065c4bea2c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1657855
Commit-Queue: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#669615}
parent 3e78c7ac
importScripts('/common/get-host-info.sub.js'); importScripts('/common/get-host-info.sub.js');
var worker_text = 'postMessage("worker loading intercepted by service worker"); '; const text = 'worker loading intercepted by service worker';
const dedicated_worker_script = `postMessage('${text}');`;
const shared_worker_script =
`onconnect = evt => evt.ports[0].postMessage('${text}');`;
self.onfetch = function(event) { self.onfetch = event => {
if (event.request.url.indexOf('synthesized') != -1) { const url = event.request.url;
event.respondWith(new Response(worker_text)); const destination = event.request.destination;
} else if (event.request.url.indexOf('same-origin') != -1) {
event.respondWith(fetch('dummy-worker-script.py')); // Request handler for a synthesized response.
} else if (event.request.url.indexOf('cors') != -1) { if (url.indexOf('synthesized') != -1) {
var path = (new URL('dummy-worker-script.py', self.location)).pathname; if (destination === 'worker')
var url = get_host_info()['HTTPS_REMOTE_ORIGIN'] + path; event.respondWith(new Response(dedicated_worker_script));
var mode = "no-cors"; else if (destination === 'sharedworker')
if (event.request.url.indexOf('no-cors') == -1) { event.respondWith(new Response(shared_worker_script));
url += '?ACAOrigin=*'; else
mode = "cors"; event.respondWith(new Response('Unexpected request! ' + destination));
return;
}
// Request handler for a same-origin response.
if (url.indexOf('same-origin') != -1) {
event.respondWith(fetch('postmessage-on-load-worker.js'));
return;
}
// Request handler for a cross-origin response.
if (url.indexOf('cors') != -1) {
const filename = 'postmessage-on-load-worker.js';
const path = (new URL(filename, self.location)).pathname;
let new_url = get_host_info()['HTTPS_REMOTE_ORIGIN'] + path;
let mode;
if (url.indexOf('no-cors') != -1) {
// Test no-cors mode.
mode = 'no-cors';
} else {
// Test cors mode.
new_url += '?pipe=header(Access-Control-Allow-Origin,*)';
mode = 'cors';
} }
event.respondWith(fetch(url, { mode: mode })); event.respondWith(fetch(new_url, { mode: mode }));
} }
}; };
def main(request, response):
headers = []
if "ACAOrigin" in request.GET:
for item in request.GET["ACAOrigin"].split(","):
headers.append(("Access-Control-Allow-Origin", item))
return headers, "postMessage('dummy-worker-script loaded');"
self.onmessage = evt => { function run_test(data, sender) {
if (evt.data == "xhr") { if (data === 'xhr') {
const xhr = new XMLHttpRequest(); const xhr = new XMLHttpRequest();
xhr.open("GET", "synthesized-response.txt", true); xhr.open('GET', 'synthesized-response.txt', true);
xhr.responseType = "text"; xhr.responseType = 'text';
xhr.send(); xhr.send();
xhr.onload = evt => postMessage(xhr.responseText); xhr.onload = evt => sender.postMessage(xhr.responseText);
xhr.onerror = () => postMessage("XHR failed!"); xhr.onerror = () => sender.postMessage('XHR failed!');
} else if (evt.data == "fetch") { } else if (data === 'fetch') {
fetch("synthesized-response.txt") fetch('synthesized-response.txt')
.then(response => response.text()) .then(response => response.text())
.then(data => postMessage(data)) .then(data => sender.postMessage(data))
.catch(error => postMessage("Fetch failed!")); .catch(error => sender.postMessage('Fetch failed!'));
} else if (evt.data == "importScripts") { } else if (data === 'importScripts') {
importScripts("synthesized-response.js"); importScripts('synthesized-response.js');
// |message| is provided by 'synthesized-response.js';
sender.postMessage(message);
} else { } else {
postMessage("Unexpected message! " + evt.data); sender.postMessage('Unexpected message! ' + data);
} }
}
// Entry point for dedicated workers.
self.onmessage = evt => run_test(evt.data, self);
// Entry point for shared workers.
self.onconnect = evt => {
evt.ports[0].onmessage = e => run_test(e.data, evt.ports[0]);
}; };
var worker = new Worker("load_worker.js"); // Entry point for dedicated workers.
self.onmessage = evt => {
self.onmessage = function (evt) { try {
worker.postMessage(evt.data); const worker = new Worker('load_worker.js');
worker.onmessage = evt => self.postMessage(evt.data);
worker.postMessage(evt.data);
} catch (err) {
self.postMessage('Unexpected error! ' + err.message);
}
}; };
worker.onmessage = function (evt) { // Entry point for shared workers.
self.postMessage(evt.data); self.onconnect = evt => {
} evt.ports[0].onmessage = e => {
try {
const worker = new Worker('load_worker.js');
worker.onmessage = e => evt.ports[0].postMessage(e.data);
worker.postMessage(evt.data);
} catch (err) {
evt.ports[0].postMessage('Unexpected error! ' + err.message);
}
};
};
if ('DedicatedWorkerGlobalScope' in self &&
self instanceof DedicatedWorkerGlobalScope) {
postMessage('dedicated worker script loaded');
} else if ('SharedWorkerGlobalScope' in self &&
self instanceof SharedWorkerGlobalScope) {
self.onconnect = evt => {
evt.ports[0].postMessage('shared worker script loaded');
};
}
...@@ -2,41 +2,56 @@ ...@@ -2,41 +2,56 @@
<script src="test-helpers.sub.js?pipe=sub"></script> <script src="test-helpers.sub.js?pipe=sub"></script>
<script> <script>
let worker_type;
async function boilerplate_test(url, msg) { async function boilerplate_test(url, msg) {
const worker = new Worker(url); let data;
const data = await new Promise((resolve, reject) => { if (worker_type === 'worker') {
worker.onmessage = e => resolve(e.data); const worker = new Worker(url);
worker.onerror = e =>reject(e); data = await new Promise((resolve, reject) => {
worker.postMessage(msg); worker.onmessage = e => resolve(e.data);
}); worker.onerror = e => reject(e);
assert_equals(data, "This load was successfully intercepted."); worker.postMessage(msg);
});
} else if (worker_type === 'sharedworker') {
const worker = new SharedWorker(url);
data = await new Promise((resolve, reject) => {
worker.port.onmessage = e => resolve(e.data);
worker.onerror = e => reject(e);
worker.port.postMessage(msg);
});
} else {
data = 'Unexpected worker type! ' + worker_type;
}
assert_equals(data, 'This load was successfully intercepted.');
} }
function xhr_test() { function xhr_test() {
return boilerplate_test("load_worker.js", "xhr"); return boilerplate_test('load_worker.js', 'xhr');
} }
function fetch_test() { function fetch_test() {
return boilerplate_test("load_worker.js", "fetch"); return boilerplate_test('load_worker.js', 'fetch');
} }
function importScripts_test() { function importScripts_test() {
return boilerplate_test("load_worker.js", "importScripts"); return boilerplate_test('load_worker.js', 'importScripts');
} }
function nested_worker_xhr_test() { function nested_worker_xhr_test() {
return boilerplate_test("nested_load_worker.js", "xhr"); return boilerplate_test('nested_load_worker.js', 'xhr');
} }
function nested_worker_fetch_test() { function nested_worker_fetch_test() {
return boilerplate_test("nested_load_worker.js", "fetch"); return boilerplate_test('nested_load_worker.js', 'fetch');
} }
function nested_worker_importScripts_test() { function nested_worker_importScripts_test() {
return boilerplate_test("nested_load_worker.js", "importScripts"); return boilerplate_test('nested_load_worker.js', 'importScripts');
} }
window.addEventListener('message', evt => { window.addEventListener('message', evt => {
worker_type = evt.data;
const port = evt.ports[0]; const port = evt.ports[0];
xhr_test() xhr_test()
.then(fetch_test) .then(fetch_test)
......
importScripts('/common/get-host-info.sub.js'); importScripts('/common/get-host-info.sub.js');
var response_text = "This load was successfully intercepted."; const response_text = 'This load was successfully intercepted.';
var response_script = "postMessage(\"This load was successfully intercepted.\");"; const response_script =
`const message = 'This load was successfully intercepted.';`;
self.onfetch = function(event) { self.onfetch = event => {
var url = event.request.url; const url = event.request.url;
if (url.indexOf("synthesized-response.txt") != -1) { if (url.indexOf('synthesized-response.txt') != -1) {
event.respondWith(new Response(response_text)); event.respondWith(new Response(response_text));
} else if (url.indexOf("synthesized-response.js") != -1) { } else if (url.indexOf('synthesized-response.js') != -1) {
event.respondWith(new Response( event.respondWith(new Response(
response_script, response_script,
{headers: {'Content-Type': 'application/javascript'}})); {headers: {'Content-Type': 'application/javascript'}}));
} }
}; };
This is a testharness.js-based test. This is a testharness.js-based test.
FAIL Verify worker script from uncontrolled document is intercepted by Service Worker promise_test: Unhandled rejection with value: undefined FAIL Verify a dedicated worker script request issued from a uncontrolled document is intercepted by worker's own service worker. promise_test: Unhandled rejection with value: undefined
FAIL Verify worker script intercepted by same-origin response succeeds promise_test: Unhandled rejection with value: undefined PASS Verify a shared worker script request issued from a uncontrolled document is intercepted by worker's own service worker.
PASS Verify worker script intercepted by cors response fails FAIL Verify a same-origin worker script served by a service worker succeeds in starting a dedicated worker. promise_test: Unhandled rejection with value: undefined
PASS Verify worker script intercepted by no-cors cross-origin response fails PASS Verify a same-origin worker script served by a service worker succeeds in starting a shared worker.
PASS Verify worker loads from controlled document are intercepted by Service Worker PASS Verify a cors worker script served by a service worker fails dedicated worker start.
PASS Verify a cors worker script served by a service worker fails shared worker start.
PASS Verify a no-cors cross-origin worker script served by a service worker fails dedicated worker start.
PASS Verify a no-cors cross-origin worker script served by a service worker fails shared worker start.
PASS Verify subresource requests on a dedicated worker controlled by a service worker.
FAIL Verify subresource requests on a shared worker controlled by a service worker. assert_equals: expected "finish" but got "failure:Error: assert_equals: expected \"This load was successfully intercepted.\" but got \"Unexpected error! Worker is not defined\""
Harness: the test ran to completion. Harness: the test ran to completion.
<!DOCTYPE html> <!DOCTYPE html>
<title>Service Worker: intercepting Worker script loads</title> <title>Service Worker: intercepting Worker script loads</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script> <script src="resources/test-helpers.sub.js"></script>
...@@ -25,8 +26,23 @@ promise_test(async t => { ...@@ -25,8 +26,23 @@ promise_test(async t => {
w.onerror = e => reject(e.message); w.onerror = e => reject(e.message);
}); });
assert_equals(data, 'worker loading intercepted by service worker'); assert_equals(data, 'worker loading intercepted by service worker');
}, 'Verify worker script from uncontrolled document is intercepted by ' + }, `Verify a dedicated worker script request issued from a uncontrolled ` +
'Service Worker'); `document is intercepted by worker's own service worker.`);
promise_test(async t => {
const worker_url = 'resources/dummy-synthesized-worker.js';
const service_worker = 'resources/dummy-worker-interceptor.js';
const scope = worker_url;
await setup_service_worker(t, service_worker, 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 => { promise_test(async t => {
const worker_url = 'resources/dummy-same-origin-worker.js'; const worker_url = 'resources/dummy-same-origin-worker.js';
...@@ -39,8 +55,24 @@ promise_test(async t => { ...@@ -39,8 +55,24 @@ promise_test(async t => {
w.onmessage = e => resolve(e.data); w.onmessage = e => resolve(e.data);
w.onerror = e => reject(e.message); w.onerror = e => reject(e.message);
}); });
assert_equals(data, 'dummy-worker-script loaded'); assert_equals(data, 'dedicated worker script loaded');
}, 'Verify worker script intercepted by same-origin response succeeds'); }, '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/dummy-same-origin-worker.js';
const service_worker = 'resources/dummy-worker-interceptor.js';
const scope = worker_url;
await setup_service_worker(t, service_worker, 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 => { promise_test(async t => {
const worker_url = 'resources/dummy-cors-worker.js'; const worker_url = 'resources/dummy-cors-worker.js';
...@@ -51,7 +83,20 @@ promise_test(async t => { ...@@ -51,7 +83,20 @@ promise_test(async t => {
const w = new Worker(worker_url); const w = new Worker(worker_url);
const watcher = new EventWatcher(t, w, ['message', 'error']); const watcher = new EventWatcher(t, w, ['message', 'error']);
await watcher.wait_for('error'); await watcher.wait_for('error');
}, 'Verify worker script intercepted by cors response fails'); }, 'Verify a cors worker script served by a service worker fails dedicated ' +
'worker start.');
promise_test(async t => {
const worker_url = 'resources/dummy-cors-worker.js';
const service_worker = 'resources/dummy-worker-interceptor.js';
const scope = worker_url;
await setup_service_worker(t, service_worker, 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 => { promise_test(async t => {
const worker_url = 'resources/dummy-no-cors-worker.js'; const worker_url = 'resources/dummy-no-cors-worker.js';
...@@ -62,10 +107,40 @@ promise_test(async t => { ...@@ -62,10 +107,40 @@ promise_test(async t => {
const w = new Worker(worker_url); const w = new Worker(worker_url);
const watcher = new EventWatcher(t, w, ['message', 'error']); const watcher = new EventWatcher(t, w, ['message', 'error']);
await watcher.wait_for('error'); await watcher.wait_for('error');
}, 'Verify worker script intercepted by no-cors cross-origin response fails'); }, '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/dummy-no-cors-worker.js';
const service_worker = 'resources/dummy-worker-interceptor.js';
const scope = worker_url;
await setup_service_worker(t, service_worker, 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.');
promise_test(async t => {
const subdoc_url = 'resources/worker-interception-iframe.https.html';
const service_worker = 'resources/worker-load-interceptor.js';
const scope = 'resources/';
await setup_service_worker(t, service_worker, scope);
const frame = await with_iframe(subdoc_url);
t.add_cleanup(() => frame.remove());
const data = await new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = e => resolve(e.data);
frame.contentWindow.postMessage('worker', '*', [channel.port2]);
});
assert_equals(data.results, 'finish');
}, 'Verify subresource requests on a dedicated worker controlled by a ' +
'service worker.');
promise_test(async t => { promise_test(async t => {
const subdoc_url = 'resources/worker-interception-iframe.https.html?bypass'; const subdoc_url = 'resources/worker-interception-iframe.https.html';
const service_worker = 'resources/worker-load-interceptor.js'; const service_worker = 'resources/worker-load-interceptor.js';
const scope = 'resources/'; const scope = 'resources/';
...@@ -75,11 +150,11 @@ promise_test(async t => { ...@@ -75,11 +150,11 @@ promise_test(async t => {
const data = await new Promise((resolve, reject) => { const data = await new Promise((resolve, reject) => {
const channel = new MessageChannel(); const channel = new MessageChannel();
channel.port1.onmessage = e => resolve(e.data); channel.port1.onmessage = e => resolve(e.data);
frame.contentWindow.postMessage("GO", "*", [channel.port2]); frame.contentWindow.postMessage('sharedworker', '*', [channel.port2]);
}); });
assert_equals(data.results, 'finish'); assert_equals(data.results, 'finish');
}, 'Verify worker loads from controlled document are intercepted by Service ' + }, 'Verify subresource requests on a shared worker controlled by a service ' +
'Worker'); 'worker.');
</script> </script>
</body> </body>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment