[COEP] Write WPTs for COEP inheritance from `filesystem:` URLs.
Refactor the existing tests to make it easier to integrate the new
tests, and add tests checking for proper inheritance from the URL
creator for `blob:` URLs too.
Bug: chromium:1291230
Change-Id: I9890697e1ed3c76c8ef880ce6799c40fdc5805eb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3427642
Commit-Queue: Titouan Rigoudy <titouan@chromium.org>
Reviewed-by: Asami Doi <asamidoi@chromium.org>
Reviewed-by: Antonio Sartori <antoniosartori@chromium.org>
Auto-Submit: Titouan Rigoudy <titouan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#966126}
diff --git a/html/cross-origin-embedder-policy/dedicated-worker.https.html b/html/cross-origin-embedder-policy/dedicated-worker.https.html
index cc0c470..1ba6241 100644
--- a/html/cross-origin-embedder-policy/dedicated-worker.https.html
+++ b/html/cross-origin-embedder-policy/dedicated-worker.https.html
@@ -3,110 +3,212 @@
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/worker-support.js"></script>
<body>
<script>
-const HOST = get_host_info();
-const FETCH_URL = `${HOST.REMOTE_ORIGIN}/common/blank.html`;
-const WORKER_URL = new URL('resources/dedicated-worker.js', location).href;
-const WITH_COEP = '?pipe=header(cross-origin-embedder-policy,require-corp)';
-async function createWorker(t, frameHasCoep, workerHasCoep, workerOptions) {
- const frame = document.createElement("iframe");
- t.add_cleanup(() => frame.remove());
- frame.src = '/common/blank.html';
- if (frameHasCoep) {
- frame.src += WITH_COEP;
- }
- document.body.append(frame);
+const targetUrl = resolveUrl("/common/blank.html", {
+ host: get_host_info().REMOTE_HOST,
+}).href;
- await new Promise(resolve => {
- frame.addEventListener('load', resolve, {once: true})
- });
- const worker = new frame.contentWindow.Worker(
- workerHasCoep ? WORKER_URL + WITH_COEP : WORKER_URL, workerOptions);
-
- return worker;
+function workerUrl(options) {
+ return resolveUrl("resources/dedicated-worker.js", options);
}
-function waitForMessage(target) {
- return new Promise(resolve => {
- target.addEventListener('message', resolve, {once: true});
+async function createWorker(t, url, options) {
+ const { ownerCoep, workerOptions } = options || {};
+
+ const frameUrl = resolveUrl("/common/blank.html", {
+ coep: ownerCoep,
});
+ const frame = await withIframe(t, frameUrl);
+
+ return new frame.contentWindow.Worker(url, workerOptions);
}
promise_test(async (t) => {
- const worker = await createWorker(t, false, false, {});
+ const worker = await createWorker(t, workerUrl());
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'LOADED');
}, 'COEP: none worker in COEP: none frame');
promise_test(async (t) => {
- const worker = await createWorker(t, true, false, {});
+ const worker = await createWorker(t, workerUrl(), {
+ ownerCoep: "require-corp",
+ });
await new Promise(resolve => {
worker.onerror = resolve;
});
}, 'COEP: none worker in COEP: require-corp frame');
promise_test(async (t) => {
- const worker = await createWorker(t, false, true, {});
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }));
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'FAILED');
}, 'COEP: require-corp worker in COEP: none frame');
promise_test(async (t) => {
- const worker = await createWorker(t, true, true, {});
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ ownerCoep: "require-corp",
+ });
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'FAILED');
}, 'COEP: require-corp worker in COEP: require-corp frame');
promise_test(async (t) => {
- const worker = await createWorker(t, false, false, {type: 'module'});
+ const worker = await createWorker(t, workerUrl(), {
+ workerOptions: { type: 'module' },
+ });
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'LOADED');
}, 'COEP: none module worker in COEP: none frame');
promise_test(async (t) => {
- const worker = await createWorker(t, true, false, {type: 'module'});
+ const worker = await createWorker(t, workerUrl(), {
+ ownerCoep: "require-corp",
+ workerOptions: { type: 'module' },
+ });
await new Promise(resolve => {
worker.onerror = resolve;
});
}, 'COEP: none module worker in COEP: require-corp frame');
promise_test(async (t) => {
- const worker = await createWorker(t, false, true, {type: 'module'});
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ workerOptions: { type: 'module' },
+ });
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'FAILED');
}, 'COEP: require-corp module worker in COEP: none frame');
promise_test(async (t) => {
- const worker = await createWorker(t, true, true, {type: 'module'});
+ const worker = await createWorker(t, workerUrl({ coep: "require-corp" }), {
+ ownerCoep: "require-corp",
+ workerOptions: { type: 'module' },
+ });
worker.onerror = t.unreached_func('Worker.onerror should not be called');
- worker.postMessage(FETCH_URL);
+ worker.postMessage(targetUrl);
const result = await waitForMessage(worker);
assert_equals(result.data, 'FAILED');
}, 'COEP: require-corp module worker in COEP: require-corp frame');
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for blob URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP from blob URL creator, not owner.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for data URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'LOADED');
+}, "COEP: worker inherits COEP from owner, not data URL creator.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const worker = await createWorker(t, url, { ownerCoep: "require-corp" });
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP for filesystem URL.");
+
+promise_test(async (t) => {
+ const url = await createLocalUrl(t, {
+ url: workerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const worker = await createWorker(t, url);
+ worker.onerror = t.unreached_func('Worker.onerror should not be called');
+
+ worker.postMessage(targetUrl);
+
+ const result = await waitForMessage(worker);
+ assert_equals(result.data, 'FAILED');
+}, "COEP: worker inherits COEP from filesystem URL creator, not owner.");
+
</script>
</body>
diff --git a/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html b/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html
new file mode 100644
index 0000000..6b0f962
--- /dev/null
+++ b/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Fetch and create Blob</title>
+<script>
+ async function responseToBlob(response) {
+ let blob;
+ try {
+ blob = await response.blob();
+ } catch (e) {
+ return { error: `blob error: ${e.name}` };
+ }
+
+ return { url: URL.createObjectURL(blob) };
+ }
+
+ async function responseToData(response) {
+ const mimeType = response.headers.get("content-type");
+
+ let text;
+ try {
+ text = await response.text();
+ } catch(e) {
+ return { error: `text error: ${e.name}` };
+ }
+
+ return { url: `data:${mimeType},${encodeURIComponent(text)}` };
+ }
+
+ async function responseToFilesystem(response) {
+ if (!window.webkitRequestFileSystem) {
+ return { error: "unimplemented" };
+ }
+
+ let blob;
+ try {
+ blob = await response.blob();
+ } catch (e) {
+ return { error: `blob error: ${e.name}` };
+ }
+
+ const fs = await new Promise(resolve => {
+ window.webkitRequestFileSystem(window.TEMPORARY, 1024*1024, resolve);
+ });
+
+ const file = await new Promise(resolve => {
+ fs.root.getFile('fetch-and-create-url', { create: true }, resolve);
+ });
+
+ const writer = await new Promise(resolve => file.createWriter(resolve));
+
+ try {
+ await new Promise((resolve, reject) => {
+ writer.onwriteend = resolve;
+ writer.onerror = reject;
+ writer.write(blob);
+ });
+ } catch (e) {
+ return { error: `file write error: ${e.name}` };
+ }
+
+ return { url: file.toURL() };
+ }
+
+ async function responseToScheme(response, scheme) {
+ switch (scheme) {
+ case "blob":
+ return responseToBlob(response);
+ case "data":
+ return responseToData(response);
+ case "filesystem":
+ return responseToFilesystem(response);
+ default:
+ return { error: `unknown scheme: ${scheme}` };
+ }
+ }
+
+ async function fetchToScheme(url, scheme) {
+ let response;
+ try {
+ response = await fetch(url);
+ } catch (e) {
+ return { error: `fetch error: ${e.name}` };
+ }
+
+ return responseToScheme(response, scheme);
+ }
+
+ const params = new URL(window.location).searchParams;
+ fetchToScheme(params.get("url"), params.get("scheme"))
+ .then((message) => { parent.postMessage(message, "*"); });
+</script>
diff --git a/html/cross-origin-embedder-policy/resources/worker-support.js b/html/cross-origin-embedder-policy/resources/worker-support.js
new file mode 100644
index 0000000..860ee68
--- /dev/null
+++ b/html/cross-origin-embedder-policy/resources/worker-support.js
@@ -0,0 +1,81 @@
+// Configures `url` such that the response carries a `COEP: ${value}` header.
+//
+// `url` must be a `URL` instance.
+function setCoep(url, value) {
+ url.searchParams
+ .set("pipe", `header(cross-origin-embedder-policy,${value})`);
+}
+
+// Resolves the given `relativeUrl` relative to the current window's location.
+//
+// `options` can contain the following keys:
+//
+// - `coep`: value passed to `setCoep()`, if present.
+// - `host`: overrides the host of the returned URL.
+//
+// Returns a `URL` instance.
+function resolveUrl(relativeUrl, options) {
+ const url = new URL(relativeUrl, window.location);
+
+ if (options !== undefined) {
+ const { coep, host } = options;
+ if (coep !== undefined) {
+ setCoep(url, coep);
+ }
+ if (host !== undefined) {
+ url.host = host;
+ }
+ }
+
+ return url;
+}
+
+// Adds an iframe loaded from `url` to the current document, waiting for it to
+// load before returning.
+//
+// The returned iframe is removed from the document at the end of the test `t`.
+async function withIframe(t, url) {
+ const frame = document.createElement("iframe");
+ frame.src = url;
+
+ t.add_cleanup(() => frame.remove());
+
+ const loadedPromise = new Promise(resolve => {
+ frame.addEventListener('load', resolve, {once: true});
+ });
+ document.body.append(frame);
+ await loadedPromise;
+
+ return frame;
+}
+
+// Asynchronously waits for a single "message" event on the given `target`.
+function waitForMessage(target) {
+ return new Promise(resolve => {
+ target.addEventListener('message', resolve, {once: true});
+ });
+}
+
+// Fetches `url` from a document with COEP `creatorCoep`, then serializes it
+// and returns a URL pointing to the fetched body with the given `scheme`.
+//
+// - `creatorCoep` is passed to `setCoep()`.
+// - `scheme` may be one of: "blob", "data" or "filesystem".
+//
+// The returned URL is valid until the end of the test `t`.
+async function createLocalUrl(t, { url, creatorCoep, scheme }) {
+ const frameUrl = resolveUrl("resources/fetch-and-create-url.html", {
+ coep: creatorCoep,
+ });
+ frameUrl.searchParams.set("url", url);
+ frameUrl.searchParams.set("scheme", scheme);
+
+ const messagePromise = waitForMessage(window);
+ const frame = await withIframe(t, frameUrl);
+
+ const evt = await messagePromise;
+ const message = evt.data;
+ assert_equals(message.error, undefined, "url creation error");
+
+ return message.url;
+}
diff --git a/html/cross-origin-embedder-policy/shared-workers.https.html b/html/cross-origin-embedder-policy/shared-workers.https.html
index 82b5e50..2558f2d 100644
--- a/html/cross-origin-embedder-policy/shared-workers.https.html
+++ b/html/cross-origin-embedder-policy/shared-workers.https.html
@@ -5,6 +5,7 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
+<script src="resources/worker-support.js"></script>
<body>
<p>Verify the Cross-Origin Embedder Policy for Shared Workers by performing a
cross-domain "fetch" request for a resource that does not specify a COEP. Only
@@ -13,51 +14,39 @@
<script>
'use strict';
-const {ORIGIN, REMOTE_ORIGIN} = get_host_info();
-const BASE = new URL("resources", location).pathname
-const testUrl = `${REMOTE_ORIGIN}${BASE}/empty-coep.py`;
-const workerHttpUrl = `${ORIGIN}${BASE}/shared-worker-fetch.js.py`;
-let workerBlobUrl;
-let workerDataUrl;
+const testUrl = resolveUrl("resources/empty-coep.py", {
+ host: get_host_info().REMOTE_HOST,
+}).href;
-promise_setup(() => {
- return fetch(workerHttpUrl)
- .then((response) => response.text())
- .then((text) => {
- workerDataUrl = 'data:text/javascript;base64,' + btoa(text);
- workerBlobUrl = URL.createObjectURL(
- new Blob([text], { 'Content-Type': 'text/javascript' })
- );
- });
-});
+function makeWorkerUrl(options) {
+ return resolveUrl("resources/shared-worker-fetch.js.py", options);
+}
/**
* Create a Shared Worker within an iframe
*
* @param {object} t - a testharness.js subtest instance (used to reset global
* state)
- * @param {string} ownerCoep - the Cross-Origin Embedder Policy of the iframe
- * @param {string} workerUrl - the URL from which the Shared Worker should be
- * created
+ * @param {string} url - the URL from which the Shared Worker should be
+ * created
+ * @param {string} options.ownerCoep - the Cross-Origin Embedder Policy of the
+ iframe
*/
-function create(t, ownerCoep, workerUrl) {
- const iframe = document.createElement('iframe');
- iframe.src = 'resources/empty-coep.py' +
- (ownerCoep ? '?value=' + ownerCoep : '');
+async function createWorker(t, url, options) {
+ const { ownerCoep } = options || {};
+ const frameUrl = resolveUrl("/common/blank.html", { coep: ownerCoep });
- return new Promise((resolve, reject) => {
- document.body.appendChild(iframe);
- t.add_cleanup(() => iframe.remove());
- iframe.onload = () => resolve(iframe);
- })
- .then((iframe) => {
- const sw = new iframe.contentWindow.SharedWorker(workerUrl);
+ const iframe = await withIframe(t, frameUrl);
- return new Promise((resolve) => {
- sw.port.addEventListener('message', () => resolve(sw), { once: true });
- sw.port.start();
- });
- });
+ const sw = new iframe.contentWindow.SharedWorker(url);
+ sw.onerror = t.unreached_func('SharedWorker.onerror should not be called');
+
+ await new Promise((resolve) => {
+ sw.port.addEventListener('message', resolve, { once: true });
+ sw.port.start();
+ });
+
+ return sw;
}
/**
@@ -76,57 +65,164 @@
});
};
-promise_test((t) => {
- return create(t, null, workerHttpUrl)
- .then((worker) => fetchFromWorker(worker, testUrl))
- .then((result) => assert_equals(result, 'success'));
+promise_test(async (t) => {
+ const worker = await createWorker(t, makeWorkerUrl());
+ const result = await fetchFromWorker(worker, testUrl);
+ assert_equals(result, 'success');
}, 'default policy (derived from response)');
-promise_test((t) => {
- return create(t, null, workerHttpUrl + '?value=require-corp')
- .then((worker) => fetchFromWorker(worker, testUrl))
- .then((result) => assert_equals(result, 'failure'));
+promise_test(async (t) => {
+ const worker = await createWorker(t, makeWorkerUrl({ coep: 'require-corp' }));
+ const result = await fetchFromWorker(worker, testUrl);
+ assert_equals(result, 'failure');
}, '"require-corp" (derived from response)');
-promise_test((t) => {
- return Promise.all([
- create(t, null, workerBlobUrl),
- create(t, null, workerBlobUrl),
- create(t, null, workerBlobUrl)
- ])
- .then((workers) => fetchFromWorker(workers[0], testUrl))
- .then((result) => assert_equals(result, 'success'));
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
}, 'default policy (derived from owner set due to use of local scheme - blob URL)');
-promise_test((t) => {
- return Promise.all([
- create(t, null, workerBlobUrl),
- create(t, 'require-corp', workerBlobUrl),
- create(t, null, workerBlobUrl)
- ])
- .then((workers) => fetchFromWorker(workers[0], testUrl))
- .then((result) => assert_equals(result, 'failure'));
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, 'require-corp (derived from blob URL creator)');
+
+promise_test(async (t) => {
+ const blobUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "blob",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, blobUrl),
+ createWorker(t, blobUrl, { ownerCoep: 'require-corp' }),
+ createWorker(t, blobUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
}, '"require-corp" (derived from owner set due to use of local scheme - blob URL)');
-promise_test((t) => {
- return Promise.all([
- create(t, null, workerDataUrl),
- create(t, null, workerDataUrl),
- create(t, null, workerDataUrl)
- ])
- .then((workers) => fetchFromWorker(workers[0], testUrl))
- .then((result) => assert_equals(result, 'success'));
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
}, 'default policy (derived from owner set due to use of local scheme - data URL)');
-promise_test((t) => {
- return Promise.all([
- create(t, null, workerDataUrl),
- create(t, 'require-corp', workerDataUrl),
- create(t, null, workerDataUrl)
- ])
- .then((workers) => fetchFromWorker(workers[0], testUrl))
- .then((result) => assert_equals(result, 'failure'));
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (not derived from data URL creator)');
+
+promise_test(async (t) => {
+ const dataUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "data",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, dataUrl),
+ createWorker(t, dataUrl, { ownercoep: 'require-corp' }),
+ createWorker(t, dataUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
}, '"require-corp" (derived from owner set due to use of local scheme - data URL)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'success');
+}, 'default policy (derived from owner set due to use of local scheme - filesystem URL)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ creatorCoep: "require-corp",
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, 'require-corp (derived from filesystem URL creator)');
+
+promise_test(async (t) => {
+ const filesystemUrl = await createLocalUrl(t, {
+ url: makeWorkerUrl(),
+ scheme: "filesystem",
+ });
+
+ const workers = await Promise.all([
+ createWorker(t, filesystemUrl),
+ createWorker(t, filesystemUrl, { ownerCoep: 'require-corp' }),
+ createWorker(t, filesystemUrl),
+ ]);
+
+ const result = await fetchFromWorker(workers[0], testUrl);
+ assert_equals(result, 'failure');
+}, '"require-corp" (derived from owner set due to use of local scheme - filesystem URL)');
</script>
</body>
</html>