| <!DOCTYPE html> |
| <title>Test postMessage on HTMLPortalElement</title> |
| <meta name="timeout" content="long"> |
| <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="resources/stash-utils.sub.js"></script> |
| <script src="/common/utils.js"></script> |
| <body> |
| <input id="input"/> |
| <script> |
| const sameOriginUrl = "resources/portal-post-message-portal.html" |
| const crossOriginUrl = "http://{{hosts[alt][www]}}:{{ports[http][0]}}/portals/resources/portal-post-message-x-origin-portal.html" |
| |
| async function createAndInsertPortal(portalSrc) { |
| assert_implements("HTMLPortalElement" in self); |
| var portal = document.createElement("portal"); |
| portal.src = portalSrc; |
| document.body.append(portal); |
| |
| var loadPromise = new Promise((resolve, reject) => { |
| portal.onload = resolve; |
| }); |
| await loadPromise; |
| return portal; |
| } |
| |
| function postMessage(portal, ...postMessageArgs) { |
| return new Promise((resolve, reject) => { |
| portal.postMessage(...postMessageArgs); |
| portal.onmessage = e => { resolve(e.data); }; |
| }); |
| } |
| |
| function postMessageWithMessagePorts(portal, message) { |
| return new Promise((resolve, reject) => { |
| var channel = new MessageChannel(); |
| channel.port1.onmessage = e => { |
| channel.port1.close(); |
| resolve(e.data); |
| }; |
| portal.postMessage(message, {transfer: [channel.port2]}); |
| }); |
| } |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var message = "test message"; |
| var {origin, data, sourceIsPortalHost} = await postMessage(portal, message); |
| assert_equals(data, message); |
| assert_equals(origin, window.location.origin); |
| assert_true(sourceIsPortalHost); |
| }, "postMessage message received by portalHost"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var message = { |
| prop1: "value1", |
| prop2: 2.5, |
| prop3: [1, 2, "3"], |
| prop4: { |
| prop4_1: "value4_1" |
| } |
| } |
| var {data} = await postMessage(portal, message); |
| assert_object_equals(data, message); |
| }, "postMessage with message object"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var message = "test message"; |
| var {data} = await postMessageWithMessagePorts(portal, message); |
| assert_equals(data, message); |
| }, "postMessage with message ports and same-origin portal"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var arrayBuffer = new ArrayBuffer(5); |
| var int8View = new Int8Array(arrayBuffer); |
| for (var i = 0; i < int8View.length; i++) |
| int8View[i] = i; |
| var message = { |
| arrayBuffer: arrayBuffer |
| }; |
| var {data} = await postMessage(portal, message); |
| assert_array_equals([0, 1, 2, 3, 4], int8View); |
| assert_array_equals([0, 1, 2, 3, 4], data.array); |
| }, "postMessage with array buffer without transfer"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var arrayBuffer = new ArrayBuffer(5); |
| var int8View = new Int8Array(arrayBuffer); |
| for (var i = 0; i < int8View.length; i++) |
| int8View[i] = i; |
| var message = { |
| arrayBuffer: arrayBuffer |
| }; |
| var {data} = await postMessage(portal, message, {transfer: [arrayBuffer]}); |
| assert_equals(int8View.length, 0); |
| assert_array_equals(data.array, [0, 1, 2, 3, 4]); |
| }, "postMessage with transferred array buffer"); |
| |
| promise_test(async t => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| |
| var {gotUserActivation} = await postMessage(portal, "test"); |
| assert_false(gotUserActivation); |
| |
| var {gotUserActivation, userActivation} = await postMessage(portal, "test", {includeUserActivation: true}); |
| assert_true(gotUserActivation); |
| assert_false(userActivation.isActive); |
| assert_false(userActivation.hasBeenActive); |
| |
| await test_driver.click(document.getElementById("input")); |
| assert_true(navigator.userActivation.isActive); |
| assert_true(navigator.userActivation.hasBeenActive); |
| |
| var {userActivation} = await postMessage(portal, "test", {includeUserActivation: true}); |
| assert_true(userActivation.isActive, "should have sent gesture"); |
| assert_true(userActivation.hasBeenActive); |
| }, "postMessage with includeUserActivation"); |
| |
| promise_test(async t => { |
| var portal = document.createElement("portal"); |
| return promise_rejects_dom(t, "InvalidStateError", |
| postMessage(portal, "test message")); |
| }, "cannot call postMessage on portal without portal browsing context"); |
| |
| promise_test(async t => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| return promise_rejects_dom(t, "DataCloneError", |
| postMessage(portal, document.body)); |
| }, "postMessage should fail if message serialization fails"); |
| |
| promise_test(async t => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| return promise_rejects_js(t, TypeError, |
| postMessage(portal, "test", {transfer: [null]})); |
| }, "postMessage should fail with invalid ports"); |
| |
| async function waitForMessage(channelName) { |
| var bc = new BroadcastChannel(channelName); |
| return new Promise((resolve, reject) => { |
| bc.onmessage = e => { |
| bc.close(); |
| resolve(e.data); |
| } |
| }); |
| } |
| |
| promise_test(async t => { |
| assert_implements("HTMLPortalElement" in self); |
| window.open("resources/portal-post-message-before-activate-window.html"); |
| let {postMessageTS, activateTS} = await waitForMessage( |
| "portals-post-message-before-activate"); |
| assert_less_than_equal(postMessageTS, activateTS); |
| }, "postMessage before activate should work and preserve order"); |
| |
| promise_test(async t => { |
| assert_implements("HTMLPortalElement" in self); |
| window.open("resources/portal-post-message-during-activate-window.html"); |
| let error = await waitForMessage("portals-post-message-during-activate"); |
| assert_equals(error, "InvalidStateError"); |
| }, "postMessage during activate throws error"); |
| |
| promise_test(async t => { |
| assert_implements("HTMLPortalElement" in self); |
| window.open("resources/portal-post-message-after-activate-window.html"); |
| let error = await waitForMessage("portals-post-message-after-activate"); |
| assert_equals(error, "InvalidStateError"); |
| }, "postMessage after activate throws error"); |
| |
| const TIMEOUT_DURATION_MS = 1000; |
| |
| promise_test(async t => { |
| const key = token(); |
| const portal = await createAndInsertPortal(`${crossOriginUrl}?key=${key}`); |
| portal.postMessage('test message'); |
| t.step_timeout(() => { |
| StashUtils.putValue(key, 'passed'); |
| }, TIMEOUT_DURATION_MS); |
| const result = await StashUtils.takeValue(key); |
| assert_equals(result, 'passed'); |
| }, 'postMessage should be blocked for cross-origin portals'); |
| |
| </script> |
| </body> |