blob: b37e8afa6db05218d8e128ce1bc352ed1f892273 [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>
<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>