blob: d556dd43d84ab413e85055bb14d000bbdb070ec7 [file] [log] [blame]
<!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>