// META: title=Service Worker: navigator.serviceWorker.ready
// META: script=resources/test-helpers.sub.js
test(() => {
assert_equals(
navigator.serviceWorker.ready,
navigator.serviceWorker.ready,
'repeated access to ready without intervening registrations should return the same Promise object'
);
}, 'ready returns the same Promise object');
promise_test(async t => {
const frame = await with_iframe('resources/blank.html?uncontrolled');
t.add_cleanup(() => frame.remove());
const promise = frame.contentWindow.navigator.serviceWorker.ready;
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(async t => {
const url = 'resources/empty-worker.js';
const scope = 'resources/blank.html?ready-controlled';
const expectedURL = normalizeURL(url);
const registration = await service_worker_unregister_and_register(t, url, scope);
t.add_cleanup(() => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');
const frame = await with_iframe(scope);
t.add_cleanup(() => frame.remove());
const readyReg = await frame.contentWindow.navigator.serviceWorker.ready;
assert_equals(readyReg.installing, null, 'installing should be null');
assert_equals(readyReg.waiting, null, 'waiting should be null');
assert_equals(readyReg.active.scriptURL, expectedURL, 'active after ready should not be null');
assert_equals(
frame.contentWindow.navigator.serviceWorker.controller,
readyReg.active,
'the controller should be the active worker'
);
assert_in_array(
readyReg.active.state,
['activating', 'activated'],
'.ready should be resolved when the registration has an active worker'
);
}, 'ready on a controlled document');
promise_test(async t => {
const url = 'resources/empty-worker.js';
const scope = 'resources/blank.html?ready-potential-controlled';
const expected_url = normalizeURL(url);
const frame = await with_iframe(scope);
t.add_cleanup(() => frame.remove());
const registration = await navigator.serviceWorker.register(url, { scope });
t.add_cleanup(() => registration.unregister());
const readyReg = await frame.contentWindow.navigator.serviceWorker.ready;
assert_equals(readyReg.installing, null, 'installing should be null');
assert_equals(readyReg.waiting, null, 'waiting should be null.')
assert_equals(readyReg.active.scriptURL, expected_url, 'active after ready should not be null');
assert_in_array(
readyReg.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(async t => {
const url = 'resources/empty-worker.js';
const scope = 'resources/blank.html?ready-installing';
await service_worker_unregister(t, scope);
const frame = await with_iframe(scope);
const promise = frame.contentWindow.navigator.serviceWorker.ready;
navigator.serviceWorker.register(url, { scope });
const registration = await promise;
t.add_cleanup(async () => {
await registration.unregister();
frame.remove();
});
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(async t => {
const scope = 'resources/register-iframe.html';
const frame = await with_iframe(scope);
const registration = await frame.contentWindow.navigator.serviceWorker.ready;
t.add_cleanup(async () => {
await registration.unregister();
frame.remove();
});
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(async t => {
const url = 'resources/empty-worker.js';
const matchedScope = 'resources/blank.html?ready-after-match';
const longerMatchedScope = 'resources/blank.html?ready-after-match-longer';
await service_worker_unregister(t, matchedScope);
await service_worker_unregister(t, longerMatchedScope);
const frame = await with_iframe(longerMatchedScope);
const registration = await navigator.serviceWorker.register(url, { scope: matchedScope });
t.add_cleanup(async () => {
await registration.unregister();
frame.remove();
});
await wait_for_state(t, registration.installing, 'activated');
const longerRegistration = await navigator.serviceWorker.register(url, { scope: longerMatchedScope });
t.add_cleanup(() => longerRegistration.unregister());
const readyReg = await frame.contentWindow.navigator.serviceWorker.ready;
assert_equals(
readyReg.scope,
normalizeURL(longerMatchedScope),
'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(async t => {
const url = 'resources/empty-worker.js';
const matchedScope = 'resources/blank.html?ready-after-resolve';
const longerMatchedScope = 'resources/blank.html?ready-after-resolve-longer';
const registration = await service_worker_unregister_and_register(t, url, matchedScope);
t.add_cleanup(() => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');
const frame = await with_iframe(longerMatchedScope);
t.add_cleanup(() => frame.remove());
const readyReg1 = await frame.contentWindow.navigator.serviceWorker.ready;
assert_equals(
readyReg1.scope,
normalizeURL(matchedScope),
'matched registration should be returned'
);
const longerReg = await navigator.serviceWorker.register(url, { scope: longerMatchedScope });
t.add_cleanup(() => longerReg.unregister());
const readyReg2 = await frame.contentWindow.navigator.serviceWorker.ready;
assert_equals(
readyReg2.scope,
normalizeURL(matchedScope),
'ready should only be resolved once'
);
}, 'access ready after it has been resolved');
promise_test(async t => {
const url1 = 'resources/empty-worker.js';
const url2 = url1 + '?2';
const matchedScope = 'resources/blank.html?ready-after-unregister';
const reg1 = await service_worker_unregister_and_register(t, url1, matchedScope);
t.add_cleanup(() => reg1.unregister());
await wait_for_state(t, reg1.installing, 'activating');
const frame = await with_iframe(matchedScope);
t.add_cleanup(() => frame.remove());
await reg1.unregister();
// Ready promise should be pending, waiting for a new registration to arrive
const readyPromise = frame.contentWindow.navigator.serviceWorker.ready;
const reg2 = await navigator.serviceWorker.register(url2, { scope: matchedScope });
t.add_cleanup(() => reg2.unregister());
const readyReg = await readyPromise;
// Wait for registration update, since it comes from another global, the states are racy.
await wait_for_state(t, reg2.installing || reg2.waiting || reg2.active, 'activated');
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');