Service Worker: Add tentative wpt test for FetchEvent WorkerTiming in frame
This patch adds tentative wpt test. Currently FetchEvent WorkerTiming is a
tentative feature. In chromium, this feature hasn't implemented completely yet,
so some tests will be failed.
Explainer : https://github.com/wanderview/fetchevent-worker-timing/blob/master/explainer.md
Bug: 900700
Change-Id: I7a43989d8833ddd65c93106de67c4ebe6bd9ed52
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1915644
Commit-Queue: Yuta Kasai <yutakasai@google.com>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#720134}
diff --git a/service-workers/service-worker/fetch-event-worker-timing-frame.tentative.https.html b/service-workers/service-worker/fetch-event-worker-timing-frame.tentative.https.html
new file mode 100644
index 0000000..6ca9247
--- /dev/null
+++ b/service-workers/service-worker/fetch-event-worker-timing-frame.tentative.https.html
@@ -0,0 +1,225 @@
+<!DOCTYPE html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+// This is tentative because currently this feature only has an explainer and
+// no formal spec.
+// https://github.com/wanderview/fetchevent-worker-timing/blob/master/explainer.md
+
+async function wait_for_performance_entries(url, performance, entry_type) {
+ const entries = await performance.getEntriesByName(url);
+ if (entries.length > 0) {
+ return entries;
+ }
+ return new Promise((resolve) => {
+ new PerformanceObserver((list, observer) => {
+ const entries = list.getEntriesByName(url);
+ if (entries.length > 0) {
+ observer.disconnect();
+ resolve(entries);
+ }
+ }).observe({ entryTypes: [entry_type] });
+ });
+}
+
+function load_image(target_document, url) {
+ return new Promise((resolve, reject) => {
+ const image = target_document.createElement('img');
+ image.onload = () => { resolve(image); }
+ image.onerror = () => { reject(`Failed to load: ${url}`); };
+ image.src = url;
+ });
+}
+
+promise_test(async (t) => {
+ const registration = await service_worker_unregister_and_register(
+ t, 'resources/fetch-event-worker-timing.js', 'resources/');
+ promise_test(async (t) => registration.unregister(),
+ 'Unregister service worker');
+ await wait_for_state(t, registration.installing, 'activated');
+}, 'Set up an active service worker');
+
+promise_test(async (t) => {
+ const frame = await with_iframe('resources/empty.html?fallback');
+ t.add_cleanup(() => frame.remove());
+
+ const performance = frame.contentWindow.performance;
+
+ const entries = await wait_for_performance_entries(
+ frame.src, performance, 'navigation');
+ assert_equals(entries.length, 1, 'PerformanceNavigationTiming is observed');
+
+ const worker_timings = await entries[0].workerTiming;
+ const expected_worker_timings = [
+ {
+ name: "network-fallback mark 1",
+ entryType: "mark",
+ detail: { foo: 'foo' }
+ },
+ {
+ name: "network-fallback mark 2",
+ entryType: "mark",
+ detail: { bar: 'bar' }
+ },
+ {
+ name: "network-fallback measure",
+ entryType: "measure",
+ detail: { baz: 'baz' }
+ }
+ ];
+ assert_equals(worker_timings.length, expected_worker_timings.length,
+ 'workerTiming is completed when PerformanceResourceTiming is observed');
+
+ for (let i = 0; i < workerTimings.length; i++) {
+ assert_equals(worker_timings[i].name,
+ expected_worker_timings[i].name, 'entry name');
+ assert_equals(worker_timings[i].entryType,
+ expected_worker_timings[i].entryType, 'entry type');
+ assert_object_equals(worker_timings[i].detail,
+ expected_worker_timings[i].detail, 'entry detail');
+ }
+}, 'workerTiming for navigation in a frame with network fallback');
+
+promise_test(async (t) => {
+ const frame= await with_iframe('resources/empty.html?fetch-event');
+ t.add_cleanup(() => frame.remove());
+
+ const performance = frame.contentWindow.performance;
+
+ const entries = await wait_for_performance_entries(
+ frame.src, performance, 'navigation');
+ assert_equals(entries.length, 1, 'PerformanceNavigationTiming is observed');
+
+ const worker_timings = await entries[0].workerTiming;
+ const expected_worker_timings = [
+ {
+ name: "fetch-event mark 1",
+ entryType: "mark",
+ detail: { foo: 'foo' }
+ },
+ {
+ name: "fetch-event mark 2",
+ entryType: "mark",
+ detail: { bar: 'bar' }
+ },
+ {
+ name: "fetch-event measure",
+ entryType: "measure",
+ detail: { baz: 'baz' }
+ }
+ ];
+ assert_equals(worker_timings.length, expected_worker_timings.length,
+ 'workerTiming is completed when PerformanceResourceTiming is observed');
+
+ for (let i = 0; i < workerTimings.length; i++) {
+ assert_equals(worker_timings[i].name,
+ expected_worker_timings[i].name, 'entry name');
+ assert_equals(worker_timings[i].entryType,
+ expected_worker_timings[i].entryType, 'entry type');
+ assert_object_equals(worker_timings[i].detail,
+ expected_worker_timings[i].detail, 'entry detail');
+ }
+}, 'workerTiming for navigation in a frame for a response from a fetch ' +
+ 'handler');
+
+promise_test(async (t) => {
+ const frame = await with_iframe('resources/empty.html');
+ t.add_cleanup(() => frame.remove());
+
+ const performance = frame.contentWindow.performance;
+
+ const image_path = base_path() + 'resources/square.png?fallback';
+ const image_url = get_host_info()['HTTPS_ORIGIN'] + image_path;
+
+ let entries = await performance.getEntriesByName(image_url);
+ assert_equals(entries.length, 0, 'No PerformanceResourceTiming');
+
+ const image = await load_image(frame.contentDocument,
+ 'square.png?fetch-event');
+ entries = await wait_for_performance_entries(
+ image.src, performance, 'resource');
+ assert_equals(entries.length, 1, 'PerformanceResourceTiming is observed');
+
+ const worker_timings = await entries[0].workerTiming;
+ const expected_worker_timings = [
+ {
+ name: "network-fallback mark 1",
+ entryType: "mark",
+ detail: { foo: 'foo' }
+ },
+ {
+ name: "network-fallback mark 2",
+ entryType: "mark",
+ detail: { bar: 'bar' }
+ },
+ {
+ name: "network-fallback measure",
+ entryType: "measure",
+ detail: { baz: 'baz' }
+ }
+ ];
+ assert_equals(worker_timings.length, expected_worker_timings.length,
+ 'workerTiming is completed when PerformanceResourceTiming is observed');
+
+ for (let i = 0; i < workerTimings.length; i++) {
+ assert_equals(worker_timings[i].name,
+ expected_worker_timings[i].name, 'entry name');
+ assert_equals(worker_timings[i].entryType,
+ expected_worker_timings[i].entryType, 'entry type');
+ assert_object_equals(worker_timings[i].detail,
+ expected_worker_timings[i].detail, 'entry detail');
+ }
+}, 'workerTiming for subresources in a frame with network fallback');
+
+promise_test(async (t) => {
+ const frame = await with_iframe('resources/empty.html');
+ t.add_cleanup(() => frame.remove());
+
+ const performance = frame.contentWindow.performance;
+
+ const image_path = base_path() + 'resources/square.png?fetch-event';
+ const image_url = get_host_info()['HTTPS_ORIGIN'] + image_path;
+
+ let entries = await performance.getEntriesByName(image_url);
+ assert_equals(entries.length, 0, 'No PerformanceResourceTiming');
+
+ const image = await load_image(frame.contentDocument,
+ 'square.png?fetch-event');
+ entries = await wait_for_performance_entries(
+ image.src, performance, 'resource');
+ assert_equals(entries.length, 1, 'PerformanceResourceTiming is observed');
+
+ const worker_timings = await entries[0].workerTiming;
+ const expected_worker_timings = [
+ {
+ name: "fetch-event mark 1",
+ entryType: "mark",
+ detail: { foo: 'foo' }
+ },
+ {
+ name: "fetch-event mark 2",
+ entryType: "mark",
+ detail: { bar: 'bar' }
+ },
+ {
+ name: "fetch-event measure",
+ entryType: "measure",
+ detail: { baz: 'baz' }
+ }
+ ];
+ assert_equals(worker_timings.length, expected_worker_timings.length,
+ 'workerTiming is completed when PerformanceResourceTiming is observed');
+
+ for (let i = 0; i < workerTimings.length; i++) {
+ assert_equals(worker_timings[i].name,
+ expected_worker_timings[i].name, 'entry name');
+ assert_equals(worker_timings[i].entryType,
+ expected_worker_timings[i].entryType, 'entry type');
+ assert_object_equals(worker_timings[i].detail,
+ expected_worker_timings[i].detail, 'entry detail');
+ }
+}, 'workerTiming for subresources in a frame for a response from a fetch' +
+ 'handler');
+</script>
diff --git a/service-workers/service-worker/resources/fetch-event-worker-timing.js b/service-workers/service-worker/resources/fetch-event-worker-timing.js
new file mode 100644
index 0000000..7f3b201
--- /dev/null
+++ b/service-workers/service-worker/resources/fetch-event-worker-timing.js
@@ -0,0 +1,39 @@
+importScripts("/resources/testharness.js");
+
+self.addEventListener('fetch', event => {
+ if (event.request.url.indexOf('fallback') >= 0) {
+ event.addPerformanceEntry(
+ performance.mark("network-fallback mark 1",
+ { detail: { foo: 'foo' } }));
+ event.addPerformanceEntry(
+ performance.mark("network-fallback mark 2",
+ { detail: { bar: 'bar' } }));
+ event.addPerformanceEntry(performance.measure("network-fallback measure",
+ {
+ start: "network-fallback mark 1", end: "network-fallback mark 2",
+ detail: { baz: 'baz' }
+ }));
+ return;
+ } else if (event.request.url.indexOf('fetch-event') >= 0) {
+ event.respondWith((async () => {
+ event.addPerformanceEntry(performance.mark("fetch-event mark 1",
+ { detail: { foo: 'foo' } }));
+ const response = await fetch(event.request);
+
+ event.waitUntil(new Promise((resolve) => {
+ // Add performance entries after settling a promise for respondWith().
+ step_timeout(() => {
+ event.addPerformanceEntry(performance.mark("fetch-event mark 2",
+ { detail: { bar: 'bar' } }));
+ event.addPerformanceEntry(performance.measure("fetch-event measure",
+ {
+ start: "fetch-event mark 1", end: "fetch-event mark 2",
+ detail: { baz: 'baz' }
+ }));
+ resolve();
+ }, 100);
+ }));
+ return response;
+ })());
+ }
+});