<!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;
let resolvedCount = 0;
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 */ };
// We want to verify that unregister() and update() do not
// resolve on a detached registration. We can't check for
// an explicit rejection, though, because not all browsers
// fire rejection callbacks on detached promises. Instead
// we wait for a sample scope to install, activate, and
// unregister before declaring that the promises did not
// resolve.
r.unregister().then(() => resolvedCount += 1,
() => {});
r.update().then(() => resolvedCount += 1,
() => {});
return wait_for_activation_on_sample_scope(t, window);
})
.then(() => {
assert_equals(resolvedCount, 0,
'methods called on a detached registration should not resolve');
frame.remove();
})
}, '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;
const ctor = frame.contentWindow.DOMException;
frame.remove();
assert_equals(worker.scriptURL, normalizeURL(script));
assert_equals(worker.state, 'activated');
worker.onstatechange = () => { /* empty */ };
assert_throws_dom(
'InvalidStateError',
ctor,
() => { 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);
iframe.remove();
});
}, '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_not_equals(get_navigator().serviceWorker, null);
}, 'accessing navigator on a removed frame');
</script>