<!DOCTYPE html>
<title>Service Worker: Registration-updateViaCache</title>
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="resources/testharness-helpers.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.sub.js"></script>
<script>
const UPDATE_VIA_CACHE_VALUES = [undefined, 'imports', 'all', 'none'];
const SCRIPT_URL = 'resources/update-max-aged-worker.py';
const SCOPE = 'resources/blank.html';
async function cleanup() {
const reg = await navigator.serviceWorker.getRegistration(SCOPE);
if (!reg) return;
if (reg.scope == new URL(SCOPE, location).href) {
return reg.unregister();
};
}
function getScriptTimes(sw, testName) {
return new Promise(resolve => {
navigator.serviceWorker.addEventListener('message', function listener(event) {
if (event.data.test !== testName) return;
navigator.serviceWorker.removeEventListener('message', listener);
resolve({
mainTime: event.data.mainTime,
importTime: event.data.importTime
});
});
sw.postMessage('');
});
}
// Test creating registrations & triggering an update.
for (const updateViaCache of UPDATE_VIA_CACHE_VALUES) {
const testName = `register-with-updateViaCache-${updateViaCache}`;
promise_test(async t => {
await cleanup();
const opts = {scope: SCOPE};
if (updateViaCache) opts.updateViaCache = updateViaCache;
const reg = await navigator.serviceWorker.register(
`${SCRIPT_URL}?test=${testName}`,
opts
);
assert_equals(reg.updateViaCache, updateViaCache || 'imports', "reg.updateViaCache");
const sw = reg.installing || reg.waiting || reg.active;
await wait_for_state(t, sw, 'activated');
const values = await getScriptTimes(sw, testName);
await reg.update();
if (updateViaCache == 'all') {
assert_equals(reg.installing, null, "No new service worker");
}
else {
const newWorker = reg.installing;
assert_true(!!newWorker, "New worker installing");
const newValues = await getScriptTimes(newWorker, testName);
if (!updateViaCache || updateViaCache == 'imports') {
assert_not_equals(values.mainTime, newValues.mainTime, "Main script should have updated");
assert_equals(values.importTime, newValues.importTime, "Imported script should be the same");
}
else if (updateViaCache == 'none') {
assert_not_equals(values.mainTime, newValues.mainTime, "Main script should have updated");
assert_not_equals(values.importTime, newValues.importTime, "Imported script should have updated");
}
else {
// We should have handled all of the possible values for updateViaCache.
// If this runs, something's gone very wrong.
throw Error(`Unexpected updateViaCache value: ${updateViaCache}`);
}
}
await cleanup();
}, testName);
}
// Test changing the updateViaCache value of an existing registration.
for (const updateViaCache1 of UPDATE_VIA_CACHE_VALUES) {
for (const updateViaCache2 of UPDATE_VIA_CACHE_VALUES) {
const testName = `register-with-updateViaCache-${updateViaCache1}-then-${updateViaCache2}`;
promise_test(async t => {
await cleanup();
const fullScriptUrl = `${SCRIPT_URL}?test=${testName}`;
let opts = {scope: SCOPE};
if (updateViaCache1) opts.updateViaCache = updateViaCache1;
const reg = await navigator.serviceWorker.register(fullScriptUrl, opts);
const sw = reg.installing;
await wait_for_state(t, sw, 'activated');
const values = await getScriptTimes(sw, testName);
const frame = await with_iframe(SCOPE);
const reg_in_frame = await frame.contentWindow.navigator.serviceWorker.getRegistration(normalizeURL(SCOPE));
assert_equals(reg_in_frame.updateViaCache, updateViaCache1 || 'imports', "reg_in_frame.updateViaCache");
opts = {scope: SCOPE};
if (updateViaCache2) opts.updateViaCache = updateViaCache2;
await navigator.serviceWorker.register(fullScriptUrl, opts);
const expected_updateViaCache = updateViaCache2 || 'imports';
assert_equals(reg.updateViaCache, expected_updateViaCache, "reg.updateViaCache updated");
// If the update happens via the cache, the scripts will come back byte-identical.
// We bypass the byte-identical check if the script URL has changed, but not if
// only the updateViaCache value has changed.
if (updateViaCache2 == 'all') {
assert_equals(reg.installing, null, "No new service worker");
}
// If there's no change to the updateViaCache value, register should be a no-op.
// The default value should behave as 'imports'.
else if ((updateViaCache1 || 'imports') == (updateViaCache2 || 'imports')) {
assert_equals(reg.installing, null, "No new service worker");
}
else {
const newWorker = reg.installing;
assert_true(!!newWorker, "New worker installing");
const newValues = await getScriptTimes(newWorker, testName);
if (!updateViaCache2 || updateViaCache2 == 'imports') {
assert_not_equals(values.mainTime, newValues.mainTime, "Main script should have updated");
assert_equals(values.importTime, newValues.importTime, "Imported script should be the same");
}
else if (updateViaCache2 == 'none') {
assert_not_equals(values.mainTime, newValues.mainTime, "Main script should have updated");
assert_not_equals(values.importTime, newValues.importTime, "Imported script should have updated");
}
else {
// We should have handled all of the possible values for updateViaCache2.
// If this runs, something's gone very wrong.
throw Error(`Unexpected updateViaCache value: ${updateViaCache}`);
}
}
// Wait for all registration related tasks on |frame| to complete.
await wait_for_activation_on_sample_scope(t, frame.contentWindow);
// The updateViaCache change should have been propagated to all
// corresponding JS registration objects.
assert_equals(reg_in_frame.updateViaCache, expected_updateViaCache, "reg_in_frame.updateViaCache updated");
frame.remove();
await cleanup();
}, testName);
}
}
// Test accessing updateViaCache of an unregistered registration.
for (const updateViaCache of UPDATE_VIA_CACHE_VALUES) {
const testName = `access-updateViaCache-after-unregister-${updateViaCache}`;
promise_test(async t => {
await cleanup();
const opts = {scope: SCOPE};
if (updateViaCache) opts.updateViaCache = updateViaCache;
const reg = await navigator.serviceWorker.register(
`${SCRIPT_URL}?test=${testName}`,
opts
);
const expected_updateViaCache = updateViaCache || 'imports';
assert_equals(reg.updateViaCache, expected_updateViaCache, "reg.updateViaCache");
await reg.unregister();
// Keep the original value.
assert_equals(reg.updateViaCache, expected_updateViaCache, "reg.updateViaCache");
await cleanup();
}, testName);
}
promise_test(async t => {
await cleanup();
t.add_cleanup(cleanup);
const registration = await navigator.serviceWorker.register(
'resources/empty.js',
{scope: SCOPE});
assert_equals(registration.updateViaCache, 'imports',
'before update attempt');
const fail = navigator.serviceWorker.register(
'resources/malformed-worker.py?parse-error',
{scope: SCOPE, updateViaCache: 'none'});
await promise_rejects_js(t, TypeError, fail);
assert_equals(registration.updateViaCache, 'imports',
'after update attempt');
}, 'updateViaCache is not updated if register() rejects');
</script>