Bug 1633673 [wpt PR 23288] - COEP ReportingObserver integration, a=testonly
authorYutaka Hirano <yhirano@chromium.org>
Wed, 13 May 2020 10:00:41 +0000
changeset 531182 180a226742fd396680e6491caf4c277fcf564182
parent 531181 f0e5fdd3cecd35183cd40f5a97174e94d158d8de
child 531183 744afc3e7b6261802cc6f1baa7d5f59efe51b170
push id37435
push userapavel@mozilla.com
push dateWed, 20 May 2020 15:28:23 +0000
treeherdermozilla-central@5415da14ec9a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstestonly
bugs1633673, 23288, 1052764, 2160291, 767369
milestone78.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633673 [wpt PR 23288] - COEP ReportingObserver integration, a=testonly Automatic update from web-platform-tests COEP ReportingObserver integration Notify reports made by COEP to ReportingObservers registered to documents and service workers. Support for dedicated workers is blocked on PlzDedicatedWorker. Support for shared workers is blocked on COEP support for shared workers. TESTING: Now we have two means to test the COEP reporting functionality. 1. Reports sent to the network. 2. Reports observed via ReportingObserver We need to have *some* tests with both ways, but we don't need to test all the cases for both ways, and generally speaking ReportingObserver is easier to use, and less flaky. Hence this CL makes cache-storage-reporting-*.https.html use ReportingObserver. This CL also adds reporting-subresource-corp.https.html for subresource loading cases. I'm planning to make more COEP reporting tests use ReportingObserver in the future. Bug: 1052764 Change-Id: Idc670bfc957f05aae813c4a3bce7e361293fc716 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2160291 Reviewed-by: Ian Clelland <iclelland@chromium.org> Reviewed-by: Makoto Shimazu <shimazu@chromium.org> Reviewed-by: Kinuko Yasuda <kinuko@chromium.org> Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org> Commit-Queue: Yutaka Hirano <yhirano@chromium.org> Cr-Commit-Position: refs/heads/master@{#767369} -- wpt-commits: 540f25e22401fe99e72bf6da2cb1ca1748a8ca4f wpt-pr: 23288
testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html
testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html
testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js
testing/web-platform/tests/lint.ignore
--- a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
@@ -12,46 +12,36 @@
   <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
   </script>
   <script src="./resources/cache-storage-reporting.js"> </script>
 </head>
 <script>
 
 promise_test(async (t) => {
   const worker_url = local(encode(worker_path + header_coep));
-  const iframe_url =
-    local(encode(iframe_path + header_coep + header_report_to));
-  dedicated_worker_script = `
-    (async function() {
-      const w = new Worker('${worker_url}');
-      w.postMessage(\`${eval_script}\`);
-    })();
-  `;
-  const iframe = await makeIframe(t, iframe_url);
-  iframe.contentWindow.postMessage(dedicated_worker_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
+  const worker = new Worker(worker_url);
+  const mc = new MessageChannel();
+  worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
 }, "COEP support on DedicatedWorker.")
 
 promise_test(async (t) => {
   const worker_url = local(encode(worker_path + header_coep_report_only));
-  const iframe_url =
-    local(encode(iframe_path + header_coep_report_only + header_report_to));
-  dedicated_worker_script = `
-    (async function() {
-      const w = new Worker('${worker_url}');
-      w.postMessage(\`${eval_script}\`);
-    })();
-  `;
-  const iframe = await makeIframe(t, iframe_url);
-  iframe.contentWindow.postMessage(dedicated_worker_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
+  const worker = new Worker(worker_url);
+  const mc = new MessageChannel();
+  worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
 }, "COEP-Report-Only support on DedicatedWorker.")
 
 </script>
 </html>
--- a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
@@ -4,40 +4,51 @@
   <title>
     Check COEP report are send for CacheStorage requests in Document.
   </title>
   <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
   <script src="/common/utils.js"></script>
-  <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
   </script>
   <script src="./resources/cache-storage-reporting.js"></script>
 </head>
 <script>
 
+async function waitReports(frame) {
+  return await new Promise((resolve) => {
+    const observer = new frame.contentWindow.ReportingObserver((reports) => {
+      observer.disconnect();
+      resolve(reports.map(r => r.toJSON()));
+    });
+    observer.observe();
+
+    frame.contentWindow.eval(eval_script);
+  });
+}
+
 promise_test(async (t) => {
-  const iframe_url =
-    local(encode(iframe_path + header_coep + header_report_to));
+  const iframe_url = local(encode(iframe_path + header_coep));
   const iframe = await makeIframe(t, iframe_url);
-  iframe.contentWindow.postMessage(eval_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(iframe_url.toString()));
+  const reports = await waitReports(iframe);
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, iframe_url);
 }, "COEP support on document.")
 
 promise_test(async (t) => {
-  const iframe_url =
-    local(encode(iframe_path + header_coep_report_only + header_report_to));
+  const iframe_url = local(encode(iframe_path + header_coep_report_only));
   const iframe = await makeIframe(t, iframe_url);
-  iframe.contentWindow.postMessage(eval_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(iframe_url.toString()));
+  const reports = await waitReports(iframe);
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, iframe_url);
 }, "COEP-Report-Only support on document.")
 
 </script>
 </html>
--- a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html
@@ -1,53 +1,60 @@
 <!doctype html>
 <html>
 <head>
   <title>
     Check COEP report are send for CacheStorage requests in ServiceWorker.
   </title>
-  <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
   <script src="/common/utils.js"></script>
   <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
   </script>
   <script src="./resources/cache-storage-reporting.js"></script>
 </head>
 <script>
 
 promise_test(async (t) => {
-  const worker_url = local(encode(
-    worker_path + header_coep + header_report_to + header_service_worker_allowed
-  ));
+  const worker_url = local(encode(worker_path + header_coep));
+  // As we don't want the service worker to control any page, generate a
+  // one-time scope.
+  const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
   const reg =
-    await service_worker_unregister_and_register(t, worker_url, SW_SCOPE);
+    await service_worker_unregister_and_register(t, worker_url, SCOPE);
   add_completion_callback(() => reg.unregister());
   const worker = reg.installing || reg.waiting || reg.active;
-  reports = fetchReport();
-  worker.postMessage(eval_script);
-  const report = await reports;
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
-}, "COEP support on ServiceWorker.")
+  const mc = new MessageChannel();
+  worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_not_equals(reports, 'TIMEOUT');
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
+}, "COEP support on ServiceWorker.");
 
 promise_test(async (t) => {
-  const worker_url = local(encode(
-    worker_path + header_coep_report_only + header_report_to + header_service_worker_allowed
-  ))
+  const worker_url = local(encode(worker_path + header_coep_report_only));
+  // As we don't want the service worker to control any page, generate a
+  // one-time scope.
+  const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
   const reg =
-    await service_worker_unregister_and_register(t, worker_url, SW_SCOPE);
+    await service_worker_unregister_and_register(t, worker_url, SCOPE);
   add_completion_callback(() => reg.unregister());
   const worker = reg.installing || reg.waiting || reg.active;
-  reports = fetchReport();
-  worker.postMessage(eval_script);
-  const report = await reports;
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
-}, "COEP-Report-Only support on ServiceWorker.")
+  const mc = new MessageChannel();
+  worker.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_not_equals(reports, 'TIMEOUT');
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
+}, "COEP-Report-Only support on ServiceWorker.");
 
 </script>
 </html>
