PlzDedicatedWorker: send COEP violation reports to an environment settings object owner
This CL makes a dedicated worker sends COEP violation reports to its
an environment settings object owner during the worker initialization
process when cross-origin-embedder-policy is invalid.
For PlzDedicatedWorker, the owner is the creator document or worker
which starts the worker directly.
For non-PlzDedicatedWorker, the owner is the nearest ancestor frame to
keep the current behavior.
Bug: 1060837, 1197041
Change-Id: I088470e7355c94f48b6ac8d51b5cf813b9d181ad
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2794353
Commit-Queue: Asami Doi <asamidoi@chromium.org>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#870917}
diff --git a/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html b/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html
new file mode 100644
index 0000000..331ad89
--- /dev/null
+++ b/html/cross-origin-embedder-policy/reporting-to-frame-owner.https.html
@@ -0,0 +1,87 @@
+<!doctype html>
+<html>
+<head>
+<title>Check COEP reports are sent to iframe for 'new Worker()' failure</title>
+</head>
+<body>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN} = get_host_info();
+const RESOURCES_PATH= new URL("resources", location).pathname;
+const iframe_path = "worker-owner-frame.html?pipe=";
+const worker_path = "universal-worker.js?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-report-only" :
+ "header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+};
+
+function checkReport(report, url, blocked_url, disposition) {
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, url);
+ assert_equals(report.body.type, "worker initialization");
+ assert_equals(report.body.blockedURL, blocked_url);
+ assert_equals(report.body.disposition, disposition);
+}
+
+// Test parameters:
+// - `owner_coep` the COEP header of the iframe document's response.
+// - `worker_coep` the COEP header of the DedicatedWorker's script response.
+//
+// Test expectations:
+// - `length` the length of reports.
+// - `disposition` the disposition in a report's body. Empty string if the
+// length of reports is expected to be 0.
+function check(
+ // Test parameters:
+ owner_coep,
+ worker_coep,
+ // Test expectations:
+ length,
+ disposition) {
+ promise_test(async (t) => {
+ const worker_url = worker_path + coep_header[worker_coep];
+ const iframe_url = iframe_path + coep_header[owner_coep];
+ const iframe = await with_iframe("./resources/" + iframe_url);
+ t.add_cleanup(() => iframe.remove());
+
+ const iframe_response = new Promise(resolve => window.onmessage = resolve);
+ iframe.contentWindow.startWorkerAndObserveReports(worker_url, length > 0);
+
+ const {data} = await iframe_response;
+ assert_equals(data.length, length);
+ if (data.length > 0) {
+ const blocked_url = `${ORIGIN}${RESOURCES_PATH}/${worker_url}`;
+ const url = `${ORIGIN}${RESOURCES_PATH}/${iframe_url}`;
+ checkReport(
+ data[0],
+ url,
+ blocked_url,
+ disposition
+ );
+ }
+ }, `Reporting to ${owner_coep} frame with ${worker_coep} worker`);
+}
+
+// -----------------------------------------------------------------------------
+// owner_coep , worker_coep , length , disposition
+// -----------------------------------------------------------------------------
+check("coep-none" , "coep-none" , 0 , "");
+check("coep-none" , "coep-report-only" , 0 , "");
+check("coep-none" , "coep-require-corp" , 0 , "");
+check("coep-report-only" , "coep-none" , 1 , "reporting");
+check("coep-report-only" , "coep-report-only" , 1 , "reporting");
+check("coep-report-only" , "coep-require-corp" , 0 , "");
+check("coep-require-corp" , "coep-none" , 1 , "enforce");
+check("coep-require-corp" , "coep-report-only" , 1 , "enforce");
+check("coep-require-corp" , "coep-require-corp" , 0 , "");
+
+</script>
+</body>
+</html>
+
diff --git a/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html b/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html
new file mode 100644
index 0000000..c001087
--- /dev/null
+++ b/html/cross-origin-embedder-policy/reporting-to-worker-owner.https.html
@@ -0,0 +1,89 @@
+<!doctype html>
+<html>
+<head>
+<title>Check COEP reports are sent to parent worker for 'new Worker()' failure</title>
+</head>
+<body>
+<script src="/common/get-host-info.sub.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+<script>
+const {ORIGIN} = get_host_info();
+const RESOURCES_PATH= new URL("resources", location).pathname;
+const parent_worker_path = "worker-owner.js?pipe=";
+const worker_path = "universal-worker.js?pipe=";
+
+const coep_header= {
+ "coep-none" : "",
+ "coep-report-only" :
+ "header(Cross-Origin-Embedder-Policy-Report-Only,require-corp)",
+ "coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
+};
+
+function checkReport(report, url, blocked_url, disposition) {
+ assert_equals(report.type, "coep");
+ assert_equals(report.url, url);
+ assert_equals(report.body.type, "worker initialization");
+ assert_equals(report.body.blockedURL, blocked_url);
+ assert_equals(report.body.disposition, disposition);
+}
+
+// Test parameters:
+// - `owner_coep` the COEP header of the parent DedicatedWorker's script
+// response.
+// - `worker_coep` the COEP header of the DedicatedWorker's script response.
+//
+// Test expectations:
+// - `length` the length of reports.
+// - `disposition` the disposition in a report's body. Empty string if the
+// length of reports is expected to be 0.
+function check(
+ // Test parameters:
+ owner_coep,
+ worker_coep,
+ // Test expectations:
+ length,
+ disposition) {
+ promise_test(async (t) => {
+ const worker_url = worker_path + coep_header[worker_coep];
+ const parent_worker_url = parent_worker_path + coep_header[owner_coep];
+ const parent_worker = new Worker('./resources/' + parent_worker_url);
+
+ const worker_response =
+ new Promise(resolve => parent_worker.onmessage = resolve);
+ parent_worker.postMessage(
+ {worker_url: worker_url, wait_for_report: length > 0});
+
+ const {data} = await worker_response;
+ assert_equals(data.length, length);
+ if (data.length > 0) {
+ const blocked_url = `${ORIGIN}${RESOURCES_PATH}/${worker_url}`;
+ const url = `${ORIGIN}${RESOURCES_PATH}/${parent_worker_url}`;
+ checkReport(
+ data[0],
+ url,
+ blocked_url,
+ disposition
+ );
+ }
+ }, `Reporting to ${owner_coep} worker with ${worker_coep} worker`);
+}
+
+// -----------------------------------------------------------------------------
+// owner_coep , worker_coep , length , disposition
+// -----------------------------------------------------------------------------
+check("coep-none" , "coep-none" , 0 , "");
+check("coep-none" , "coep-report-only" , 0 , "");
+check("coep-none" , "coep-require-corp" , 0 , "");
+check("coep-report-only" , "coep-none" , 1 , "reporting");
+check("coep-report-only" , "coep-report-only" , 1 , "reporting");
+check("coep-report-only" , "coep-require-corp" , 0 , "");
+check("coep-require-corp" , "coep-none" , 1 , "enforce");
+check("coep-require-corp" , "coep-report-only" , 1 , "enforce");
+check("coep-require-corp" , "coep-require-corp" , 0 , "");
+
+</script>
+</body>
+</html>
+
diff --git a/html/cross-origin-embedder-policy/resources/worker-owner-frame.html b/html/cross-origin-embedder-policy/resources/worker-owner-frame.html
new file mode 100644
index 0000000..509c904
--- /dev/null
+++ b/html/cross-origin-embedder-policy/resources/worker-owner-frame.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<script src="worker-owner.js"></script>
diff --git a/html/cross-origin-embedder-policy/resources/worker-owner.js b/html/cross-origin-embedder-policy/resources/worker-owner.js
new file mode 100644
index 0000000..d1f172a
--- /dev/null
+++ b/html/cross-origin-embedder-policy/resources/worker-owner.js
@@ -0,0 +1,36 @@
+const is_worker = !('window' in self);
+const parent_or_self = is_worker ? self : self.parent;
+
+function startWorkerAndObserveReports(worker_url, wait_for_report) {
+ const worker = new Worker(worker_url);
+ const result_promise = new Promise(resolve => {
+ worker.onmessage = _ => resolve('success');
+ worker.onerror = _ => resolve('error');
+ });
+ worker.postMessage("postMessage('reply to owner from worker');");
+
+ const report_promise = new Promise(resolve => {
+ const observer = new ReportingObserver(reports => {
+ observer.disconnect();
+ resolve(reports.map(r => r.toJSON()));
+ });
+ observer.observe();
+ });
+
+ if (wait_for_report) {
+ Promise.all([result_promise, report_promise]).then(results => {
+ parent_or_self.postMessage(results[1]);
+ });
+ } else {
+ result_promise.then(result => {
+ parent_or_self.postMessage([]);
+ });
+ }
+}
+
+if (is_worker) {
+ onmessage = e => {
+ startWorkerAndObserveReports(e.data.worker_url, e.data.wait_for_report);
+ };
+}
+