blob: 600c518eabaf4164f03a0a1858590cd04569e6d4 [file] [log] [blame]
<!DOCTYPE html>
<title>Service Worker: UseCounter</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/test-helpers.js"></script>
<script>
// Dummy values for features.
const kFeature = 675;
const kDeprecatedFeature = 166;
function isUseCounted(win, feature) {
return win.internals.isUseCounted(win.document, feature);
}
function observeUseCounter(win, feature) {
return win.internals.observeUseCounter(win.document, feature);
}
// Use a window instead of an iframe because UseCounter is shared among frames
// in a document and these tests cannot be conducted in such an environment.
// A window has its own UseCounter.
function openWindow(url) {
return new Promise(resolve => {
const win = window.open(url, '_blank');
add_completion_callback(() => win.close());
window.onmessage = e => {
assert_equals(e.data, 'LOADED');
resolve(win);
};
});
}
promise_test(async t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?basic';
const registration =
await service_worker_unregister_and_register(t, kUrl, kScope);
t.add_cleanup(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, registration.installing, 'activated');
const win1 = await openWindow(kScope);
const win2 = await openWindow(kScope);
assert_false(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
// Request to count a feature.
worker.postMessage({ type: 'COUNT_FEATURE', feature: kFeature });
await observeUseCounter(win1, kFeature);
await observeUseCounter(win2, kFeature);
// API use on ServiceWorkerGlobalScope should be recorded in all controlled
// windows.
assert_true(isUseCounted(win1, kFeature));
assert_true(isUseCounted(win2, kFeature));
assert_false(isUseCounted(win1, kDeprecatedFeature));
assert_false(isUseCounted(win2, kDeprecatedFeature));
// Request to count a deprecated feature.
worker.postMessage(
{ type: 'COUNT_DEPRECATION', feature: kDeprecatedFeature });
await observeUseCounter(win1, kDeprecatedFeature);
await observeUseCounter(win2, kDeprecatedFeature);
// Deprecated API use on ServiceWorkerGlobalScope should be recorded in all
// controlled windows.
assert_true(isUseCounted(win1, kDeprecatedFeature));
assert_true(isUseCounted(win2, kDeprecatedFeature));
// Check UseCounters have been sent to the new window. Since this can happen
// after the new window's document load, an observer is used to wait until it
// happens.
const win = await openWindow(kScope);
await observeUseCounter(win, kFeature);
await observeUseCounter(win, kDeprecatedFeature);
}, 'UseCounter on ServiceWorkerGlobalScope');
promise_test(async t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?claim';
const win1 = await openWindow(kScope);
const win2 = await openWindow(kScope);
const registration =
await service_worker_unregister_and_register(t, kUrl, kScope);
t.add_cleanup(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, registration.installing, 'activated');
// Request to count a feature.
worker.postMessage({type: 'COUNT_FEATURE', feature: kFeature});
let msgEvent = await new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
// There is no way to verify that API use is never counted. As a workaround,
// wait for only one round-trip.
worker.postMessage({type: 'PING'});
});
assert_equals(msgEvent.data.type, 'PONG');
// API use on ServiceWorkerGlobalScope should not be recorded in windows
// because they are not controlled yet.
assert_false(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
// Request to count a deprecated feature.
worker.postMessage(
{ type: 'COUNT_DEPRECATION', feature: kDeprecatedFeature });
msgEvent = await new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
// There is no way to verify that API use is never counted. As a workaround,
// wait for only one round-trip.
worker.postMessage({type: 'PING'});
});
assert_equals(msgEvent.data.type, 'PONG');
// Deprecated API use on ServiceWorkerGlobalScope should not be recorded in
// windows because they are not controlled yet.
assert_false(isUseCounted(win1, kDeprecatedFeature));
assert_false(isUseCounted(win2, kDeprecatedFeature));
assert_equals(win1.navigator.serviceWorker.controller, null);
assert_equals(win2.navigator.serviceWorker.controller, null);
// Request to claim.
msgEvent = await new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
worker.postMessage({type: 'CLAIM'});
});
assert_equals(msgEvent.data.type, 'CLAIMED');
assert_false(msgEvent.data.restarted);
assert_not_equals(win1.navigator.serviceWorker.controller, null);
assert_not_equals(win2.navigator.serviceWorker.controller, null);
// The windows are now controlled by the service worker. Their UseCounter
// should be synchronized with worker's counter.
assert_true(isUseCounted(win1, kFeature));
assert_true(isUseCounted(win2, kFeature));
assert_true(isUseCounted(win1, kDeprecatedFeature));
assert_true(isUseCounted(win2, kDeprecatedFeature));
}, 'UseCounter on ServiceWorkerGlobalScope - A use counter owned by newly ' +
'controlled window should be synchronized with worker\'s counter');
// Test that features used during service worker installation are persisted.
// This test could be non-deterministic because there is no handy way to sweep
// out on-memory representation of ServiceWorker in the browser process and make
// sure to restore it from the storage.
promise_test(async t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html' +
'?type=features-during-install' +
'&feature=' + kFeature +
'&deprecated=' + kDeprecatedFeature;
const win1 = await openWindow(kScope);
const win2 = await openWindow(kScope);
// A service worker will call some APIs during the install event.
const registration =
await service_worker_unregister_and_register(t, kUrl, kScope);
t.add_cleanup(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, registration.installing, 'activated');
assert_equals(win1.navigator.serviceWorker.controller, null);
assert_equals(win2.navigator.serviceWorker.controller, null);
// API use on ServiceWorkerGlobalScope should not be recorded in windows
// because they are not controlled yet.
assert_false(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
assert_false(isUseCounted(win1, kDeprecatedFeature));
assert_false(isUseCounted(win2, kDeprecatedFeature));
// Terminate the service worker.
await internals.terminateServiceWorker(worker);
// Request to claim. This will restart the service worker.
const msgEvent = await new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
worker.postMessage({type: 'CLAIM'});
});
assert_equals(msgEvent.data.type, 'CLAIMED');
assert_true(msgEvent.data.restarted);
assert_not_equals(win1.navigator.serviceWorker.controller, null);
assert_not_equals(win2.navigator.serviceWorker.controller, null);
// The windows are now controlled by the service worker. Their UseCounter
// should be synchronized with worker's counter retrieved from the storage.
assert_true(isUseCounted(win1, kFeature));
assert_true(isUseCounted(win2, kFeature));
assert_true(isUseCounted(win1, kDeprecatedFeature));
assert_true(isUseCounted(win2, kDeprecatedFeature));
}, 'UseCounter on ServiceWorkerGlobalScope - counts during the install ' +
'event should be persisted');
// TODO(nhiroki): Test that features used after service worker installation are
// not persisted. This could be impossible because there is no handy way to
// sweep out on-memory representation of ServiceWorker in the browser process
// and make sure to restore it from the storage.
promise_test(async t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?type=skip-waiting';
const registration1 =
await service_worker_unregister_and_register(t, kUrl, kScope);
t.add_cleanup(() => registration1.unregister());
const worker1 = registration1.installing;
await wait_for_state(t, registration1.installing, 'activated');
const win1 = await openWindow(kScope);
assert_false(isUseCounted(win1, kFeature));
// Request to count a feature.
worker1.postMessage({type: 'COUNT_FEATURE', feature: kFeature});
await observeUseCounter(win1, kFeature);
// API use on ServiceWorkerGlobalScope should be recorded in a controlled
// window.
assert_true(isUseCounted(win1, kFeature));
// Update a controller using skipWaiting().
const registration2 = await navigator.serviceWorker.register(
kUrl + '?skip-waiting', {scope: kScope});
t.add_cleanup(() => registration2.unregister());
const worker2 = registration2.installing;
// Wait until the new worker gets activated.
await wait_for_state(t, worker2, 'activated');
const win2 = await openWindow(kScope);
// This window wasn't controlled by the previous worker.
assert_not_equals(win2.navigator.serviceWorker.controller, undefined);
// An updated worker does not take over the previous counter, so API use on
// the previous worker should not be recorded in the newly controlled window.
assert_true(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
assert_false(isUseCounted(win1, kDeprecatedFeature));
assert_false(isUseCounted(win2, kDeprecatedFeature));
// Request to count a deprecated feature.
worker2.postMessage(
{ type: 'COUNT_DEPRECATION', feature: kDeprecatedFeature });
await observeUseCounter(win1, kDeprecatedFeature);
await observeUseCounter(win2, kDeprecatedFeature);
// Deprecated API use on the updated worker should be recorded in all
// controlled windows.
assert_true(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
assert_true(isUseCounted(win1, kDeprecatedFeature));
assert_true(isUseCounted(win2, kDeprecatedFeature));
}, 'UseCounter on ServiceWorkerGlobalScope - an updated worker should not ' +
'take over a previous counter');
promise_test(async t => {
const kFetchEventIsReload = 2032; // from web_feature.mojom
const kUrl = 'resources/use-isReload-worker.js';
const kScope = 'resources/usecounter-window.html?isReload';
const registration =
await service_worker_unregister_and_register(t, kUrl, kScope);
add_result_callback(() => registration.unregister());
await wait_for_state(t, registration.installing, 'activated');
const win = await openWindow(kScope);
await observeUseCounter(win, kFetchEventIsReload);
assert_true(isUseCounted(win, kFetchEventIsReload));
}, 'FetchEvent.isReload is counted');
promise_test(async t => {
const kServiceWorkerFrameType = 2033; // from web_feature.mojom
const kUrl = 'resources/feature-worker.js';
const kScope = 'resources/usecounter-window.html?frameType';
const registration =
await service_worker_unregister_and_register(t, kUrl, kScope);
add_result_callback(() => registration.unregister());
const worker = registration.installing;
await wait_for_state(t, worker, 'activated');
const win = await openWindow(kScope);
worker.postMessage('use-frameType');
await observeUseCounter(win, kServiceWorkerFrameType);
assert_true(isUseCounted(win, kServiceWorkerFrameType));
}, 'Client.frameType is counted');
// TODO(nhiroki): Test a case where ServiceWorker controls SharedWorker that is
// connected from multiple windows. In such a case, API use on ServiceWorker
// should be propagated to all connecting windows via SharedWorker.
</script>
</html>