diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:47:29 +0000 |
commit | 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch) | |
tree | a31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /testing/web-platform/tests/fenced-frame/resources | |
parent | Initial commit. (diff) | |
download | firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip |
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/fenced-frame/resources')
200 files changed, 3754 insertions, 0 deletions
diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html new file mode 100644 index 0000000000..28fadb296c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<script src="utils.js"></script> +<title>Header Inheritance CSP Reporting Page</title> +<body> +<script> +// This file is embedded in an iframe by ancestor-throttle-inner.https.html +// which in turn has been embedded in a fenced frame by +// ancestor-throttle.https.html +async function init() { + const [ancestor_key] = parseKeylist(); + writeValueToServer(ancestor_key, "loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html.headers new file mode 100644 index 0000000000..bb76329b1d --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +Content-Security-Policy: frame-ancestors 'self' diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html new file mode 100644 index 0000000000..267aa076c0 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html> +<script src="utils.js"></script> +<title>Header Inheritance XFO Reporting Page</title> +<body> +<script> +// This file is embedded in an iframe by ancestor-throttle-inner.https.html +// which in turn has been embedded in a fenced frame by +// ancestor-throttle.https.html +async function init() { + const [ancestor_key] = parseKeylist(); + writeValueToServer(ancestor_key, "loaded"); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html.headers new file mode 100644 index 0000000000..63d5019c35 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +X-Frame-Options: SAMEORIGIN diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html new file mode 100644 index 0000000000..e0977c73f0 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<html> +<script src="utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<title>Header Inheritance Inner Page</title> +<body> +<script> +// This file is embedded in a fenced frame by ancestor-throttle.https.html. +// This is an intermediate step that embeds another page in an iframe to check +// that the child page only checks up to this page's origin when deciding +// if it should load. +async function init() { + const [ancestor_key, embed_url, cross_origin_iframe] = + parseKeylist(); + // The URL will be ancestor-throttle-iframe-*.https.html + let iframe_url; + if (cross_origin_iframe == "true") { + iframe_url = generateURL(new URL(embed_url, + get_host_info().HTTPS_REMOTE_ORIGIN), parseKeylist()); + } else { + iframe_url = generateURL(new URL(embed_url, + get_host_info().HTTPS_ORIGIN), parseKeylist()); + } + + const iframe = document.createElement('iframe'); + iframe.src = iframe_url; + document.body.append(iframe); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html new file mode 100644 index 0000000000..a26b7bfdc2 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<script src="utils.js"></script> +<title>Header Inheritance CSP Reporting Page</title> +<body> +<script> +// This file is embedded in an iframe by ancestor-throttle-inner.https.html +// which in turn has been embedded in a fenced frame by +// ancestor-throttle.https.html. This in turn will load a same-origin iframe. +async function init() { + const url = new URL(location.href); + const embed_url = generateURL(url.searchParams.get("nested_url"), + parseKeylist()); + const iframe = document.createElement('iframe'); + iframe.src = embed_url; + document.body.append(iframe); +} + +init(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py new file mode 100644 index 0000000000..f783c8530c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py @@ -0,0 +1,25 @@ +"""Automatic beacon store server. + +- When a request body is not specified, serves a 200 response whose body + contains the stored value from the last automatic beacon. If the stored value + doesn't exist, serves a 200 response with an empty body. +- When a request body is specified, stores the data in the body and serves a 200 + response without body. +""" +# Use an arbitrary key since `request.server.stash.put` expects a valid UUID. +BEACON_KEY = "0c02dba4-f01e-11ed-a05b-0242ac120003" + +def main(request, response): + # Requests with a body imply they were sent as an automatic beacon for + # reserved.top_navigation. Note that this only stores the most recent beacon + # that was sent. + if request.body: + request.server.stash.put(BEACON_KEY, request.body) + return (200, [], b"") + + # Requests without a body imply they were sent as the request from + # nextAutomaticBeacon(). + data = request.server.stash.take(BEACON_KEY) + if not data and data != "": + return (200, [], b"<Not set>") + return (200, [], data) diff --git a/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html new file mode 100644 index 0000000000..1bca25a957 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of background fetch</title> + +<body> + <script> + (async function () { + const [background_fetch_register_key, method] = parseKeylist(); + const file = 'background-fetch-inner.https.html.headers'; + + navigator.serviceWorker.register("empty-worker.js", { scope: location.href }); + const registration = await navigator.serviceWorker.ready; + + const url = new URL(location.href); + + let promise; + switch (method) { + case 'fetch': + promise = registration.backgroundFetch.fetch('test-fetch', file); + break; + case 'get': + promise = registration.backgroundFetch.get('test-fetch'); + break; + case 'getIds': + promise = registration.backgroundFetch.getIds(); + break + default: + promise = Promise.resolve(); + } + + promise + .then(() => { + writeValueToServer(background_fetch_register_key, + `[backgroundFetch.${method}] Unexpectedly started`); + }) + .catch(() => { + writeValueToServer(background_fetch_register_key, + `[backgroundFetch.${method}] Failed inside fencedframe as expected`); + }) + .finally(() => { + registration.unregister(); + }); + })(); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html new file mode 100644 index 0000000000..78e58e5bbf --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of background fetch in SW</title> + +<body> + <script type="module"> + const [background_fetch_register_key, method] = parseKeylist(); + + const getController = () => { + if (navigator.serviceWorker.controller) { + return navigator.serviceWorker.controller; + } + return new Promise(resolve => { + navigator.serviceWorker.addEventListener('controllerchange', () => { + resolve(navigator.serviceWorker.controller); + }); + }); + }; + + const sendMessageToServiceWorker = async () => { + const ctrl = await getController(); + return new Promise(resolve => { + ctrl.postMessage(method); + navigator.serviceWorker.onmessage = e => { + resolve(e.data); + } + }); + }; + + await navigator.serviceWorker.register( + "background-fetch-sw.js", { scope: location.href }); + const data = await sendMessageToServiceWorker(); + + writeValueToServer(background_fetch_register_key, data); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw.js b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw.js new file mode 100644 index 0000000000..44b7d087b5 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-fetch-sw.js @@ -0,0 +1,36 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async e => { + const method = e.data; + + let promise; + switch (method) { + case 'fetch': + promise = self.registration.backgroundFetch.fetch( + 'test-fetch', ['background-fetch-inner.https.html.headers'], + {title: 'Background Fetch'}); + break; + case 'get': + promise = self.registration.backgroundFetch.get('test-fetch') + break; + case 'getIds': + promise = registration.backgroundFetch.getIds(); + break; + default: + promise = Promise.resolve(); + break; + } + + const message = + await promise + .then(() => { + return `[backgroundFetch.${method}] Unexpectedly started`; + }) + .catch((e) => { + return `[backgroundFetch.${ + method}] Failed inside fencedframe as expected`; + }); + + e.source.postMessage(message); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-helper.js b/testing/web-platform/tests/fenced-frame/resources/background-sync-helper.js new file mode 100644 index 0000000000..78b69f15de --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-helper.js @@ -0,0 +1,23 @@ +const getOneShotSyncPromise = (registration, method) => { + if (method === 'register') { + return registration.sync.register('fencedframe-oneshot'); + } else if (method === 'getTags') { + return registration.sync.getTags(); + } + return Promise.resolve(); +}; + +const getPeriodicSyncPromise = (registration, method) => { + if (method === 'register') { + return registration.periodicSync.register( + 'fencedframe-periodic', {minInterval: 1000}); + } else if (method === 'getTags') { + return registration.periodicSync.getTags(); + } else if (method === 'unregister') { + return registration.periodicSync.unregister('fencedframe-periodic'); + } else { + return Promise.resolve(); + } +}; + +export {getOneShotSyncPromise, getPeriodicSyncPromise} diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html new file mode 100644 index 0000000000..81974c803a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of background sync's register</title> + +<body> +<script type="module"> + import {getOneShotSyncPromise, getPeriodicSyncPromise} from './background-sync-helper.js'; + + const [background_sync_register_key] = parseKeylist(); + const searchParams = new URL(location.href).searchParams; + const method = searchParams.get('method'); + const periodic = searchParams.get('periodic'); + + navigator.serviceWorker.register("empty-worker.js", { scope: location.href }); + const registration = await navigator.serviceWorker.ready; + + try { + if (periodic) { + await getPeriodicSyncPromise(registration, method); + } else { + await getOneShotSyncPromise(registration, method); + } + writeValueToServer(background_sync_register_key, "unexpectedly registered"); + } catch (e) { + writeValueToServer(background_sync_register_key, e.message); + } finally { + registration.unregister(); + } +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html new file mode 100644 index 0000000000..b9521a4e20 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of background sync's register in SW</title> + +<body> +<script type="module"> + const [background_sync_register_key] = parseKeylist(); + const searchParams = new URL(location.href).searchParams; + const method = searchParams.get('method'); + const isPeriodic = searchParams.get('periodic'); + + const getController = () => { + if (navigator.serviceWorker.controller) { + return navigator.serviceWorker.controller; + } + return new Promise(resolve => { + navigator.serviceWorker.addEventListener('controllerchange', () => { + resolve(navigator.serviceWorker.controller); + }); + }); + }; + + const sendMessageToServiceWorker = async () => { + const ctrl = await getController(); + return new Promise(resolve => { + ctrl.postMessage({method, isPeriodic}); + navigator.serviceWorker.onmessage = e => { + resolve(e.data); + } + }); + }; + + await navigator.serviceWorker.register( + "background-sync-sw.js", { scope: location.href, type: "module" }); + const data = await sendMessageToServiceWorker(); + + writeValueToServer(background_sync_register_key, data); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/background-sync-sw.js b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw.js new file mode 100644 index 0000000000..5b0c791f0d --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/background-sync-sw.js @@ -0,0 +1,21 @@ +import {getOneShotSyncPromise, getPeriodicSyncPromise} from './background-sync-helper.js'; + +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async e => { + const {method, isPeriodic} = e.data; + const promise = isPeriodic ? + getPeriodicSyncPromise(self.registration, method) : + getOneShotSyncPromise(self.registration, method); + const message = + await promise + .then(() => { + return `[background synnc ${method}] Unexpectedly started`; + }) + .catch((e) => { + return e.message; + }); + + e.source.postMessage(message); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/badging-sw.js b/testing/web-platform/tests/fenced-frame/resources/badging-sw.js new file mode 100644 index 0000000000..5bc3c9a190 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/badging-sw.js @@ -0,0 +1,23 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async e => { + const method = e.data; + + let promise; + if (method === 'setAppBadge') { + promise = self.navigator.setAppBadge(1); + } else if (method === 'clearAppBadge') { + promise = self.navigator.clearAppBadge(); + } else { + promise = Promise.resolve(); + } + + const error = await promise + .then(() => { + return `[Badging API ${method}] Unexpectedly started`; + }) + .catch((e) => e); + + e.source.postMessage(error); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html b/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html new file mode 100644 index 0000000000..6d23cf88a3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the beforeunload event is not fired</title> + +<body> +<script> +window.onload = () => { + const [before_unload_key] = parseKeylist(); + const url = new URL(location.href); + const next_url = url.searchParams.get('next_url'); + + if (next_url != null) { + writeValueToServer( + before_unload_key, 'Loaded the next url in a fenced frame'); + return; + } + + window.onbeforeunload = () => { + writeValueToServer( + before_unload_key, 'The beforeunload event is unexpectely fired.'); + }; + + location.href = + generateURL('before-unload-inner.html?next_url', [before_unload_key]); +}; +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/check-header-referrer.py b/testing/web-platform/tests/fenced-frame/resources/check-header-referrer.py new file mode 100644 index 0000000000..b06fbc2704 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/check-header-referrer.py @@ -0,0 +1,16 @@ +import os + +from wptserve.utils import isomorphic_decode + + +def main(request, response): + response.headers.set(b"supports-loading-mode", b"fenced-frame") + + script = u""" + <script src="utils.js"></script> + <script> + const [referrer_key, _] = parseKeylist(); + writeValueToServer(referrer_key, "%s") + </script> + """ % (isomorphic_decode(request.headers.get(b"referer", b""))) + return (200, [], script) diff --git a/testing/web-platform/tests/fenced-frame/resources/check-header-sec-fetch-dest.py b/testing/web-platform/tests/fenced-frame/resources/check-header-sec-fetch-dest.py new file mode 100644 index 0000000000..98231079b3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/check-header-sec-fetch-dest.py @@ -0,0 +1,14 @@ +import os + + +def main(request, response): + response.headers.set(b"supports-loading-mode", b"fenced-frame") + + script = u""" + <script src="utils.js"></script> + <script> + const [secfetch_key] = parseKeylist(); + writeValueToServer(secfetch_key, "%s") + </script> + """ % (request.headers.get(b"sec-fetch-dest", b"none")) + return (200, [], script) diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html b/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html new file mode 100644 index 0000000000..d02abd6957 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>Client Hint Echoing Iframe</title> +<body> +<script> +window.parent.postMessage({'headers': { + 'sec-ch-viewport-width': '{{header_or_default(sec-ch-viewport-width, )}}', + 'sec-ch-ua-reduced': '{{header_or_default(sec-ch-ua-reduced, )}}', + 'sec-ch-ua-mobile': '{{header_or_default(sec-ch-ua-mobile, )}}', +}}, '*'); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html.headers new file mode 100644 index 0000000000..f500a60ae8 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html.headers @@ -0,0 +1,4 @@ +Supports-Loading-Mode: fenced-frame +Accept-CH: sec-ch-viewport-width, sec-ch-ua-reduced +Feature-Policy: ch-viewport-width *, ch-ua-reduced * +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html b/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html new file mode 100644 index 0000000000..0271d0290d --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Client Hints Helper</title> +<body> +<script type="module"> +const [key] = parseKeylist(); +let iframe = document.createElement('iframe'); +let p = new Promise((resolve, reject) => { + window.addEventListener('message', e => { + resolve(e.data); + }); +}); +iframe.src = 'client-hints-iframe-inner.sub.https.html'; +document.body.appendChild(iframe); +const response = await p; +const result = { + 'root-fenced-frame-headers': { + 'sec-ch-viewport-width': '{{header_or_default(sec-ch-viewport-width, )}}', + 'sec-ch-ua-reduced': '{{header_or_default(sec-ch-ua-reduced, )}}', + 'sec-ch-ua-mobile': '{{header_or_default(sec-ch-ua-mobile, )}}', + }, + 'iframe-headers': response.headers, +}; +writeValueToServer(key, JSON.stringify(result)); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html.headers new file mode 100644 index 0000000000..ea4cf59d16 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html.headers @@ -0,0 +1,5 @@ +Supports-Loading-Mode: fenced-frame +Accept-CH: sec-ch-viewport-width, sec-ch-ua-reduced +Feature-Policy: ch-viewport-width *, ch-ua-reduced * +Access-Control-Allow-Origin: * + diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html new file mode 100644 index 0000000000..9afb5c6a85 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<meta http-equiv="Accept-CH" + content="sec-ch-viewport-width, sec-ch-ua-reduced"/> +<meta http-equiv="Feature-Policy" + content="ch-viewport-width *, ch-ua-reduced *"/> +<title>Client Hint Echoing Iframe</title> +<body> +<script> +window.parent.postMessage({'headers': { + 'sec-ch-viewport-width': '{{header_or_default(sec-ch-viewport-width, )}}', + 'sec-ch-ua-reduced': '{{header_or_default(sec-ch-ua-reduced, )}}', + 'sec-ch-ua-mobile': '{{header_or_default(sec-ch-ua-mobile, )}}', +}}, '*'); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html.headers new file mode 100644 index 0000000000..b7952e5d05 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html.headers @@ -0,0 +1,2 @@ + +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html new file mode 100644 index 0000000000..b84f16ffd0 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta http-equiv="Accept-CH" + content="sec-ch-viewport-width, sec-ch-ua-reduced"/> +<meta http-equiv="Feature-Policy" + content="ch-viewport-width *, ch-ua-reduced *"/> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Client Hints Helper</title> +<body> +<script type="module"> +const [key] = parseKeylist(); +let iframe = document.createElement('iframe'); +let p = new Promise((resolve, reject) => { + window.addEventListener('message', e => { + resolve(e.data); + }); +}); +iframe.src = 'client-hints-meta-iframe-inner.sub.https.html'; +document.body.appendChild(iframe); +const response = await p; +const result = { + 'root-fenced-frame-headers': { + 'sec-ch-viewport-width': '{{header_or_default(sec-ch-viewport-width, )}}', + 'sec-ch-ua-reduced': '{{header_or_default(sec-ch-ua-reduced, )}}', + 'sec-ch-ua-mobile': '{{header_or_default(sec-ch-ua-mobile, )}}', + }, + 'iframe-headers': response.headers, +}; +writeValueToServer(key, JSON.stringify(result)); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html.headers new file mode 100644 index 0000000000..afe7b4f317 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +Access-Control-Allow-Origin: * diff --git a/testing/web-platform/tests/fenced-frame/resources/content-index-sw.js b/testing/web-platform/tests/fenced-frame/resources/content-index-sw.js new file mode 100644 index 0000000000..c2759d9630 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/content-index-sw.js @@ -0,0 +1,28 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async event => { + const method = event.data; + const {index} = self.registration; + const id = 'fenced-frame-id-sw'; + + let promise; + if (method === 'add') { + promise = index.add({ + id, + title: 'same title', + description: 'same description', + url: 'resources/' + }); + } else if (method === 'delete') { + promise = index.delete(id); + } else if (method === 'getAll') { + promise = index.getAll(); + } else { + promise = Promise.resolve(); + } + + const message = await promise.then(() => 'success').catch(e => e.message); + + event.source.postMessage(message); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html b/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html new file mode 100644 index 0000000000..34e5681139 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Page loaded in a frame in a fenced frame tree</title> +<script> + // This page is loaded either in an iframe or a fenced frame + // nested inside a root fenced frame. + document.cookie = 'G=nested_in_fenced_frame; SameSite=Lax'; + const [cookie_value_key] = parseKeylist() + const cookie_value = document.cookie; + writeValueToServer(cookie_value_key, cookie_value); +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html new file mode 100644 index 0000000000..5725177f21 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/webauthn/helpers.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.credentials.create</title> + +<body> +<script> +function base_path() { + return location.pathname.replace(/\/[^\/]*$/, '/'); +} + +standardSetup(function() { + 'use strict'; + async function init() { + // This file is meant to be navigated to from a <fencedframe> element. It + // reports back to the page hosting the <fencedframe> whether or not + // `navigator.credentials.create` is allowed. + const [key] = parseKeylist(); + + // Report whether or not `credentials.create` is allowed. + createCredential().then( + () => { + writeValueToServer(key, 'createCredential passed'); + }, + () => { + writeValueToServer(key, 'createCredential failed'); + }, + ); + } + + init(); +}); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/create-popup.html b/testing/web-platform/tests/fenced-frame/resources/create-popup.html new file mode 100644 index 0000000000..a6cd81ec14 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/create-popup.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Nested frames in a Fenced Frame tree creating popups</title> +<script> + // It is the document that `popup-noopener-inner.html` loads in a nested + // iframe/fenced frame. + // It's expected that the opener/openee references should be null, and + // window.name should be the empty string. + const [popup_noopener_key, popup_openee_key, popup_name_key] = parseKeylist(); + const src_popup = generateURL(`popup-noopener-destination.html`, + [popup_noopener_key, popup_name_key]); + const popup = window.open(src_popup, "foo"); + if (popup) { + writeValueToServer(popup_openee_key, "FAIL"); + } else { + writeValueToServer(popup_openee_key, "PASS"); + } +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/create-popup.html.headers b/testing/web-platform/tests/fenced-frame/resources/create-popup.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/create-popup.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html new file mode 100644 index 0000000000..bdb448c347 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Page embedded as a fenced frame</title> +<script> + const [key] = parseKeylist(); + writeValueToServer(key, "loaded"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html new file mode 100644 index 0000000000..990f5ee469 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Page embedded as a fenced frame</title> +<script> + // This file is expected to be unreachable from + // `csp-fenced-frame-src-blocked.html` in the parent directory because of CSP + // violation. + const [key] = parseKeylist(); + writeValueToServer(key, "loaded"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html new file mode 100644 index 0000000000..bdb448c347 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Page embedded as a fenced frame</title> +<script> + const [key] = parseKeylist(); + writeValueToServer(key, "loaded"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html new file mode 100644 index 0000000000..eb90bb94e9 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Page embedded as a fenced frame</title> +<script> + // This file is expected to be unreachable from `csp-frame-src-blocked.html` + // in the parent directory because of CSP violation. + const [key] = parseKeylist(); + writeValueToServer(key, "loaded"); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-inner.html b/testing/web-platform/tests/fenced-frame/resources/csp-inner.html new file mode 100644 index 0000000000..99df39fdc5 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-inner.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> + <script src="utils.js"></script> + + <style> + body {background-color: red;} + </style> + + <title>Fenced frame content to test Content Security Policies</title> + + <body> + <script> + const [csp_key] = parseKeylist(); + + function fail() { + writeValueToServer(csp_key, + "FAIL: img-src policy was not honored in fenced frame"); + } + + function pass() { + // The parent page is going to attempt to pass a + // style-src: 'none' CSP to the fenced frame. Make sure that + // the header is not honored. + const bgcolor = window.getComputedStyle(document.body, null) + .getPropertyValue('background-color'); + + if (bgcolor != "rgb(255, 0, 0)") { + writeValueToServer(csp_key, + "FAIL: style-src policy was passed to fenced frame"); + return; + } + + writeValueToServer(csp_key, "pass"); + } + </script> + <img src="csp.png" id="my_img" onload="fail();" onerror="pass();"> + </body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/csp-inner.html.headers new file mode 100644 index 0000000000..e89be70a43 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp-inner.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +Content-Security-Policy: img-src 'none'
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/csp.png b/testing/web-platform/tests/fenced-frame/resources/csp.png Binary files differnew file mode 100644 index 0000000000..53a9748ae0 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/csp.png diff --git a/testing/web-platform/tests/fenced-frame/resources/dangling-markup-helper.js b/testing/web-platform/tests/fenced-frame/resources/dangling-markup-helper.js new file mode 100644 index 0000000000..9e0fff2301 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/dangling-markup-helper.js @@ -0,0 +1,15 @@ +// These are used in tests that rely on URLs containing dangling markup. See +// https://github.com/whatwg/fetch/pull/519. +const kDanglingMarkupSubstrings = [ + "blo\nck<ed", + "blo\rck<ed", + "blo\tck<ed", + "blo<ck\ned", + "blo<ck\red", + "blo<ck\ted", +]; + +function getTimeoutPromise(t) { + return new Promise(resolve => + t.step_timeout(() => resolve("NOT LOADED"), 1500)); +}
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-helper.js b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-helper.js new file mode 100644 index 0000000000..821ebf69e3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-helper.js @@ -0,0 +1,45 @@ +// This is a helper file used for the attribution-reporting-*.https.html tests. +// To use this, make sure you import these scripts: +// <script src="/resources/testharness.js"></script> +// <script src="/resources/testharnessreport.js"></script> +// <script src="/common/utils.js"></script> +// <script src="/common/dispatcher/dispatcher.js"></script> +// <script src="resources/utils.js"></script> +// <script src="/common/get-host-info.sub.js"></script> + +async function runDefaultEnabledFeaturesTest(t, should_load, fenced_origin, + generator_api="fledge", allow="") { + const fencedframe = await attachFencedFrameContext({ + generator_api: generator_api, + attributes: [["allow", allow]], + origin: fenced_origin}); + + if (!should_load) { + const fencedframe_blocked = new Promise(r => t.step_timeout(r, 1000)); + const fencedframe_loaded = fencedframe.execute(() => {}); + assert_equals(await Promise.any([ + fencedframe_blocked.then(() => "blocked"), + fencedframe_loaded.then(() => "loaded"), + ]), "blocked", "The fenced frame should not be loaded."); + return; + } + + await fencedframe.execute((generator_api) => { + assert_true( + document.featurePolicy.allowsFeature('attribution-reporting'), + "Attribution reporting should be allowed if the fenced " + + "frame loaded using FLEDGE or shared storage."); + + if (generator_api == "fledge") { + assert_false( + document.featurePolicy.allowsFeature('shared-storage'), + "Shared storage should be disallowed if the fenced " + + "frame loaded using FLEDGE."); + } else { + assert_true( + document.featurePolicy.allowsFeature('shared-storage'), + "Shared storage should be allowed if the fenced " + + "frame loaded using shared storage."); + } + }, [generator_api]); +} diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html new file mode 100644 index 0000000000..000b1eb6dd --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<title>Fenced frame attribution reporting redirect test</title> + +<body> +<script> +// This helper function will redirect a fenced frame to a remote origin page +// while embedded in a frame that does not allow attribution reporting on +// remote origins. That redirect should fail to load because of the attribution +// reporting restriction. +const [key1, key2] = parseKeylist(); + +if (location.origin == get_host_info().ORIGIN) { + const result_val = + document.featurePolicy.allowsFeature('attribution-reporting') & + document.featurePolicy.allowsFeature('shared-storage'); + + writeValueToServer(key1, + document.featurePolicy.allowsFeature('attribution-reporting')); + + const next_url = getRemoteOriginURL(generateURL( + "attribution-reporting-redirect.https.html", [key1, key2])); + location.href = next_url; +} else { + // The redirect should have been unsuccessful, so we should not reach this + // point. + writeValueToServer(key2, "FAIL"); +} + +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-redirect.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html new file mode 100644 index 0000000000..e098736528 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> + +<body> +<script> +// This page is loaded into a fenced frame. The document policies for this page +// disable shared storage. This then creates a child iframe to determine if +// document deliviered policies are reflected in the child frame. +const [key, should_restrict_select_url] = parseKeylist(); + +const iframe_url = generateURL( + 'default-enabled-features-subframe-iframe.https.html', [key]); +const iframe = document.createElement("iframe"); +iframe.src = iframe_url; +if (should_restrict_select_url == "true") { + iframe.allow = "shared-storage-select-url 'none';" +} +document.body.appendChild(iframe); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html.headers new file mode 100644 index 0000000000..e52511f18a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +Permissions-Policy: shared-storage=() diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html new file mode 100644 index 0000000000..a3ab056944 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> + +<body> +<script> +// This page is loaded into an iframe that is nested within a fenced frame tree. +// This is used to tell the test whether policies that are restricted by a +// fenced frame's document policies also are restricted in subframes. +const [key] = parseKeylist(); + +const allows_shared_storage = + document.featurePolicy.allowsFeature('shared-storage'); +const allows_select_url = + document.featurePolicy.allowsFeature('shared-storage-select-url'); + +writeValueToServer(key, allows_shared_storage + "," + allows_select_url); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/download-helper.js b/testing/web-platform/tests/fenced-frame/resources/download-helper.js new file mode 100644 index 0000000000..011d5c867f --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/download-helper.js @@ -0,0 +1,29 @@ +function StreamDownloadFinishDelay() { + return 1000; +} + +function DownloadVerifyDelay() { + return 1000; +} + +async function VerifyDownload(test_obj, token) { + const verifyToken = async (token) => { + const url = `resources/download-stash.py?verify-token&token=${token}`; + const response = await fetch(url); + if (!response.ok) { + throw new Error('An error happened in the server'); + } + const message = await response.text(); + return message === 'TOKEN_SET'; + }; + + return new Promise((resolve) => { + test_obj.step_wait( + async () => { + const result = await verifyToken(token); + resolve(result); + }, + 'Check if the download has finished or not', + StreamDownloadFinishDelay() + DownloadVerifyDelay()); + }); +} diff --git a/testing/web-platform/tests/fenced-frame/resources/download-inner.html b/testing/web-platform/tests/fenced-frame/resources/download-inner.html new file mode 100644 index 0000000000..9bc816cbf3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/download-inner.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> + +<head> + <title>The page triggering download embedded as a Fenced Frame</title> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="download-helper.js"></script> + <script src="utils.js"></script> + <script> + window.addEventListener('DOMContentLoaded', async () => { + const [download_key, download_ack_key] = parseKeylist(); + const type = new URL(location).searchParams.get('type'); + const href = `download-stash.py?token=${download_key}`; + + if (type == 'anchor') { + const anchor = document.querySelector('#download'); + anchor.href = href; + test_driver.click(anchor); + } else { + const delay = StreamDownloadFinishDelay(); + location.href = `${href}&finish-delay=${delay}` + } + + await writeValueToServer(download_ack_key, 'Triggered the action for download'); + }); + </script> +</head> + +<body> + <a id="download" download>Download</a> +</body> + +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/download-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/download-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/download-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/download-stash.py b/testing/web-platform/tests/fenced-frame/resources/download-stash.py new file mode 100644 index 0000000000..497f7cb018 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/download-stash.py @@ -0,0 +1,28 @@ +import time + + +def main(request, response): + token = request.GET[b"token"] + response.status = 200 + response.headers.append(b"Content-Type", b"text/html") + if b"verify-token" in request.GET: + if request.server.stash.take(token): + return u'TOKEN_SET' + return u'TOKEN_NOT_SET' + + if b"finish-delay" not in request.GET: + # <a download> + request.server.stash.put(token, True) + return + + # navigation to download + response.headers.append(b"Content-Disposition", b"attachment") + response.write_status_headers() + finish_delay = float(request.GET[b"finish-delay"]) / 1E3 + count = 10 + single_delay = finish_delay / count + for i in range(count): # pylint: disable=unused-variable + time.sleep(single_delay) + if not response.writer.write_content(b"\n"): + return + request.server.stash.put(token, True) diff --git a/testing/web-platform/tests/fenced-frame/resources/dummy.html b/testing/web-platform/tests/fenced-frame/resources/dummy.html new file mode 100644 index 0000000000..a0cf50713e --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/dummy.html @@ -0,0 +1,2 @@ +<!DOCTYPE html> +<title>Dummy page</title> diff --git a/testing/web-platform/tests/fenced-frame/resources/embeddee.html b/testing/web-platform/tests/fenced-frame/resources/embeddee.html new file mode 100644 index 0000000000..3423be9aa4 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/embeddee.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>A page embedded as a Fenced Frame for COEP tests</title> +<script> +const [uuid] = parseKeylist(); +writeValueToServer(uuid, "PASS"); +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/embeddee.html.headers b/testing/web-platform/tests/fenced-frame/resources/embeddee.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/embeddee.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/embedder-policy.js b/testing/web-platform/tests/fenced-frame/resources/embedder-policy.js new file mode 100644 index 0000000000..8c96afafce --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/embedder-policy.js @@ -0,0 +1,39 @@ +// This file should be loaded alongside with utils.js. +// +// This file is loaded by: +// - embedder-no-coep.https.html +// - embedder-require-corp.https.html + +// Make input list to be used as a wptserve pipe +// (https://web-platform-tests.org/writing-tests/server-pipes.html). +// e.g. +// args: ['content-type,text/plain','Age,0'] +// return: 'header(content-type,text/plain)|header(Age,0)' +function generateHeader(headers) { + return headers.map((h) => { + return 'header(' + h + ')'; + }).join('|'); +} + +// Setup a fenced frame for embedder-* WPTs. +async function setupTest(test_type, uuid, hostname='') { + let headers = ["Supports-Loading-Mode,fenced-frame"]; + switch (test_type) { + case "coep:require-corp": + headers.push("cross-origin-embedder-policy,require-corp"); + headers.push("cross-origin-resource-policy,same-origin"); + break; + case "no coep": + break; + default: + assert_unreachable("unknown test_type:" + test_type); + break; + } + const tmp_url = new URL('resources/embeddee.html', location.href); + if (hostname) { + tmp_url.hostname = hostname; + } + tmp_url.searchParams.append("pipe", generateHeader(headers)); + const url = generateURL(tmp_url.toString(), [uuid]); + return attachFencedFrame(url); +} diff --git a/testing/web-platform/tests/fenced-frame/resources/empty-worker.js b/testing/web-platform/tests/fenced-frame/resources/empty-worker.js new file mode 100644 index 0000000000..49ceb2648a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/empty-worker.js @@ -0,0 +1 @@ +// Do nothing. diff --git a/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html new file mode 100644 index 0000000000..f30cd77838 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.credentials.get</title> + +<body> +<script> +function isExpectedErrorMessage(e) { + return e.name === 'NotAllowedError' && + e.message === + 'The credential operation is not allowed in a fenced frame tree.'; +} + +// This file is meant to be navigated to from a <fencedframe> element. It +// reports back to the page hosting the <fencedframe> whether or not +// `navigator.credentials.get` is allowed. +const [key] = parseKeylist(); + +const test_options = { + federated: { + providers: [{ + configURL: 'https://idp.test/fedcm.json', + clientId: '1', + nonce: '2', + }] + } +}; +navigator.credentials.get(test_options) + .then( + () => { + writeValueToServer(key, 'unexpected passed'); + }, + (e) => { + if (isExpectedErrorMessage(e)) { + writeValueToServer(key, 'navigator.credentials.get failed'); + } else { + writeValueToServer( + key, 'navigator.credentials.get failed by unexpected reason'); + } + }, + ); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html new file mode 100644 index 0000000000..814ea78559 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to test window.fence object</title> + +<body> +<script> + + // Get the token for communication with the parent. + const [fence_api_token] = parseKeylist(); + + // Check that window.fence is visible inside fenced frames. + assert_true(window.fence != null, + "window.fence should be visible inside fenced frames"); + assert_true(fence != null, + "fence should be visible inside fenced frames"); + + // Tell the parent that the test succeeded. + writeValueToServer(fence_api_token, ""); + +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html new file mode 100644 index 0000000000..0054762783 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html> +<script src="utils.js"></script> +<title>Fenced frame loaded</title> +<body> +<script> +(async function() { + const [parent_key] = parseKeylist(); + writeValueToServer(parent_key, "fenced frame loaded"); +})(); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html.headers b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html new file mode 100644 index 0000000000..9b67be775e --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Nested fenced frame named navigation helper</title> +<!-- This is a helper file. It is meant to be the document loaded inside a + nested fenced frame by `navigate-by-name-inner.html`. Once this document is + loaded and changes its `window.name` to `target_frame`, it reports to the + server so that the outermost document can attempt to navigate it by name. + (The navigation should not succeed - see the test expectations). +--> +<body> +<script> + const [ready_for_navigation_key] = parseKeylist(); + window.name = "target_frame"; + writeValueToServer(ready_for_navigation_key, "READY"); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html.headers b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js new file mode 100644 index 0000000000..1a22bcec3d --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js @@ -0,0 +1,37 @@ +// These functions are used by FLEDGE to determine the logic for the ad buyer. +// For our testing purposes, we only need the minimal amount of boilerplate +// code in place to allow them to be invoked properly and move the FLEDGE +// process along. The tests do not deal with reporting results, so we leave +// `reportWin` empty. See `generateURNFromFledge` in "utils.js" to see how +// these files are used. + +function generateBid(interestGroup, auctionSignals, perBuyerSignals, + trustedBiddingSignals, browserSignals) { + const ad = interestGroup.ads[0]; + + // `auctionSignals` controls whether or not component auctions are allowed. + let allowComponentAuction = + typeof auctionSignals === 'string' && + auctionSignals.includes('bidderAllowsComponentAuction'); + + let result = { + 'ad': ad, + 'bid': 1, + 'render': { url: ad.renderUrl, width: "100px", height: "50px" }, + 'allowComponentAuction': allowComponentAuction + }; + if (interestGroup.adComponents && interestGroup.adComponents.length > 0) + result.adComponents = interestGroup.adComponents.map((component) => { + return { + url: component.renderUrl, + width: "100px", + height: "50px" + }; + }); + return result; +} + +function reportWin( + auctionSignals, perBuyerSignals, sellerSignals, browserSignals) { + return; +} diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js.headers b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js.headers new file mode 100644 index 0000000000..776bf2987b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic-with-size.js.headers @@ -0,0 +1,2 @@ +Content-Type: Application/Javascript +X-Allow-FLEDGE: true
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js new file mode 100644 index 0000000000..ab6685f184 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js @@ -0,0 +1,34 @@ +// These functions are used by FLEDGE to determine the logic for the ad buyer. +// For our testing purposes, we only need the minimal amount of boilerplate +// code in place to allow them to be invoked properly and move the FLEDGE +// process along. The tests do not deal with reporting results, so we leave +// `reportWin` empty. See `generateURNFromFledge` in "utils.js" to see how +// these files are used. + +function generateBid(interestGroup, auctionSignals, perBuyerSignals, + trustedBiddingSignals, browserSignals) { + const ad = interestGroup.ads[0]; + + // `auctionSignals` controls whether or not component auctions are allowed. + let allowComponentAuction = + typeof auctionSignals === 'string' && + auctionSignals.includes('bidderAllowsComponentAuction'); + + let result = {'ad': ad, 'bid': 1, 'render': ad.renderUrl, + 'allowComponentAuction': allowComponentAuction}; + if (interestGroup.adComponents && interestGroup.adComponents.length > 0) + result.adComponents = interestGroup.adComponents.map((component) => { + return component.renderUrl; + }); + return result; +} + +function reportWin( + auctionSignals, perBuyerSignals, sellerSignals, browserSignals) { + registerAdBeacon({ + 'reserved.top_navigation': + browserSignals.interestGroupOwner + + '/fenced-frame/resources/automatic-beacon-store.py' + }); + return; +} diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js.headers b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js.headers new file mode 100644 index 0000000000..776bf2987b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.js.headers @@ -0,0 +1,2 @@ +Content-Type: Application/Javascript +X-Allow-FLEDGE: true
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js new file mode 100644 index 0000000000..dc92f5a5e1 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js @@ -0,0 +1,15 @@ +// These functions are used by FLEDGE to determine the logic for the ad seller. +// For our testing purposes, we only need the minimal amount of boilerplate +// code in place to allow them to be invoked properly and move the FLEDGE +// process along. The tests do not deal with reporting results, so we leave +// `reportResult` empty. See `generateURNFromFledge` in "utils.js" to see how +// these files are used. + +function scoreAd( + adMetadata, bid, auctionConfig, trustedScoringSignals, browserSignals) { + return 2*bid; +} + +function reportResult(auctionConfig, browserSignals) { + return; +} diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js.headers b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js.headers new file mode 100644 index 0000000000..776bf2987b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.js.headers @@ -0,0 +1,2 @@ +Content-Type: Application/Javascript +X-Allow-FLEDGE: true
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html new file mode 100644 index 0000000000..9a56a3d9fb --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="utils.js"></script> +<title>Test nested fenced frame navigation (by a parent frame setting its src).</title> + +<body> + <script> + async function init() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It communicates with + // the embedder to confirm that nested fenced frames can be navigated. + + const [navigation_key, navigation_ack_key] = parseKeylist(); + + // Create URL prefixes to simulate different origins. + // (www1 and www2 are different origins) + const simple_url = generateURL("frame-navigation-inner-simple.https.html", + [navigation_key, navigation_ack_key]); + + const origin1_simple_url = getRemoteOriginURL(simple_url); + const origin2_simple_url = getRemoteOriginURL(simple_url) + .toString().replace("www1", "www2"); + + const url_prefix = location.href + "/../"; + + // Tell the embedder that this frame has loaded. + writeValueToServer(navigation_key, "create-nested"); + await nextValueFromServer(navigation_ack_key); + + // Create an inner frame. + inner_frame = attachFencedFrame(origin1_simple_url); + // Wait for our parent to tell us they're done communicating. + await nextValueFromServer(navigation_ack_key); + + // Navigate (cross-origin) and wait. + inner_frame.config = new FencedFrameConfig( + generateURL(origin2_simple_url, [])); + await nextValueFromServer(navigation_ack_key); + + // Navigate (same-origin) and wait. + inner_frame.config = new FencedFrameConfig( + generateURL(origin2_simple_url, [])); + await nextValueFromServer(navigation_ack_key); + } + + init(); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html new file mode 100644 index 0000000000..643ea48a76 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Test that a fenced frame successfully loaded.</title> + +<body> + <script> + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page to confirm that loading succeeded. + const [navigation_key, navigation_ack_key] = parseKeylist(); + writeValueToServer(navigation_key, "pass"); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html b/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html new file mode 100644 index 0000000000..dd36b20399 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<div id="target" style="width: 100px; height: 100px; position: fixed; top: 0px; left: 0px"></div> +<script> +let next_token = 0; +function init() { + const tokens = parseKeylist(); + let observer = new IntersectionObserver((entries) => { + assert_equals(entries.length, 1); + let rect = entries[0].intersectionRect.x + "," + + entries[0].intersectionRect.y + "," + + entries[0].intersectionRect.width + "," + + entries[0].intersectionRect.height + "," + + entries[0].isVisible; + writeValueToServer(tokens[next_token], rect); + next_token = next_token + 1; + + if (next_token == tokens.length) { + observer.disconnect(); + } + }, {trackVisibility: true, delay: 100, threshold: [0.6, 0.75]}); + observer.observe(document.getElementById("target")); +} + +init(); +</script> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html.headers b/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html b/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html new file mode 100644 index 0000000000..3e253e4915 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>getGamepads should throw an error in a fenced frame</title> +<script> +const [key] = parseKeylist(); +try { + navigator.getGamepads(); + writeValueToServer(key, 'Expected exception but successed'); +} catch (e) { + writeValueToServer(key, e.name); +} +</script> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html new file mode 100644 index 0000000000..a83ba93aa6 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Send the results of getNestedConfigs() to the embedder</title> +<script> +const [key] = parseKeylist(); +const configs = window.fence.getNestedConfigs(); +const data_to_send = [configs.length, configs.length > 0 ? configs[0].url : "N/a"]; +writeValueToServer(key, data_to_send.join(",")); +</script> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html new file mode 100644 index 0000000000..9bd5d9f492 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>getGamepads should throw an error in a fenced frame</title> +<body> + <script> + const [key] = parseKeylist(); + attachIFrame(generateURL("get-nested-configs-inner.html", [key])); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html.headers b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/get_battery.html b/testing/web-platform/tests/fenced-frame/resources/get_battery.html new file mode 100644 index 0000000000..0532deca4b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get_battery.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>getBattery should fail in a fenced frame</title> +<script> +async function init() { // Needed in order to use top-level await. + const [uuid] = parseKeylist(); + try { + await navigator.getBattery(); + writeValueToServer(uuid, 'Expected an exception but the call succeeded'); + } catch (err) { + writeValueToServer(uuid, err.name); + } +} + +init(); +</script> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/get_battery.html.headers b/testing/web-platform/tests/fenced-frame/resources/get_battery.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/get_battery.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html b/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html new file mode 100644 index 0000000000..2940dbac8e --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of header.referrer</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page the value of `referer` in the request header: + // 1.) Nested iframes inside a fenced frame + // 2.) Nested fenced frames + // 3.) Top-level fenced frames (aka this frame) after initial navigation + const [referrer_key, referrer_ack_key] = parseKeylist(); + + const referrer_url = generateURL("check-header-referrer.py", + [referrer_key, referrer_ack_key]); + + const iframe = document.createElement('iframe'); + iframe.src = referrer_url; + document.body.append(iframe); + + // Wait for ACK, so we know that the outer page has read the last value from + // the `referrer_key` stash that the iframe above wrote to, and we can write + // to it again. + await nextValueFromServer(referrer_ack_key); + + attachFencedFrame(referrer_url); + + // Wait for ACK, so we know that the outer page has read the last value from + // the `referrer_key` stash that the nested fenced frame wrote to, and we can + // can write to it again. + await nextValueFromServer(referrer_ack_key); + + location.href = referrer_url; +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html b/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html new file mode 100644 index 0000000000..aa3fe9e34c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of `Sec-Fetch-Dest` header</title> + +<body> +<script> +(() => { + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page the value of `Sec-Fetch-Dest` in the request header for + // nested iframes inside a fenced frame. + const [sec_fetch_dest_value_key] = parseKeylist(); + const https_origin = get_host_info().HTTPS_REMOTE_ORIGIN; + const https_origin_url = + getRemoteOriginURL( + generateURL( + 'check-header-sec-fetch-dest.py', + [sec_fetch_dest_value_key])); + + const iframe = document.createElement('iframe'); + iframe.src = https_origin_url; + document.body.append(iframe); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html b/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html new file mode 100644 index 0000000000..9620249d76 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>history-back-and-forward-should-not-work-in-fenced-tree-inner</title> + +<body> + <script> + // This is a helper file that will serve as the document loaded inside + // a fenced frame by 'history-back-and-forward-should-not-work-in-fenced + // -tree' Once loaded, it will sequentially perform the back and forward + // history navigations to observe whether these methods were successfuly + // restricted for the fenced tree. + + const [history_navigation_performed_key, outer_page_ready_key, + embed_scope] = parseKeylist(); + + (async function () { + const url = new URL(location.href); + const test = url.searchParams.get("test"); + + writeValueToServer(history_navigation_performed_key, "yes"); + + // Execute history.back() within fenced frame and iframe. + await nextValueFromServer(outer_page_ready_key); + window.history.back(); + writeValueToServer(history_navigation_performed_key, "yes"); + + // Execute history.forward() within fenced frame and iframe. + await nextValueFromServer(outer_page_ready_key); + window.history.forward(); + writeValueToServer(history_navigation_performed_key, "yes"); + + if (embed_scope === "outerPage::fencedFrame::iframe") return; + + const iframe = document.createElement('iframe'); + const iframe_embed_scope = "outerPage::fencedFrame::iframe"; + iframe.src = generateURL( + "history-back-and-forward-should-not-work-in-fenced-tree-" + + "inner.html", + [history_navigation_performed_key, outer_page_ready_key, + iframe_embed_scope]); + document.body.append(iframe); + })(); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html b/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html new file mode 100644 index 0000000000..726fafd65b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Nested fenced frame named navigation helper</title> + +<body> +<script> +(async () => { + // We need to wait for the window's `load` event to fire, because client-side + // redirect navigations that take place before a document is "completely + // loaded" [1] are carried out with replacement, as specified in [2]. Just + // waiting for `load` is not enough though! After the `load` event is fired + // (but before a document is marked "completely loaded"), a microtask + // checkpoint is performed, which is where the below `Promise`'s `then` + // handler is invoked (i.e., the rest of the script). So if we just resolve + // the promise and continue, the whole script continues in the next immediate + // microtask before the document is completely loaded. So we have to queue + // another task so that we only continue executing once the document is + // considered completely loaded, and then `location.href` assignments will not + // be made with replacement history handling. + // + // [1]: https://html.spec.whatwg.org/C#the-end:completely-finish-loading + // [2]: https://html.spec.whatwg.org/#the-location-interface:completely-loaded + await new Promise(resolve => { + window.onload = e => { + setTimeout(resolve, 0); + }; + }); + + const kNavigationLimit = 5; + // This is a helper file meant to be loaded inside a fenced frame. It performs + // various navigations inside of the "fence" defined by this document, and + // ensures that they are all done in a replace-only fashion [1]. + // Once we ensure that they are all done with replacement, we report back to + // the outermost page via the server stash, and it ensures that there was no + // impact on the joint session history as observed from beyond the fence. + // + // [1]: https://html.spec.whatwg.org/C/#hh-replace + + // See documentation in the outer page. + const [fenced_navigation_complete_key, + outer_page_ready_for_next_fenced_navigation_key, + level] = parseKeylist(); + + const url = new URL(location.href); + const is_top_level_fenced_frame = (level == "top-level-fenced-frame"); + + ////////////// Navigation code that may impact `history.length` should go here + // The code in this block performs navigations that will run inside: + // - The top-level fenced frame + // - The nested fenced frame + // - The nested iframe + + // First, perform some real navigations to this same page. Normally this would + // increase `history.length`. + if (url.searchParams.get("navigationNumber") == null) + url.searchParams.append("navigationNumber", 0); + + let navigationNumber = parseInt(url.searchParams.get("navigationNumber")); + + if (navigationNumber <= kNavigationLimit) { + url.searchParams.set('navigationNumber', navigationNumber + 1); + location.href = url; + return; + } + + // At this point we're done performing 5 subsequent navigations... + + // Next, perform `history.pushState()`s. + history.pushState({} , ""); + history.pushState({} , ""); + history.pushState({} , ""); + ////////////// END + + // Finally observe `history.length` from within the fenced frame, and report + // the results back to the outermost page. + if (history.length == 1) { + writeValueToServer(fenced_navigation_complete_key, "PASS > " + + level); + } else { + writeValueToServer(fenced_navigation_complete_key, + "FAIL > " + level + " history.length: " + + history.length); + } + + // We're only testing fenced frames, nested fenced frames, and iframes nested + // within fenced frames. The below code adds a nested fenced frame and a + // nested iframe, so it should only be reached by the top-level fenced frame. + if (level != "top-level-fenced-frame") + return; + + // Only top-level fenced frames will attach a nested fenced frame and run the + // same tests there. + await nextValueFromServer(outer_page_ready_for_next_fenced_navigation_key); + const nested_fenced_frame_level = "nested-fenced-frame"; + attachFencedFrame(generateURL( + "history-length-fenced-navigations-replace-do-not-" + + "contribute-to-joint-inner.html", + [fenced_navigation_complete_key, + outer_page_ready_for_next_fenced_navigation_key, + nested_fenced_frame_level])); + + await nextValueFromServer(outer_page_ready_for_next_fenced_navigation_key); + const iframe = document.createElement('iframe'); + const nested_iframe_level = "nested-iframe"; + iframe.src = generateURL( + "history-length-fenced-navigations-replace-do-not-contribute-to-joint-" + + "inner.html", + [fenced_navigation_complete_key, + outer_page_ready_for_next_fenced_navigation_key, + nested_iframe_level]); + document.body.append(iframe); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html b/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html new file mode 100644 index 0000000000..2bdb90ab64 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>history-length-outer-page-navigation-not-reflected-in-fenced-inner</title> + +<body> +<script> +(async () => { + // This is a helper file that will servec as the document loaded inside + // a fenced frame b 'history-length-outer-page-navigation-not-reflected-in- + // fenced' Once loaded, it reports to the observed value of history.length to + // the server so that the outer document can assert the value is 1. + + const [fenced_history_length_key, outer_page_ready_for_next_navigation_key, + embed_scope, embed_scope_reporting] = + parseKeylist(); + + const url = new URL(location.href); + + if (embed_scope == "outer_page::iframe"){ + ////////////// BEGIN NAVIGATIONS + // This block performs a sequence of 'kNavigationLimit' navigations in: + // -- the iframe + const kNavigationLimit = 5 + + const url = new URL(location.href); + + // First, perform some real navigations as well as history.pushState to + // this same page. Normally this would increase `history.length`. + if (url.searchParams.get("navigationCount") == null) + url.searchParams.append("navigationCount", 1); + + let navigationCount = parseInt(url.searchParams.get("navigationCount")); + + if (navigationCount <= kNavigationLimit) { + url.searchParams.set('navigationCount', ++navigationCount); + location.href = url; + history.pushState({} , ""); + return; + } + ////////////// END + writeValueToServer(outer_page_ready_for_next_navigation_key, "READY"); + return + } + + if (embed_scope == embed_scope_reporting) { + // Observe 'history.length' from within the 'embed_scope_reporting', + // and report the results back to the outer page. + if (history.length == 1) { + writeValueToServer(fenced_history_length_key, + "PASS > " + " history.length: " + history.length); + } else { + writeValueToServer(fenced_history_length_key, + "FAIL > " + " history.length: " + history.length); + } + return + } + + if (embed_scope_reporting == "outer_page::fenced_frame::iframe") { + // Append an iframe to the 'outer_page::fenced_frame' to report + // history.length to the outer_page from within the iframe + const iframe = document.createElement('iframe'); + const embed_scope = "outer_page::fenced_frame::iframe"; + iframe.src = generateURL( + "history-length-outer-page-navigation-not-reflected-in-fenced-inner.html", + [fenced_history_length_key, outer_page_ready_for_next_navigation_key, + embed_scope, embed_scope_reporting]); + document.body.append(iframe); + return + } +})(); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html b/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html new file mode 100644 index 0000000000..4fe496f29c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>child frame with delayed onload event</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> + +<body> +</body> +<script> + (function () { + const [element_type, toplevel_loaded_key, result_key] = parseKeylist(); + + // Delays the onload event of the iframe for 2 sec. + if (element_type == "iframe") { + const img = document.createElement("img"); + img.src = "/common/square.png?pipe=trickle(d2)"; + document.body.appendChild(img); + return; + } + + const iframe = document.createElement('iframe'); + iframe.src = generateURL("ignore-child-fenced-frame-onload-event-inner." + + "html", ["iframe"]); + document.body.append(iframe); + + let iframe_loaded = false; + let result = "passed"; + window.onload = async function () { + const toplevel_loaded = await readValueFromServer(toplevel_loaded_key); + if (!toplevel_loaded.status || !iframe_loaded) + result = "failed"; + writeValueToServer(result_key, result); + } + + iframe.onload = function () { + iframe_loaded = true; + } + })(); +</script> + +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/key-value-store.py b/testing/web-platform/tests/fenced-frame/resources/key-value-store.py new file mode 100644 index 0000000000..c9fd81b2a3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/key-value-store.py @@ -0,0 +1,46 @@ +"""Key-Value store server. + +The request takes "key=" and "value=" URL parameters. The key must be UUID +generated by token(). + +- When only the "key=" is specified, serves a 200 response whose body contains + the stored value specified by the key. If the stored value doesn't exist, + serves a 200 response with an empty body. +- When both the "key=" and "value=" are specified, stores the pair and serves + a 200 response without body. +""" + + +def main(request, response): + key = request.GET.get(b"key") + value = request.GET.get(b"value") + + # Store the value. + # We have two ways to check the truthiness of `value`: + # 1. `if value` + # 2. `if value != None` + # While (1) is the most intuitive, we actually need (2), which is a looser + # check. We need the looser check so that if the URL contains `&value=` to + # set the value equal to the empty string (a case we need to support), this + # condition still evaluates to true and we enter this branch. + if value != None: + # We opted for (2) above which is the looser of the truthiness tests + # that lets empty strings into this branch. So you might think that when + # the URL contains `&value=`, then the `value` variable here would be + # equal `""`, but instead it equals the empty byte string. If you just + # store that empty byte string into the stash and retrieve it later, you + # actually get <Not set> because it doesn't recognize the empty byte + # string as a real value, so we instead have to override it to the empty + # normal string, and then we can store it for later use. This is + # because we have to support storage and retrieval of empty strings. + if type(value) is bytes and value == b'': + value = "" + + request.server.stash.put(key, value) + return (200, [], b"") + + # Get the value. + data = request.server.stash.take(key) + if not data and data != "": + return (200, [], b"<Not set>") + return (200, [], data) diff --git a/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html new file mode 100644 index 0000000000..3c9411c520 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of location.ancestorOrigins</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page the value of `location.ancestorOrigins` correct for: + // 1.) Top-level fenced frames + // 2.) Nested iframes inside a fenced frame + // 3.) Nested fenced frames + const url = new URL(location.href); + + const [location_ao_key, location_ao_ack_key, nested] = parseKeylist(); + + const is_nested_fenced_frame = nested == "nested"; + + // Report `location.ancestorOrigins`. + writeValueToServer(location_ao_key, Array.from(location.ancestorOrigins).join()); + + // If this page is a nested fenced frame, all we need to do is report the + // top-level value. + if (is_nested_fenced_frame) + return; + + // Wait for ACK, so we know that the outer page has read the last value from + // the `location_ao_key` stash and we can write to it again. + await nextValueFromServer(location_ao_ack_key); + + const nested_url = generateURL("location-ancestorOrigins-inner.https.html", + [location_ao_key, location_ao_ack_key, "nested"]); + + // Send `location.ancestorOrigins` from an iframe. + const iframe = document.createElement('iframe'); + iframe.src = nested_url; + const load_promise = new Promise((resolve, reject) => { + iframe.onload = resolve; + iframe.onerror = reject; + }); + document.body.append(iframe); + + // Wait for ACK, so we know that the outer page has read the ancestorOrigins + // from the iframe. + await nextValueFromServer(location_ao_ack_key); + + attachFencedFrame(nested_url); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html new file mode 100644 index 0000000000..f12849c8ec --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Nested fenced frame named navigation helper</title> +<!-- See `navigate-ancestor-from-nested-{fencedframe,iframe}.https.html` for + more documentation --> +<script> + // This is a helper file. It is the document that + // `navigate-ancestor-from-nested{fencedframe,iframe}-helper.https.html` + // explicitly navigates the "correct" ancestor frame to, for any test run by + // `navigate-ancestor-test-runner.https.html`. The test itself is responsible + // for verifying that this document loaded in the correct frame. We just + // simply report that we successfully wound up in this document, to indicate + // that we're finished. + const [navigate_ancestor_key] = parseKeylist(); + writeValueToServer(navigate_ancestor_key, + "navigate-ancestor-destination.https.html"); +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html new file mode 100644 index 0000000000..74800b969f --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<title>Navigate ancestor helper</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> + +<body> +<script> +(async () => { + // This document is loaded into a fenced frame by + // `navigate-ancestor-test-runner.https.html`. It creates a nested fenced + // frame and navigates it to `navigate-ancestor-helper.https.html`. + + // - navigate_ancestor_key: + // Sent by `navigate-ancestor-destination.https.html`. We listen to it, and + // report back to our embedder that it loaded correctly and did not navigate + // *this* frame. + // - navigate_ancestor_from_nested_key: + // Sent by us to our embedder to indicate (depending on the message) either: + // - PASS: The nested fenced frame was navigated correctly when it tried to + // navigate its ancestor (parent or top) frame + // - FAIL: When the nested fenced frame tried to navigate its ancestor + // frame, it actually navigated *this frame*, which is wrong + const [navigate_ancestor_key, navigate_ancestor_from_nested_key, + ancestor_type] = parseKeylist(); + + window.onbeforeunload = e => { + writeValueToServer(navigate_ancestor_from_nested_key, + `FAIL nested fenced frame ${ancestor_type}`); + } + + attachFencedFrame(generateURL(`navigate-ancestor-helper.https.html`, + [navigate_ancestor_key, ancestor_type])); + await nextValueFromServer(navigate_ancestor_key); + window.onbeforeunload = null; + writeValueToServer(navigate_ancestor_from_nested_key, "PASS"); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html new file mode 100644 index 0000000000..63a0cca8b4 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>Navigate ancestor helper from nested fenced frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> + +<body> +<script> +(async () => { + // This document is loaded into a fenced frame by + // `navigate-ancestor-test-runner.https.html`. It creates a nested iframe and + // navigates it to `navigate-ancestor-helper.https.html`. + + // navigate_ancestor_from_nested_key sent by us to our embedder to + // indicate that an message was sent from the nested iframe when it failed to + // navigate the ancestor (this) frame. + const [navigate_ancestor_key, navigate_ancestor_from_nested_key, + ancestor_type] = parseKeylist(); + + // An message should be sent from the iframe. + window.addEventListener('message', (e) => { + window.onbeforeunload = null; + writeValueToServer( + navigate_ancestor_from_nested_key, + `PASS: [${ancestor_type}] ${e.data}`); + }); + + // When the iframe tries to navigate its ancestor frame, it should not + // navigate *this* frame, because the sandboxed navigation browsing context + // flag [1] must be set in fenced frame trees. + // [1] https://html.spec.whatwg.org/multipage/origin.html#sandboxed-navigation-browsing-context-flag + const iframe = document.createElement('iframe'); + iframe.src = generateURL(`navigate-ancestor-helper.https.html`, + [navigate_ancestor_key, ancestor_type]); + document.body.append(iframe); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html new file mode 100644 index 0000000000..2cd8fcf786 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<title>Navigate ancestor helper</title> + +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<body> +<script> +(async () => { + // This document is loaded into either a top-level fenced frame, a nested + // fenced frame, or an iframe in a top-level fenced frame. In any case, this + // document is always the inner-most document in any test. It navigates an + // ancestor frame by clicking the anchor above via script. When this document + // is loaded in a fenced frame, the frame that should actually navigate is + // this one, since fenced frames are top-level browsing contexts. + // When this document is loaded into a top-level fenced frame or a nested + // fenced frame, we test that the right frame is navigated in + // `navigate-ancestor-test-runner.https.html`. When this document is loaded + // into an iframe in a top-level fenced frame, we test that the navigation is + // blocked due to the sandbox behavior of fenced frame trees. + const [navigate_ancestor_key, ancestor_type] = parseKeylist(); + const url = generateURL(`navigate-ancestor-destination.https.html`, + [navigate_ancestor_key]); + await simulateGesture(); + try { + window[ancestor_type].location = url; + } catch (e) { + window[ancestor_type].postMessage('location change failed.'); + } +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.js b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.js new file mode 100644 index 0000000000..6178ea78da --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.js @@ -0,0 +1,28 @@ + +async function runNavigateAncestorTest(test_type, ancestor_type) { + // See documentation in `resources/navigate-ancestor-test-runner.https.html`. + // For each test type here, this document opens a new auxiliary window that + // runs the actual test. The tests in some way or another, direct a frame + // *inside* a fenced frame to navigate an ancestor frame via an + // <a target="_parent|_top"></a>. We need to run the real test in a new window + // so that if that window ends up navigating unexpectedly (because the fenced + // frame can accidentally navigated its embedder, for example) we can detect + // it from ths page, which never navigates away. + const navigate_ancestor_key = token(); + const navigate_ancestor_from_nested_key = token(); + + const win = window.open(generateURL( + "resources/navigate-ancestor-test-runner.https.html", + [navigate_ancestor_key, navigate_ancestor_from_nested_key])); + await new Promise(resolve => { + win.onload = resolve; + }); + + const unloadPromise = new Promise(resolve => { + win.onunload = resolve; + }); + + await win.runTest(test_type, ancestor_type); + win.close(); + await unloadPromise; +} diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-test-runner.https.html b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-test-runner.https.html new file mode 100644 index 0000000000..d0f2e8d694 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-test-runner.https.html @@ -0,0 +1,76 @@ +<!DOCTYPE html> +<title>Test navigating an ancestor frame from within a fenced frame</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> + +<body> +<script> +// This function is called by `window.opener`, which is a same-origin window. +window.runTest = function(test_type, ancestor_type) { + // Messages by this key are sent from + // `navigate-ancestor-destination.https.html` to let us know if the "_parent" + // navigations performed inside fenced frames landed on the right page. + // If somehow *this document* gets navigated unexpectedly, the test will fail + // given `beforeunloadPromise` below. + // For "nested" tests, this document hosts a top-level fenced frame navigated + // to `navigate-ancestor-from-nested-{fenced-frame,iframe}.https.html`, + // which itself hosts a nested fenced frame or iframe. The top-level fenced + // frame will wait for the right confirmation that the nested document has + // operated correctly, and report back to *us* that everything is OK via this + // key below. + const [navigate_ancestor_key, navigate_ancestor_from_nested_key] = + parseKeylist(); + + const beforeunloadPromise = new Promise((resolve, reject) => { + window.onbeforeunload = e => { + reject(`The top-level test runner document does not navigate when a ` + + `${test_type} navigates ${ancestor_type}`); + } + }); + + let test_promise = null; + switch (test_type) { + case 'top-level fenced frame': + // This fenced frame will attempt to navigate its parent to + // `navigate-ancestor-destination.https.html`. It should end up navigating + // *itself* since it is a top-level browsing context. Just in case it + // accidentally navigates *this* frame, we have an `onbeforeunload` + // handler that will automatically fail the test before. + attachFencedFrame(generateURL( + `navigate-ancestor-helper.https.html`, + [navigate_ancestor_key, ancestor_type])); + test_promise = nextValueFromServer(navigate_ancestor_key); + break; + case 'nested fenced frame': + attachFencedFrame(generateURL( + `navigate-ancestor-from-nested-fenced-frame.https.html`, + [navigate_ancestor_key, navigate_ancestor_from_nested_key, + ancestor_type])); + test_promise = nextValueFromServer(navigate_ancestor_from_nested_key) + .then(message => { + if (message != "PASS") { + throw message; + } + }); + break; + case 'nested iframe': + attachFencedFrame(generateURL( + `navigate-ancestor-from-nested-iframe.https.html`, + [navigate_ancestor_key, navigate_ancestor_from_nested_key, + ancestor_type])); + test_promise = nextValueFromServer(navigate_ancestor_from_nested_key) + .then(message => { + if (message != `PASS: [${ancestor_type}] location change failed.`) { + throw message; + } + }); + + break; + } + + return Promise.race([test_promise, beforeunloadPromise]); +} +</script> + +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html new file mode 100644 index 0000000000..c7d7d6f278 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame hosting named frames</title> + +<body> +<script> +async function init() { + // This file is meant to run in a <fencedframe>. It sets up multiple frames + // all with the name `target_frame` in the following arrangements: + // 1.) A top-level fenced frame + // 2.) An iframe within a fenced frame + // 3.) A nested fenced frame + // Navigations to all of the above should fail, and thus should open a new + // top-level popup window instead of navigating these frames. + + const [ready_for_navigation_key, test_type] = parseKeylist(); + + switch (test_type) { + case "top-level fenced frame": + // Set up the named frame and report to the outer document that we're ready + // for it to try and navigate the named frame. + window.name = "target_frame"; + writeValueToServer(ready_for_navigation_key, "READY"); + break; + case "nested iframe": + const iframe = document.createElement('iframe'); + iframe.name = "target_frame"; + document.body.append(iframe); + writeValueToServer(ready_for_navigation_key, "READY"); + break; + case "nested fenced frame": + // This fenced frame will report to the outermost document when it is ready. + const ff = + attachFencedFrame(generateURL( + "fenced-frame-set-name-and-report-ready-for-" + + "outermost-document-to-navigate.html", + [ready_for_navigation_key])); + break; + } +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html new file mode 100644 index 0000000000..d3bd955697 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Navigate reporting helper</title> +<!-- This document is used as a helper by `../navigate-by-name.html`. That test + attempts to navigate various frames all named `target_frame`, to this + document. All of these navigations should fail, due to the frames being + unreachable to the initiator (because of the "fence" of the fenced frame). + As a result, this document should always load in a new top-level + "outermost" pop-up window. +--> + +<script> +const [navigation_success_key] = parseKeylist(); + +// We're currently using `window.opener` as a proxy for "did this load in a new +// outermost popup window?". Note that if we try and test navigations initiated +// from inside a fenced frame and they open up in a new outermost popup, there +// will be no opener by default (crbug.com/1250694) so using `window.opener` as +// a signal will be insufficient. In order to test anchor navigations to this +// document from within a fenced frame, we'll need a better signal for +// outermost-ness. We should consider adding a value to `document.loadingMode` +// for fenced frames: +// https://wicg.github.io/nav-speculation/prerendering.htmlprerendering.html#browsing-context-loading-mode. +// +// Alternatively if we really want to detect if this loaded inside a fenced +// frame, we could just remove the opt-in headers and then implementations that +// support fenced frame opt-ins would timeout if they somehow don't honor the +// fence on named frame navigations, but that's not a very good outcome. +if (window.opener) { + writeValueToServer(navigation_success_key, "PASS"); +} else { + writeValueToServer(navigation_success_key, "FAIL"); +} +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html b/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html new file mode 100644 index 0000000000..85c5194c6c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Navigate a fenced frame to a nested config</title> +<body> +<script> + const [key] = parseKeylist(); + const configs = window.fence.getNestedConfigs(); + const ff = document.createElement("fencedframe"); + ff.config = configs[0] + document.body.appendChild(ff); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html new file mode 100644 index 0000000000..59170c7512 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of navigator.keyboard.getLayoutMap</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to be navigated to from a <fencedframe> element. It + // reports back to the page hosting the <fencedframe> whether or not + // `keyboard.getLayoutMap` is allowed. + const keyboard_layout_key = parseKeylist(); + // Report whether or not `navigator.keyboard.getLayoutMap()` is allowed. + navigator.keyboard.getLayoutMap().then( + () => { writeValueToServer(keyboard_layout_key, "resolved"); }, + () => { writeValueToServer(keyboard_layout_key, "rejected");}, + ); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html new file mode 100644 index 0000000000..105166c7ad --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of navigator.keyboard.lock</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to be navigated to from a <fencedframe> element. It + // reports back to the page hosting the <fencedframe> whether or not + // `keyboard.lock` is allowed. + const [keyboard_lock_key] = parseKeylist(); + // Report whether or not `navigator.keyboard.lock()` is allowed. + navigator.keyboard.lock().then( + () => { writeValueToServer(keyboard_lock_key, "resolved"); }, + () => { writeValueToServer(keyboard_lock_key, "rejected");}, + ); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/notification-sw.js b/testing/web-platform/tests/fenced-frame/resources/notification-sw.js new file mode 100644 index 0000000000..e9b1e2b9dd --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/notification-sw.js @@ -0,0 +1,20 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async event => { + const method = event.data; + + if (method === 'constructor') { + try { + new Notification('test'); + } catch (e) { + event.source.postMessage(e.message); + } + } else if (method === 'showNotification') { + try { + await self.registration.showNotification('test', {body: 'test'}); + } catch (e) { + event.source.postMessage(e.message); + } + } +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/opaque-ad-sizes-utils.js b/testing/web-platform/tests/fenced-frame/resources/opaque-ad-sizes-utils.js new file mode 100644 index 0000000000..edf8640f20 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/opaque-ad-sizes-utils.js @@ -0,0 +1,47 @@ +async function runOpaqueAdSizesTest(input_width, input_height, output_width, output_height) { + // Attach a FLEDGE fenced frame whose outer container has dimensions + // `input_width` by `input_height`. + const frame = await attachFencedFrameContext({ + generator_api: "fledge", resolve_to_config: true, attributes: [ + ["width", input_width], ["height", input_height]]}); + + const assert_dimensions = + (label, input_width, input_height, output_width, output_height) => { + assert_equals(getComputedStyle(document.documentElement).width, + output_width+"px", + label + " the computed width coerces to " + output_width); + assert_equals(window.innerWidth, output_width, + label + " the innerWidth " + input_width + " coerces to " + output_width); + assert_equals(window.innerHeight, output_height, + label + " the innerHeight " + input_height + " coerces to " + output_height); + } + + // Assert that the fenced frame sees its dimensions rounded to the nearest + // ad size. + await frame.execute(assert_dimensions, + ["After navigation", input_width, input_height, output_width, output_height]); + + // Assert that the embedder sees the fenced frame's original dimensions. + assert_equals(frame.width, input_width.toString(), + "The outer container width is the requested width."); + assert_equals(frame.height, input_height.toString(), + "The outer container height is the requested height."); + + // Resize the fenced frame's outer container. + const new_size_x = 320; + const new_size_y = 50; + frame.width = new_size_x; + frame.height = new_size_y; + + // Refresh the fenced frame. + await frame.execute(() => { + window.executor.suspend(() => { + location.href = location.href; + }); + }); + + // Observe that navigations after the first don't change the fenced frame's + // inner dimensions. + await frame.execute(assert_dimensions, + ["After resizing", input_width, input_height, output_width, output_height]); +} diff --git a/testing/web-platform/tests/fenced-frame/resources/payment-handler-sw.js b/testing/web-platform/tests/fenced-frame/resources/payment-handler-sw.js new file mode 100644 index 0000000000..8b5e83cddf --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/payment-handler-sw.js @@ -0,0 +1,10 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', event => { + try { + self.registration.paymentManager; + } catch (e) { + event.source.postMessage(e); + } +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html b/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html new file mode 100644 index 0000000000..06724ac061 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.permissions.query</title> + +<body> + <script> + (async () => { + const [permission_key, permission_name] = parseKeylist(); + // Push permission without userVisibleOnly:true is not supported. + let user_visible_only = permission_name === 'push' ? true : false; + const result = await navigator.permissions.query({ name: permission_name, userVisibleOnly: user_visible_only }); + writeValueToServer(permission_key, `result: ${result.state}`); + })(); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html new file mode 100644 index 0000000000..07c3e662bf --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.geolocation.getCurrentPosition()</title> + +<body> +<script> +(async () => { + const [permission_geolocation_key] = parseKeylist(); + const result = await new Promise(resolve => { + navigator.geolocation.getCurrentPosition( + () => resolve('granted'), () => resolve('denied')); + }); + writeValueToServer(permission_geolocation_key, `result: ${result}`); +})(); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-test-runner.html b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-test-runner.html new file mode 100644 index 0000000000..724a35ce9a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-geolocation-test-runner.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<title>Fenced frame content to report the result of navigator.geolocation.getCurrentPosition()</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> + +<body> +<script> + +window.runTest = async (fenced_frame_url) => { + const [permission_geolocation_key] = parseKeylist(); + await test_driver.set_permission({name: 'geolocation'}, 'granted', true); + + attachFencedFrame(generateURL(fenced_frame_url, [permission_geolocation_key])); + const actual_result = await nextValueFromServer(permission_geolocation_key); + + assert_equals( + actual_result, 'result: denied', + 'geolocation permission is not permitted for fenced frames.'); +}; +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html b/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html new file mode 100644 index 0000000000..d01d10034c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of Notification.requestPermission</title> + +<body> +<script> +(async () => { + const [permission_notification_key] = parseKeylist(); + const result = await Notification.requestPermission(); + writeValueToServer(permission_notification_key, `result: ${result}`); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/popup-noopener-destination.html b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-destination.html new file mode 100644 index 0000000000..30cc21f22c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-destination.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Destination page opened by a frame in a Fenced Frame tree</title> +<script> + // It is the document that `popup-noopener-inner.html` loads in a new + // window/tab from a root fenced frame, an iframe in a fenced frame and from + // a nested fenced frame. It's expected that any popup opened from a Fenced + // Frame tree cannot reach the opener. + const [popup_noopener_key, popup_name_key] = parseKeylist(); + if (window.opener) { + writeValueToServer(popup_noopener_key, "FAIL: window.opener is not null"); + } else { + writeValueToServer(popup_noopener_key, "PASS"); + } + if (window.name) { + writeValueToServer(popup_name_key, "FAIL: window.name is not empty"); + } else { + writeValueToServer(popup_name_key, "PASS"); + } +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html new file mode 100644 index 0000000000..6a79fd21b2 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame creating popups</title> + +<body> +<script> +async function init() { + // This file is meant to run in a <fencedframe>. It sets up multiple frames + // in the following arrangements: + // 1.) A top-level fenced frame + // 2.) An iframe within a fenced frame + // 3.) A nested fenced frame + // All of the above frames create a popup which should not return a reference + // to the new window. + + const [popup_noopener_key, popup_openee_key, popup_name_key, test_type] = + parseKeylist(); + + switch (test_type) { + case "top-level fenced frame": + src_popup = generateURL(`popup-noopener-destination.html`, + [popup_noopener_key, popup_name_key]); + const popup = window.open(src_popup, "foo"); + if (popup) { + writeValueToServer(popup_openee_key, "FAIL"); + } else { + writeValueToServer(popup_openee_key, "PASS"); + } + break; + case "nested iframe": + const iframe = document.createElement('iframe'); + document.body.append(iframe); + iframe.src = generateURL(`create-popup.html`, + [popup_noopener_key, popup_openee_key, popup_name_key]); + break; + case "nested fenced frame": + const ff = + attachFencedFrame(generateURL(`create-popup.html`, + [popup_noopener_key, popup_openee_key, popup_name_key])); + break; + } + +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html b/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html new file mode 100644 index 0000000000..a523ef31c1 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of prerendering</title> + +<body> +<script> +(() => { + [prerender_ready_key, prerender_loaded_key, prerender_activated_key] = + parseKeylist(); + document.addEventListener('prerenderingchange', () => { + writeValueToServer(prerender_activated_key, 'activated'); + }); + if (document.prerendering) { + writeValueToServer(prerender_ready_key, 'ready'); + } else { + writeValueToServer(prerender_loaded_key, 'loaded'); + } +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/prerender-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html b/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html new file mode 100644 index 0000000000..2e170dd91b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of navigator.presentation.receiver</title> + +<body> +<script> +(async () => { + const [presentation_receiver_key] = parseKeylist(); + const result = await navigator.presentation.receiver; + if (result == null) { + writeValueToServer(presentation_receiver_key, "denied"); + } else { + writeValueToServer(presentation_receiver_key, "allowed"); + } +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/remote-context-executor.https.html b/testing/web-platform/tests/fenced-frame/resources/remote-context-executor.https.html new file mode 100644 index 0000000000..6b2f5ccc00 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/remote-context-executor.https.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<title>Script to wait for instructions from RemoteContext.</title> + +<body> +<script> +window.addEventListener("load", async () => { + // Find the uuid to communicate with the parent. + const uuid = new URLSearchParams(window.location.search).get('uuid'); + + // Wait for the window to have its size computed and become visible, + // so that simulated user gestures will be handled properly. + while (window.innerWidth == 0) { + await new Promise(resolve => requestAnimationFrame(resolve)); + } + + // Create a RemoteContext Executor, which will wait in the background + // for scripts to execute. + window.executor = new Executor(uuid); +}); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/report-url.html b/testing/web-platform/tests/fenced-frame/resources/report-url.html new file mode 100644 index 0000000000..e0b7d0982a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/report-url.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>A page embedded as a fenced frame that reports the document URL</title> +<script> +const [uuid] = parseKeylist(); +writeValueToServer(uuid, location.href); +</script> diff --git a/testing/web-platform/tests/fenced-frame/resources/report-url.html.headers b/testing/web-platform/tests/fenced-frame/resources/report-url.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/report-url.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html new file mode 100644 index 0000000000..cb17789b45 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> + <script src="utils.js"></script> + <title>Fenced frame content to report any changes in inner dimensions</title> + + <style> + body { + width: 500px; + height: 500px; + background: grey; + } + </style> + <body> + <script> + let eventCount = 0; + async function init() { + const [resize_lock_inner_page_is_ready_key, + resize_lock_resize_is_done_key, + resize_lock_report_click_location_key, + resize_lock_report_click_location_key_after_resize, + resize_lock_report_click_location_key_after_resize_2] = parseKeylist(); + + window.addEventListener('mousedown', async (event) => { + eventCount++; + let point = event.clientX + "," + event.clientY; + if (eventCount == 1) { + writeValueToServer(resize_lock_report_click_location_key, point); + await nextValueFromServer(resize_lock_resize_is_done_key) + } else if (eventCount == 2) { + writeValueToServer( + resize_lock_report_click_location_key_after_resize, point); + } else { + writeValueToServer( + resize_lock_report_click_location_key_after_resize_2, point); + } + }); + + writeValueToServer(resize_lock_inner_page_is_ready_key, "ready"); + } + + init(); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html.headers b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner-input.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html new file mode 100644 index 0000000000..fbaf436330 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> + <script src="utils.js"></script> + <title>Fenced frame content to report any changes in inner dimensions</title> + + <body> + <script> + async function init() { + const [resize_lock_inner_page_is_ready_key, + resize_lock_resize_is_done_key, + resize_lock_report_inner_dimensions_key] = parseKeylist(); + + writeValueToServer(resize_lock_inner_page_is_ready_key, "ready"); + + await nextValueFromServer(resize_lock_resize_is_done_key); + + const response = window.innerWidth + "x" + window.innerHeight; + writeValueToServer(resize_lock_report_inner_dimensions_key, response); + } + + init(); + </script> + </body> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/response-204.py b/testing/web-platform/tests/fenced-frame/resources/response-204.py new file mode 100644 index 0000000000..e6cf8d4ac9 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/response-204.py @@ -0,0 +1,4 @@ +def main(request, response): + response_headers = [] + body = "No content" + return (204, response_headers, body)
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-iframe.sub.html b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-iframe.sub.html new file mode 100644 index 0000000000..7ee8b7d98f --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-iframe.sub.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<title>Iframe content to load a fenced frame and report a value to the server</title> +<script src="utils.js"></script> + +<body> +<script> + const fencedframe = document.createElement("fencedframe"); + fencedframe.config = new FencedFrameConfig( + generateURL("sandbox-mandatory-flags-inner.sub.html?key={{GET[key]}}" + + "&value={{GET[value]}}", [])); + document.body.appendChild(fencedframe); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html new file mode 100644 index 0000000000..5f400b5bde --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<title>Fenced frame content to report a value to the server</title> + +<body> +<img src="key-value-store.py?key={{GET[key]}}&value={{GET[value]}}"> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html.headers b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-looser-restriction.sub.html b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-looser-restriction.sub.html new file mode 100644 index 0000000000..0ad64c1a5c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-looser-restriction.sub.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<title>Iframe content to load a nested sandboxed iframe with all mandatory allow-* flags</title> + +<body> +<iframe + src="sandbox-mandatory-flags-iframe.sub.html?key={{GET[key]}}&value={{GET[value]}}" + sandbox="allow-same-origin + allow-forms + allow-scripts + allow-popups + allow-popups-to-escape-sandbox + allow-top-navigation-by-user-activation"> +</iframe> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html new file mode 100644 index 0000000000..f3bcbc8ba1 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<script src="sandboxed-features.js"></script> +<body> +<script> +(async () => { + try { + await {{GET[test_func]}}(); + } catch (e) { + writeValueToServer('{{GET[key]}}', e.message); + return; + } + writeValueToServer('{{GET[key]}}', 'done'); +})() +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html.headers b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html new file mode 100644 index 0000000000..44584440e1 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<body> +<iframe + src="sandboxed-features-inner.sub.html?key={{GET[key]}}&test_func={{GET[test_func]}}" + sandbox="allow-forms + allow-modals + allow-orientation-lock + allow-pointer-lock + allow-popups + allow-popups-to-escape-sandbox + allow-presentation + allow-same-origin + allow-scripts + allow-top-navigation + allow-top-navigation-by-user-activation + allow-downloads"> +</iframe> + +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html.headers b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/sandboxed-features.js b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features.js new file mode 100644 index 0000000000..1cbd4a48f3 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/sandboxed-features.js @@ -0,0 +1,126 @@ +const run_in_fenced_frame = (func_name, description, is_nested) => { + promise_test(async test => { + const key = token(); + const url = is_nested ? + 'resources/sandboxed-features-looser-restriction.sub.html?' : + 'resources/sandboxed-features-inner.sub.html?'; + let params = new URLSearchParams(); + params.set('key', key); + params.set('test_func', func_name); + const frame = document.createElement('fencedframe'); + const frame_url = 'resources/sandboxed-features-inner.sub.html?' + + params.toString(); + const config = new FencedFrameConfig(generateURL(frame_url, [])); + frame.config = config; + test.add_cleanup(() => { + frame.remove(); + }); + document.body.appendChild(frame); + assert_equals(await nextValueFromServer(key), 'done'); + }, description); +}; + +const run_sanboxed_feature_test = (func_name, description) => { + run_in_fenced_frame(func_name, description, false); + run_in_fenced_frame(func_name, description + '[looser sandboxed]', true); +}; + +async function test_prompt() { + assert_equals( + window.prompt('Test prompt'), + null, + 'window.prompt() must synchronously return null in a fenced frame without' + + ' blocking on user input.'); +} + +async function test_alert() { + assert_equals( + window.alert('Test alert'), + undefined, + 'window.alert() must synchronously return undefined in a fenced frame' + + ' without blocking on user input.'); +} + +async function test_confirm() { + assert_equals( + window.confirm('Test confirm'), + false, + 'window.confirm() must synchronously return false in a fenced frame' + + ' without blocking on user input.'); +} + +async function test_print() { + assert_equals( + window.print(), + undefined, + 'window.print() must synchronously return undefined in a fenced frame' + + ' without blocking on user input.'); + + assert_equals( + document.execCommand('print', false, null), + false, + 'execCommand(\'print\') must synchronously return false in a fenced frame' + + ' without blocking on user input.'); +} + +async function test_document_domain() { + assert_throws_dom('SecurityError', () => { + document.domain = 'example.test'; + }); + assert_throws_dom('SecurityError', () => { + document.domain = document.domain; + }); + assert_throws_dom('SecurityError', () => { + (new Document).domain = document.domain; + }); + assert_throws_dom('SecurityError', () => { + document.implementation.createHTMLDocument().domain = document.domain; + }); + assert_throws_dom('SecurityError', () => { + document.implementation.createDocument(null, '').domain = document.domain; + }); + assert_throws_dom('SecurityError', () => { + document.createElement('template').content.ownerDocument.domain = + document.domain; + }); +} + +async function test_presentation_request() { + assert_throws_dom('SecurityError', () => { + new PresentationRequest([location.href]); + }); +} + +async function test_screen_orientation_lock() { + try { + await screen.orientation.lock('portrait'); + } catch (e) { + assert_equals( + e.name, + 'SecurityError', + 'orientation.lock() must throw a SecurityError in a fenced frame.'); + return; + } + assert_unreached('orientation.lock() must throw an error'); +} + +async function test_pointer_lock() { + await simulateGesture(); + + const canvas = document.createElement('canvas'); + document.body.appendChild(canvas); + const pointerlockerror_promise = new Promise(resolve => { + document.addEventListener('pointerlockerror', resolve); + }); + try { + await canvas.requestPointerLock(); + } catch (e) { + assert_equals( + e.name, + 'SecurityError', + 'orientation.lock() must throws a SecurityError in a fenced frame.'); + await pointerlockerror_promise; + return; + } + assert_unreached('requestPointerLock() must fail in a fenced frame'); +} diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html new file mode 100644 index 0000000000..02f28bd82e --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<script src="utils.js"></script> + +<body> + <script type="module"> + // Ask the worker to do a fetch request that will be handled by the service + // worker via postMessage. + const checkIfServiceWorkerCanControlWebWorker = async () => { + const dedicated_worker = new Worker('serviceWorker-dedicated-worker.js'); + return new Promise((resolve, reject) => { + dedicated_worker.addEventListener('message', e => { + resolve(e.data) + }); + dedicated_worker.postMessage('fetch'); + }) + } + + const [key] = parseKeylist(); + const url = new URL(location.href); + if (url.searchParams.get('useServiceWorkerInFencedFrame')) { + await navigator.serviceWorker.register('serviceWorker-dedicated-worker-sw.js'); + await navigator.serviceWorker.ready; + } + + const result = await checkIfServiceWorkerCanControlWebWorker(); + writeValueToServer(key, result); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js new file mode 100644 index 0000000000..027995a218 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js @@ -0,0 +1,18 @@ +self.addEventListener('fetch', async (e) => { + if (e.request.url.includes('fenced_frame_dedicated_worker_test')) { + e.respondWith(new Response('OK')); + return; + } + + e.respondWith(fetch(e.request).catch(() => { + return new Response('not found'); + })); +}) + +self.addEventListener('install', () => { + return self.skipWaiting(); +}); + +self.addEventListener('activate', () => { + return self.clients.claim(); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js.headers new file mode 100644 index 0000000000..d0b9633bb0 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js.headers @@ -0,0 +1 @@ +Service-Worker-Allowed: / diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker.js b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker.js new file mode 100644 index 0000000000..8a9fa5ef36 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker.js @@ -0,0 +1,8 @@ +self.addEventListener('message', async (e) => { + if (e.data === 'fetch') { + // Send a request to non-existing URL but handled by SW. + const res = await fetch('./fenced_frame_dedicated_worker_test'); + const data = res.ok ? await res.text() : res.statusText; + self.postMessage(data); + } +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html new file mode 100644 index 0000000000..103236e52a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<body> +<script> + +function getFrameType(service_worker, url) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + channel.port1.onmessage = e => { + resolve(e.data); + }; + service_worker.postMessage({port:channel.port2, url:url}, + [channel.port2]); + }); +} + +(async function() { + await navigator.serviceWorker.register('serviceWorker-frameType.js'); + const registration = await navigator.serviceWorker.ready; + const service_worker = registration.active; + + const [frame_type_key, frame_type_ack_key] = parseKeylist(); + + const frame_type = await getFrameType(service_worker, location.href); + writeValueToServer(frame_type_key, frame_type); + + // Wait for ACK, so we know that the outer page has read the last value from + // the `serviceWorker.frameType` stash and we can write to it again. + await nextValueFromServer(frame_type_ack_key); + + const iframe = document.createElement('iframe'); + iframe.src = generateURL("serviceWorker-frameType-nested.html", + [frame_type_key]); + document.body.append(iframe); +})(); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html new file mode 100644 index 0000000000..10bb7ff8bd --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<body> +<script> + +function getFrameType(service_worker, url) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); + channel.port1.onmessage = e => { + resolve(e.data); + }; + service_worker.postMessage({port:channel.port2, url:url}, + [channel.port2]); + }); +} + +(async function() { + const service_worker = navigator.serviceWorker.controller; + const frame_type = await getFrameType(service_worker, location.href); + + const [frame_type_key] = parseKeylist(); + writeValueToServer(frame_type_key, frame_type); +})(); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType.js b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType.js new file mode 100644 index 0000000000..91003fc131 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType.js @@ -0,0 +1,19 @@ +self.onmessage = function(e) { + var port = e.data.port; + var url = e.data.url; + + e.waitUntil(self.clients.matchAll({includeUncontrolled: true}) + .then(function(clients) { + var frame_type = "none"; + for (client of clients) { + if (client.url === url) { + frame_type = client.frameType; + break; + } + } + port.postMessage(frame_type); + }) + .catch(e => { + port.postMessage('clients.matchAll() rejected: ' + e); + })); +};
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html new file mode 100644 index 0000000000..4d77d9e9a6 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<body> +<script> + +(async function() { + const [navigate_key] = parseKeylist(); + writeValueToServer(navigate_key, 'success'); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html new file mode 100644 index 0000000000..aaf330f4f6 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<body> +<script> + +(async function() { + await navigator.serviceWorker.register('serviceWorker-navigate.js'); + const registration = await navigator.serviceWorker.ready; + const service_worker = registration.active; + + const [navigate_key] = parseKeylist(); + + service_worker.postMessage({key:navigate_key, url:location.href}); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate.js b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate.js new file mode 100644 index 0000000000..a7a4db52ee --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate.js @@ -0,0 +1,18 @@ +self.importScripts('utils.js'); + +self.onmessage = function(e) { + var key = e.data.key; + var url = e.data.url; + + e.waitUntil(self.clients.claim().then(() => { + return self.clients.matchAll({type: 'window'}); + }).then(clients => { + return clients.map(client => { + // Check to make sure WindowClient.navigate() is supported. + if (client.url === url) { + return client.navigate(generateURL('serviceWorker-navigate-inner-success.html', + [key])); + } + }); + })); +}; diff --git a/testing/web-platform/tests/fenced-frame/resources/serviceWorker-push-sw.js b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-push-sw.js new file mode 100644 index 0000000000..e344b45fd8 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/serviceWorker-push-sw.js @@ -0,0 +1,19 @@ +self.addEventListener('install', e => e.waitUntil(skipWaiting())); +self.addEventListener('activate', e => e.waitUntil(clients.claim())); + +self.addEventListener('message', async e => { + const method = e.data; + + const promise = method === 'subscribe' ? + self.registration.pushManager.subscribe({userVisibleOnly: true}) : + Promise.resolve(); + const message = await promise + .then(() => { + return `${method}: Unexpectedly started`; + }) + .catch((e) => { + return e.message; + }); + + e.source.postMessage(message); +}); diff --git a/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html new file mode 100644 index 0000000000..1cf3fc8680 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame accessing cookies</title> + +<body> +<script> +async function init() { + // This file is meant to run in a <fencedframe>. It sets up multiple frames + // in the following arrangements: + // 1.) A top-level fenced frame + // 2.) An iframe within a fenced frame + // 3.) A nested fenced frame + + // Set cookies in the root fenced frame via document and cookieStore APIs. + const [cookie_value_key, test_type] = parseKeylist(); + document.cookie = 'C=fenced; SameSite=Lax'; + document.cookie = 'D=fenced; SameSite=None; Secure'; + await cookieStore.set('E', 'fenced'); + + const cookie_access_url = generateURL("cookie-access.https.html", + [cookie_value_key]); + + switch (test_type) { + case "top-level fenced frame": + const cookie_value = document.cookie; + writeValueToServer(cookie_value_key, cookie_value); + break; + case "nested iframe": + const iframe = document.createElement('iframe'); + document.body.append(iframe); + iframe.src = cookie_access_url; + break; + case "nested fenced frame": + const ff = attachFencedFrame(cookie_access_url); + break; + } +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html.headers new file mode 100644 index 0000000000..e2b453f463 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html.headers @@ -0,0 +1,2 @@ +Supports-Loading-Mode: fenced-frame +Set-Cookie: F=fenced; SameSite=Lax diff --git a/testing/web-platform/tests/fenced-frame/resources/utils.js b/testing/web-platform/tests/fenced-frame/resources/utils.js new file mode 100644 index 0000000000..4638f37cbb --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/utils.js @@ -0,0 +1,501 @@ +const STORE_URL = '/fenced-frame/resources/key-value-store.py'; +const BEACON_URL = '/fenced-frame/resources/automatic-beacon-store.py'; +const REMOTE_EXECUTOR_URL = '/fenced-frame/resources/remote-context-executor.https.html'; +const FLEDGE_BIDDING_URL = '/fenced-frame/resources/fledge-bidding-logic.js'; +const FLEDGE_BIDDING_WITH_SIZE_URL = '/fenced-frame/resources/fledge-bidding-logic-with-size.js'; +const FLEDGE_DECISION_URL = '/fenced-frame/resources/fledge-decision-logic.js'; + +// Creates a URL that includes a list of stash key UUIDs that are being used +// in the test. This allows us to generate UUIDs on the fly and let anything +// (iframes, fenced frames, pop-ups, etc...) that wouldn't have access to the +// original UUID variable know what the UUIDs are. +// @param {string} href - The base url of the page being navigated to +// @param {string list} keylist - The list of key UUIDs to be used. Note that +// order matters when extracting the keys +function generateURL(href, keylist) { + const ret_url = new URL(href, location.href); + ret_url.searchParams.append("keylist", keylist.join(',')); + return ret_url; +} + +function getRemoteContextURL(origin) { + return new URL(REMOTE_EXECUTOR_URL, origin); +} + +async function runSelectRawURL(href, resolve_to_config = false) { + try { + await sharedStorage.worklet.addModule( + "/shared-storage/resources/simple-module.js"); + } catch (e) { + // Shared Storage needs to have a module added before we can operate on it. + // It is generated on the fly with this call, and since there's no way to + // tell through the API if a module already exists, wrap the addModule call + // in a try/catch so that if it runs a second time in a test, it will + // gracefully fail rather than bring the whole test down. + } + return await sharedStorage.selectURL( + 'test-url-selection-operation', [{url: href}], { + data: {'mockResult': 0}, + resolveToConfig: resolve_to_config, + keepAlive: true + }); +} + +// Similar to generateURL, but creates +// 1. An urn:uuid if `resolve_to_config` is false. +// 2. A fenced frame config object if `resolve_to_config` is true. +// This relies on a mock Shared Storage auction, since it is the simplest +// WP-exposed way to turn a url into an urn:uuid or a fenced frame config. +// Note: this function, unlike generateURL, is asynchronous and needs to be +// called with an await operator. +// @param {string} href - The base url of the page being navigated to +// @param {string list} keylist - The list of key UUIDs to be used. Note that +// order matters when extracting the keys +// @param {boolean} [resolve_to_config = false] - Determines whether the result +// of `sharedStorage.selectURL()` +// is an urn:uuid or a fenced +// frame config. +// Note: +// 1. There is a limit of 3 calls per origin per pageload for +// `sharedStorage.selectURL()`, so `runSelectURL()` must also respect this +// limit. +// 2. If `resolve_to_config` is true, blink feature `FencedFramesAPIChanges` +// needs to be enabled for `selectURL()` to return a fenced frame config. +// Otherwise `selectURL()` will fall back to the old behavior that returns an +// urn:uuid. +async function runSelectURL(href, keylist = [], resolve_to_config = false) { + const full_url = generateURL(href, keylist); + return await runSelectRawURL(full_url, resolve_to_config); +} + +async function generateURNFromFledgeRawURL(href, + nested_urls, + resolve_to_config = false, + ad_with_size = false, + requested_size = null) { + const bidding_token = token(); + const seller_token = token(); + + const ad_components_list = nested_urls.map((url) => { + return ad_with_size ? + { renderUrl: url, sizeGroup: "group1" } : + { renderUrl: url } + }); + + const interestGroup = ad_with_size ? + { + name: 'testAd1', + owner: location.origin, + biddingLogicUrl: new URL(FLEDGE_BIDDING_WITH_SIZE_URL, location.origin), + ads: [{ renderUrl: href, sizeGroup: "group1", bid: 1 }], + userBiddingSignals: { biddingToken: bidding_token }, + trustedBiddingSignalsKeys: ['key1'], + adComponents: ad_components_list, + adSizes: { "size1": { width: "100px", height: "50px" } }, + sizeGroups: { "group1": ["size1"] } + } : + { + name: 'testAd1', + owner: location.origin, + biddingLogicUrl: new URL(FLEDGE_BIDDING_URL, location.origin), + ads: [{ renderUrl: href, bid: 1 }], + userBiddingSignals: { biddingToken: bidding_token }, + trustedBiddingSignalsKeys: ['key1'], + adComponents: ad_components_list, + }; + + // Pick an arbitrarily high duration to guarantee that we never leave the + // ad interest group while the test runs. + navigator.joinAdInterestGroup(interestGroup, /*durationSeconds=*/3000000); + + let auctionConfig = { + seller: location.origin, + interestGroupBuyers: [location.origin], + decisionLogicUrl: new URL(FLEDGE_DECISION_URL, location.origin), + auctionSignals: {biddingToken: bidding_token, sellerToken: seller_token}, + resolveToConfig: resolve_to_config + }; + if (requested_size) { + auctionConfig['requestedSize'] = {width: requested_size[0], height: requested_size[1]}; + } + return navigator.runAdAuction(auctionConfig); +} + +// Similar to runSelectURL, but uses FLEDGE instead of Shared Storage as the +// auctioning tool. +// Note: this function, unlike generateURL, is asynchronous and needs to be +// called with an await operator. @param {string} href - The base url of the +// page being navigated to @param {string list} keylist - The list of key UUIDs +// to be used. Note that order matters when extracting the keys +// @param {string} href - The base url of the page being navigated to +// @param {string list} keylist - The list of key UUIDs to be used. Note that +// order matters when extracting the keys +// @param {string list} nested_urls - A list of urls that will eventually become +// the nested configs/ad components +// @param {boolean} [resolve_to_config = false] - Determines whether the result +// of `navigator.runAdAuction()` +// is an urn:uuid or a fenced +// frame config. +// @param {boolean} [ad_with_size = false] - Determines whether the auction is +// run with ad sizes specified. +async function generateURNFromFledge(href, keylist, nested_urls=[], resolve_to_config = false, ad_with_size = false, requested_size = null) { + const full_url = generateURL(href, keylist); + return generateURNFromFledgeRawURL(full_url, nested_urls, resolve_to_config, ad_with_size, requested_size); +} + +// Extracts a list of UUIDs from the from the current page's URL. +// @returns {string list} - The list of UUIDs extracted from the page. This can +// be read into multiple variables using the +// [key1, key2, etc...] = parseKeyList(); pattern. +function parseKeylist() { + const url = new URL(location.href); + const keylist = url.searchParams.get("keylist"); + return keylist.split(','); +} + +// Converts a same-origin URL to a cross-origin URL +// @param {URL} url - The URL object whose origin is being converted +// @param {boolean} [https=true] - Whether or not to use the HTTPS origin +// +// @returns {URL} The new cross-origin URL +function getRemoteOriginURL(url, https=true) { + const same_origin = location.origin; + const cross_origin = https ? get_host_info().HTTPS_REMOTE_ORIGIN + : get_host_info().HTTP_REMOTE_ORIGIN; + return new URL(url.toString().replace(same_origin, cross_origin)); +} + +// Builds a URL to be used as a remote context executor. +function generateRemoteContextURL(headers, origin) { + // Generate the unique id for the parent/child channel. + const uuid = token(); + + // Use the absolute path of the remote context executor source file, so that + // nested contexts will work. + const url = getRemoteContextURL(origin ? origin : location.origin); + url.searchParams.append('uuid', uuid); + + // Add the header to allow loading in a fenced frame. + headers.push(["Supports-Loading-Mode", "fenced-frame"]); + + // Transform the headers into the expected format. + // https://web-platform-tests.org/writing-tests/server-pipes.html#headers + function escape(s) { + return s.replace('(', '\\(').replace(')', '\\)'); + } + const formatted_headers = headers.map((header) => { + return `header(${escape(header[0])}, ${escape(header[1])})`; + }); + url.searchParams.append('pipe', formatted_headers.join('|')); + + return [uuid, url]; +} + +function buildRemoteContextForObject(object, uuid, html) { + // https://github.com/web-platform-tests/wpt/blob/master/common/dispatcher/README.md + const context = new RemoteContext(uuid); + if (html) { + context.execute_script( + (html_source) => { + document.body.insertAdjacentHTML('beforebegin', html_source); + }, + [html]); + } + + // We need a little bit of boilerplate in the handlers because Proxy doesn't + // work so nicely with HTML elements. + const handler = { + get: (target, key) => { + if (key == "execute") { + return context.execute_script; + } + if (key == "element") { + return object; + } + if (key in target) { + return target[key]; + } + return context[key]; + }, + set: (target, key, value) => { + target[key] = value; + return value; + } + }; + + const proxy = new Proxy(object, handler); + return proxy; +} + +// Attaches an object that waits for scripts to execute from RemoteContext. +// (In practice, this is either a frame or a window.) +// Returns a proxy for the object that first resolves to the object itself, +// then resolves to the RemoteContext if the property isn't found. +// The proxy also has an extra attribute `execute`, which is an alias for the +// remote context's `execute_script(fn, args=[])`. +function attachContext(object_constructor, html, headers, origin) { + const [uuid, url] = generateRemoteContextURL(headers, origin); + const object = object_constructor(url); + return buildRemoteContextForObject(object, uuid, html); +} + +// TODO(crbug.com/1347953): Update this function to also test +// `sharedStorage.selectURL()` that returns a fenced frame config object. +// This should be done after fixing the following flaky tests that use this +// function. +// 1. crbug.com/1372536: resize-lock-input.https.html +// 2. crbug.com/1394559: unfenced-top.https.html +async function attachOpaqueContext(generator_api, resolve_to_config, ad_with_size, requested_size, object_constructor, html, headers, origin) { + const [uuid, url] = generateRemoteContextURL(headers, origin); + const id = await (generator_api == 'fledge' ? generateURNFromFledge(url, [], [], resolve_to_config, ad_with_size, requested_size) : runSelectURL(url, [], resolve_to_config)); + const object = object_constructor(id); + return buildRemoteContextForObject(object, uuid, html); +} + +function attachPotentiallyOpaqueContext(generator_api, resolve_to_config, ad_with_size, requested_size, frame_constructor, html, headers, origin) { + generator_api = generator_api.toLowerCase(); + if (generator_api == 'fledge' || generator_api == 'sharedstorage') { + return attachOpaqueContext(generator_api, resolve_to_config, ad_with_size, requested_size, frame_constructor, html, headers, origin); + } else { + return attachContext(frame_constructor, html, headers, origin); + } +} + +function attachFrameContext(element_name, generator_api, resolve_to_config, ad_with_size, requested_size, html, headers, attributes, origin) { + frame_constructor = (id) => { + frame = document.createElement(element_name); + attributes.forEach(attribute => { + frame.setAttribute(attribute[0], attribute[1]); + }); + if (element_name == "iframe") { + frame.src = id; + } else if (id instanceof FencedFrameConfig) { + frame.config = id; + } else { + const config = new FencedFrameConfig(id); + frame.config = config; + } + document.body.append(frame); + return frame; + }; + return attachPotentiallyOpaqueContext(generator_api, resolve_to_config, ad_with_size, requested_size, frame_constructor, html, headers, origin); +} + +function replaceFrameContext(frame_proxy, {generator_api="", resolve_to_config=false, ad_with_size=false, requested_size=null, html="", headers=[], origin=""}={}) { + frame_constructor = (id) => { + if (frame_proxy.element.nodeName == "IFRAME") { + frame_proxy.element.src = id; + } else if (id instanceof FencedFrameConfig) { + frame_proxy.element.config = id; + } else { + const config = new FencedFrameConfig(id); + frame_proxy.element.config = config; + } + return frame_proxy.element; + }; + return attachPotentiallyOpaqueContext(generator_api, resolve_to_config, ad_with_size, requested_size, frame_constructor, html, headers, origin); +} + +// Attach a fenced frame that waits for scripts to execute. +// Takes as input a(n optional) dictionary of configs: +// - generator_api: the name of the API that should generate the urn/config. +// Supports (case-insensitive) "fledge" and "sharedstorage", or any other +// value as a default. +// If you generate a urn, then you need to await the result of this function. +// - resolve_to_config: whether a config should be used. (currently only works +// for FLEDGE and sharedStorage generator_api) +// - ad_with_size: whether an ad auction is run with size specified for the ads +// and ad components. (currently only works for FLEDGE) +// - requested_size: A 2-element list with the width and height for +// requestedSize in the FLEDGE auction config. +// - html: extra HTML source code to inject into the loaded frame +// - headers: an array of header pairs [[key, value], ...] +// - attributes: an array of attribute pairs to set on the frame [[key, value], ...] +// - origin: origin of the url, default to location.origin if not set +// Returns a proxy that acts like the frame HTML element, but with an extra +// function `execute`. See `attachFrameContext` or the README for more details. +function attachFencedFrameContext({generator_api="", resolve_to_config=false, ad_with_size=false, requested_size=null, html = "", headers=[], attributes=[], origin=""}={}) { + return attachFrameContext('fencedframe', generator_api, resolve_to_config, ad_with_size, requested_size, html, headers, attributes, origin); +} + +// Attach an iframe that waits for scripts to execute. +// See `attachFencedFrameContext` for more details. +function attachIFrameContext({generator_api="", html="", headers=[], attributes=[], origin=""}={}) { + return attachFrameContext('iframe', generator_api, resolve_to_config=false, ad_with_size=false, requested_size=null, html, headers, attributes, origin); +} + +// Open a window that waits for scripts to execute. +// Returns a proxy that acts like the window object, but with an extra +// function `execute`. See `attachContext` for more details. +function attachWindowContext({target="_blank", html="", headers=[], origin=""}={}) { + window_constructor = (url) => { + return window.open(url, target); + } + + return attachContext(window_constructor, html, headers, origin); +} + +// Converts a key string into a key uuid using a cryptographic hash function. +// This function only works in secure contexts (HTTPS). +async function stringToStashKey(string) { + // Compute a SHA-256 hash of the input string, and convert it to hex. + const data = new TextEncoder().encode(string); + const digest = await crypto.subtle.digest('SHA-256', data); + const digest_array = Array.from(new Uint8Array(digest)); + const digest_as_hex = digest_array.map(b => b.toString(16).padStart(2, '0')).join(''); + + // UUIDs are structured as 8X-4X-4X-4X-12X. + // Use the first 32 hex digits and ignore the rest. + const digest_slices = [digest_as_hex.slice(0,8), + digest_as_hex.slice(8,12), + digest_as_hex.slice(12,16), + digest_as_hex.slice(16,20), + digest_as_hex.slice(20,32)]; + return digest_slices.join('-'); +} + +// Create a fenced frame. Then navigate it using the given `target`, which can +// be either an urn:uuid or a fenced frame config object. +function attachFencedFrame(target) { + assert_implements( + window.HTMLFencedFrameElement, + 'The HTMLFencedFrameElement should be exposed on the window object'); + + const fenced_frame = document.createElement('fencedframe'); + + if (target instanceof FencedFrameConfig) { + fenced_frame.config = target; + } else { + const config = new FencedFrameConfig(target); + fenced_frame.config = config; + } + + document.body.append(fenced_frame); + return fenced_frame; +} + +function attachIFrame(url) { + const iframe = document.createElement('iframe'); + iframe.src = url; + document.body.append(iframe); + return iframe; +} + +// Reads the value specified by `key` from the key-value store on the server. +async function readValueFromServer(key) { + // Resolve the key if it is a Promise. + key = await key; + + const serverUrl = `${STORE_URL}?key=${key}`; + const response = await fetch(serverUrl); + if (!response.ok) + throw new Error('An error happened in the server'); + const value = await response.text(); + + // The value is not stored in the server. + if (value === "<Not set>") + return { status: false }; + + return { status: true, value: value }; +} + +// Convenience wrapper around the above getter that will wait until a value is +// available on the server. +async function nextValueFromServer(key) { + // Resolve the key if it is a Promise. + key = await key; + + while (true) { + // Fetches the test result from the server. + const { status, value } = await readValueFromServer(key); + if (!status) { + // The test result has not been stored yet. Retry after a while. + await new Promise(resolve => setTimeout(resolve, 20)); + continue; + } + + return value; + } +} + +// Reads the data from the latest automatic beacon sent to the server. +async function readAutomaticBeaconDataFromServer() { + const serverUrl = `${BEACON_URL}`; + const response = await fetch(serverUrl); + if (!response.ok) + throw new Error('An error happened in the server'); + const value = await response.text(); + + // The value is not stored in the server. + if (value === "<Not set>") + return { status: false }; + + return { status: true, value: value }; +} + +// Convenience wrapper around the above getter that will wait until a value is +// available on the server. +async function nextAutomaticBeacon() { + while (true) { + // Fetches the test result from the server. + const { status, value } = await readAutomaticBeaconDataFromServer(); + if (!status) { + // The test result has not been stored yet. Retry after a while. + await new Promise(resolve => setTimeout(resolve, 20)); + continue; + } + + return value; + } +} + +// Writes `value` for `key` in the key-value store on the server. +async function writeValueToServer(key, value, origin = '') { + // Resolve the key if it is a Promise. + key = await key; + + const serverUrl = `${origin}${STORE_URL}?key=${key}&value=${value}`; + await fetch(serverUrl, {"mode": "no-cors"}); +} + +// Simulates a user gesture. +async function simulateGesture() { + // Wait until the window size is initialized. + while (window.innerWidth == 0) { + await new Promise(resolve => requestAnimationFrame(resolve)); + } + await test_driver.bless('simulate gesture'); +} + +// Fenced frames are always put in the public IP address space which is the +// least privileged. In case a navigation to a local data: URL or blob: URL +// resource is allowed, they would only be able to fetch things that are *also* +// in the public IP address space. So for the document described by these local +// URLs, we'll set them up to only communicate back to the outer page via +// resources obtained in the public address space. +function createLocalSource(key, url) { + return ` + <head> + <script src="${url}"><\/script> + </head> + <body> + <script> + writeValueToServer("${key}", "LOADED", /*origin=*/"${url.origin}"); + <\/script> + </body> + `; +} + +function setupCSP(csp, second_csp=null) { + let meta = document.createElement('meta'); + meta.httpEquiv = "Content-Security-Policy"; + meta.content = "fenced-frame-src " + csp; + document.head.appendChild(meta); + + if (second_csp != null) { + let second_meta = document.createElement('meta'); + second_meta.httpEquiv = "Content-Security-Policy"; + second_meta.content = "frame-src " + second_csp; + document.head.appendChild(second_meta); + } +} diff --git a/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html b/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html new file mode 100644 index 0000000000..3236886b97 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to test Web Bluetooth</title> + +<body> +<button id="button">Button</button> +<script> +(async () => { + await simulateGesture(); + const [bluetooth_request_device_key] = parseKeylist(); + try { + await navigator.bluetooth.requestDevice({filters: [{name: 'device'}]}); + writeValueToServer(bluetooth_request_device_key, + 'Web Bluetooth requestDevice() succeeded'); + } catch(e) { + if (e.name == 'NotAllowedError' && + e.message.includes( + 'Web Bluetooth is not allowed in a fenced frame tree.')) { + writeValueToServer(bluetooth_request_device_key, + 'Web Bluetooth requestDevice() failed'); + } else { + writeValueToServer( + bluetooth_request_device_key, + 'Web Bluetooth requestDevice() failed with unknown error - ' + + `${e.name}: ${e.message}`); + } + } +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html new file mode 100644 index 0000000000..682805d5d2 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of Web NFC API</title> + +<body> +<script> +async function init() { + const [ndef_write_key, ndef_scan_key] = parseKeylist(); + + const ndef = new NDEFReader(); + ndef.write("Hello").then( + () => { writeValueToServer(ndef_write_key, "resolved"); }, + () => { writeValueToServer(ndef_write_key, "rejected"); } + ); + ndef.scan().then( + () => { writeValueToServer(ndef_scan_key, "resolved"); }, + () => { writeValueToServer(ndef_scan_key, "rejected"); } + ); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html b/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html new file mode 100644 index 0000000000..aada6f04e1 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to test Web Share</title> + +<body> +<script> +(async () => { + await simulateGesture(); + const [navigator_share_key] = parseKeylist(); + try { + await navigator.share({text: 'hello world'}); + writeValueToServer(navigator_share_key, 'Web Share succeeded'); + } catch(error) { + writeValueToServer(navigator_share_key, 'Web Share failed'); + } +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/web-share-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html b/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html new file mode 100644 index 0000000000..897d9a0d59 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of window.frameElement</title> + +<body> +<script> +(async () => { + // Report whether or not `window.frameElement` is null + const [frame_element_key] = parseKeylist(); + let result = (window.frameElement == null) ? "PASS" : "FAIL"; + writeValueToServer(frame_element_key, result); +})(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html b/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html new file mode 100644 index 0000000000..e5e5adef1d --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of window.navigation</title> + +<body> +<script> +function init() { + // This file is meant to be navigated to from a <fencedframe> element. It + // reports back to the page hosting the <fencedframe> after manual timeout + // indicating that the 204 navigation succeeds without navigating away. + location.href = "response-204.py"; + + step_timeout(function() { + const [window_data_key] = parseKeylist(); + writeValueToServer(window_data_key, "still in page"); + }, 1000); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html.headers new file mode 100644 index 0000000000..1b63235b7c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame diff --git a/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html b/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html new file mode 100644 index 0000000000..81dee800fc --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<script src="utils.js"></script> +<title>Fenced frame content to report the result of prerendering</title> + +<body> + <script> + async function report() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It reports its dimensions + // back to the outermost page, which in turn checks for correctness. + const [window_outer_size_key, window_inner_size_key, dimension, + extra_children] = parseKeylist(); + + const url = new URL(location.href); + + if (extra_children == "0") { + let outer_result = (dimension == "width") ? + window.outerWidth : window.outerHeight; + + let inner_result = (dimension == "width") ? + window.innerWidth : window.innerHeight; + + writeValueToServer(window_outer_size_key, outer_result); + writeValueToServer(window_inner_size_key, inner_result); + } else { + const iframe = document.createElement('iframe'); + const frame_url = generateURL('window-outer-dimensions-inner.html', + [window_outer_size_key, window_inner_size_key, dimension, + (parseInt(extra_children) - 1)]); + iframe.src = frame_url; + document.body.append(iframe); + } + + } + report(); + </script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html b/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html new file mode 100644 index 0000000000..9008d7d923 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of window.parent</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page whether or not the value of `window.parent` was correct for: + // 1.) Top-level fenced frames + // 2.) Nested iframes inside a fenced frame + // 3.) Nested fenced frames + const url = new URL(location.href); + + const [window_parent_key, window_parent_ack_key, nested] = parseKeylist(); + const is_nested_fenced_frame = (nested == "nested"); + + // Report whether or not `window.parent` was correct. + let pass_string = ""; + if (is_nested_fenced_frame) + pass_string = "pass: fenced frame > fenced frame"; + else + pass_string = "pass: fenced frame"; + + let result = (window.parent == window) ? pass_string : "fail"; + writeValueToServer(window_parent_key, result); + + // If this page is a nested fenced frame, all we need to do is report the + // top-level value. + if (is_nested_fenced_frame) + return; + + // Wait for ACK, so we know that the outer page has read the last value from + // the `window_parent_key` stash and we can write to it again. + await nextValueFromServer(window_parent_ack_key); + + // Now test `window.parent` inside an iframe. + const iframe = document.createElement('iframe'); + iframe.src = "dummy.html"; + const load_promise = new Promise((resolve, reject) => { + iframe.onload = resolve; + iframe.onerror = reject; + }); + document.body.append(iframe); + + await load_promise; + + // Report whether or not the subframe's `window.parent` was correct. + result = (iframe.contentWindow.parent == window) ? + "pass: fenced frame > iframe" : "fail"; + writeValueToServer(window_parent_key, result); + + // Wait for ACK, so we know that the outer page has read the last value from + // the `window_parent_key` stash and we can write to it again. + await nextValueFromServer(window_parent_ack_key); + + attachFencedFrame(generateURL("window-parent-inner.html", + [window_parent_key, window_parent_ack_key, "nested"])); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html b/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html new file mode 100644 index 0000000000..ddc30bf71b --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="utils.js"></script> +<title>Fenced frame content to report the value of window.top</title> + +<body> +<script> +async function init() { // Needed in order to use top-level await. + // This file is meant to run in a <fencedframe>. It reports back to the + // outermost page whether or not the value of `window.top` was correct for: + // 1.) Top-level fenced frames + // 2.) Nested iframes inside a fenced frame + // 3.) Nested fenced frames + const url = new URL(location.href); + + const [window_top_key, window_top_ack_key, nested] = parseKeylist(); + + // Report whether or not `window.top` was correct. + let pass_string = ""; + if (nested == "nested") + pass_string = "pass: fenced frame > fenced frame"; + else + pass_string = "pass: fenced frame"; + + let result = (window.top == window) ? pass_string : "fail"; + writeValueToServer(window_top_key, result); + + // If this page is a nested fenced frame, all we need to do is report the + // top-level value. + if (nested == "nested") + return; + + // Wait for ACK, so we know that the outer page has read the last value from + // the `window_top_key` stash and we can write to it again. + await nextValueFromServer(window_top_ack_key); + + // Now test `window.top` inside an iframe. + const iframe = document.createElement('iframe'); + iframe.src = "dummy.html"; + const load_promise = new Promise((resolve, reject) => { + iframe.onload = resolve; + iframe.onerror = reject; + }); + document.body.append(iframe); + + await load_promise; + + // Report whether or not the subframe's `window.top` was correct. + result = (iframe.contentWindow.top == window) ? + "pass: fenced frame > iframe" : "fail"; + writeValueToServer(window_top_key, result); + + // Wait for ACK, so we know that the outer page has read the last value from + // the `window_top_key` stash and we can write to it again. + await nextValueFromServer(window_top_ack_key); + + attachFencedFrame(generateURL("window-top-inner.html", + [window_top_key, window_top_ack_key, "nested"])); +} + +init(); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html.headers b/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html.headers new file mode 100644 index 0000000000..6247f6d632 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/window-top-inner.html.headers @@ -0,0 +1 @@ +Supports-Loading-Mode: fenced-frame
\ No newline at end of file |