--- a/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
@@ -1,44 +1,45 @@
 <!doctype html>
 <html>
 <head>
   <title>
-    Check COEP report are send for CacheStorage requests in DedicatedWorker
+    Check COEP report are send for CacheStorage requests in SharedWorker
   </title>
   <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
   <script src="/common/utils.js"></script>
-  <script src="/service-workers/service-worker/resources/test-helpers.sub.js">
-  </script>
   <script src="./resources/cache-storage-reporting.js"> </script>
 </head>
 <script>
 
 promise_test(async (t) => {
-  const worker_url =
-    local(encode(worker_path + header_coep + header_report_to));
-  const worker = new Worker(worker_url);
-  worker.postMessage(eval_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
+  const worker_url = local(encode(worker_path + header_coep));
+  const worker = new SharedWorker(worker_url);
+  const mc = new MessageChannel();
+  worker.port.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
 }, "COEP support on SharedWorker.")
 
 promise_test(async (t) => {
-  const worker_url =
-    local(encode(worker_path + header_coep_report_only + header_report_to));
-  const worker = new Worker(worker_url);
-  worker.postMessage(eval_script);
-  const report = await fetchReport();
-  assert_equals(report["body"]["blocked-url"], image_url.toString());
-  assert_equals(report["body"]["type"], "corp");
-  assert_equals(report["type"], "coep");
-  assert_equals(normalize(report["url"]), normalize(worker_url.toString()));
+  const worker_url = local(encode(worker_path + header_coep_report_only));
+  const worker = new SharedWorker(worker_url);
+  const mc = new MessageChannel();
+  worker.port.postMessage({script: eval_script, port: mc.port2}, [mc.port2]);
+  const reports = (await new Promise(r => mc.port1.onmessage = r)).data;
+  assert_equals(reports.length, 1);
+  const report = reports[0];
+  assert_equals(report.body["blocked-url"], image_url);
+  assert_equals(report.body.type, "corp");
+  assert_equals(report.type, "coep");
+  assert_equals(report.url, worker_url);
 }, "COEP-Report-Only support on SharedWorker.")
 
-
 </script>
 </html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/reporting-subresource-corp.https.html
@@ -0,0 +1,161 @@
+<!doctype html>
+<html>
+<meta name="timeout" content="long">
+<body>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
+const BASE = new URL("resources", location).pathname
+const FRAME_URL = `${ORIGIN}/common/blank.html` +
+  '?pipe=header(cross-origin-embedder-policy,require-corp)' +
+  `|header(cross-origin-embedder-policy-report-only,require-corp)`;
+const WORKER_URL = `${ORIGIN}${BASE}/reporting-worker.js` +
+  '?pipe=header(cross-origin-embedder-policy,require-corp)' +
+  `|header(cross-origin-embedder-policy-report-only,require-corp)`;
+
+function wait(ms) {
+  return new Promise(resolve => step_timeout(resolve, ms));
+}
+
+function checkReport(report, contextUrl, blockedUrl) {
+  assert_equals(report.type, 'coep');
+  assert_equals(report.url, contextUrl);
+  assert_equals(report.body.type, 'corp');
+  assert_equals(report.body['blocked-url'], blockedUrl);
+}
+
+async function fetchInFrame(t, url) {
+  const reports = [];
+  const frame = await with_iframe(FRAME_URL);
+  t.add_cleanup(() => frame.remove());
+
+  const observer = new frame.contentWindow.ReportingObserver((rs) => {
+    for (const report of rs) {
+      reports.push(report.toJSON());
+    }
+  });
+  observer.observe();
+  const init = { mode: 'no-cors', cache: 'no-store' };
+  await frame.contentWindow.fetch(url, init).catch(() => {});
+
+  // Wait 200ms for reports to settle.
+  await wait(200);
+  return reports;
+}
+
+async function fetchInWorker(workerOrPort, url) {
+  const script =
+    `fetch('${url}', {mode: 'no-cors', cache: 'no-store'}).catch(() => {});`;
+  const mc = new MessageChannel();
+  workerOrPort.postMessage({script, port: mc.port2}, [mc.port2]);
+  return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+// We want to test several URLs in various environments (document,
+// dedicated worker, shared worser, service worker). As expectations
+// are independent of environment except for the context URLs in reports,
+// we define ENVIRONMENTS and CASES to reduce the code duplication.
+//
+// ENVIRONMENTS is a list of dictionaries. Each dictionary consists of:
+//  - tag: the name of the environment
+//  - contextUrl: the URL of the environment settings object
+//  - run: an async function which generates reports
+//    - test: a testharness Test object
+//    - url: the URL for a test case (see below)
+//
+// CASES is a list of test cases. Each test case consists of:
+//  - name: the name of the test case
+//  - url: the URL of the test case
+//  - check: a function to check the results
+//    - reports: the generated reports
+//    - url: the URL of the test case
+//    - contextUrl: the URL of the environment settings object (see
+//                  ENVORONMENTS)
+
+const ENVIRONMENTS = [{
+  tag: 'document',
+  contextUrl: FRAME_URL,
+  run: async (test, url) => {
+    return await fetchInFrame(test, url);
+  },
+}, {
+  tag: 'dedicated worker',
+  contextUrl: WORKER_URL,
+  run: async (test, url) => {
+    const worker = new Worker(WORKER_URL);
+    worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+    test.add_cleanup(() => worker.terminate());
+    return await fetchInWorker(worker, url);
+  },
+}, {
+  tag: 'shared worker',
+  contextUrl: WORKER_URL,
+  run: async (test, url) => {
+    const worker = new SharedWorker(WORKER_URL);
+    worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+    return await fetchInWorker(worker.port, url);
+  },
+}, {
+  tag: 'service worker',
+  contextUrl: WORKER_URL,
+  run: async (test, url) => {
+    // As we don't want the service worker to control any page, generate a
+    // one-time scope.
+    const SCOPE = new URL(`resources/${token()}.html`, location).pathname;
+    const reg =
+      await service_worker_unregister_and_register(test, WORKER_URL, SCOPE);
+    test.add_cleanup(() => reg.unregister());
+    const worker = reg.installing || reg.waiting || reg.active;
+    worker.addEventListener('error', test.unreached_func('Worker.onerror'));
+    return await fetchInWorker(worker, url);
+  },
+}];
+
+const CASES = [{
+  name: 'same-origin',
+  url: '/common/text-plain.txt',
+  check: (reports, url, contextUrl) => {
+    assert_equals(reports.length, 0);
+  }
+}, {
+  name: 'blocked by CORP: same-origin',
+  url: `${REMOTE_ORIGIN}${BASE}/nothing-same-origin-corp.txt`,
+  check: (reports, url, contextUrl) => {
+    assert_equals(reports.length, 0);
+  }
+}, {
+  name: 'blocked due to COEP',
+  url: `${REMOTE_ORIGIN}/common/text-plain.txt`,
+  check: (reports, contextUrl, url) => {
+    // One for COEP, one for COEP-RO.
+    assert_equals(reports.length, 2);
+    checkReport(reports[0], contextUrl, url);
+    checkReport(reports[1], contextUrl, url);
+  }
+}, {
+  name: 'blocked during redirect',
+  url: `${ORIGIN}/common/redirect.py?location=` +
+       encodeURIComponent(`${REMOTE_ORIGIN}/common/text-plain.txt`),
+  check: (reports, contextUrl, url) => {
+    // One for COEP, one for COEP-RO.
+    assert_equals(reports.length, 2);
+    checkReport(reports[0], contextUrl, url);
+    checkReport(reports[1], contextUrl, url);
+  },
+}];
+
+for (const env of ENVIRONMENTS) {
+  for (const testcase of CASES) {
+    promise_test(async (t) => {
+      const reports = await env.run(t, testcase.url);
+
+      testcase.check(reports, env.contextUrl, testcase.url);
+    }, `[${env.tag}] ${testcase.name}`);
+  }
+}
+
+</script>
--- a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
@@ -1,47 +1,29 @@
 function remote(path) {
   const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
-  return new URL(path, REMOTE_ORIGIN);
+  return new URL(path, REMOTE_ORIGIN).href;
 }
 
 function local(path) {
-  return new URL(path, location.origin);
+  return new URL(path, location.origin).href;
 }
 
-let encode = function(url) {
-  return encodeURI(url).replace(/\;/g,"%3B");
+function encode(url) {
+  return encodeURI(url).replace(/\;/g, '%3B');
 }
 
-const resource_path = (new URL("./resources", location)).pathname;
-const report_token= token();
-const report_endpoint_url = local(resource_path + `/report.py?key=${report_token}`)
-const endpoint =
-{
-   "group":"endpoint",
-   "max_age":3600,
-   "endpoints":[{ "url":report_endpoint_url.toString() }]
-};
-let endpoint_string =
-  JSON.stringify(endpoint)
-   .replace(/,/g, "\\,")
-   .replace(/\(/g, "\\\(")
-   .replace(/\)/g, "\\\)=");
-const header_report_to = `|header(report-to,${endpoint_string})`;
-const header_coep =
-  '|header(Cross-Origin-Embedder-Policy,require-corp;report-to="endpoint")';
+const resource_path = (new URL('./resources', location)).pathname;
+const header_coep = '|header(Cross-Origin-Embedder-Policy,require-corp)';
 const header_coep_report_only =
-  '|header(Cross-Origin-Embedder-Policy-Report-Only,require-corp;report-to="endpoint")';
-const SW_SCOPE = local(resource_path + "/");
-const header_service_worker_allowed =
-  `|header(service-worker-allowed,${SW_SCOPE})`;
+    '|header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)';
 
-const iframe_path = resource_path + "/iframe.html?pipe=";
-const worker_path = resource_path + "/universal-worker.js?pipe=";
-const image_url = remote("/images/blue.png");
+const iframe_path = resource_path + '/iframe.html?pipe=';
+const worker_path = resource_path + '/reporting-worker.js?pipe=';
+const image_url = remote('/images/blue.png');
 
 // This script attempt to load a COEP:require-corp CORP:undefined response from
 // the CacheStorage.
 //
 // Executed from different context:
 // - A Document
 // - A ServiceWorker
 // - A DedicatedWorker
@@ -56,49 +38,26 @@ const eval_script = `
       const response = await cache.match(request);
     } catch(e) {
     }
   })()
 `;
 
 promise_setup(async (t) => {
   const cache = await caches.open('v1');
-  const fetch_request = new Request(image_url, {mode: 'no-cors'});
-  const fetch_response = await fetch(fetch_request);
-  await cache.put(fetch_request, fetch_response);
-}, "Setup: store a CORS:cross-origin COEP:none response into CacheStorage")
+  const request = new Request(image_url, {mode: 'no-cors'});
+  const response = await fetch(request);
+  await cache.put(request, response);
+}, 'Setup: store a CORS:cross-origin COEP:none response into CacheStorage')
 
 async function makeIframe(test, iframe_url) {
-  const iframe = document.createElement("iframe");
+  const iframe = document.createElement('iframe');
   test.add_cleanup(() => iframe.remove());
   iframe.src = iframe_url;
   const iframe_loaded = new Promise(resolve => iframe.onload = resolve);
   document.body.appendChild(iframe);
   await iframe_loaded;
   return iframe;
 }
 
 function wait(ms) {
   return new Promise(resolve => step_timeout(resolve, ms));
 }
-
-async function fetchReport() {
-  const fetch_report_path = resource_path + `/report.py?key=${report_token}`;
-  while(true) {
-    const response = await fetch(encode(fetch_report_path));
-    const reports = await response.json();
-    if (reports.length == 0) {
-      wait(200);
-      continue;
-    }
-    if (reports.length != 1)
-      throw "Too many reports received";
-
-    return reports[0];
-  }
-  throw "Report not send";
-}
-
-// Remove parts of the URL that are different at runtime.
-function normalize(url) {
-  url = new URL(url);
-  return url.origin + url.pathname;
-}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js
@@ -0,0 +1,25 @@
+function run({script, port}) {
+  const reports = [];
+  const observer = new ReportingObserver((rs) => {
+    for (const r of rs) {
+      reports.push(r.toJSON());
+    }
+  });
+  // Wait 200ms for reports to settle.
+  setTimeout(() => {
+    observer.disconnect();
+    port.postMessage(reports);
+  }, 200);
+  observer.observe();
+
+  // This eval call may generate some reports.
+  eval(script);
+}
+
+// For DedicatedWorker and ServiceWorker
+self.addEventListener('message', (e) => run(e.data));
+
+// For SharedWorker
+self.addEventListener('connect', (e) => {
+  e.ports[0].onmessage = (ev) => run(ev.data);
+});
--- a/testing/web-platform/tests/lint.ignore
+++ b/testing/web-platform/tests/lint.ignore
@@ -293,16 +293,17 @@ SET TIMEOUT: css/css-fonts/font-display/
 SET TIMEOUT: css/css-fonts/font-display/font-display-feature-policy-01.tentative.html
 SET TIMEOUT: css/css-fonts/font-display/font-display-feature-policy-02.tentative.html
 SET TIMEOUT: css/css-fonts/font-display/font-display-preload.html
 SET TIMEOUT: document-policy/font-display/font-display-document-policy-01.tentative.html
 SET TIMEOUT: html/browsers/windows/auxiliary-browsing-contexts/resources/close-opener.html
 SET TIMEOUT: html/cross-origin-embedder-policy/resources/navigate-none.sub.html
 SET TIMEOUT: html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
 SET TIMEOUT: html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html
+SET TIMEOUT: html/cross-origin-embedder-policy/resources/reporting-worker.js
 SET TIMEOUT: html/dom/documents/dom-tree-accessors/Document.currentScript.html
 SET TIMEOUT: html/webappapis/timers/*
 SET TIMEOUT: orientation-event/resources/orientation-event-helpers.js
 SET TIMEOUT: portals/history/resources/portal-harness.js
 SET TIMEOUT: resources/chromium/*
 SET TIMEOUT: resources/test/tests/functional/add_cleanup.html
 SET TIMEOUT: resources/test/tests/functional/add_cleanup_async.html
 SET TIMEOUT: resources/test/tests/functional/add_cleanup_async_rejection.html