blob: d345982343f05f1b71e89cc46a00f057eec89d8e [file] [log] [blame]
<!DOCTYPE html>
<meta charset="utf-8">
<title>Service WorkerRegistration from a removed iframe</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
</body>
<script>
// NOTE: This file tests corner case behavior that might not be defined in the
// spec. See https://github.com/w3c/ServiceWorker/issues/1221
promise_test(t => {
const url = 'resources/blank.html';
const scope_for_iframe = 'removed-registration'
const scope_for_main = 'resources/' + scope_for_iframe;
const script = 'resources/empty-worker.js';
let frame;
return service_worker_unregister(t, scope_for_main)
.then(() => {
return with_iframe(url);
})
.then(f => {
frame = f;
return navigator.serviceWorker.register(script,
{scope: scope_for_main});
})
.then(r => {
add_completion_callback(() => { r.unregister(); });
return wait_for_state(t, r.installing, 'activated');
})
.then(() => {
return frame.contentWindow.navigator.serviceWorker.getRegistration(
scope_for_iframe);
})
.then(r => {
frame.remove();
assert_equals(r.installing, null);
assert_equals(r.waiting, null);
assert_equals(r.active.state, 'activated');
assert_equals(r.scope, normalizeURL(scope_for_main));
r.onupdatefound = () => { /* empty */ };
return Promise.all([
promise_rejects(t, 'InvalidStateError', r.unregister()),
promise_rejects(t, 'InvalidStateError', r.update())]);
})
}, 'accessing a ServiceWorkerRegistration from a removed iframe');
promise_test(t => {
const script = 'resources/empty-worker.js';
const scope = 'resources/scope/serviceworker-from-detached';
return service_worker_unregister_and_register(t, script, scope)
.then(registration => {
add_completion_callback(() => { registration.unregister(); });
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => { return with_iframe(scope); })
.then(frame => {
const worker = frame.contentWindow.navigator.serviceWorker.controller;
frame.remove();
assert_equals(worker.scriptURL, normalizeURL(script));
assert_equals(worker.state, 'activated');
worker.onstatechange = () => { /* empty */ };
assert_throws(
{ name: 'InvalidStateError' },
() => { worker.postMessage(''); },
'postMessage on a detached client should throw an exception.');
});
}, 'accessing a ServiceWorker object from a removed iframe');
promise_test(t => {
const iframe = document.createElement('iframe');
iframe.src = 'resources/blank.html';
document.body.appendChild(iframe);
const f = iframe.contentWindow.Function;
function get_navigator() {
return f('return navigator')();
}
return new Promise(resolve => {
assert_equals(iframe.contentWindow.navigator, get_navigator());
iframe.src = 'resources/blank.html?navigate-to-new-url';
iframe.onload = resolve;
}).then(function() {
assert_not_equals(get_navigator().serviceWorker, null);
assert_equals(
get_navigator().serviceWorker,
iframe.contentWindow.navigator.serviceWorker);
});
}, 'accessing navigator.serviceWorker on a detached iframe');
test(t => {
const iframe = document.createElement('iframe');
iframe.src = 'resources/blank.html';
document.body.appendChild(iframe);
const f = iframe.contentWindow.Function;
function get_navigator() {
return f('return navigator')();
}
assert_not_equals(get_navigator().serviceWorker, null);
iframe.remove();
assert_throws({name: 'TypeError'}, () => get_navigator());
}, 'accessing navigator on a removed frame');
// It seems weird that about:blank and blank.html (the test above) have
// different behavior. These expectations are based on Chromium behavior, which
// might not be right.
test(t => {
const iframe = document.createElement('iframe');
iframe.src = 'about:blank';
document.body.appendChild(iframe);
const f = iframe.contentWindow.Function;
function get_navigator() {
return f('return navigator')();
}
assert_not_equals(get_navigator().serviceWorker, null);
iframe.remove();
assert_equals(get_navigator().serviceWorker, null);
}, 'accessing navigator.serviceWorker on a removed about:blank frame');
</script>