"use strict";
function receiveEventOnce(target, name) {
return new Promise(resolve => {
target.addEventListener(
name,
ev => {
resolve(ev);
},
{ once: true }
);
});
}
async function postAndTestMessageEvent(data, transfer, title) {
postMessage(data, "*", transfer);
const messagePortCount = transfer.filter(i => i instanceof MessagePort)
.length;
const ev = await receiveEventOnce(window, "message");
assert_equals(
ev.ports.length,
messagePortCount,
`Correct number of ports ${title}`
);
for (const [i, port] of ev.ports.entries()) {
assert_true(
port instanceof MessagePort,
`ports[${i}] include MessagePort ${title}`
);
}
for (const [key, value] of Object.entries(data)) {
assert_true(
ev.data[key] instanceof value.constructor,
`data.${key} has correct interface ${value.constructor.name} ${title}`
);
}
}
async function transferMessagePortWithOrder1(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ stream, port2: channel.port2 },
[stream, channel.port2],
`when transferring [${stream.constructor.name}, MessagePort]`
);
}
async function transferMessagePortWithOrder2(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ stream, port2: channel.port2 },
[channel.port2, stream],
`when transferring [MessagePort, ${stream.constructor.name}]`
);
}
async function transferMessagePortWithOrder3(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ port1: channel.port1, stream, port2: channel.port2 },
[channel.port1, stream, channel.port2],
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort]`
);
}
async function transferMessagePortWithOrder4(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{},
[channel.port1, stream, channel.port2],
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with empty data`
);
}
async function transferMessagePortWithOrder5(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ port2: channel.port2, port1: channel.port1, stream },
[channel.port1, stream, channel.port2],
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with data having different order`
);
}
async function transferMessagePortWithOrder6(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ port2: channel.port2, port1: channel.port1 },
[channel.port1, stream, channel.port2],
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with stream not being in the data`
);
}
async function transferMessagePortWithOrder7(stream) {
const channel = new MessageChannel();
await postAndTestMessageEvent(
{ stream },
[channel.port1, stream, channel.port2],
`when transferring [MessagePort, ${stream.constructor.name}, MessagePort] but with ports not being in the data`
);
}
async function transferMessagePortWith(constructor) {
await transferMessagePortWithOrder1(new constructor());
await transferMessagePortWithOrder2(new constructor());
await transferMessagePortWithOrder3(new constructor());
}
async function advancedTransferMessagePortWith(constructor) {
await transferMessagePortWithOrder4(new constructor());
await transferMessagePortWithOrder5(new constructor());
await transferMessagePortWithOrder6(new constructor());
await transferMessagePortWithOrder7(new constructor());
}
async function mixedTransferMessagePortWithOrder1() {
const channel = new MessageChannel();
const readable = new ReadableStream();
const writable = new WritableStream();
const transform = new TransformStream();
await postAndTestMessageEvent(
{
readable,
writable,
transform,
port1: channel.port1,
port2: channel.port2,
},
[readable, writable, transform, channel.port1, channel.port2],
`when transferring [ReadableStream, WritableStream, TransformStream, MessagePort, MessagePort]`
);
}
async function mixedTransferMessagePortWithOrder2() {
const channel = new MessageChannel();
const readable = new ReadableStream();
const writable = new WritableStream();
const transform = new TransformStream();
await postAndTestMessageEvent(
{ readable, writable, transform },
[transform, channel.port1, readable, channel.port2, writable],
`when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream]`
);
}
async function mixedTransferMessagePortWithOrder3() {
const channel = new MessageChannel();
const readable1 = new ReadableStream();
const readable2 = new ReadableStream();
const writable1 = new WritableStream();
const writable2 = new WritableStream();
const transform1 = new TransformStream();
const transform2 = new TransformStream();
await postAndTestMessageEvent(
{ readable1, writable1, transform1, readable2, writable2, transform2 },
[
transform2,
channel.port1,
readable1,
channel.port2,
writable2,
readable2,
writable1,
transform1,
],
`when transferring [TransformStream, MessagePort, ReadableStream, MessagePort, WritableStream, ReadableStream, WritableStream, TransformStream] but with the data having different order`
);
}
async function mixedTransferMessagePortWith() {
await mixedTransferMessagePortWithOrder1();
await mixedTransferMessagePortWithOrder2();
await mixedTransferMessagePortWithOrder3();
}
promise_test(async t => {
await transferMessagePortWith(ReadableStream);
}, "Transferring a MessagePort with a ReadableStream should set `.ports`");
promise_test(async t => {
await transferMessagePortWith(WritableStream);
}, "Transferring a MessagePort with a WritableStream should set `.ports`");
promise_test(async t => {
await transferMessagePortWith(TransformStream);
}, "Transferring a MessagePort with a TransformStream should set `.ports`");
promise_test(async t => {
await advancedTransferMessagePortWith(ReadableStream);
}, "Transferring a MessagePort with a ReadableStream should set `.ports`, advanced");
promise_test(async t => {
await advancedTransferMessagePortWith(WritableStream);
}, "Transferring a MessagePort with a WritableStream should set `.ports`, advanced");
promise_test(async t => {
await advancedTransferMessagePortWith(TransformStream);
}, "Transferring a MessagePort with a TransformStream should set `.ports`, advanced");
promise_test(async t => {
await mixedTransferMessagePortWith();
}, "Transferring a MessagePort with multiple streams should set `.ports`");
test(() => {
assert_throws_dom("DataCloneError", () =>
postMessage({ stream: new ReadableStream() }, "*")
);
}, "ReadableStream must not be serializable");
test(() => {
assert_throws_dom("DataCloneError", () =>
postMessage({ stream: new WritableStream() }, "*")
);
}, "WritableStream must not be serializable");
test(() => {
assert_throws_dom("DataCloneError", () =>
postMessage({ stream: new TransformStream() }, "*")
);
}, "TransformStream must not be serializable");