blob: 92be7d41adb52f60788a0b9d99387421b58f9113 [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>
const kFeature = 675; // From UseCounter.h
const kDeprecatedFeature = 538; // From Deprecation.h
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 => {
let win = window.open(url, '_blank');
add_completion_callback(() => win.close());
window.onmessage = e => {
assert_equals(e.data, 'LOADED');
resolve(win);
};
});
}
promise_test(t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?basic';
let worker;
let win1;
let win2;
return service_worker_unregister_and_register(t, kUrl, kScope)
.then(registration => {
add_completion_callback(function() { registration.unregister(); });
worker = registration.installing;
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => { return openWindow(kScope); })
.then(win => {
win1 = win;
return openWindow(kScope);
})
.then(win => {
win2 = win;
assert_false(isUseCounted(win1, kFeature));
assert_false(isUseCounted(win2, kFeature));
// Request to count a feature.
worker.postMessage({type: 'COUNT_FEATURE', feature: kFeature});
return Promise.all([
observeUseCounter(win1, kFeature),
observeUseCounter(win2, kFeature)
]);
})
.then(() => {
// 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});
return Promise.all([
observeUseCounter(win1, kDeprecatedFeature),
observeUseCounter(win2, kDeprecatedFeature)
]);
})
.then(() => {
// Deprecated API use on ServiceWorkerGlobalScope should be recorded
// in all controlled windows.
assert_true(isUseCounted(win1, kDeprecatedFeature));
assert_true(isUseCounted(win2, kDeprecatedFeature));
return openWindow(kScope);
})
.then(win => {
// Check UseCounters have been sent to the new window. Since this is can
// happen after the new window's document load, an observer is used to
// wait until it happens.
return Promise.all([
observeUseCounter(win, kFeature),
observeUseCounter(win, kDeprecatedFeature)
]);
})
}, 'UseCounter on ServiceWorkerGlobalScope');
promise_test(t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?claim';
let worker;
let win1;
let win2;
return openWindow(kScope)
.then(win => {
win1 = win;
return openWindow(kScope);
})
.then(win => {
win2 = win;
return service_worker_unregister_and_register(t, kUrl, kScope)
})
.then(registration => {
add_completion_callback(function() { registration.unregister(); });
worker = registration.installing;
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => {
// Request to count a feature.
worker.postMessage({type: 'COUNT_FEATURE', feature: kFeature});
return 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'});
});
})
.then(e => {
assert_equals(e.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});
return 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'});
});
})
.then(e => {
assert_equals(e.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.
return new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
worker.postMessage({type: 'CLAIM'});
});
})
.then(e => {
assert_equals(e.data.type, 'CLAIMED');
assert_false(e.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(t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html' +
'?type=features-during-install' +
'&feature=' + kFeature +
'&deprecated=' + kDeprecatedFeature;
let worker;
let win1;
let win2;
return openWindow(kScope)
.then(win => {
win1 = win;
return openWindow(kScope);
})
.then(win => {
win2 = win;
// A service worker will call some APIs during the install event.
return service_worker_unregister_and_register(t, kUrl, kScope)
})
.then(registration => {
add_completion_callback(function() { registration.unregister(); });
worker = registration.installing;
return wait_for_state(t, registration.installing, 'activated');
})
.then(e => {
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.
return internals.terminateServiceWorker(worker);
})
.then(() => {
// Request to claim. This will restart the service worker.
return new Promise(resolve => {
navigator.serviceWorker.onmessage = resolve;
worker.postMessage({type: 'CLAIM'});
});
})
.then(e => {
assert_equals(e.data.type, 'CLAIMED');
assert_true(e.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(t => {
const kUrl = 'resources/usecounter-worker.js';
const kScope = 'resources/usecounter-window.html?type=skip-waiting';
let worker1;
let worker2;
let win1;
let win2;
return service_worker_unregister_and_register(t, kUrl, kScope)
.then(registration => {
add_completion_callback(function() { registration.unregister(); });
worker1 = registration.installing;
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => { return openWindow(kScope); })
.then(win => {
win1 = win;
assert_false(isUseCounted(win1, kFeature));
// Request to count a feature.
worker1.postMessage({type: 'COUNT_FEATURE', feature: kFeature});
return observeUseCounter(win1, kFeature);
})
.then(e => {
// API use on ServiceWorkerGlobalScope should be recorded in a
// controlled window.
assert_true(isUseCounted(win1, kFeature));
// Update a controller using skipWaiting().
return navigator.serviceWorker.register(
kUrl + '?skip-waiting', {scope: kScope});
})
.then(registration => {
add_completion_callback(function() { registration.unregister(); });
worker2 = registration.installing;
// Wait until the new worker gets activated.
return wait_for_state(t, worker2, 'activated');
})
.then(() => { return openWindow(kScope); })
.then(win => {
// This window wasn't controlled by the previous worker.
win2 = win;
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});
return Promise.all([
observeUseCounter(win1, kDeprecatedFeature),
observeUseCounter(win2, kDeprecatedFeature)
]);
})
.then(e => {
// 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(t => {
const kFetchEventIsReload = 2032; // from web_feature.mojom
const kUrl = 'resources/use-isReload-worker.js';
const kScope = 'resources/usecounter-window.html?isReload';
let win;
return service_worker_unregister_and_register(t, kUrl, kScope)
.then(registration => {
add_result_callback(() => {
if (win) {
win.close();
}
registration.unregister();
});
return wait_for_state(t, registration.installing, 'activated');
})
.then(() => { return openWindow(kScope); })
.then(new_window => {
win = new_window;
return observeUseCounter(win, kFetchEventIsReload);
})
.then(() => {
assert_true(isUseCounted(win, kFetchEventIsReload));
});
}, 'FetchEvent.isReload is counted');
promise_test(t => {
const kServiceWorkerFrameType = 2033; // from web_feature.mojom
const kUrl = 'resources/feature-worker.js';
const kScope = 'resources/usecounter-window.html?frameType';
let worker;
let win;
return service_worker_unregister_and_register(t, kUrl, kScope)
.then(registration => {
add_result_callback(() => {
if (win) {
win.close();
}
registration.unregister();
});
worker = registration.installing;
return wait_for_state(t, worker, 'activated');
})
.then(() => { return openWindow(kScope); })
.then(new_window => {
win = new_window;
worker.postMessage('use-frameType');
return observeUseCounter(win, kServiceWorkerFrameType);
})
.then(() => {
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>