Refine crossOriginIsolated implementation for workers (#25748)
- Fix WindowOrWorketGlobalScope.crossOriginIsolated behavior for
workers.
- For SharedWorkers and ServiceWorkers it always return false. See
https://crbug.com/1131403 and https://crbug.com/1131404.
- Rename ExecutionContext::IsCrossOriginIsolated to
CrossOriginCapability. This is aligned with
https://html.spec.whatwg.org/C/#concept-settings-object-cross-origin-isolated-capability.
- Fix wpt/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html.
I originally planned to do that in
https://github.com/web-platform-tests/wpt/pull/24600 but I couldn't
do that due to some flakiness.
- Add more tests for workers in
wpt/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html.
Bug: 1115379, 1018680, 1131403, 1131404
Change-Id: I2afcb01403f67a11fd06aefde1238aba16b68f36
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2416428
Commit-Queue: Yutaka Hirano <yhirano@chromium.org>
Reviewed-by: Domenic Denicola <domenic@chromium.org>
Reviewed-by: Robert Flack <flackr@chromium.org>
Reviewed-by: Hiroki Nakagawa <nhiroki@chromium.org>
Cr-Commit-Position: refs/heads/master@{#810130}
Co-authored-by: Yutaka Hirano <yhirano@chromium.org>
diff --git a/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html b/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html
index aec1b85..2fc545d 100644
--- a/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html
+++ b/html/cross-origin-embedder-policy/cross-origin-isolated-permission.https.html
@@ -3,24 +3,28 @@
<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>
<body>
<script>
const {ORIGIN, HTTPS_REMOTE_ORIGIN} = get_host_info();
-const PATH =
+const FRAME_PATH =
new URL('resources/cross-origin-isolated-frame.html', location).pathname;
+const WORKER_URL =
+ new URL('resources/cross-origin-isolated-worker.js', location).href;
const PIPE =
'?pipe=' +
'|header(cross-origin-embedder-policy,require-corp)' +
'|header(cross-origin-resource-policy,cross-origin)';
-async function getCrossOriginIsolatedFor(t, origin, value) {
+async function getCrossOriginIsolatedForFrame(t, origin, value) {
const parentFrame = document.createElement('iframe');
t.add_cleanup(() => parentFrame.remove());
let pipe = PIPE;
if (value !== undefined) {
pipe += `|header(permissions-policy,cross-origin-isolated=${value})`;
}
- parentFrame.src = `${PATH}${pipe}`;
+ parentFrame.src = `${FRAME_PATH}${pipe}`;
document.body.append(parentFrame);
await new Promise((resolve) => {
@@ -28,7 +32,7 @@
});
const frame = parentFrame.contentDocument.createElement('iframe');
- frame.src = `${origin}${PATH}${PIPE}`;
+ frame.src = `${origin}${FRAME_PATH}${PIPE}`;
parentFrame.contentDocument.body.append(frame);
frame.addEventListener('error', t.unreached_func('frame.error'));
await new Promise((resolve) => {
@@ -37,35 +41,149 @@
const mc = new MessageChannel();
frame.contentWindow.postMessage({port: mc.port2}, '*', [mc.port2]);
-
- const e = await new Promise((resolve) => {
- mc.port1.onmessage = resolve;
- });
- assert_equals(typeof(e.data), 'boolean');
- return e.data;
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
}
-function generateTest(origin, value, expectation) {
+async function getCrossOriginIsolatedForDedicatedWorker(t, scheme, value) {
+ const frame = document.createElement('iframe');
+ t.add_cleanup(() => frame.remove());
+ let pipe = PIPE;
+ if (value !== undefined) {
+ pipe += `|header(permissions-policy,cross-origin-isolated=${value})`
+ }
+ frame.src = `${FRAME_PATH}${pipe}`;
+ document.body.append(frame);
+
+ frame.addEventListener('error', t.unreached_func('frame.error'));
+ await new Promise((resolve) => {
+ frame.addEventListener('load', resolve);
+ });
+
+ let workerURL;
+ if (scheme === 'https') {
+ workerURL = `${WORKER_URL}${PIPE}`;
+ } else if (scheme === 'data') {
+ const res = await fetch(WORKER_URL);
+ const text = await res.text();
+
+ workerURL = `data:application/javascript;base64,${btoa(text)}`;
+ } else if (scheme === 'blob') {
+ const res = await fetch(WORKER_URL);
+ const blob = await res.blob();
+
+ workerURL = URL.createObjectURL(blob);
+ } else {
+ assert_unreached('scheme should be one of "https", "data" and "blob".');
+ }
+
+ const worker = new frame.contentWindow.Worker(workerURL);
+ const mc = new MessageChannel();
+ worker.postMessage({port: mc.port2}, [mc.port2]);
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+async function getCrossOriginIsolatedForSharedWorker(t, withCoopCoep) {
+ const workerURL = `${WORKER_URL}${withCoopCoep ? PIPE : ''}`;
+ const worker = new SharedWorker(workerURL);
+ worker.addEventListener('error', t.unreached_func('worker.error'));
+
+ const mc = new MessageChannel();
+ worker.port.postMessage({port: mc.port2}, [mc.port2]);
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+async function getCrossOriginIsolatedForServiceWorker(t, withCoopCoep) {
+ // 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 workerURL = `${WORKER_URL}${withCoopCoep ? PIPE : ''}`;
+ const reg =
+ await service_worker_unregister_and_register(t, workerURL, SCOPE);
+ t.add_cleanup(() => reg.unregister());
+ const worker = reg.installing;
+
+ const mc = new MessageChannel();
+ worker.postMessage({port: mc.port2}, [mc.port2]);
+ return (await new Promise(r => mc.port1.onmessage = r)).data;
+}
+
+function generateFrameTest(origin, value, expectation) {
async function run(t) {
assert_equals(
- await getCrossOriginIsolatedFor(t, origin, value), expectation);
+ await getCrossOriginIsolatedForFrame(t, origin, value), expectation);
}
// We use async_test, not promise_test here to run tests in parallel.
async_test((t) => {
run(t).then(() => t.done(), (e) => t.step(() => {throw e;}));
- }, `origin = ${origin}, value = ${value}`);
+ }, `frame: origin = ${origin}, value = ${value}`);
}
-generateTest(ORIGIN, undefined, true);
-generateTest(ORIGIN, '*', true);
-generateTest(ORIGIN, "self", true);
+function generateDedicatedWorkerTest(scheme, value, expectation) {
+ async function run(t) {
+ assert_equals(
+ await getCrossOriginIsolatedForDedicatedWorker(t, scheme, value),
+ expectation);
+ }
+ // We use async_test, not promise_test here to run tests in parallel.
+ async_test((t) => {
+ run(t).then(() => t.done(), (e) => t.step(() => {throw e;}));
+ }, `dedicated worker: scheme = ${scheme}, value = ${value}`);
+}
+
+function generateSharedWorkerTest(withCoopCoep) {
+ async function run(t) {
+ assert_equals(
+ await getCrossOriginIsolatedForSharedWorker(t, withCoopCoep),
+ withCoopCoep);
+ }
+ // We use async_test, not promise_test here to run tests in parallel.
+ async_test((t) => {
+ run(t).then(() => t.done(), (e) => t.step(() => {throw e;}));
+ }, `shared worker: withCoopCoep = ${withCoopCoep}`);
+}
+
+function generateServiceWorkerTest(withCoopCoep) {
+ // Here we use promise_test as we want to use a cleanup callback that returns
+ // a promise.
+ promise_test(async (t) => {
+ assert_equals(
+ await getCrossOriginIsolatedForServiceWorker(t, withCoopCoep),
+ withCoopCoep);
+ }, `service worker: withCoopCoep = ${withCoopCoep}`);
+}
+
+generateFrameTest(ORIGIN, undefined, true);
+generateFrameTest(ORIGIN, '*', true);
+generateFrameTest(ORIGIN, 'self', true);
// We need the backslash to escape the close parenthesis in a wpt pipe.
-generateTest(ORIGIN, "(\\)", false);
-generateTest(HTTPS_REMOTE_ORIGIN, undefined, false);
-generateTest(HTTPS_REMOTE_ORIGIN, '*', true);
-generateTest(HTTPS_REMOTE_ORIGIN, "self", false);
+generateFrameTest(ORIGIN, '(\\)', false);
+generateFrameTest(HTTPS_REMOTE_ORIGIN, undefined, false);
+generateFrameTest(HTTPS_REMOTE_ORIGIN, '*', true);
+generateFrameTest(HTTPS_REMOTE_ORIGIN, 'self', false);
+// We need the backslash to escape the close parenthesis in a wpt pipe.
+generateFrameTest(HTTPS_REMOTE_ORIGIN, '(\\)', false);
+
+generateDedicatedWorkerTest('https', undefined, true);
+generateDedicatedWorkerTest('https', '*', true);
+generateDedicatedWorkerTest('https', 'self', true);
// We need the backslash to escape the close parenthesis in a wpt pipe.
-generateTest(HTTPS_REMOTE_ORIGIN, "(\\)", false);
+generateDedicatedWorkerTest('https', '(\\)', false);
+generateDedicatedWorkerTest('data', undefined, false);
+generateDedicatedWorkerTest('data', '*', false);
+generateDedicatedWorkerTest('data', 'self', false);
+// We need the backslash to escape the close parenthesis in a wpt pipe.
+generateDedicatedWorkerTest('data', '(\\)', false);
+generateDedicatedWorkerTest('blob', undefined, true);
+generateDedicatedWorkerTest('blob', '*', true);
+generateDedicatedWorkerTest('blob', 'self', true);
+// We need the backslash to escape the close parenthesis in a wpt pipe.
+generateDedicatedWorkerTest('blob', '(\\)', false);
+
+generateSharedWorkerTest(false);
+generateSharedWorkerTest(true);
+
+generateServiceWorkerTest(false);
+generateServiceWorkerTest(true);
</script>
</body>
</html>
diff --git a/html/cross-origin-embedder-policy/resources/cross-origin-isolated-worker.js b/html/cross-origin-embedder-policy/resources/cross-origin-isolated-worker.js
new file mode 100644
index 0000000..9f1e371
--- /dev/null
+++ b/html/cross-origin-embedder-policy/resources/cross-origin-isolated-worker.js
@@ -0,0 +1,11 @@
+// For DedicatedWorker and ServiceWorker
+self.addEventListener('message', (e) => {
+ e.data.port.postMessage(self.crossOriginIsolated);
+});
+
+// For SharedWorker
+self.addEventListener('connect', (e) => {
+ e.ports[0].onmessage = (ev) => {
+ ev.data.port.postMessage(self.crossOriginIsolated);
+ };
+});
\ No newline at end of file
diff --git a/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html b/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html
index dbc73e9..bfcc8b6 100644
--- a/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html
+++ b/html/infrastructure/safe-passing-of-structured-data/shared-array-buffers/blob-data.https.html
@@ -50,14 +50,14 @@
`;
}
-function propertyTests(name) {
+function propertyTests(name, crossOriginIsolated) {
return `
test(() => {
assert_equals(self.origin, self.location.origin);
}, "${name}: self.origin");
test(() => {
- assert_true(self.crossOriginIsolated);
+ assert_equals(self.crossOriginIsolated, ${crossOriginIsolated});
}, "${name}: self.crossOriginIsolated");
test(() => {
@@ -74,7 +74,7 @@
${blobWorkerIncrementerTest("blob worker", self.location.origin)}
-${propertyTests("blob worker")}
+${propertyTests("blob worker", true)}
done();
`;
@@ -90,7 +90,7 @@
${blobWorkerIncrementerTest("blob frame", self.location.origin)}
-${propertyTests("blob frame")}
+${propertyTests("blob frame", true)}
<\/script>
`;
@@ -106,7 +106,7 @@
${blobWorkerIncrementerTest("data worker")}
-${propertyTests("data worker")}
+${propertyTests("data worker", false)}
done();
`;
@@ -121,7 +121,7 @@
${blobWorkerIncrementerTest("data frame")}
-${propertyTests("data frame")}
+${propertyTests("data frame", true)}
<\/script>
`;