summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/portals/resources
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/portals/resources
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/portals/resources')
-rw-r--r--testing/web-platform/tests/portals/resources/attempt-portal-load.html11
-rw-r--r--testing/web-platform/tests/portals/resources/blank-host.html8
-rw-r--r--testing/web-platform/tests/portals/resources/eval-portal.html10
-rw-r--r--testing/web-platform/tests/portals/resources/focus-page-with-autofocus.html23
-rw-r--r--testing/web-platform/tests/portals/resources/focus-page-with-button.html35
-rw-r--r--testing/web-platform/tests/portals/resources/focus-page-with-x-origin-iframe.sub.html28
-rw-r--r--testing/web-platform/tests/portals/resources/invalid.asis1
-rw-r--r--testing/web-platform/tests/portals/resources/open-blank-host.js14
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-broadcastchannel.html8
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-data-portal.html9
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-event-portal.html21
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-in-handler.html51
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-inside-portal.html11
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-twice-window-1.html12
-rw-r--r--testing/web-platform/tests/portals/resources/portal-activate-twice-window-2.html22
-rw-r--r--testing/web-platform/tests/portals/resources/portal-close-window.html7
-rw-r--r--testing/web-platform/tests/portals/resources/portal-embed-and-activate.html15
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-cross-origin-navigate.sub.html7
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-hidden-after-activation-portal.html14
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-post-message-after-activate.html19
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-1.html5
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-2.html4
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-post-message-x-origin.html4
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host-post-message.html51
-rw-r--r--testing/web-platform/tests/portals/resources/portal-host.html11
-rw-r--r--testing/web-platform/tests/portals/resources/portal-inside-iframe.html4
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-after-activate-window.html23
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-before-activate-portal.html24
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-before-activate-window.html13
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-during-activate-window.html21
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-portal.html29
-rw-r--r--testing/web-platform/tests/portals/resources/portal-post-message-x-origin-portal.html11
-rw-r--r--testing/web-platform/tests/portals/resources/portal-repeated-activate-window.html20
-rw-r--r--testing/web-platform/tests/portals/resources/portals-adopt-predecessor-portal.html77
-rw-r--r--testing/web-platform/tests/portals/resources/portals-adopt-predecessor.html20
-rw-r--r--testing/web-platform/tests/portals/resources/portals-nested-portal.html9
-rw-r--r--testing/web-platform/tests/portals/resources/portals-rendering-portal.html8
-rw-r--r--testing/web-platform/tests/portals/resources/postmessage-referrer.sub.html8
-rw-r--r--testing/web-platform/tests/portals/resources/predecessor-fires-unload-watch-unload.html25
-rw-r--r--testing/web-platform/tests/portals/resources/simple-portal-adopts-and-activates-predecessor.html6
-rw-r--r--testing/web-platform/tests/portals/resources/simple-portal-adopts-predecessor.html7
-rw-r--r--testing/web-platform/tests/portals/resources/simple-portal.html6
-rw-r--r--testing/web-platform/tests/portals/resources/stash-utils.sub.js43
43 files changed, 755 insertions, 0 deletions
diff --git a/testing/web-platform/tests/portals/resources/attempt-portal-load.html b/testing/web-platform/tests/portals/resources/attempt-portal-load.html
new file mode 100644
index 0000000000..183178006f
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/attempt-portal-load.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<body>
+<script>
+portalLoaded = new Promise((resolve, reject) => {
+ let portal = document.createElement('portal');
+ portal.src = 'simple-portal.html';
+ portal.onload = resolve;
+ document.body.appendChild(portal);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/blank-host.html b/testing/web-platform/tests/portals/resources/blank-host.html
new file mode 100644
index 0000000000..d9f3a61eb8
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/blank-host.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<!--
+ This is a blank page used when a test needs a new window to host and activate
+ a portal. Tests cannot simply use window.open() without a URL as about:blank
+ may not host a portal.
+-->
+<body>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/eval-portal.html b/testing/web-platform/tests/portals/resources/eval-portal.html
new file mode 100644
index 0000000000..a473501b01
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/eval-portal.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<script>
+let logic = new Function('event', (new URL(location)).searchParams.get('logic'));
+onload = () => window.portalHost.postMessage('ready');
+onportalactivate = event => {
+ Promise.resolve(event)
+ .then(logic)
+ .then(reply => event.data.replyPort.postMessage(reply));
+};
+</script>
diff --git a/testing/web-platform/tests/portals/resources/focus-page-with-autofocus.html b/testing/web-platform/tests/portals/resources/focus-page-with-autofocus.html
new file mode 100644
index 0000000000..d498ef6335
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/focus-page-with-autofocus.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<body>
+ <button id="one">one</button>
+ <button id="two" autofocus>two</button>
+ <button id="three">three</button>
+ <script>
+ function messageHandler(e) {
+ if (e.data === 'check-active-element') {
+ window.requestAnimationFrame(() => {
+ let autofocusedButton = document.querySelector('#two');
+ e.source.postMessage(document.activeElement === autofocusedButton);
+ });
+ }
+ }
+
+ window.portalHost.onmessage = messageHandler;
+ window.onportalactivate = e => {
+ let portal = e.adoptPredecessor();
+ portal.onmessage = messageHandler;
+ document.body.appendChild(portal);
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/focus-page-with-button.html b/testing/web-platform/tests/portals/resources/focus-page-with-button.html
new file mode 100644
index 0000000000..81ed5465ab
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/focus-page-with-button.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ function handleMessage(e) {
+ if (e.data == "focus") {
+ let button = document.querySelector("button");
+ button.onfocus = () => e.source.postMessage({focused: true}, {targetOrigin: "*"});
+ button.focus();
+ }
+
+ if (e.data == "focus-update-active-element") {
+ let button = document.querySelector("button");
+ button.focus();
+ e.source.postMessage({activeElementUpdated: document.activeElement === button}, {targetOrigin: "*"});
+ }
+ }
+
+ if (window.portalHost)
+ window.portalHost.onmessage = handleMessage;
+
+ window.onmessage = handleMessage;
+
+ window.onportalactivate = e => {
+ let portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+ portal.onmessage = handleMessage;
+ };
+
+ window.onfocus = () => {
+ if (window.portalHost)
+ window.portalHost.postMessage("window focused");
+ };
+ </script>
+ <button>A</button>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/focus-page-with-x-origin-iframe.sub.html b/testing/web-platform/tests/portals/resources/focus-page-with-x-origin-iframe.sub.html
new file mode 100644
index 0000000000..df7974e75b
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/focus-page-with-x-origin-iframe.sub.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ async function handleMessage(e) {
+ if (e.data == "focus" || e.data == "focus-update-active-element") {
+ let iframe = document.querySelector("iframe");
+ iframe.contentWindow.postMessage(e.data, "*");
+ }
+ }
+
+ if (window.portalHost)
+ window.portalHost.onmessage = handleMessage;
+
+ window.onportalactivate = e => {
+ var portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+ portal.onmessage = handleMessage;
+ }
+
+ window.onmessage = e => {
+ if (window.portalHost)
+ window.portalHost.postMessage(e.data);
+ else
+ document.querySelector("portal").postMessage(e.data);
+ }
+ </script>
+ <iframe src="http://{{hosts[alt][www]}}:{{ports[http][0]}}/portals/resources/focus-page-with-button.html"></iframe>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/invalid.asis b/testing/web-platform/tests/portals/resources/invalid.asis
new file mode 100644
index 0000000000..20f7c7f7e5
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/invalid.asis
@@ -0,0 +1 @@
+This is an invalid HTTP response used to produce a network error.
diff --git a/testing/web-platform/tests/portals/resources/open-blank-host.js b/testing/web-platform/tests/portals/resources/open-blank-host.js
new file mode 100644
index 0000000000..f7580bd152
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/open-blank-host.js
@@ -0,0 +1,14 @@
+// Portal tests often need to create portals in a context other than the one
+// in which the tests are running. This is because the host context may be
+// discarded during the course of the test.
+
+// Opens a blank page for use as a portal host.
+// Tests cannot simply use window.open() without a URL as about:blank may not
+// host a portal.
+async function openBlankPortalHost() {
+ let hostWindow = window.open('/portals/resources/blank-host.html');
+ await new Promise((resolve) => {
+ hostWindow.addEventListener('load', resolve, {once: true});
+ });
+ return hostWindow;
+}
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-broadcastchannel.html b/testing/web-platform/tests/portals/resources/portal-activate-broadcastchannel.html
new file mode 100644
index 0000000000..b922afaec2
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-broadcastchannel.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+onportalactivate = e => {
+ let bc = new BroadcastChannel(new URL(location).searchParams.get('bc'));
+ bc.postMessage({event: 'portalactivate', data: e.data});
+ bc.close();
+};
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-data-portal.html b/testing/web-platform/tests/portals/resources/portal-activate-data-portal.html
new file mode 100644
index 0000000000..0842ad82ef
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-data-portal.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script>
+let logic = new Function('event', (new URL(location)).searchParams.get('logic'));
+onload = () => window.portalHost.postMessage('ready');
+onportalactivate = event => {
+ var portal = event.adoptPredecessor();
+ portal.postMessage(logic(event));
+};
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-event-portal.html b/testing/web-platform/tests/portals/resources/portal-activate-event-portal.html
new file mode 100644
index 0000000000..6de5aafca7
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-event-portal.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Tests that the PortalActivateEvent is dispatched when a portal is activated</title>
+<script>
+ var test = (new URL(location)).searchParams.get("test");
+
+ function portalActivate(e) {
+ var bc = new BroadcastChannel("test-" + test);
+ bc.postMessage("passed");
+ bc.close();
+ }
+
+ if (test == "bodyeventhandler") {
+ document.write('<body onportalactivate="portalActivate()"></body>');
+ } else if (test == "eventhandler") {
+ window.onportalactivate = portalActivate;
+ } else if (test == "eventlistener") {
+ window.addEventListener("portalactivate", portalActivate);
+ }
+
+ window.portalHost.postMessage("loaded");
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-in-handler.html b/testing/web-platform/tests/portals/resources/portal-activate-in-handler.html
new file mode 100644
index 0000000000..746ffa2b39
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-in-handler.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <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>
+ </head>
+ <body>
+ </body>
+ <script>
+ // This page is reused with a different query parameter indicating which
+ // handler to register and activate a portal from.
+ const handler_name = window.location.search.substring(1);
+
+ const portal_element = document.createElement('portal');
+ portal_element.src = 'simple-portal.html';
+ document.body.appendChild(portal_element);
+
+ let page_loaded = false;
+ let portal_loaded = false;
+
+ function notifyReady() {
+ if (page_loaded && portal_loaded) {
+ window.opener.postMessage('done', '*');
+ }
+ }
+
+ portal_element.addEventListener('load', () => {
+ portal_loaded = true;
+ notifyReady();
+ });
+
+ window.addEventListener('load', () => {
+ page_loaded = true;
+ notifyReady();
+ });
+
+ // This will be used to let the parent page know the handler has run and
+ // |portal_promise| is now valid.
+ window.opener.handler_called_promise = new Promise((resolve) => {
+ window.addEventListener(handler_name, () => {
+ window.opener.portal_promise = portal_element.activate();
+
+ // Let the parent page know it can now look at |portal_promise|.
+ resolve();
+ }, {once: true});
+ });
+
+ </script>
+</html>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-inside-portal.html b/testing/web-platform/tests/portals/resources/portal-activate-inside-portal.html
new file mode 100644
index 0000000000..ff8bead324
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-inside-portal.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "simple-portal.html";
+ portal.onload = () => {
+ portal.activate().catch(e => window.portalHost.postMessage(e.name));
+ }
+ document.body.appendChild(portal);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-twice-window-1.html b/testing/web-platform/tests/portals/resources/portal-activate-twice-window-1.html
new file mode 100644
index 0000000000..fbc5a6e93d
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-twice-window-1.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "simple-portal.html"
+ portal.onload = () => {
+ portal.activate();
+ portal.activate().catch(e => window.opener.postMessage(e.name, "*"));
+ }
+ document.body.append(portal);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-activate-twice-window-2.html b/testing/web-platform/tests/portals/resources/portal-activate-twice-window-2.html
new file mode 100644
index 0000000000..6ba8dc5839
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-activate-twice-window-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal1 = document.createElement("portal");
+ portal1.src = "simple-portal.html"
+ var portal2 = document.createElement("portal");
+ portal2.src = "simple-portal.html"
+
+ var waitForPortalToLoad = portal => new Promise((resolve, reject) => {
+ portal.onload = resolve;
+ });
+
+ Promise.all([waitForPortalToLoad(portal1),
+ waitForPortalToLoad(portal2)]).then(() => {
+ portal1.activate();
+ portal2.activate().catch(e => window.opener.postMessage(e.name, "*"));
+ });
+
+ document.body.append(portal1);
+ document.body.append(portal2);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-close-window.html b/testing/web-platform/tests/portals/resources/portal-close-window.html
new file mode 100644
index 0000000000..a12af3cd7a
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-close-window.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<script>
+onload = () => {
+ window.close();
+ window.portalHost.postMessage(window.closed);
+};
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-embed-and-activate.html b/testing/web-platform/tests/portals/resources/portal-embed-and-activate.html
new file mode 100644
index 0000000000..04f15b7fda
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-embed-and-activate.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!--
+ Embeds a portal (src specified by query parameter "url") and activates it after
+ receiving a message from the portal.
+-->
+</title>
+<body>
+ <script>
+ var searchParams = new URL(location).searchParams;
+ let portal = document.createElement("portal");
+ portal.src = searchParams.get("url");
+ portal.onmessage = () => { portal.activate(); }
+ document.body.appendChild(portal);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-cross-origin-navigate.sub.html b/testing/web-platform/tests/portals/resources/portal-host-cross-origin-navigate.sub.html
new file mode 100644
index 0000000000..26f655a0db
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-cross-origin-navigate.sub.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ let key = (new URLSearchParams(window.location.search)).get('key');
+ window.location.href = `http://{{hosts[alt][www]}}:{{ports[http][0]}}/portals/resources/portal-host.html?key=${key}`;
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-hidden-after-activation-portal.html b/testing/web-platform/tests/portals/resources/portal-host-hidden-after-activation-portal.html
new file mode 100644
index 0000000000..491d184f97
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-hidden-after-activation-portal.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<script>
+ window.addEventListener("portalactivate", function(e) {
+ var bc = new BroadcastChannel("portals-host-hidden-after-activation");
+ bc.postMessage({ hasHost: !!window.portalHost });
+ bc.close();
+ });
+
+ var bc = new BroadcastChannel("portals-host-hidden-after-activation");
+ bc.postMessage({hasHost: !!window.portalHost });
+ bc.close();
+
+ window.portalHost.postMessage("loaded");
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-post-message-after-activate.html b/testing/web-platform/tests/portals/resources/portal-host-post-message-after-activate.html
new file mode 100644
index 0000000000..7b03ac0294
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-post-message-after-activate.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<script>
+ window.portalHost.postMessage("loaded");
+
+ var ph = window.portalHost;
+
+ window.onportalactivate = e => {
+ var exception_name = ""
+ try {
+ ph.postMessage("message");
+ }
+ catch (error) {
+ exception_name = error.name;
+ }
+ bc = new BroadcastChannel("portal-host-post-message-after-activate");
+ bc.postMessage(exception_name, "*");
+ bc.close();
+ };
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-1.html b/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-1.html
new file mode 100644
index 0000000000..a59144e7e1
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<script>
+ window.portalHost.postMessage("loaded");
+ window.location.href = "portal-host-post-message-navigate-2.html"
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-2.html b/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-2.html
new file mode 100644
index 0000000000..571c4f122e
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-post-message-navigate-2.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.portalHost.postMessage("loaded");
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-post-message-x-origin.html b/testing/web-platform/tests/portals/resources/portal-host-post-message-x-origin.html
new file mode 100644
index 0000000000..6cbc7f4b88
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-post-message-x-origin.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<script>
+ window.portalHost.postMessage('test message');
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host-post-message.html b/testing/web-platform/tests/portals/resources/portal-host-post-message.html
new file mode 100644
index 0000000000..1935ee898e
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host-post-message.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script>
+ function postMessageWithMessagePorts() {
+ var channel = new MessageChannel();
+ channel.port1.onmessage = e => {
+ e.ports[0].postMessage("received");
+ }
+ window.portalHost.postMessage("sending port", {transfer: [channel.port2]});
+ }
+
+ function postMessageWithArrayBuffer(array, withTransfer) {
+ var arrayBuffer = new Int8Array(array).buffer;
+ if (withTransfer) {
+ window.portalHost.postMessage({arrayBuffer}, {transfer: [arrayBuffer]});
+ } else {
+ window.portalHost.postMessage({arrayBuffer});
+ }
+ }
+
+ function postMessageAndCatchException(...params) {
+ try {
+ window.portalHost.postMessage(...params);
+ } catch (e) {
+ window.portalHost.postMessage({errorType: e.name});
+ }
+ }
+
+ window.portalHost.addEventListener("message", e => {
+ if (e.data.type) {
+ var type = e.data.type;
+ switch (type) {
+ case "message-port":
+ postMessageWithMessagePorts();
+ return;
+ case "array-buffer-without-transfer":
+ postMessageWithArrayBuffer(e.data.array, false);
+ return;
+ case "array-buffer-with-transfer":
+ postMessageWithArrayBuffer(e.data.array, true);
+ return;
+ case "invalid-message":
+ postMessageAndCatchException(document.body);
+ return;
+ case "invalid-port":
+ postMessageAndCatchException("", {transfer: [null]});
+ return;
+ }
+ }
+ window.portalHost.postMessage(...e.data);
+ });
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-host.html b/testing/web-platform/tests/portals/resources/portal-host.html
new file mode 100644
index 0000000000..e577208236
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-host.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script src="stash-utils.sub.js"></script>
+<body>
+ <script>
+ let queryParams = new URLSearchParams(window.location.search);
+ let key = queryParams.get('key');
+ if (key) {
+ StashUtils.putValue(key, window.portalHost ? "passed" : "failed");
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-inside-iframe.html b/testing/web-platform/tests/portals/resources/portal-inside-iframe.html
new file mode 100644
index 0000000000..5db75d5b5f
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-inside-iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ <portal id="portal" />
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-after-activate-window.html b/testing/web-platform/tests/portals/resources/portal-post-message-after-activate-window.html
new file mode 100644
index 0000000000..73d2c11558
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-after-activate-window.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "portal-post-message-portal.html";
+ document.body.appendChild(portal);
+
+ portal.onload = () => {
+ portal.activate().then(() => {
+ error = "";
+ try {
+ portal.postMessage("message");
+ }
+ catch(err) {
+ error = err.name;
+ }
+ bc = new BroadcastChannel("portals-post-message-after-activate");
+ bc.postMessage(error);
+ bc.close();
+ });
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-portal.html b/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-portal.html
new file mode 100644
index 0000000000..d34875f981
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-portal.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<script>
+ var postMessagePromise = new Promise((resolve, reject) => {
+ window.portalHost.addEventListener("message", () => {
+ resolve(performance.now());
+ });
+ });
+
+ var activatePromise = new Promise((resolve, reject) => {
+ window.onportalactivate = () => {
+ resolve(performance.now());
+ }
+ });
+
+ Promise.all([postMessagePromise, activatePromise])
+ .then(values => {
+ bc = new BroadcastChannel("portals-post-message-before-activate");
+ bc.postMessage({
+ postMessageTS: values[0],
+ activateTS: values[1]
+ });
+ bc.close();
+ });
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-window.html b/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-window.html
new file mode 100644
index 0000000000..6389829c7c
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-before-activate-window.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "portal-post-message-before-activate-portal.html";
+ document.body.appendChild(portal);
+
+ portal.onload = () => {
+ portal.postMessage("message");
+ portal.activate();
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-during-activate-window.html b/testing/web-platform/tests/portals/resources/portal-post-message-during-activate-window.html
new file mode 100644
index 0000000000..6e220277d9
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-during-activate-window.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "simple-portal.html";
+ document.body.appendChild(portal);
+
+ portal.onload = () => {
+ portal.activate();
+ error = "";
+ try {
+ portal.postMessage("message");
+ } catch (err) {
+ error = err.name;
+ }
+ bc = new BroadcastChannel("portals-post-message-during-activate");
+ bc.postMessage(error);
+ bc.close();
+ }
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-portal.html b/testing/web-platform/tests/portals/resources/portal-post-message-portal.html
new file mode 100644
index 0000000000..e83ae56e08
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-portal.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<script>
+ window.portalHost.onmessage = e => {
+ var message = {
+ origin: e.origin,
+ data: e.data,
+ sourceIsPortalHost: e.source === window.portalHost,
+ gotUserActivation: !!e.userActivation,
+ userActivation: {
+ isActive: e.userActivation && e.userActivation.isActive,
+ hasBeenActive: e.userActivation && e.userActivation.hasBeenActive
+ }
+ };
+
+ if (e.data.arrayBuffer) {
+ message.data = {
+ array: Array.from(new Uint8Array(e.data.arrayBuffer))
+ };
+ }
+
+ if (e.ports.length > 0) {
+ e.ports[0].postMessage(message);
+ e.ports[0].close();
+ return;
+ }
+
+ window.portalHost.postMessage(message);
+ };
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-post-message-x-origin-portal.html b/testing/web-platform/tests/portals/resources/portal-post-message-x-origin-portal.html
new file mode 100644
index 0000000000..57631f385c
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-post-message-x-origin-portal.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<script src="stash-utils.sub.js"></script>
+<script>
+ const queryParams = new URLSearchParams(window.location.search);
+ const key = queryParams.get('key');
+ if (key) {
+ window.portalHost.onmessage = () => {
+ StashUtils.putValue(key, 'failed');
+ };
+ }
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portal-repeated-activate-window.html b/testing/web-platform/tests/portals/resources/portal-repeated-activate-window.html
new file mode 100644
index 0000000000..e716034eff
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portal-repeated-activate-window.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<body>
+ <portal src="simple-portal-adopts-and-activates-predecessor.html">
+ <script>
+ function activate() {
+ var portal = document.querySelector("portal");
+ portal.activate().then(() => document.body.removeChild(portal));
+ }
+
+ var count = 0;
+ window.onportalactivate = e => {
+ ++count;
+ if (count == 1) {
+ e.adoptPredecessor().activate();
+ } else {
+ window.opener.postMessage("done", "*");
+ }
+ };
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portals-adopt-predecessor-portal.html b/testing/web-platform/tests/portals/resources/portals-adopt-predecessor-portal.html
new file mode 100644
index 0000000000..b838b38be1
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portals-adopt-predecessor-portal.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<script>
+ var searchParams = new URL(location).searchParams;
+ var test = searchParams.get("test");
+
+ window.onportalactivate = function(e) {
+ if (test == "adopt-once") {
+ var portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+
+ if (portal instanceof HTMLPortalElement) {
+ portal.postMessage("adopted");
+ }
+ }
+ if (test == "adopt-twice") {
+ var portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+
+ try {
+ e.adoptPredecessor();
+ } catch(e) {
+ if (e.name == "InvalidStateError") {
+ portal.postMessage("passed");
+ }
+ }
+ }
+ if (test == "adopt-after-event") {
+ setTimeout(function() {
+ try {
+ e.adoptPredecessor();
+ } catch(e) {
+ if (e.name == "InvalidStateError") {
+ var bc_test = new BroadcastChannel(`test-${test}`);
+ bc_test.postMessage("passed");
+ bc_test.close();
+ }
+ }
+ });
+ }
+ if (test == "adopt-and-activate") {
+ var portal = e.adoptPredecessor();
+ portal.activate();
+ }
+ if (test == "adopt-attach-remove") {
+ var portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+ setTimeout(() => {
+ document.body.removeChild(portal);
+ var bc_test = new BroadcastChannel(`test-${test}`);
+ bc_test.postMessage("passed");
+ bc_test.close();
+ });
+ }
+ if (test == "adopt-and-discard") {
+ var portal = e.adoptPredecessor();
+ setTimeout(() => {
+ // portal should be inactive and activate should fail.
+ portal.activate().catch(e => {
+ if (e.name == "InvalidStateError") {
+ var bc_test = new BroadcastChannel(`test-${test}`);
+ bc_test.postMessage("passed");
+ bc_test.close();
+ }
+ });
+ });
+ }
+ if (test == "adopt-to-disconnected-node") {
+ var portal = e.adoptPredecessor();
+ document.body.appendChild(portal);
+ var node = document.createElement("div");
+ node.appendChild(portal);
+ var bc_test = new BroadcastChannel(`test-${test}`);
+ bc_test.postMessage("passed");
+ bc_test.close();
+ }
+ }
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portals-adopt-predecessor.html b/testing/web-platform/tests/portals/resources/portals-adopt-predecessor.html
new file mode 100644
index 0000000000..66d47d12ac
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portals-adopt-predecessor.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<body>
+</body>
+<script>
+ var searchParams = new URL(location).searchParams;
+ var test = searchParams.get("test");
+ var portal = document.createElement("portal");
+ portal.src = `portals-adopt-predecessor-portal.html?test=${test}`;
+ portal.onload = () => {
+ portal.activate().then(() => {
+ window.addEventListener("portalactivate", e => {
+ window.opener.postMessage({test, message: "passed"}, "*");
+ });
+ window.portalHost.addEventListener("message", e => {
+ window.opener.postMessage({test, message: e.data}, "*");
+ });
+ });
+ }
+ document.body.appendChild(portal);
+</script>
diff --git a/testing/web-platform/tests/portals/resources/portals-nested-portal.html b/testing/web-platform/tests/portals/resources/portals-nested-portal.html
new file mode 100644
index 0000000000..278b32eea0
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portals-nested-portal.html
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ var portal = document.createElement("portal");
+ portal.src = "simple-portal.html";
+ portal.onload = e => window.portalHost.postMessage(e.data);
+ document.body.appendChild(portal);
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/portals-rendering-portal.html b/testing/web-platform/tests/portals/resources/portals-rendering-portal.html
new file mode 100644
index 0000000000..31b3f4a990
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/portals-rendering-portal.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<body style="background-color: green">
+ <script>
+ window.requestAnimationFrame(function(ts) {
+ window.portalHost.postMessage("loaded");
+ });
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/postmessage-referrer.sub.html b/testing/web-platform/tests/portals/resources/postmessage-referrer.sub.html
new file mode 100644
index 0000000000..c3837dc79d
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/postmessage-referrer.sub.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script>
+ let message = {
+ httpReferrer: '{{header_or_default(Referer, no-http-referrer)}}',
+ documentReferrer: document.referrer || 'no-document-referrer',
+ };
+ window.portalHost.postMessage(message);
+</script>
diff --git a/testing/web-platform/tests/portals/resources/predecessor-fires-unload-watch-unload.html b/testing/web-platform/tests/portals/resources/predecessor-fires-unload-watch-unload.html
new file mode 100644
index 0000000000..f58da48ca1
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/predecessor-fires-unload-watch-unload.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<body>
+<script>
+function nextEvent(target, type) {
+ return new Promise((resolve, reject) => target.addEventListener(type, e => resolve(e), {once: true}));
+}
+
+onload = async function() {
+ const portal = document.createElement('portal');
+ portal.src = new URL('simple-portal.html', location.href);
+ document.body.appendChild(portal);
+ await nextEvent(portal, 'load');
+
+ let firedEvents = [];
+ for (let type of ['pagehide', 'unload']) {
+ nextEvent(window, type).then(() => {
+ firedEvents.push(type);
+ localStorage.setItem('predecessor-fires-unload-events', firedEvents.join(' '));
+ });
+ }
+
+ portal.activate();
+};
+</script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/simple-portal-adopts-and-activates-predecessor.html b/testing/web-platform/tests/portals/resources/simple-portal-adopts-and-activates-predecessor.html
new file mode 100644
index 0000000000..56bfd10f64
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/simple-portal-adopts-and-activates-predecessor.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ window.onportalactivate = e => e.adoptPredecessor().activate();
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/simple-portal-adopts-predecessor.html b/testing/web-platform/tests/portals/resources/simple-portal-adopts-predecessor.html
new file mode 100644
index 0000000000..b5ea9f029d
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/simple-portal-adopts-predecessor.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ window.portalHost.postMessage("ready");
+ onportalactivate = e => document.body.appendChild(e.adoptPredecessor());
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/simple-portal.html b/testing/web-platform/tests/portals/resources/simple-portal.html
new file mode 100644
index 0000000000..7d7b678cad
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/simple-portal.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<body>
+ <script>
+ window.portalHost.postMessage("ready");
+ </script>
+</body>
diff --git a/testing/web-platform/tests/portals/resources/stash-utils.sub.js b/testing/web-platform/tests/portals/resources/stash-utils.sub.js
new file mode 100644
index 0000000000..30efe83633
--- /dev/null
+++ b/testing/web-platform/tests/portals/resources/stash-utils.sub.js
@@ -0,0 +1,43 @@
+const STASH_RESPONDER = "wss://{{host}}:{{ports[wss][0]}}/stash_responder_blocking";
+
+class StashUtils {
+ /**
+ * Sends a request to store (|key|, |tuple|) in Stash
+ * (https://web-platform-tests.org/tools/wptserve/docs/stash.html).
+ * @param {string} key A UUID that acts as a key that can be used to retrieve |value| later.
+ * @param {string} value Value to be stored in Stash.
+ * @returns {Promise} Promise that resolves once the server responds.
+ */
+ static putValue(key, value) {
+ return new Promise(resolve => {
+ const ws = new WebSocket(STASH_RESPONDER);
+ ws.onopen = () => {
+ ws.send(JSON.stringify({action: 'set', key: key, value: value}));
+ };
+ ws.onmessage = e => {
+ ws.close();
+ resolve();
+ };
+ });
+ }
+
+ /**
+ * Retrieves value associated with |key| in Stash. If no value has been
+ * associated with |key| yet, the method waits for putValue to be called with
+ * |key|, and a value to be associated, before resolving the return promise.
+ * @param {string} key A UUID that uniquely identifies the value to retrieve.
+ * @returns {Promise<string>} A promise that resolves with the value associated with |key|.
+ */
+ static takeValue(key) {
+ return new Promise(resolve => {
+ const ws = new WebSocket(STASH_RESPONDER);
+ ws.onopen = () => {
+ ws.send(JSON.stringify({action: 'get', key: key}));
+ };
+ ws.onmessage = e => {
+ ws.close();
+ resolve(JSON.parse(e.data).value);
+ };
+ });
+ }
+}