summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/fenced-frame/resources
diff options
context:
space:
mode:
Diffstat (limited to 'testing/web-platform/tests/fenced-frame/resources')
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-csp.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-iframe-xfo.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html33
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html22
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ancestor-throttle-nested.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js104
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py44
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html47
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-fetch-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html38
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-fetch-sw-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-fetch-sw.js36
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-helper.js23
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html32
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html41
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-sw-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/background-sync-sw.js21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/badging-sw.js23
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html27
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/before-unload-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/check-header-referrer.py16
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/check-header-sec-fetch-dest.py14
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html11
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-iframe-inner.sub.https.html.headers4
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html27
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-inner.sub.https.html.headers5
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html15
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-meta-iframe-inner.sub.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html31
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/client-hints-meta-inner.sub.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/close.html5
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html37
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html30
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/content-index-sw.js28
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html11
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/cookie-access.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html38
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/create-credential-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/create-popup.html18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/create-popup.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html7
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-allowed-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html10
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-fenced-frame-src-blocked-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html7
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-frame-src-allowed-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html9
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-frame-src-blocked-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-inner.html37
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp-inner.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/csp.pngbin0 -> 4476 bytes
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/dangling-markup-helper.js15
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-helper.js53
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html25
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-fencedframe.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html19
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/default-enabled-features-subframe-iframe.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/download-helper.js29
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/download-inner.html34
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/download-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/download-stash.py28
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/dummy.html2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/embeddee.html7
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/embeddee.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/embedder-policy.js39
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/empty-worker.js1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html45
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fedcm-get-credential-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html22
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fence-api-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html13
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fenced-frame-loaded.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html16
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fenced-frame-set-name-and-report-ready-for-outermost-document-to-navigate.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py116
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.py66
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html48
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-create-nested.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html13
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-navigation-inner-simple.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html29
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/frame-with-intersection-observer.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html13
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/gamepad-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html10
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get-nested-configs-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html10
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get-nested-configs-nested-iframe.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get_battery.html17
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/get_battery.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html40
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/header-referrer-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html26
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/header-secFetchDest-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html45
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-back-and-forward-should-not-work-in-fenced-tree-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html115
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html72
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/history-length-outer-page-navigation-not-reflected-in-fenced-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html41
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/ignore-child-fenced-frame-onload-event-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/key-value-store.py46
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html53
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/location-ancestorOrigins-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html17
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-destination.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html39
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-fenced-frame.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html38
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-from-nested-iframe.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html33
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-helper.js28
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-ancestor-test-runner.https.html76
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html45
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-by-name-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html34
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-by-name-reporting-helper.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html14
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigate-nested-config.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-layout-map-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/navigator-keyboard-lock-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/notification-sw.js20
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/opaque-ad-sizes-utils.js47
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/payment-handler-sw.js10
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html15
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-api-denied-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html16
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-geolocation-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-geolocation-test-runner.html24
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html13
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/permission-notification-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/popup-noopener-destination.html20
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html48
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/popup-noopener-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/postmessage-config.html14
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/prerender-inner.html20
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/prerender-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html17
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/presentation-receiver-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/remote-context-executor.https.html29
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/report-url.html7
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/report-url.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html23
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/resize-lock-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/response-204.py4
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-iframe.sub.html13
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html6
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-inner.sub.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandbox-mandatory-flags-looser-restriction.sub.html14
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html20
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandboxed-features-inner.sub.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html19
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandboxed-features-looser-restriction.sub.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/sandboxed-features.js126
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html28
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker-sw.js.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-dedicated-worker.js8
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html37
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html25
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType-nested.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-frameType.js19
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html11
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner-success.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html16
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-navigate.js18
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/serviceWorker-push-sw.js19
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html42
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/unique-cookie-partition-inner.https.html.headers2
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/unreached.https.html15
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/utils.js648
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html33
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-bluetooth-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html23
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-nfc-inner.https.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-share-inner.html21
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/web-share-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html14
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-frameElement-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html23
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-navigation-204-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html36
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-outer-dimensions-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html64
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-parent-inner.html.headers1
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-top-inner.html63
-rw-r--r--testing/web-platform/tests/fenced-frame/resources/window-top-inner.html.headers1
205 files changed, 4214 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-helper.js b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js
new file mode 100644
index 0000000000..d0a4133e84
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js
@@ -0,0 +1,104 @@
+// This is a helper file used for the automatic-beacon-*.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="/resources/testdriver.js"></script>
+// <script src="/resources/testdriver-actions.js"></script>
+// <script src="/resources/testdriver-vendor.js"></script>
+// <script src="/common/get-host-info.sub.js"></script>
+
+const NavigationTrigger = {
+ Click: 0,
+ ClickOnce: 1,
+ CrossOriginClick: 2,
+ CrossOriginClickNoOptIn: 3
+};
+
+// Registers an automatic beacon in a given remote context frame, and registers
+// the navigation handler for the frame that will trigger the beacon.
+// remote_context: The context for the fenced frame or URN iframe.
+// beacon_events: An array of FenceEvents to register with the frame.
+// navigation_url: The URL the frame will navigate to.
+// navigation_trigger: How the navigation will be performed. Either through a
+// click, a click with a `once` event, a click in a
+// cross-origin subframe, or a click in a cross-origin
+// subframe with no opt-in header.
+// target: the target of the navigation. Either '_blank' or
+// '_unfencedTop'.
+async function setupAutomaticBeacon(
+ remote_context, beacon_events, navigation_url = 'resources/dummy.html',
+ navigation_trigger = NavigationTrigger.Click, target = '_blank') {
+ const full_url = new URL(navigation_url, location.href);
+ await remote_context.execute(
+ async (
+ NavigationTrigger, beacon_events, navigation_trigger, full_url,
+ target) => {
+ switch (navigation_trigger) {
+ case NavigationTrigger.Click:
+ addEventListener('click', (event) => {
+ beacon_events.forEach((beacon_event) => {
+ window.fence.setReportEventDataForAutomaticBeacons(
+ beacon_event);
+ });
+ window.open(full_url, target);
+ });
+ break;
+ case NavigationTrigger.ClickOnce:
+ beacon_events.forEach((beacon_event) => {
+ window.fence.setReportEventDataForAutomaticBeacons(beacon_event);
+ });
+ addEventListener('click', (event) => {
+ window.open(full_url, target);
+ });
+ break;
+ case NavigationTrigger.CrossOriginClick:
+ case NavigationTrigger.CrossOriginClickNoOptIn:
+ beacon_events.forEach((beacon_event) => {
+ window.fence.setReportEventDataForAutomaticBeacons(beacon_event);
+ });
+ // Add a cross-origin iframe that will perform the top-level
+ // navigation. Do not set the 'Allow-Fenced-Frame-Automatic-Beacons'
+ // header to true.
+ const iframe = await attachIFrameContext({
+ origin: get_host_info().HTTPS_REMOTE_ORIGIN,
+ headers: [[
+ 'Allow-Fenced-Frame-Automatic-Beacons',
+ navigation_trigger == NavigationTrigger.CrossOriginClick ?
+ 'true' :
+ 'false'
+ ]]
+ });
+ await iframe.execute(async (full_url, target) => {
+ addEventListener('click', (event) => {
+ window.open(full_url, target);
+ });
+ }, [full_url, target]);
+ break;
+ }
+ },
+ [NavigationTrigger, beacon_events, navigation_trigger, full_url, target]);
+}
+
+// Checks if an automatic beacon of type `event_type` with contents `event_data`
+// was sent out or not.
+// event_type: The automatic beacon type to check.
+// event_data: The automatic beacon data to check.
+// expected_success: Whether we expect the automatic beacon to be sent.
+// t: The WPT's test object. Only required if
+// expected_success = false.
+async function verifyBeaconData(
+ event_type, event_data, expected_success = true, t) {
+ if (expected_success) {
+ const beacon_initiator_origin =
+ await nextAutomaticBeacon(event_type, event_data);
+ assert_equals(beacon_initiator_origin, get_host_info().HTTPS_ORIGIN);
+ } else {
+ const timeout = new Promise(r => t.step_timeout(r, 1000));
+ const result = await Promise.race(
+ [nextAutomaticBeacon(event_type, event_data), timeout]);
+ assert_true(typeof result === 'undefined');
+ }
+}
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..ba1b73201b
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py
@@ -0,0 +1,44 @@
+"""
+Automatic beacon store server.
+
+- When a request body is specified, stores the data in the body and serves a 200
+ response without body.
+- When a request body is not specified, serves a 200 response whose body
+ contains the stored value from the automatic beacon. Since the data is stored
+ using a hash of the data as the key, it expects an `expected_body` query
+ parameter to know what key to look up. If the stored value doesn't exist,
+ serves a 200 response with an empty body.
+"""
+import uuid
+import hashlib
+
+NO_DATA_STRING = b"<No data>"
+NOT_SET_STRING = b"<Not set>"
+
+# The server stash requires a uuid to store data. Use a hash of the automatic
+# beacon data as the uuid to store and retrieve the data.
+def string_to_uuid(input):
+ hash_value = hashlib.md5(str(input).encode("UTF-8")).hexdigest()
+ return str(uuid.UUID(hex=hash_value))
+
+def main(request, response):
+ stash = request.server.stash;
+ event_type = request.GET.first(b"type", NO_DATA_STRING)
+
+ # The stash is accessed concurrently by many clients. A lock is used to
+ # avoid interleaved read/write from different clients.
+ with stash.lock:
+ # Requests with a body imply they were sent as an automatic beacon. Note
+ # that this only stores the most recent beacon that was sent.
+ if request.method == "POST":
+ request_body = request.body or NO_DATA_STRING
+ request_headers = request.headers.get("Origin") or NO_DATA_STRING
+ stash.put(string_to_uuid(event_type + request_body),
+ request_headers)
+ return (200, [], b"")
+
+ # Requests without a body imply they were sent as the request from
+ # nextAutomaticBeacon().
+ expected_body = request.GET.first(b"expected_body", NO_DATA_STRING)
+ data = stash.take(string_to_uuid(event_type + expected_body)) or NOT_SET_STRING
+ return(200, [], data)
diff --git a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html
new file mode 100644
index 0000000000..4ce7e0d78a
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Page navigated to by an _unfencedTop navigation</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<body>
+<script>
+ promise_test(async(t) => {
+ // This page is navigated to from an '_unfencedTop' navigation by
+ // '../automatic-beacon-unfenced-top.https.html'. An automatic beacon will
+ // have been sent as a result of the navigation.
+ const beacon_data = "This is the beacon data!";
+ const beacon_initiator_origin = await nextAutomaticBeacon(
+ "reserved.top_navigation_commit", beacon_data);
+ assert_equals(beacon_initiator_origin, get_host_info().HTTPS_ORIGIN);
+ });
+</script>
+</body>
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/close.html b/testing/web-platform/tests/fenced-frame/resources/close.html
new file mode 100644
index 0000000000..7fd946d6ff
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/close.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<title>This window will close when it loads</title>
+<script>
+ window.close();
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html
new file mode 100644
index 0000000000..211fe216c7
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<title>Fenced frame attribution reporting self navigation test</title>
+
+<body>
+<script>
+// This helper function will navigate a fenced frame to a remote origin page.
+// It will then check to make sure that window.fence APIs are not allowed after
+// the navigation.
+const [key] = parseKeylist();
+
+if (location.origin == get_host_info().ORIGIN) {
+ const configs = window.fence.getNestedConfigs();
+ const next_url = getRemoteOriginURL(generateURL(
+ "config-cross-origin-apis-inner.https.html", [key]));
+ location.href = next_url;
+} else {
+ const event = {
+ eventType: "reserved.top_navigation_commit",
+ eventData: "data!",
+ destination: ["buyer"],
+ }
+
+ // These should gracefully fail without badmessaging the renderer.
+ window.fence.setReportEventDataForAutomaticBeacons(event);
+ window.fence.reportEvent(event);
+
+ const configs = window.fence.getNestedConfigs();
+
+ // Report how many configs were obtained. Cross-origin pages should not
+ // obtain any nested configs.
+ writeValueToServer(key, configs.length);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html.headers
new file mode 100644
index 0000000000..1b63235b7c
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-apis-inner.https.html.headers
@@ -0,0 +1 @@
+Supports-Loading-Mode: fenced-frame
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html
new file mode 100644
index 0000000000..17c8347720
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<title>Fenced frame attribution reporting self navigation test</title>
+
+<body>
+<script>
+// This helper function will navigate a child iframe to a remote origin page.
+// It will then check to make sure that window.fence APIs are not allowed after
+// the navigation. This code is meant to run in a fenced frame.
+const [key] = parseKeylist();
+
+const event = {
+ eventType: "reserved.top_navigation_commit",
+ eventData: "data!",
+ destination: ["buyer"],
+}
+
+// These should gracefully fail without badmessaging the renderer.
+window.fence.setReportEventDataForAutomaticBeacons(event);
+window.fence.reportEvent(event);
+
+const configs = window.fence.getNestedConfigs();
+
+// Report how many configs were obtained. Cross-origin pages should not
+// obtain any nested configs.
+writeValueToServer(key, configs.length);
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html.headers
new file mode 100644
index 0000000000..1b63235b7c
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-cross-origin-iframe.https.html.headers
@@ -0,0 +1 @@
+Supports-Loading-Mode: fenced-frame
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html b/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html
new file mode 100644
index 0000000000..f21afee011
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<title>Fenced frame attribution reporting self navigation test</title>
+
+<body>
+<script>
+// This helper function will navigate a child iframe to a remote origin page.
+// It will then check to make sure that window.fence APIs are not allowed after
+// the navigation. This code is meant to run in a fenced frame.
+const [key] = parseKeylist();
+
+const configs = window.fence.getNestedConfigs();
+const next_url = getRemoteOriginURL(generateURL(
+ "config-cross-origin-iframe.https.html", [key]));
+attachIFrame(next_url);
+</script>
+</body>
diff --git a/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html.headers
new file mode 100644
index 0000000000..1b63235b7c
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/config-embed-cross-origin-iframe.https.html.headers
@@ -0,0 +1 @@
+Supports-Loading-Mode: fenced-frame
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
new file mode 100644
index 0000000000..53a9748ae0
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/csp.png
Binary files differ
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..5b4c292622
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-helper.js
@@ -0,0 +1,53 @@
+// 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_true(
+ document.featurePolicy.allowsFeature('shared-storage'),
+ "Shared Storage should be allowed if the fenced " +
+ "frame loaded using FLEDGE.");
+ assert_true(
+ document.featurePolicy.allowsFeature('private-aggregation'),
+ "Private Aggregation should be allowed 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.");
+ assert_false(
+ document.featurePolicy.allowsFeature('private-aggregation'),
+ "Private Aggregation should be disabled if the fenced " +
+ "frame loaded using Shared Storage.");
+ }
+ }, [generator_api]);
+}
diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html
new file mode 100644
index 0000000000..6bfb033400
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+<title>Fenced frame attribution reporting self navigation test</title>
+
+<body>
+<script>
+// This helper function will navigate a fenced frame to a remote origin page.
+// That redirect should succeed to load and the permissions from the previous page should be in
+// place.
+const [key1, key2] = parseKeylist();
+
+const result_val = document.featurePolicy.allowsFeature('attribution-reporting');
+if (location.origin == get_host_info().ORIGIN) {
+ writeValueToServer(key1, result_val);
+
+ const next_url = getRemoteOriginURL(generateURL(
+ "default-enabled-features-navigate.https.html", [key1, key2]));
+ location.href = next_url;
+} else {
+ writeValueToServer(key2, result_val);
+}
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html.headers b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.https.html.headers
new file mode 100644
index 0000000000..1b63235b7c
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/default-enabled-features-navigate.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.py b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py
new file mode 100644
index 0000000000..c91b31fd02
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py
@@ -0,0 +1,116 @@
+# 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 generally do usually not deal with reporting results,
+# so we leave `reportWin` empty unless we need to call registerAdBeacon(). See
+# `generateURNFromFledge` in "utils.js" to see how this file is used.
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ # Set up response headers.
+ headers = [
+ ('Content-Type', 'Application/Javascript'),
+ ('Ad-Auction-Allowed', 'true')
+ ]
+
+ # Parse URL params.
+ requested_size = request.GET.first(b"requested-size", None)
+ ad_with_size = request.GET.first(b"ad-with-size", None)
+ automatic_beacon = request.GET.first(b"automatic-beacon", None)
+
+ # Use URL params to modify Javascript.
+ requested_size_check = ''
+ if requested_size is not None:
+ # request.GET stores URL keys and values in iso-8859-1 binary encoding. We
+ # have to decode the values back to a string to parse width/height. Don't
+ # bother sanitizing the size, because it is sanitized before auction logic
+ # runs already.
+ width, height = isomorphic_decode(requested_size).split('-')
+
+ requested_size_check = (
+ f'''
+ if (!(browserSignals.requestedSize.width === '{width}') &&
+ (browserSignals.requestedSize.height === '{height}')) {{
+ throw new Error('requestedSize missing/incorrect in browserSignals');
+ }}
+ '''
+ )
+
+ render_obj = 'ad.renderURL'
+ if ad_with_size is not None:
+ render_obj = '{ url: ad.renderURL, width: "100px", height: "50px" }'
+
+ component_render_obj = 'component.renderURL'
+ if ad_with_size is not None:
+ component_render_obj = (
+ '''{
+ url: component.renderURL,
+ width: "100px",
+ height: "50px"
+ }
+ '''
+ )
+
+ register_ad_beacon = ''
+ if automatic_beacon is not None:
+ register_ad_beacon = (
+ '''registerAdBeacon({
+ 'reserved.top_navigation_start':
+ browserSignals.interestGroupOwner +
+ '/fenced-frame/resources/automatic-beacon-store.py?type=reserved.top_navigation_start',
+ 'reserved.top_navigation_commit':
+ browserSignals.interestGroupOwner +
+ '/fenced-frame/resources/automatic-beacon-store.py?type=reserved.top_navigation_commit',
+ });
+ '''
+ )
+
+ # Generate Javascript.
+ # Note: Python fstrings use double-brackets ( {{, }} ) to insert bracket
+ # literals instead of substitution sequences.
+ generate_bid = (
+ f'''function generateBid(
+ interestGroup,
+ auctionSignals,
+ perBuyerSignals,
+ trustedBiddingSignals,
+ browserSignals) {{
+ {requested_size_check}
+ 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': {render_obj},
+ 'allowComponentAuction': allowComponentAuction
+ }};
+ if (interestGroup.adComponents && interestGroup.adComponents.length > 0)
+ result.adComponents = interestGroup.adComponents.map((component) => {{
+ return {component_render_obj};
+ }});
+ return result;
+ }}
+ '''
+ )
+
+ report_win = (
+ f'''function reportWin(
+ auctionSignals,
+ perBuyerSignals,
+ sellerSignals,
+ browserSignals) {{
+ {register_ad_beacon}
+ return;
+ }}
+ '''
+ )
+
+ content = f'{generate_bid}\n{report_win}'
+
+ return (headers, content)
diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.py b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.py
new file mode 100644
index 0000000000..63b544552c
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/fledge-decision-logic.py
@@ -0,0 +1,66 @@
+# 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
+# this file is used.
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+ # Set up response headers.
+ headers = [
+ ('Content-Type', 'Application/Javascript'),
+ ('Ad-Auction-Allowed', 'true')
+ ]
+
+ # Parse URL params.
+ requested_size = request.GET.first(b"requested-size", None)
+
+ # Use URL params to modify Javascript.
+ requested_size_check = ''
+ if requested_size is not None:
+ # request.GET stores URL keys and values in iso-8859-1 binary encoding. We
+ # have to decode the values back to a string to parse width/height. Don't
+ # bother sanitizing the size, because it is sanitized before auction logic
+ # runs already.
+ width, height = isomorphic_decode(requested_size).split('-')
+
+ requested_size_check = (
+ f'''
+ if (!(auctionConfig.requestedSize.width === '{width}') &&
+ (auctionConfig.requestedSize.height === '{height}')) {{
+ throw new Error('requestedSize missing/incorrect in auctionConfig');
+ }}
+ '''
+ )
+
+ # Generate Javascript.
+ # Note: Python fstrings use double-brackets ( {{, }} ) to insert bracket
+ # literals instead of substitution sequences.
+ score_ad = (
+ f'''function scoreAd(
+ adMetadata,
+ bid,
+ auctionConfig,
+ trustedScoringSignals,
+ browserSignals) {{
+ {requested_size_check}
+ return 2*bid;
+ }}
+ '''
+ )
+
+ report_result = (
+ f'''function reportResult(
+ auctionConfig,
+ browserSignals) {{
+ {requested_size_check}
+ return;
+ }}
+ '''
+ )
+
+ content = f'{score_ad}\n{report_result}'
+
+ return (headers, content)
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..122debfe27
--- /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];
+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..ade17c69f2
--- /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 pagehidePromise = new Promise(resolve => {
+ win.onpagehide = resolve;
+ });
+
+ await win.runTest(test_type, ancestor_type);
+ win.close();
+ await pagehidePromise;
+}
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/postmessage-config.html b/testing/web-platform/tests/fenced-frame/resources/postmessage-config.html
new file mode 100644
index 0000000000..3c20190420
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/postmessage-config.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script src="utils.js"></script>
+<title>A page opened in a pop-up that sends a FencedFrameConfig</title>
+<script>
+ async function init() {
+ const [key] = parseKeylist();
+
+ const config = await generateURNFromFledge(
+ "embeddee.html", [key], [], true);
+
+ window.opener.postMessage(config, "*");
+ }
+ init();
+</script>
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.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/unreached.https.html b/testing/web-platform/tests/fenced-frame/resources/unreached.https.html
new file mode 100644
index 0000000000..bd389ec4fb
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/unreached.https.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<title>File used to assert that navigations do not succeed.</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="utils.js"></script>
+<script src="/common/get-host-info.sub.js"></script>
+
+<body>
+<script>
+ promise_test(async(t) => {
+ assert_unreached('This navigation should not have succeeded.');
+ });
+</script>
+</body>
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..cbea173f17
--- /dev/null
+++ b/testing/web-platform/tests/fenced-frame/resources/utils.js
@@ -0,0 +1,648 @@
+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';
+
+// If your test needs to modify FLEDGE bidding or decision logic, you should
+// update the generated JS in the corresponding handler below.
+const FLEDGE_BIDDING_URL = '/fenced-frame/resources/fledge-bidding-logic.py';
+const FLEDGE_DECISION_URL = '/fenced-frame/resources/fledge-decision-logic.py';
+
+// 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,
+ reportingMetadata: {
+ 'reserved.top_navigation_start': BEACON_URL +
+ "?type=reserved.top_navigation_start",
+ 'reserved.top_navigation_commit': BEACON_URL +
+ "?type=reserved.top_navigation_commit",
+ }}], {
+ 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, automatic_beacon = false) {
+ 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 }
+ });
+
+ let interestGroup =
+ {
+ 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,
+ };
+
+ let biddingURLParams =
+ new URLSearchParams(interestGroup.biddingLogicURL.search);
+ if (requested_size)
+ biddingURLParams.set(
+ 'requested-size', requested_size[0] + '-' + requested_size[1]);
+ if (ad_with_size)
+ biddingURLParams.set('ad-with-size', 1);
+ if (automatic_beacon)
+ biddingURLParams.set('automatic-beacon', 1);
+ interestGroup.biddingLogicURL.search = biddingURLParams;
+
+ if (ad_with_size) {
+ interestGroup.ads[0].sizeGroup = 'group1';
+ interestGroup.adSizes = {'size1': {width: '100px', height: '50px'}};
+ interestGroup.sizeGroups = {'group1': ['size1']};
+ }
+
+ // 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) {
+ let decisionURLParams =
+ new URLSearchParams(auctionConfig.decisionLogicURL.search);
+ decisionURLParams.set(
+ 'requested-size', requested_size[0] + '-' + requested_size[1]);
+ auctionConfig.decisionLogicURL.search = decisionURLParams;
+
+ 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.
+// @param {boolean} [automatic_beacon = false] - If true, FLEDGE logic will
+// register an automatic beacon
+// after completion.
+async function generateURNFromFledge(
+ href, keylist, nested_urls = [], resolve_to_config = false,
+ ad_with_size = false, requested_size = null, automatic_beacon = false) {
+ const full_url = generateURL(href, keylist);
+ return generateURNFromFledgeRawURL(
+ full_url, nested_urls, resolve_to_config, ad_with_size, requested_size,
+ automatic_beacon);
+}
+
+// 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,
+ automatic_beacon, object_constructor, html, headers, origin,
+ num_components) {
+ const [uuid, url] = generateRemoteContextURL(headers, origin);
+
+ let components_list = [];
+ for (let i = 0; i < num_components; i++) {
+ let [component_uuid, component_url] =
+ generateRemoteContextURL(headers, origin);
+ // This field will be read by attachComponentFrameContext() in order to
+ // know what uuid to point to when building the remote context.
+ html += '<input type=\'hidden\' id=\'component_uuid_' + i + '\' value=\'' +
+ component_uuid + '\'>';
+ components_list.push(component_url);
+ }
+
+ const id = await (
+ generator_api == 'fledge' ?
+ generateURNFromFledge(
+ url, [], components_list, resolve_to_config, ad_with_size,
+ requested_size, automatic_beacon) :
+ 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,
+ automatic_beacon, frame_constructor, html, headers, origin,
+ num_components) {
+ generator_api = generator_api.toLowerCase();
+ if (generator_api == 'fledge' || generator_api == 'sharedstorage') {
+ return attachOpaqueContext(
+ generator_api, resolve_to_config, ad_with_size, requested_size,
+ automatic_beacon, frame_constructor, html, headers, origin,
+ num_components);
+ } else {
+ return attachContext(frame_constructor, html, headers, origin);
+ }
+}
+
+function attachFrameContext(
+ element_name, generator_api, resolve_to_config, ad_with_size,
+ requested_size, automatic_beacon, html, headers, attributes, origin,
+ num_components) {
+ 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,
+ automatic_beacon, frame_constructor, html, headers, origin,
+ num_components);
+}
+
+function replaceFrameContext(frame_proxy, {
+ generator_api = '',
+ resolve_to_config = false,
+ ad_with_size = false,
+ requested_size = null,
+ automatic_beacon = false,
+ 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,
+ automatic_beacon, 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. This is different from
+// ad_with_size, which refers to size information provided alongside the ads
+// themselves.
+// - automatic_beacon: If true and generator_api = "fledge", an automatic beacon
+// will be registered for a top-level navigation after the FLEDGE auction
+// completes.
+// - 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,
+ automatic_beacon = false,
+ html = '',
+ headers = [],
+ attributes = [],
+ origin = '',
+ num_components = 0
+} = {}) {
+ return attachFrameContext(
+ 'fencedframe', generator_api, resolve_to_config, ad_with_size,
+ requested_size, automatic_beacon, html, headers, attributes, origin,
+ num_components);
+}
+
+// Attach an iframe that waits for scripts to execute.
+// See `attachFencedFrameContext` for more details.
+function attachIFrameContext({
+ generator_api = '',
+ automatic_beacon = false,
+ html = '',
+ headers = [],
+ attributes = [],
+ origin = '',
+ num_components = 0
+} = {}) {
+ return attachFrameContext(
+ 'iframe', generator_api, resolve_to_config = false, ad_with_size = false,
+ requested_size = null, automatic_beacon, html, headers, attributes,
+ origin, num_components);
+}
+
+// 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);
+}
+
+// Attaches an ad component in a fenced frame. For this to work, this must be
+// called in a frame that was generated with attachFrameContext() using the
+// Protected Audience API (generator_api: 'fledge').
+function attachComponentFencedFrameContext(
+ index = 0, {attributes = [], html = ''} = {}) {
+ const urn = window.fence.getNestedConfigs()[index];
+ return attachComponentFrameContext(
+ index, 'fencedframe', urn, attributes, html);
+}
+
+// Same as attachComponentFencedFrameContext, but in a urn iframe.
+function attachComponentIFrameContext(
+ index = 0, {attributes = [], html = ''} = {}) {
+ const urn = navigator.adAuctionComponents(index + 1)[index];
+ return attachComponentFrameContext(index, 'iframe', urn, attributes, html);
+}
+
+function attachComponentFrameContext(
+ index, element_name, urn, attributes, html) {
+ assert_not_equals(
+ document.getElementById('component_uuid_' + index), null,
+ 'Component frames can only be attached to frames loaded with ' +
+ 'attach*FrameContext() with `num_components` set to at least ' +
+ (index + 1) + '.');
+
+ let frame = document.createElement(element_name);
+ attributes.forEach(attribute => {
+ frame.setAttribute(attribute[0], attribute[1]);
+ });
+ if (element_name == 'iframe') {
+ frame.src = urn;
+ } else {
+ frame.config = urn;
+ }
+ document.body.append(frame);
+ const context_uuid = document.getElementById('component_uuid_' + index).value;
+ return buildRemoteContextForObject(frame, context_uuid, html);
+}
+
+// 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;
+ }
+}
+
+// Checks the automatic beacon data server to see if it has received an
+// automatic beacon with a given event type and body.
+async function readAutomaticBeaconDataFromServer(event_type, expected_body) {
+ let serverURL = `${BEACON_URL}`;
+ const response = await fetch(serverURL + "?" + new URLSearchParams({
+ type: event_type,
+ expected_body: expected_body,
+ }));
+ if (!response.ok)
+ throw new Error('An error happened in the server ' + response.status);
+ 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. The server uses a hash of the concatenated event
+// type and beacon data as the key when storing the beacon in the database. To
+// retrieve it, we need to supply the endpoint with both pieces of information.
+async function nextAutomaticBeacon(event_type, expected_body) {
+ while (true) {
+ // Fetches the test result from the server.
+ const { status, value } =
+ await readAutomaticBeaconDataFromServer(event_type, expected_body);
+ 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