| <!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> |