| <!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> |
| <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-portal.html" |
| |
| async function createAndInsertPortal(portalSrc) { |
| 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, targetOrigin) { |
| return new Promise((resolve, reject) => { |
| var channel = new MessageChannel(); |
| channel.port1.onmessage = e => { |
| channel.port1.close(); |
| resolve(e.data); |
| }; |
| portal.postMessage(message, targetOrigin, [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(crossOriginUrl); |
| 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 in cross-origin portal"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| var message = "test message"; |
| var {data} = await postMessage(portal, message); |
| assert_equals(data, message); |
| var {data} = await postMessage(portal, message, "/"); |
| assert_equals(data, message); |
| var {data} = await postMessage(portal, message, |
| "http://{{host}}:{{ports[http][0]}}"); |
| assert_equals(data, message); |
| }, "postMessage received by portal host in same-origin portal for multiple valid target origins"); |
| |
| promise_test(async () => { |
| var portal = await createAndInsertPortal(crossOriginUrl); |
| var message = "test message"; |
| var {data} = await postMessage(portal, message, |
| "http://{{hosts[alt][www]}}:{{ports[http][0]}}"); |
| assert_equals(data, message); |
| }, "postMessage received by portal host in cross-origin portal when target origin is specified"); |
| |
| 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(crossOriginUrl); |
| var message = "test message"; |
| var {data} = await postMessageWithMessagePorts(portal, message, "*"); |
| assert_equals(data, message); |
| }, "postMessage with message ports and cross-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, "*", [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(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(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(t, new TypeError(), |
| postMessage(portal, "test", "*", [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 => { |
| 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 => { |
| 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 => { |
| 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"); |
| |
| // TODO(adithyas): Use async_test instead of promise_test (for tests that |
| // use a timeout) once we implement postMessage in the other direction and |
| // no longer need to use broadcast channel. |
| const TIMEOUT_DURATION_MS = 1000; |
| |
| promise_test(async t => { |
| var portal = await createAndInsertPortal(crossOriginUrl); |
| return new Promise((resolve, reject) => { |
| postMessage(portal, "test").then(() => { reject("message delivered"); }); |
| t.step_timeout(resolve, TIMEOUT_DURATION_MS); |
| }); |
| }, "message should not be delivered to cross-origin portal when targetOrigin is not specified"); |
| |
| promise_test(async t => { |
| var portal = await createAndInsertPortal(sameOriginUrl); |
| return new Promise((resolve, reject) => { |
| postMessage(portal, "test", "http://differentorigin.com:8002").then( |
| () => { reject("message delivered"); }); |
| t.step_timeout(resolve, TIMEOUT_DURATION_MS); |
| }); |
| }, "message should not be delivered to portal when targetOrigin does not match"); |
| </script> |
| </body> |