summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/html/cross-origin-embedder-policy/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/cross-origin-embedder-policy/resources
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/html/cross-origin-embedder-policy/resources')
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js63
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js19
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py31
-rwxr-xr-xtesting/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py15
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html91
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js6
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html3
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html38
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html34
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html29
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html4
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py41
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis9
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html5
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js25
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js23
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js27
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js30
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py24
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js7
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js31
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js12
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js1
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html2
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js36
-rw-r--r--testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js81
42 files changed, 764 insertions, 0 deletions
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html
new file mode 100644
index 0000000000..928d404672
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html
@@ -0,0 +1,24 @@
+<body>
+<script src="script-factory.js"></script>
+<script>
+const query = new URLSearchParams(window.location.search);
+const id = query.get("id");
+const variant = query.get("variant");
+let parent = "parent";
+if (variant === "subframe") {
+ parent = "parent.parent";
+} else if (variant === "popup") {
+ parent = "opener.parent";
+}
+const blob = new Blob([`<script>${createScript(window.origin, query.get("crossOrigin"), parent, id)}<\/script>`], { type: "text/html" });
+const blobURL = URL.createObjectURL(blob);
+if (variant === "subframe") {
+ const frame = document.createElement("iframe");
+ frame.src = blobURL;
+ document.body.append(frame);
+} else if (variant === "popup") {
+ window.open(blobURL);
+} else {
+ window.location = blobURL;
+}
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers
new file mode 100644
index 0000000000..4e798cd9f5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/blob-url-factory.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: cross-origin
diff --git 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
new file mode 100644
index 0000000000..86dff9c845
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/cache-storage-reporting.js
@@ -0,0 +1,63 @@
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN).href;
+}
+
+function local(path) {
+ return new URL(path, location.origin).href;
+}
+
+function encode(url) {
+ return encodeURI(url).replace(/\;/g, '%3B');
+}
+
+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)';
+
+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
+// - A SharedWorker
+//
+// The context has either COEP or COEP-Report-Only defined.
+const eval_script = `
+ (async function() {
+ try {
+ const cache = await caches.open('v1');
+ const request = new Request('${image_url}', { mode: 'no-cors' });
+ const response = await cache.match(request);
+ } catch(e) {
+ }
+ })()
+`;
+
+promise_setup(async (t) => {
+ const cache = await caches.open('v1');
+ 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');
+ 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));
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html
new file mode 100644
index 0000000000..78c1331132
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html
@@ -0,0 +1,2 @@
+<!doctype html>
+<p><code>Cross-Origin-Embedder-Policy: require-corp</code> header set.
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/coep-frame.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js
new file mode 100644
index 0000000000..8f038a7278
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/common.js
@@ -0,0 +1,19 @@
+async function createIsolatedFrame(origin, headers) {
+ const parent = document.createElement('iframe');
+ const parent_loaded = new Promise(r => parent.onload = () => { r(parent); });
+ const error = new Promise(r => parent.onerror = r);
+ parent.src = origin + "/common/blank.html?pipe=" + headers;
+ parent.anonymous = false;
+ document.body.appendChild(parent);
+ return [parent_loaded, error];
+}
+
+async function IsCrossOriginIsolated(from_token) {
+ const reply_token = token();
+ send(from_token, `
+ send("${reply_token}", self.crossOriginIsolated);
+ `);
+ const reply = await receive(reply_token);
+ assert_true(reply.match(/true|false/) != null);
+ return reply == 'true';
+} \ No newline at end of file
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py
new file mode 100644
index 0000000000..e507846181
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/corp-image.py
@@ -0,0 +1,31 @@
+import json
+import base64
+
+# A 1x1 PNG image.
+# Source: https://commons.wikimedia.org/wiki/File:1x1.png (Public Domain)
+IMAGE = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAXRSTlMAQObYZgAAAApJREFUCNdjYAAAAAIAAeIhvDMAAAAASUVORK5CYII="
+
+def main(request, response):
+ response.headers.set(b'Access-Control-Allow-Origin', b'*')
+ response.headers.set(b'Access-Control-Allow-Methods', b'OPTIONS, GET, POST')
+ response.headers.set(b'Access-Control-Allow-Headers', b'Content-Type')
+
+ # CORS preflight
+ if request.method == u'OPTIONS':
+ return u''
+
+ if b'true' == request.GET.get(b'revalidate', None):
+ response.headers.set(b'Cache-Control', b'max-age=0, must-revalidate')
+ else:
+ response.headers.set(b'Cache-Control', b'max-age=3600');
+
+ if b'some-etag' == request.headers.get(b'If-None-Match', None):
+ response.status = 304
+ return u''
+
+ if request.GET.get(b'corp-cross-origin', None):
+ response.headers.set(b'Cross-Origin-Resource-Policy', b'cross-origin')
+
+ response.headers.set(b'Etag', b'some-etag')
+ response.headers.set(b'Content-Type', b'image/png')
+ return base64.b64decode(IMAGE)
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py
new file mode 100755
index 0000000000..eef86d1c55
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker-supporting-revalidation.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+
+def main(request, response):
+ headers = []
+ if request.headers.get(b'if-none-match', None):
+ status = 304, u"Not Modified"
+ return status, headers, u""
+ else:
+ headers.append((b"Content-Type", b"text/javascript"))
+ headers.append((b"Cross-Origin-Embedder-Policy", b"require-corp"))
+ headers.append((b"Cache-Control", b"private, max-age=0, must-revalidate"))
+ headers.append((b"ETag", b"abcdef"))
+ status = 200, u"OK"
+ return status, headers, u"self.onmessage = (e) => { self.postMessage('LOADED'); };"
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js
new file mode 100644
index 0000000000..66f3cc3d41
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/dedicated-worker.js
@@ -0,0 +1,7 @@
+self.onmessage = (e) => {
+ fetch(e.data, {mode: 'no-cors'}).then(() => {
+ self.postMessage('LOADED');
+ }, () => {
+ self.postMessage('FAILED');
+ });
+};
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py
new file mode 100644
index 0000000000..d0e547b130
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/empty-coep.py
@@ -0,0 +1,7 @@
+def main(request, response):
+ headers = [(b'Content-Type', b'text/html')]
+
+ for value in request.GET.get_list(b'value'):
+ headers.append((b'Cross-Origin-Embedder-Policy', value))
+
+ return (200, headers, u'')
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-and-create-url.html
new file mode 100644
index 0000000000..6b0f96221d
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js
new file mode 100644
index 0000000000..bd60d07952
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/fetch-in-dedicated-worker.js
@@ -0,0 +1,6 @@
+self.addEventListener('message', async (e) => {
+ const param = e.data;
+ // Ignore network error.
+ await fetch(param.url, param.init).catch(() => {});
+ self.postMessage(param.url);
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html
new file mode 100644
index 0000000000..a6b74ad924
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/iframe.html
@@ -0,0 +1,3 @@
+<script>
+ window.addEventListener("message", message => eval(message.data));
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html
new file mode 100644
index 0000000000..288610046e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html>
+<script src="/common/get-host-info.sub.js"></script>
+<script>
+
+function remote(path) {
+ const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
+ return new URL(path, REMOTE_ORIGIN);
+}
+
+let params = new URLSearchParams(location.search);
+let token = params.get('token');
+let revalidate = params.get('revalidate');
+
+let image_path = `/html/cross-origin-embedder-policy/resources/corp-image.py?token=${token}&revalidate=${revalidate}`;
+
+window.addEventListener("load", async () => {
+ await new Promise(resolve => {
+ let img = document.createElement("img");
+ img.src = remote(image_path);
+ img.onload = () => { window.parent.postMessage({corp: false, loaded: true}, "*"); resolve(); };
+ img.onerror = (e) => { window.parent.postMessage({corp: false, loaded: false}, "*"); resolve(); };
+ document.body.appendChild(img);
+ });
+
+ await new Promise(resolve => {
+ let img = document.createElement("img");
+ img.src = remote(image_path + "&corp-cross-origin=1");
+ img.onload = () => { window.parent.postMessage({corp: true, loaded: true}, "*"); resolve(); };
+ img.onerror = (e) => { window.parent.postMessage({corp: true, loaded: false}, "*"); resolve(); };
+ document.body.appendChild(img);
+ });
+
+ window.parent.postMessage({done: true}, "*")
+});
+
+</script>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers
new file mode 100644
index 0000000000..8df98474b5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/load-corp-images.html.headers
@@ -0,0 +1 @@
+cross-origin-embedder-policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html
new file mode 100644
index 0000000000..f1437ba90a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-none.sub.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<script>
+ let current = new URL(window.location.href);
+ let navigateTo = current.searchParams.get("to");
+ let channelName = current.searchParams.get("channelName");
+ let postMessageTo = current.searchParams.get("postMessageTo");
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location.href = next.href;
+ });
+ }
+
+ let target = undefined;
+ if (channelName) {
+ target = new BroadcastChannel(channelName);
+ } else if (postMessageTo) {
+ target = eval(postMessageTo);
+ }
+
+ if (target) {
+ // Broadcast only once the DOM is loaded, so that the caller can
+ // access reliably this document's body.
+ window.addEventListener("DOMContentLoaded", () =>
+ target.postMessage("loaded", "*"));
+
+ // The page can also be restored from the back-forward cache:
+ window.addEventListener('pageshow', function(event) {
+ if (event.persisted)
+ target.postMessage("loaded", "*");
+ });
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html
new file mode 100644
index 0000000000..910317d29b
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<script>
+ const current = new URL(window.location.href);
+ const token = current.searchParams.get("token");
+ const navigateTo = current.searchParams.get("to");
+ const channelName = current.searchParams.get("channelName");
+ const clearOpener = current.searchParams.get("clearOpener");
+
+ if (clearOpener) {
+ window.opener = null;
+ }
+
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location = next.href;
+ });
+ }
+
+ if (channelName) {
+ let bc = new BroadcastChannel(channelName);
+ bc.postMessage("loaded");
+ }
+
+ if (parent !== window && token) {
+ parent.postMessage(token, "*");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers
new file mode 100644
index 0000000000..56d0ac3428
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp-same-site.sub.html.headers
@@ -0,0 +1,2 @@
+Cross-Origin-Embedder-Policy: require-corp
+Cross-Origin-Resource-Policy: same-site
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
new file mode 100644
index 0000000000..0a3c4dd8d7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<script>
+ let current = new URL(window.location.href);
+ let navigateTo = current.searchParams.get("to");
+ let channelName = current.searchParams.get("channelName");
+ let clearOpener = current.searchParams.get("clearOpener");
+
+ if (clearOpener) {
+ window.opener = null;
+ }
+
+ current.search = "";
+ if (navigateTo) {
+ let next = new URL(navigateTo, current);
+ window.addEventListener("load", () => {
+ window.location.href = next.href;
+ });
+ }
+
+ if (channelName) {
+ let bc = new BroadcastChannel(channelName);
+ bc.postMessage("loaded");
+ }
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/navigate-require-corp.sub.html.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js
new file mode 100644
index 0000000000..662e9364f9
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js
@@ -0,0 +1 @@
+/* Just an empty JS file */
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers
new file mode 100644
index 0000000000..1b88136c01
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-cross-origin-corp.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Resource-Policy: cross-origin
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt
new file mode 100644
index 0000000000..b9ba801f78
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt
@@ -0,0 +1 @@
+nothing with same-origin CORP
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers
new file mode 100644
index 0000000000..30ddeac2e7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/nothing-same-origin-corp.txt.headers
@@ -0,0 +1 @@
+Cross-Origin-Resource-Policy: same-origin
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html
new file mode 100644
index 0000000000..3282711dbc
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/postmessage-ready.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ opener.postMessage("ready", "*");
+</script>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py
new file mode 100644
index 0000000000..100c642d6c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/report.py
@@ -0,0 +1,41 @@
+import json
+
+def main(request, response):
+ response.headers.set(b'Access-Control-Allow-Origin', b'*')
+ response.headers.set(b'Access-Control-Allow-Methods', b'OPTIONS, GET, POST')
+ response.headers.set(b'Access-Control-Allow-Headers', b'Content-Type')
+ response.headers.set(b'Cache-Control', b'no-cache, no-store, must-revalidate')
+
+ # CORS preflight
+ if request.method == u'OPTIONS':
+ return u''
+
+ uuidMap = {
+ b'endpoint': b'01234567-0123-0123-0123-0123456789AB',
+ b'report-only-endpoint': b'01234567-0123-0123-0123-0123456789CD'
+ }
+ key = 0
+ if b'endpoint' in request.GET:
+ key = uuidMap.get(request.GET[b'endpoint'], 0)
+
+ if b'key' in request.GET:
+ key = request.GET[b'key']
+
+ if key == 0:
+ response.status = 400
+ return u'invalid endpoint'
+
+ path = u'/'.join(request.url_parts.path.split(u'/')[:-1]) + u'/'
+ if request.method == u'POST':
+ reports = request.server.stash.take(key, path) or []
+ for report in json.loads(request.body):
+ reports.append(report)
+ request.server.stash.put(key, reports, path)
+ return u'done'
+
+ if request.method == u'GET':
+ response.headers.set(b'Content-Type', b'application/json')
+ return json.dumps(request.server.stash.take(key, path) or [])
+
+ response.status = 400
+ return u'invalid method'
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis
new file mode 100644
index 0000000000..c0b352f1c7
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame-multiple-headers.html.asis
@@ -0,0 +1,9 @@
+HTTP/1.1 200 OK
+Content-Type: text/html
+cross-origin-embedder-policy: require-corp; foo="
+cross-origin-embedder-policy: "; report-to="endpoint"
+cross-origin-embedder-policy-report-only: require-corp; foo="
+cross-origin-embedder-policy-report-only: "; report-to="report-only-endpoint"
+
+<!doctype html>
+<meta charset="utf-8">
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html
new file mode 100644
index 0000000000..b1579add2e
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-empty-frame.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<html>
+<body>
+</body>
+</html>
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/reporting-worker.js
new file mode 100644
index 0000000000..0f8a2ce4c8
--- /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);
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js
new file mode 100644
index 0000000000..e652c5bf30
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js
@@ -0,0 +1,23 @@
+// Service worker with 'COEP: require-corp' response header.
+// This service worker issues a network request to import scripts with or
+// without CORP response header.
+
+importScripts("/common/get-host-info.sub.js");
+
+function url_for_empty_js(corp) {
+ const url = new URL(get_host_info().HTTPS_REMOTE_ORIGIN);
+ url.pathname = '/service-workers/service-worker/resources/empty.js';
+ if (corp) {
+ url.searchParams.set(
+ 'pipe', `header(Cross-Origin-Resource-Policy, ${corp})`);
+ }
+ return url.href;
+}
+
+const params = new URL(location.href).searchParams;
+
+if (params.get('corp') === 'cross-origin') {
+ importScripts(url_for_empty_js('cross-origin'));
+} else {
+ importScripts(url_for_empty_js());
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw-import-scripts.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js
new file mode 100644
index 0000000000..fedecee9ea
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js
@@ -0,0 +1,27 @@
+// Service worker with 'COEP: require-corp' response header.
+// This service worker issues a network request for a resource with or without
+// CORP response header.
+
+importScripts("/common/get-host-info.sub.js");
+
+self.addEventListener('message', e => {
+ e.waitUntil((async () => {
+ let result;
+ try {
+ let url;
+ if (e.data === 'WithCorp') {
+ url = get_host_info().HTTPS_REMOTE_ORIGIN +
+ '/html/cross-origin-embedder-policy/resources/' +
+ 'nothing-cross-origin-corp.js';
+ } else if (e.data === 'WithoutCorp') {
+ url = get_host_info().HTTPS_REMOTE_ORIGIN + '/common/blank.html';
+ }
+ const response = await fetch(url, { mode: 'no-cors' });
+ result = response.type;
+ } catch (error) {
+ result = `Exception: ${error.name}`;
+ } finally {
+ e.source.postMessage(result);
+ }
+ })());
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers
new file mode 100644
index 0000000000..6604450991
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/require-corp-sw.js.headers
@@ -0,0 +1 @@
+Cross-Origin-Embedder-Policy: require-corp
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js
new file mode 100644
index 0000000000..ac7a1fda06
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/script-factory.js
@@ -0,0 +1,30 @@
+// This creates a serialized <script> element that is useful for blob/data/srcdoc-style tests.
+
+function createScript(sameOrigin, crossOrigin, type="parent", id="") {
+ return `const data = { id: "${id}",
+ opener: !!window.opener,
+ origin: window.origin,
+ sameOriginNoCORPSuccess: false,
+ crossOriginNoCORPFailure: false };
+function record(promise, token, expectation) {
+ return promise.then(() => data[token] = expectation, () => data[token] = !expectation);
+}
+
+const records = [
+ record(fetch("${crossOrigin}/common/blank.html", { mode: "no-cors" }), "crossOriginNoCORPFailure", false)
+];
+
+if ("${sameOrigin}" !== "null") {
+ records.push(record(fetch("${sameOrigin}/common/blank.html", { mode: "no-cors" }), "sameOriginNoCORPSuccess", true));
+}
+
+Promise.all(records).then(() => {
+ // Using BroadcastChannel is useful for blob: URLs, which are always same-origin
+ if ("${type}" === "channel") {
+ const bc = new BroadcastChannel("${id}");
+ bc.postMessage(data);
+ } else {
+ window.${type}.postMessage(data, "*");
+ }
+});`;
+}
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py
new file mode 100644
index 0000000000..112d7ecbeb
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker-fetch.js.py
@@ -0,0 +1,24 @@
+body = b'''
+'use strict';
+
+onconnect = (event) => {
+ const port = event.ports[0];
+
+ port.onmessage = (event) => {
+ fetch(event.data, { mode: 'no-cors' })
+ .then(
+ () => port.postMessage('success'),
+ () => port.postMessage('failure')
+ );
+ };
+
+ port.postMessage('ready');
+};'''
+
+def main(request, response):
+ headers = [(b'Content-Type', b'text/javascript')]
+
+ for value in request.GET.get_list(b'value'):
+ headers.append((b'Cross-Origin-Embedder-Policy', value))
+
+ return (200, headers, body)
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js
new file mode 100644
index 0000000000..c5f2c3cc2c
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/shared-worker.js
@@ -0,0 +1,7 @@
+onconnect = (event) => {
+ const port = event.ports[0];
+ port.onmessage = (event) => {
+ eval(event.data);
+ };
+ port.postMessage('ready');
+};
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js
new file mode 100644
index 0000000000..00b9e9395a
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw-store-to-cache-storage.js
@@ -0,0 +1,31 @@
+self.addEventListener('activate', (e) => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('message', (e) => {
+ e.waitUntil((async () => {
+
+ const url = new URL(e.data.url);
+ const request = new Request(url, {mode: e.data.mode});
+ const cache = await caches.open('v1');
+
+ let response;
+ switch(e.data.source) {
+ case "service-worker":
+ response = new Response('foo');
+ break;
+
+ case "network":
+ try {
+ response = await fetch(request);
+ } catch(error) {
+ e.source.postMessage('not-stored');
+ return;
+ }
+ break;
+ }
+
+ await cache.put(request, response);
+ e.source.postMessage('stored');
+ })());
+})
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js
new file mode 100644
index 0000000000..57f0b41ba5
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/sw.js
@@ -0,0 +1,12 @@
+self.addEventListener('activate', (e) => {
+ e.waitUntil(clients.claim());
+});
+
+self.addEventListener('fetch', (e) => {
+ const url = new URL(e.request.url);
+ if (url.searchParams.has('passthrough')) {
+ return;
+ }
+
+ e.respondWith(fetch(e.request));
+});
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js
new file mode 100644
index 0000000000..5d46edcde2
--- /dev/null
+++ b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/universal-worker.js
@@ -0,0 +1 @@
+onmessage = message => eval(message.data);
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner-frame.html
new file mode 100644
index 0000000000..509c904d2b
--- /dev/null
+++ b/testing/web-platform/tests/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/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-owner.js
new file mode 100644
index 0000000000..d1f172a0b8
--- /dev/null
+++ b/testing/web-platform/tests/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);
+ };
+}
+
diff --git a/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js b/testing/web-platform/tests/html/cross-origin-embedder-policy/resources/worker-support.js
new file mode 100644
index 0000000000..860ee6826c
--- /dev/null
+++ b/testing/web-platform/tests/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;
+}