diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /testing/web-platform/tests/html/cross-origin-embedder-policy/resources | |
parent | Initial commit. (diff) | |
download | firefox-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')
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; +} |