blob: 6c1ed822a8778c84ff151a680ed0b4c4fbdb53ca [file] [log] [blame]
<!DOCTYPE html>
<title>Service Worker: navigator.serviceWorker.ready</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<body>
<script>
test(function() {
var promise = navigator.serviceWorker.ready;
assert_equals(promise, navigator.serviceWorker.ready,
'repeated access to ready without intervening ' +
'registrations should return the same Promise object');
}, 'ready returns the same Promise object');
promise_test(function(t) {
return with_iframe('resources/blank.html?uncontrolled')
.then(t.step_func(function(frame) {
var promise = frame.contentWindow.navigator.serviceWorker.ready;
t.add_cleanup(function() {
frame.remove();
});
assert_equals(Object.getPrototypeOf(promise),
frame.contentWindow.Promise.prototype,
'the Promise should be in the context of the ' +
'related document');
}));
}, 'ready returns a Promise object in the context of the related document');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html?ready-controlled';
var expected_url = normalizeURL(url);
var frame;
return service_worker_unregister_and_register(t, url, scope)
.then(function(registration) {
add_completion_callback(function() {
registration.unregister();
});
return wait_for_state(t, registration.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
t.add_cleanup(function() {
f.remove();
});
frame = f;
return frame.contentWindow.navigator.serviceWorker.ready;
})
.then(function(registration) {
assert_equals(registration.installing, null,
'installing should be null');
assert_equals(registration.waiting, null,
'waiting should be null');
assert_equals(registration.active.scriptURL, expected_url,
'active after ready should not be null');
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
registration.active,
'the controller should be the active worker');
assert_in_array(registration.active.state,
['activating', 'activated'],
'.ready should be resolved when the registration ' +
'has an active worker');
});
}, 'ready on a controlled document');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html?ready-potential-controlled';
var expected_url = normalizeURL(url);
var frame;
return with_iframe(scope)
.then(function(f) {
t.add_cleanup(function() {
f.remove();
});
frame = f;
return navigator.serviceWorker.register(url, {scope:scope});
})
.then(function(r) {
add_completion_callback(function() {
r.unregister();
});
return frame.contentWindow.navigator.serviceWorker.ready;
})
.then(function(registration) {
assert_equals(registration.installing, null,
'installing should be null');
assert_equals(registration.waiting, null,
'waiting should be null.')
assert_equals(registration.active.scriptURL, expected_url,
'active after ready should not be null');
assert_in_array(registration.active.state,
['activating', 'activated'],
'.ready should be resolved when the registration ' +
'has an active worker');
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
null,
'uncontrolled document should not have a controller');
});
}, 'ready on a potential controlled document');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var scope = 'resources/blank.html?ready-installing';
return service_worker_unregister(t, scope)
.then(function() {
return with_iframe(scope);
})
.then(function(f) {
var promise = f.contentWindow.navigator.serviceWorker.ready;
t.add_cleanup(function() {
f.remove();
});
navigator.serviceWorker.register(url, {scope: scope});
return promise;
})
.then(function(registration) {
add_completion_callback(function() {
registration.unregister();
});
assert_equals(registration.installing, null,
'installing should be null');
assert_equals(registration.waiting, null, 'waiting should be null');
assert_not_equals(registration.active, null,
'active after ready should not be null');
assert_in_array(registration.active.state,
['activating', 'activated'],
'.ready should be resolved when the registration ' +
'has an active worker');
});
}, 'ready on an iframe whose parent registers a new service worker');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var scope = 'resources/register-iframe.html';
var expected_url = normalizeURL(url);
return with_iframe(scope)
.then(function(f) {
t.add_cleanup(function() {
f.remove();
});
return f.contentWindow.navigator.serviceWorker.ready;
})
.then(function(registration) {
add_completion_callback(function() {
registration.unregister();
});
assert_equals(registration.installing, null,
'installing should be null');
assert_equals(registration.waiting, null, 'waiting should be null');
assert_not_equals(registration.active, null,
'active after ready should not be null');
assert_in_array(registration.active.state,
['activating', 'activated'],
'.ready should be resolved with "active worker"');
});
}, 'ready on an iframe that installs a new service worker');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var matched_scope = 'resources/blank.html?ready-after-match';
var longer_matched_scope = 'resources/blank.html?ready-after-match-longer';
var frame, registration;
return Promise.all([service_worker_unregister(t, matched_scope),
service_worker_unregister(t, longer_matched_scope)])
.then(function() {
return with_iframe(longer_matched_scope);
})
.then(function(f) {
t.add_cleanup(function() {
f.remove();
});
frame = f;
return navigator.serviceWorker.register(url, {scope: matched_scope});
})
.then(function(r) {
add_completion_callback(function() {
r.unregister();
});
registration = r;
return wait_for_state(t, r.installing, 'activated');
})
.then(function() {
return navigator.serviceWorker.register(
url, {scope: longer_matched_scope});
})
.then(function(r) {
add_completion_callback(function() {
r.unregister();
});
return frame.contentWindow.navigator.serviceWorker.ready;
})
.then(function(r) {
assert_equals(r.scope, normalizeURL(longer_matched_scope),
'longer matched registration should be returned');
assert_equals(frame.contentWindow.navigator.serviceWorker.controller,
null, 'controller should be null');
});
}, 'ready after a longer matched registration registered');
promise_test(function(t) {
var url = 'resources/empty-worker.js';
var matched_scope = 'resources/blank.html?ready-after-resolve';
var longer_matched_scope =
'resources/blank.html?ready-after-resolve-longer';
var frame, registration;
return service_worker_unregister_and_register(t, url, matched_scope)
.then(function(r) {
add_completion_callback(function() {
r.unregister();
});
registration = r;
return wait_for_state(t, r.installing, 'activated');
})
.then(function() {
return with_iframe(longer_matched_scope);
})
.then(function(f) {
t.add_cleanup(function() {
f.remove();
});
frame = f;
return f.contentWindow.navigator.serviceWorker.ready;
})
.then(function(r) {
assert_equals(r.scope, normalizeURL(matched_scope),
'matched registration should be returned');
return navigator.serviceWorker.register(
url, {scope: longer_matched_scope});
})
.then(function(r) {
add_completion_callback(function() {
r.unregister();
});
return frame.contentWindow.navigator.serviceWorker.ready;
})
.then(function(r) {
assert_equals(r.scope, normalizeURL(matched_scope),
'ready should only be resolved once');
});
}, 'access ready after it has been resolved');
promise_test(async function(t) {
const url1 = 'resources/empty-worker.js';
const url2 = url1 + '?2';
const matched_scope = 'resources/blank.html?ready-after-unregister';
const reg1 = await service_worker_unregister_and_register(t, url1, matched_scope);
t.add_cleanup(() => reg1.unregister());
await wait_for_state(t, reg1.installing, 'activating');
// This registration will resolve all ready promises in clients that match the scope.
// But there are no clients.
const frame = await with_iframe(matched_scope);
t.add_cleanup(() => frame.remove());
await reg1.unregister();
// Access the ready promise while the registration is unregistering.
const readyPromise = frame.contentWindow.navigator.serviceWorker.ready;
// Create a new registration.
const reg2 = await navigator.serviceWorker.register(url2, { scope: matched_scope });
t.add_cleanup(() => reg2.unregister());
// This registration will resolve all ready promises in clients that match the scope.
// That includes frame's client.
const readyReg = await readyPromise;
assert_equals(readyReg.active.scriptURL, reg2.active.scriptURL, 'Resolves with the second registration');
assert_not_equals(reg1, reg2, 'Registrations should be different');
}, 'resolve ready after unregistering and reregistering');
promise_test(async function(t) {
const url1 = 'resources/empty-worker.js';
const url2 = url1 + '?2';
const matched_scope = 'resources/blank.html?ready-after-unregister';
const frame = await with_iframe(matched_scope);
t.add_cleanup(() => frame.remove());
const reg1 = await service_worker_unregister_and_register(t, url1, matched_scope);
t.add_cleanup(() => reg1.unregister());
await wait_for_state(t, reg1.installing, 'activated');
// This registration will resolve all ready promises in clients that match the scope.
// That includes frame's client.
const reg1Active = reg1.active;
await reg1.unregister();
// Access the ready promise while the registration is unregistering.
const readyPromise = frame.contentWindow.navigator.serviceWorker.ready;
// Create a new registration.
const reg2 = await navigator.serviceWorker.register(url2, { scope: matched_scope });
t.add_cleanup(() => reg2.unregister());
// This registration will resolve all ready promises in clients that match the scope.
// That includes frame's client, but its ready promise has already resolved.
const readyReg = await readyPromise;
assert_equals(readyReg.active.scriptURL, reg1Active.scriptURL, 'Resolves with the first registration');
assert_not_equals(reg1, reg2, 'Registrations should be different');
}, 'resolve ready before unregistering and reregistering');
</script>