mirror of
https://github.com/nodejs/node.git
synced 2026-10-14 10:26:26 -04:00
test: run webmessaging/broadcastchannel WPT
Refs: https://github.com/nodejs/node/issues/38803 PR-URL: https://github.com/nodejs/node/pull/41962 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Mestery <mestery@protonmail.com>
This commit is contained in:
1 parent
fdab137e64
commit
9caceb28aa
20 files changed
+1353
No files matched your search
@@ -0,0 +1,120 @@
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('eventType');
|
||||
let c2 = new BroadcastChannel('eventType');
|
||||
|
||||
c2.onmessage = t.step_func(e => {
|
||||
assert_true(e instanceof MessageEvent);
|
||||
assert_equals(e.target, c2);
|
||||
assert_equals(e.type, 'message');
|
||||
assert_equals(e.origin, location.origin, 'origin');
|
||||
assert_equals(e.data, 'hello world');
|
||||
assert_equals(e.source, null, 'source');
|
||||
t.done();
|
||||
});
|
||||
c1.postMessage('hello world');
|
||||
}, 'postMessage results in correct event');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('order');
|
||||
let c2 = new BroadcastChannel('order');
|
||||
let c3 = new BroadcastChannel('order');
|
||||
|
||||
let events = [];
|
||||
let doneCount = 0;
|
||||
let handler = t.step_func(e => {
|
||||
events.push(e);
|
||||
if (e.data == 'done') {
|
||||
doneCount++;
|
||||
if (doneCount == 2) {
|
||||
assert_equals(events.length, 6);
|
||||
assert_equals(events[0].target, c2, 'target for event 0');
|
||||
assert_equals(events[0].data, 'from c1');
|
||||
assert_equals(events[1].target, c3, 'target for event 1');
|
||||
assert_equals(events[1].data, 'from c1');
|
||||
assert_equals(events[2].target, c1, 'target for event 2');
|
||||
assert_equals(events[2].data, 'from c3');
|
||||
assert_equals(events[3].target, c2, 'target for event 3');
|
||||
assert_equals(events[3].data, 'from c3');
|
||||
assert_equals(events[4].target, c1, 'target for event 4');
|
||||
assert_equals(events[4].data, 'done');
|
||||
assert_equals(events[5].target, c3, 'target for event 5');
|
||||
assert_equals(events[5].data, 'done');
|
||||
t.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
c1.onmessage = handler;
|
||||
c2.onmessage = handler;
|
||||
c3.onmessage = handler;
|
||||
|
||||
c1.postMessage('from c1');
|
||||
c3.postMessage('from c3');
|
||||
c2.postMessage('done');
|
||||
}, 'messages are delivered in port creation order');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('closed');
|
||||
let c2 = new BroadcastChannel('closed');
|
||||
let c3 = new BroadcastChannel('closed');
|
||||
|
||||
c2.onmessage = t.unreached_func();
|
||||
c2.close();
|
||||
c3.onmessage = t.step_func(() => t.done());
|
||||
c1.postMessage('test');
|
||||
}, 'messages aren\'t delivered to a closed port');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('closed');
|
||||
let c2 = new BroadcastChannel('closed');
|
||||
let c3 = new BroadcastChannel('closed');
|
||||
|
||||
c2.onmessage = t.unreached_func();
|
||||
c3.onmessage = t.step_func(() => t.done());
|
||||
c1.postMessage('test');
|
||||
c2.close();
|
||||
}, 'messages aren\'t delivered to a port closed after calling postMessage.');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('create-in-onmessage');
|
||||
let c2 = new BroadcastChannel('create-in-onmessage');
|
||||
|
||||
c2.onmessage = t.step_func(e => {
|
||||
assert_equals(e.data, 'first');
|
||||
c2.close();
|
||||
let c3 = new BroadcastChannel('create-in-onmessage');
|
||||
c3.onmessage = t.step_func(event => {
|
||||
assert_equals(event.data, 'done');
|
||||
t.done();
|
||||
});
|
||||
c1.postMessage('done');
|
||||
});
|
||||
c1.postMessage('first');
|
||||
c2.postMessage('second');
|
||||
}, 'closing and creating channels during message delivery works correctly');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('close-in-onmessage');
|
||||
let c2 = new BroadcastChannel('close-in-onmessage');
|
||||
let c3 = new BroadcastChannel('close-in-onmessage');
|
||||
let events = [];
|
||||
c1.onmessage = e => events.push('c1: ' + e.data);
|
||||
c2.onmessage = e => events.push('c2: ' + e.data);
|
||||
c3.onmessage = e => events.push('c3: ' + e.data);
|
||||
|
||||
// c2 closes itself when it receives the first message
|
||||
c2.addEventListener('message', e => {
|
||||
c2.close();
|
||||
});
|
||||
|
||||
c3.addEventListener('message', t.step_func(e => {
|
||||
if (e.data == 'done') {
|
||||
assert_array_equals(events, [
|
||||
'c2: first',
|
||||
'c3: first',
|
||||
'c3: done']);
|
||||
t.done();
|
||||
}
|
||||
}));
|
||||
c1.postMessage('first');
|
||||
c1.postMessage('done');
|
||||
}, 'Closing a channel in onmessage prevents already queued tasks from firing onmessage events');
|
||||
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
async_test(t => {
|
||||
const c1 = new BroadcastChannel('blob');
|
||||
const c2 = new BroadcastChannel('blob');
|
||||
const c3 = new BroadcastChannel('blob');
|
||||
|
||||
let readCount = 0;
|
||||
c2.onmessage = t.step_func(e => {
|
||||
// check blob
|
||||
assert_true('blob' in e.data);
|
||||
assert_true(e.data.blob instanceof Blob);
|
||||
assert_equals(e.data.blob.size, 6);
|
||||
const reader = new FileReader();
|
||||
reader.onerror = t.unreached_func();
|
||||
reader.onload = t.step_func(() => {
|
||||
assert_equals(reader.result, 'foobar');
|
||||
if (++readCount == 2)
|
||||
t.done();
|
||||
});
|
||||
reader.readAsText(e.data.blob);
|
||||
});
|
||||
c3.onmessage = c2.onmessage;
|
||||
(() => {
|
||||
c1.postMessage({blob: new Blob(['foo', 'bar'])});
|
||||
})();
|
||||
// TODO(https://github.com/web-platform-tests/wpt/issues/7899): Change to
|
||||
// some sort of cross-browser GC trigger.
|
||||
if (self.gc) self.gc();
|
||||
}, 'Blobs work on BroadcastChannel');
|
||||
|
||||
async_test(t => {
|
||||
const c1 = new BroadcastChannel('blobworker');
|
||||
const c2 = new BroadcastChannel('blobworker');
|
||||
const events = [];
|
||||
|
||||
const verifyEvents = function() {
|
||||
assert_equals(events.length, 5);
|
||||
assert_equals(events[0], 'from worker');
|
||||
assert_equals(events[1], 'from worker');
|
||||
assert_true(events[2].blob instanceof Blob);
|
||||
assert_equals(events[2].blob.size, 11);
|
||||
assert_true(events[3].blob instanceof Blob);
|
||||
assert_equals(events[3].blob.size, 11);
|
||||
assert_equals(events[4], 'done');
|
||||
const reader = new FileReader();
|
||||
reader.onerror = t.unreached_func();
|
||||
reader.onload = t.step_func(() => {
|
||||
assert_equals(reader.result, 'hello-world');
|
||||
t.done();
|
||||
});
|
||||
reader.readAsText(events[3].blob);
|
||||
};
|
||||
|
||||
let receivedDone = false;
|
||||
let receivedWorkerDone = false;
|
||||
|
||||
c1.onmessage = e => events.push(e.data);
|
||||
c2.onmessage = e => events.push(e.data);
|
||||
c2.addEventListener('message', t.step_func(e => {
|
||||
if (e.data.blob)
|
||||
c1.postMessage('done');
|
||||
if (e.data === 'done')
|
||||
receivedDone = true;
|
||||
if (receivedDone && receivedWorkerDone)
|
||||
verifyEvents();
|
||||
}));
|
||||
|
||||
const worker = new Worker('resources/worker.js');
|
||||
worker.onmessage = t.step_func(e => {
|
||||
receivedWorkerDone = true;
|
||||
if (receivedDone && receivedWorkerDone)
|
||||
verifyEvents();
|
||||
});
|
||||
worker.postMessage({channel: 'blobworker'});
|
||||
worker.postMessage({blob: ['hello-world']});
|
||||
|
||||
}, 'Blobs work with workers on BroadcastChannel');
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,38 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<!-- Pull in the with_iframe helper function from the service worker tests -->
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
const events = [];
|
||||
|
||||
function testCompletion(t) {
|
||||
return new Promise((resolve) => {
|
||||
window.addEventListener("message", t.step_func(e => {
|
||||
if (e.data == 'done') {
|
||||
assert_equals(events.length, 0);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
|
||||
const bc0 = new BroadcastChannel('no-cross-origin-messages');
|
||||
bc0.onmessage = e => {window.events.push(e);};
|
||||
|
||||
const testResults = testCompletion(t);
|
||||
const url = get_host_info().HTTPS_NOTSAMESITE_ORIGIN +
|
||||
'/webmessaging/broadcastchannel/resources/cross-origin.html';
|
||||
await with_iframe(url);
|
||||
|
||||
return testResults;
|
||||
}, "Messages aren't delivered across origins");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="/common/utils.js"></script>
|
||||
<script src="/common/dispatcher/dispatcher.js"></script>
|
||||
<!-- Pull in executor_path needed by newPopup / newIframe -->
|
||||
<script src="/html/cross-origin-embedder-policy/credentialless/resources/common.js"></script>
|
||||
<!-- Pull in newPopup / newIframe -->
|
||||
<script src="/html/cross-origin-embedder-policy/anonymous-iframe/resources/common.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
const emit_script = (channel_name, message, done_queue_name) => `
|
||||
const bc = new BroadcastChannel("${channel_name}");
|
||||
bc.postMessage("${message}");
|
||||
send("${done_queue_name}", "done");
|
||||
`;
|
||||
|
||||
promise_test(async t => {
|
||||
const origin = get_host_info().HTTPS_ORIGIN;
|
||||
const not_same_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
|
||||
const response_queue_uuid = token();
|
||||
|
||||
const popup_init_script = `
|
||||
const importScript = ${importScript};
|
||||
await importScript("/html/cross-origin-embedder-policy/credentialless" +
|
||||
"/resources/common.js");
|
||||
await importScript("/html/cross-origin-embedder-policy/anonymous-iframe" +
|
||||
"/resources/common.js");
|
||||
await importScript("/common/utils.js");
|
||||
send("${response_queue_uuid}", newIframe("${origin}"));
|
||||
`;
|
||||
|
||||
// Create a same-origin iframe in a cross-site popup.
|
||||
const not_same_site_popup_uuid = newPopup(t, not_same_site_origin);
|
||||
send(not_same_site_popup_uuid, popup_init_script);
|
||||
const iframe_1_uuid = await receive(response_queue_uuid);
|
||||
|
||||
// Create a same-origin iframe in a same-site popup.
|
||||
const same_origin_popup_uuid = newPopup(t, origin);
|
||||
send(same_origin_popup_uuid, popup_init_script);
|
||||
const iframe_2_uuid = await receive(response_queue_uuid);
|
||||
|
||||
const channel_name = token();
|
||||
const bc = new BroadcastChannel(channel_name);
|
||||
bc.onmessage = t.step_func(e => {
|
||||
assert_equals(e.data, "msg from iframe2");
|
||||
t.done();
|
||||
});
|
||||
|
||||
// Instruct the not-same-top-level-site iframe to send a message on the BC
|
||||
// channel we are listening on. This message should not be received since
|
||||
// the iframe should be in a different partition.
|
||||
send(iframe_1_uuid,
|
||||
emit_script(channel_name, "msg from iframe1", response_queue_uuid));
|
||||
assert_equals(await receive(response_queue_uuid), "done");
|
||||
|
||||
// Now instruct the same-top-level-site iframe to send a BC message. By
|
||||
// the time we send the script to execute, have it send the BC message,
|
||||
// and then receive the BC message in our BC instance, it should be
|
||||
// reasonable to assume that the message from the first iframe would have
|
||||
// been delivered if it was going to be.
|
||||
send(iframe_2_uuid,
|
||||
emit_script(channel_name, "msg from iframe2", response_queue_uuid));
|
||||
assert_equals(await receive(response_queue_uuid), "done");
|
||||
|
||||
}, "BroadcastChannel messages aren't received from a cross-partition iframe");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,174 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<!-- Pull in the with_iframe helper function from the service worker tests -->
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const TEST_IFRAME_CLASS_NAME = 'test-iframe';
|
||||
const events = [];
|
||||
var bc1;
|
||||
const DONE_MSG = 'done';
|
||||
|
||||
function DetachedIframeTestCheckForOneMessage(t) {
|
||||
return new Promise((resolve) => {
|
||||
bc1.onmessage = t.step_func(e => {
|
||||
events.push(e);
|
||||
if (e.data == DONE_MSG) {
|
||||
assert_equals(events.length, 1);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const IframeAction = {
|
||||
REMOVE_BEFORE_CREATION: 'remove-before-creation',
|
||||
REMOVE_AFTER_CREATION: 'remove-after-creation',
|
||||
};
|
||||
|
||||
async function doMessageSentTest(t, channelName, action) {
|
||||
await with_iframe('about:blank');
|
||||
const iframe = document.getElementsByClassName(TEST_IFRAME_CLASS_NAME)[0];
|
||||
const iframe_BroadcastChannel = iframe.contentWindow.BroadcastChannel;
|
||||
|
||||
if (action === IframeAction.REMOVE_BEFORE_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
events.length = 0;
|
||||
|
||||
bc1 = new BroadcastChannel(channelName);
|
||||
const bc2 = new BroadcastChannel(channelName);
|
||||
const iframe_bc = new iframe_BroadcastChannel(channelName);
|
||||
|
||||
if (action === IframeAction.REMOVE_AFTER_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
const testResultsPromise = DetachedIframeTestCheckForOneMessage(t);
|
||||
|
||||
iframe_bc.postMessage('test');
|
||||
bc2.postMessage(DONE_MSG);
|
||||
|
||||
bc2.close();
|
||||
iframe_bc.close();
|
||||
t.add_cleanup(() => bc1.close());
|
||||
|
||||
return testResultsPromise;
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageSentTest(
|
||||
t, 'postMessage-from-detached-iframe-pre',
|
||||
IframeAction.REMOVE_AFTER_CREATION);
|
||||
}, 'BroadcastChannel messages from detached iframe to parent should be ignored (BC created before detaching)');
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageSentTest(
|
||||
t, 'postMessage-from-detached-iframe-post',
|
||||
IframeAction.REMOVE_BEFORE_CREATION);
|
||||
}, 'BroadcastChannel messages from detached iframe to parent should be ignored (BC created after detaching)');
|
||||
|
||||
|
||||
async function doMessageReceivedTest(t, channelName, action) {
|
||||
await with_iframe('about:blank');
|
||||
const iframe = document.getElementsByClassName(TEST_IFRAME_CLASS_NAME)[0];
|
||||
const iframe_BroadcastChannel = iframe.contentWindow.BroadcastChannel;
|
||||
|
||||
if (action === IframeAction.REMOVE_BEFORE_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
events.length = 0;
|
||||
|
||||
// `iframe_bc` must be created first so that it receives messages before
|
||||
// `bc1`. That way we can tell whether `iframe_bc` received a message by
|
||||
// inspecting `events` in the `bc1` message handler.
|
||||
const iframe_bc = new iframe_BroadcastChannel(channelName);
|
||||
iframe_bc.onmessage = e => {
|
||||
events.push(e)
|
||||
};
|
||||
bc1 = new BroadcastChannel(channelName);
|
||||
const bc2 = new BroadcastChannel(channelName);
|
||||
|
||||
if (action === IframeAction.REMOVE_AFTER_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
const testResultsPromise = DetachedIframeTestCheckForOneMessage(t);
|
||||
bc2.postMessage(DONE_MSG);
|
||||
|
||||
bc2.close();
|
||||
iframe_bc.close();
|
||||
t.add_cleanup(() => bc1.close());
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageReceivedTest(
|
||||
t, 'postMessage-to-detached-iframe-pre',
|
||||
IframeAction.REMOVE_AFTER_CREATION);
|
||||
}, 'BroadcastChannel messages from parent to detached iframe should be ignored (BC created before detaching)');
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageReceivedTest(
|
||||
t, 'postMessage-to-detached-iframe-post',
|
||||
IframeAction.REMOVE_BEFORE_CREATION);
|
||||
}, 'BroadcastChannel messages from parent to detached iframe should be ignored (BC created after detaching)');
|
||||
|
||||
|
||||
async function doMessageSendReceiveTest(t, channelName, action) {
|
||||
await with_iframe('about:blank');
|
||||
const iframe = document.getElementsByClassName(TEST_IFRAME_CLASS_NAME)[0];
|
||||
const iframe_BroadcastChannel = iframe.contentWindow.BroadcastChannel;
|
||||
|
||||
if (action === IframeAction.REMOVE_BEFORE_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
const iframe_bc1 = new iframe_BroadcastChannel(channelName);
|
||||
const iframe_bc2 = new iframe_BroadcastChannel(channelName);
|
||||
iframe_bc1.onmessage = t.unreached_func(
|
||||
'Detached iframe BroadcastChannel instance received message unexpectedly');
|
||||
|
||||
if (action === IframeAction.REMOVE_AFTER_CREATION) {
|
||||
iframe.remove();
|
||||
}
|
||||
|
||||
iframe_bc2.postMessage(DONE_MSG);
|
||||
|
||||
iframe_bc2.close();
|
||||
t.add_cleanup(() => iframe_bc1.close());
|
||||
|
||||
// To avoid calling t.step_timeout here, instead just create two new
|
||||
// BroadcastChannel instances and complete the test when a message is passed
|
||||
// between them. Per the spec, all "BroadcastChannel objects whose relevant
|
||||
// agents are the same" must have messages delivered to them in creation
|
||||
// order, so if we get this message then it's safe to assume the earlier
|
||||
// message would have been delivered if it was going to be.
|
||||
const bc1 = new BroadcastChannel(channelName);
|
||||
const bc2 = new BroadcastChannel(channelName);
|
||||
return new Promise((resolve) => {
|
||||
bc1.onmessage = t.step_func(e => {
|
||||
resolve();
|
||||
});
|
||||
bc2.postMessage(DONE_MSG);
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageSendReceiveTest(
|
||||
t, 'postMessage-within-detached-iframe-pre',
|
||||
IframeAction.REMOVE_AFTER_CREATION);
|
||||
}, 'BroadcastChannel messages within detached iframe should be ignored (BCs created before detaching)');
|
||||
|
||||
promise_test(async t => {
|
||||
return doMessageSendReceiveTest(
|
||||
t, 'postMessage-within-detached-iframe-post',
|
||||
IframeAction.REMOVE_BEFORE_CREATION);
|
||||
}, 'BroadcastChannel messages within detached iframe should be ignored (BCs created after detaching)');
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,65 @@
|
||||
test(() => assert_throws_js(TypeError, () => new BroadcastChannel()),
|
||||
'Should throw if no name is provided');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel(null);
|
||||
assert_equals(c.name, 'null');
|
||||
}, 'Null name should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel(undefined);
|
||||
assert_equals(c.name, 'undefined');
|
||||
}, 'Undefined name should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('fooBar');
|
||||
assert_equals(c.name, 'fooBar');
|
||||
}, 'Non-empty name should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel(123);
|
||||
assert_equals(c.name, '123');
|
||||
}, 'Non-string name should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
assert_throws_js(TypeError, () => c.postMessage());
|
||||
}, 'postMessage without parameters should throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
c.postMessage(null);
|
||||
}, 'postMessage with null should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
c.close();
|
||||
}, 'close should not throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
c.close();
|
||||
c.close();
|
||||
}, 'close should not throw when called multiple times');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
c.close();
|
||||
assert_throws_dom('InvalidStateError', () => c.postMessage(''));
|
||||
}, 'postMessage after close should throw');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
assert_not_equals(c.onmessage, undefined);
|
||||
}, 'BroadcastChannel should have an onmessage event');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
assert_throws_dom('DataCloneError', () => c.postMessage(Symbol()));
|
||||
}, 'postMessage should throw with uncloneable data');
|
||||
|
||||
test(() => {
|
||||
let c = new BroadcastChannel('');
|
||||
c.close();
|
||||
assert_throws_dom('InvalidStateError', () => c.postMessage(Symbol()));
|
||||
}, 'postMessage should throw InvalidStateError after close, even with uncloneable data');
|
||||
@@ -0,0 +1,35 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title></title>
|
||||
<script src=/resources/testharness.js></script>
|
||||
<script src=/resources/testharnessreport.js></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
<!--
|
||||
promise_test(t => {
|
||||
return new Promise((resolve) => {
|
||||
let ifr = document.createElement("iframe");
|
||||
ifr.src =
|
||||
"data:text/html,<script> let bc = new BroadcastChannel(\"test\");" +
|
||||
"bc.onmessage = (e) => {" +
|
||||
" if (e.data == \"ping\") bc.postMessage('pong');"+
|
||||
" else parent.postMessage({workerMessageOrigin: e.data, messageOrigin: e.origin}, \"*\"); };" +
|
||||
"new Worker(URL.createObjectURL(new Blob([\"" +
|
||||
"let bc2 = new BroadcastChannel('test'); bc2.postMessage('ping'); " +
|
||||
"bc2.onmessage = e => bc2.postMessage(e.origin);" +
|
||||
"\"], {type: 'text/javascript'}))); </script>";
|
||||
window.addEventListener("message", t.step_func(e => {
|
||||
assert_equals(e.data.workerMessageOrigin, "null");
|
||||
assert_equals(e.data.messageOrigin, "null");
|
||||
resolve();
|
||||
}), {once: true});
|
||||
document.body.appendChild(ifr);
|
||||
});
|
||||
}, "Opaque origin should be serialized to \"null\"");
|
||||
//-->
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<!-- Pull in the with_iframe helper function from the service worker tests -->
|
||||
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
const BC0_FIRST_MSG = 'from BC0 - first';
|
||||
const BC1_FIRST_MSG = 'from BC1 - first';
|
||||
const BC2_FIRST_MSG = 'from BC2 - first';
|
||||
const BC3_FIRST_MSG = 'from BC3 - first';
|
||||
const BC0_SECOND_MSG = 'from BC0 - second';
|
||||
const BC1_SECOND_MSG = 'from BC1 - second';
|
||||
const BC2_SECOND_MSG = 'from BC2 - second';
|
||||
const BC3_SECOND_MSG = 'done';
|
||||
const BC0_TARGET_NAME = 'BC1';
|
||||
const BC1_TARGET_NAME = 'BC1';
|
||||
const BC2_TARGET_NAME = 'BC2';
|
||||
const BC3_TARGET_NAME = 'BC3';
|
||||
const MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME = 'multi-frame-order';
|
||||
|
||||
const bc0 = new BroadcastChannel(MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME);
|
||||
const messages = [];
|
||||
|
||||
function logReceivedMessage(targetname, e) {
|
||||
messages.push({'target': targetname, 'data': e.data});
|
||||
}
|
||||
|
||||
function postMessagesToChannel() {
|
||||
return new Promise((resolve) => {
|
||||
bc0.postMessage(BC0_FIRST_MSG);
|
||||
bc0.postMessage(BC0_SECOND_MSG);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
// Expected flow of messages between the BroadcastChannel objects (based on
|
||||
// the requirement that messages get delivered to BroadcastChannel objects
|
||||
// "in creation order, oldest first") and comments describing the actions
|
||||
// taken in response to each event
|
||||
const EXPECTED_RESULTS = [
|
||||
// -> BC0 sends two messages, BC1 and BC2 are connected to the channel
|
||||
|
||||
{'data': BC0_FIRST_MSG, 'target': BC1_TARGET_NAME},
|
||||
// -> BC1 Creates BC3 and sends first message
|
||||
|
||||
{'data': BC0_FIRST_MSG, 'target': BC2_TARGET_NAME},
|
||||
// -> BC2 sends two messages
|
||||
|
||||
// BC3 isn't expected to receive the messages sent before it was created, so
|
||||
// no corresponding entries here for messages from BC0.
|
||||
|
||||
{'data': BC0_SECOND_MSG, 'target': BC1_TARGET_NAME},
|
||||
// -> BC1 sends second message
|
||||
|
||||
{'data': BC0_SECOND_MSG, 'target': BC2_TARGET_NAME},
|
||||
// -> BC2 closes
|
||||
|
||||
{'data': BC1_FIRST_MSG, 'target': BC0_TARGET_NAME},
|
||||
|
||||
{'data': BC1_FIRST_MSG, 'target': BC3_TARGET_NAME},
|
||||
// -> BC3 sends first message
|
||||
|
||||
{'data': BC2_FIRST_MSG, 'target': BC0_TARGET_NAME},
|
||||
|
||||
{'data': BC2_FIRST_MSG, 'target': BC1_TARGET_NAME},
|
||||
// -> BC1 closes
|
||||
|
||||
{'data': BC2_FIRST_MSG, 'target': BC3_TARGET_NAME},
|
||||
// -> BC3 sends second message
|
||||
|
||||
{'data': BC2_SECOND_MSG, 'target': BC0_TARGET_NAME},
|
||||
|
||||
{'data': BC2_SECOND_MSG, 'target': BC3_TARGET_NAME},
|
||||
// -> BC3 closes
|
||||
|
||||
{'data': BC1_SECOND_MSG, 'target': BC0_TARGET_NAME},
|
||||
|
||||
{'data': BC3_FIRST_MSG, 'target': BC0_TARGET_NAME},
|
||||
|
||||
{'data': BC3_SECOND_MSG, 'target': BC0_TARGET_NAME},
|
||||
];
|
||||
|
||||
function testCompletion(t) {
|
||||
return new Promise((resolve) => {
|
||||
bc0.onmessage = t.step_func(e => {
|
||||
logReceivedMessage(BC0_TARGET_NAME, e);
|
||||
if (e.data == BC3_SECOND_MSG) {
|
||||
assert_equals(messages.length, EXPECTED_RESULTS.length);
|
||||
for(var i = 0; i < messages.length; i++) {
|
||||
assert_equals(messages[i].target, EXPECTED_RESULTS[i].target, `Message ${i+1} has unexpected target`);
|
||||
assert_equals(messages[i].data, EXPECTED_RESULTS[i].data, `Message ${i+1} has unexpected message contents`);
|
||||
}
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
|
||||
const testResults = testCompletion(t);
|
||||
// Await them sequentially because we need the BroadcastChannel object in
|
||||
// iframe1 to be created first, we need the BroadcastChannel object in
|
||||
// iframe2 to be created second, and then we only want to call
|
||||
// postMessagesToChannel once both BroadcastChannels have been created.
|
||||
await with_iframe('resources/ordering.html?id=iframe1');
|
||||
await with_iframe('resources/ordering.html?id=iframe2');
|
||||
await postMessagesToChannel();
|
||||
return testResults;
|
||||
}, "Messages are delivered in port creation order across multiple frames");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
@@ -0,0 +1,10 @@
|
||||
async_test(t => {
|
||||
const crossOriginURL = new URL("resources/origin.html", self.location.href).href.replace("://", "://天気の良い日."),
|
||||
frame = document.createElement("iframe");
|
||||
frame.src = crossOriginURL;
|
||||
document.body.appendChild(frame);
|
||||
t.add_cleanup(() => frame.remove());
|
||||
self.onmessage = t.step_func_done(e => {
|
||||
assert_equals(e.data, self.origin.replace("://", "://xn--n8j6ds53lwwkrqhv28a."));
|
||||
});
|
||||
}, "Serialization of BroadcastChannel origin");
|
||||
@@ -0,0 +1,15 @@
|
||||
<body></body>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
bc1 = new BroadcastChannel('no-cross-origin-messages');
|
||||
bc2 = new BroadcastChannel('no-cross-origin-messages');
|
||||
bc2.onmessage = e => {
|
||||
parent.postMessage('done', "*");
|
||||
};
|
||||
// Post a message on bc1 and once we receive it in bc2, we know that the
|
||||
// message should have been sent to bc0 if messages were being passed
|
||||
// across origin (assuming compliance with the spec regarding message
|
||||
// delivery in port creation order).
|
||||
bc1.postMessage('ignition');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,78 @@
|
||||
<body></body>
|
||||
<script>
|
||||
const BC0_FIRST_MSG = 'from BC0 - first';
|
||||
const BC1_FIRST_MSG = 'from BC1 - first';
|
||||
const BC2_FIRST_MSG = 'from BC2 - first';
|
||||
const BC3_FIRST_MSG = 'from BC3 - first';
|
||||
const BC0_SECOND_MSG = 'from BC0 - second';
|
||||
const BC1_SECOND_MSG = 'from BC1 - second';
|
||||
const BC2_SECOND_MSG = 'from BC2 - second';
|
||||
const BC3_SECOND_MSG = 'done';
|
||||
const BC0_TARGET_NAME = 'BC1';
|
||||
const BC1_TARGET_NAME = 'BC1';
|
||||
const BC2_TARGET_NAME = 'BC2';
|
||||
const BC3_TARGET_NAME = 'BC3';
|
||||
const MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME = 'multi-frame-order';
|
||||
|
||||
var bc1, bc2, bc3;
|
||||
var sentMessageCountForBc1 = 0;
|
||||
var sentMessageCountForBc2 = 0;
|
||||
var sentMessageCountForBc3 = 0;
|
||||
|
||||
var bc1_handler = e => {
|
||||
window.top.logReceivedMessage(BC1_TARGET_NAME, e);
|
||||
switch(sentMessageCountForBc1) {
|
||||
case 0:
|
||||
bc3 = new BroadcastChannel(MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME);
|
||||
bc3.onmessage = bc3_handler;
|
||||
bc1.postMessage(BC1_FIRST_MSG);
|
||||
break;
|
||||
case 1:
|
||||
bc1.postMessage(BC1_SECOND_MSG);
|
||||
break;
|
||||
case 2:
|
||||
bc1.close();
|
||||
return;
|
||||
}
|
||||
sentMessageCountForBc1 += 1;
|
||||
}
|
||||
var bc2_handler = e => {
|
||||
window.top.logReceivedMessage(BC2_TARGET_NAME, e);
|
||||
switch(sentMessageCountForBc2) {
|
||||
case 0:
|
||||
bc2.postMessage(BC2_FIRST_MSG);
|
||||
bc2.postMessage(BC2_SECOND_MSG);
|
||||
sentMessageCountForBc2 += 2;
|
||||
break;
|
||||
case 2:
|
||||
bc2.close();
|
||||
return;
|
||||
}
|
||||
};
|
||||
var bc3_handler = e => {
|
||||
window.top.logReceivedMessage(BC3_TARGET_NAME, e);
|
||||
switch(sentMessageCountForBc3) {
|
||||
case 0:
|
||||
bc3.postMessage(BC3_FIRST_MSG);
|
||||
break;
|
||||
case 1:
|
||||
bc3.postMessage(BC3_SECOND_MSG);
|
||||
break;
|
||||
case 2:
|
||||
bc3.close();
|
||||
return;
|
||||
}
|
||||
sentMessageCountForBc3 += 1;
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('id') === 'iframe1') {
|
||||
bc1 = new BroadcastChannel(MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME);
|
||||
bc1.onmessage = bc1_handler;
|
||||
} else if (params.get('id') === 'iframe2') {
|
||||
bc2 = new BroadcastChannel(MULTI_FRAME_ORDERING_TEST_CHANNEL_NAME);
|
||||
bc2.onmessage = bc2_handler;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,8 @@
|
||||
<script>
|
||||
const bc1 = new BroadcastChannel("ladila"),
|
||||
bc2 = new BroadcastChannel("ladila");
|
||||
bc2.onmessage = e => {
|
||||
parent.postMessage(e.origin, "*");
|
||||
}
|
||||
bc1.postMessage("does not matter");
|
||||
</script>
|
||||
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script>
|
||||
try {
|
||||
let c = new BroadcastChannel('foo');
|
||||
parent.postMessage('Created', '*');
|
||||
} catch (e) {
|
||||
parent.postMessage('Exception: ' + e.name, '*');
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,15 @@
|
||||
let promise_func = null;
|
||||
let promise = new Promise(resolve => promise_func = resolve);
|
||||
|
||||
const SERVICE_WORKER_TEST_CHANNEL_NAME = 'service worker';
|
||||
const bc3 = new BroadcastChannel(SERVICE_WORKER_TEST_CHANNEL_NAME);
|
||||
bc3.onmessage = e => {
|
||||
bc3.postMessage('done');
|
||||
promise_func();
|
||||
};
|
||||
bc3.postMessage('from worker');
|
||||
|
||||
// Ensure that the worker stays alive for the duration of the test
|
||||
self.addEventListener('install', evt => {
|
||||
evt.waitUntil(promise);
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
var c;
|
||||
|
||||
function handler(e, reply) {
|
||||
if (e.data.ping) {
|
||||
c.postMessage(e.data.ping);
|
||||
return;
|
||||
}
|
||||
if (e.data.blob) {
|
||||
(() => {
|
||||
c.postMessage({blob: new Blob(e.data.blob)});
|
||||
})();
|
||||
// TODO(https://github.com/web-platform-tests/wpt/issues/7899): Change to
|
||||
// some sort of cross-browser GC trigger.
|
||||
if (self.gc) self.gc();
|
||||
}
|
||||
c = new BroadcastChannel(e.data.channel);
|
||||
let messages = [];
|
||||
c.onmessage = e => {
|
||||
if (e.data === 'ready') {
|
||||
// Ignore any 'ready' messages from the other thread since there could
|
||||
// be some race conditions between this BroadcastChannel instance
|
||||
// being created / ready to receive messages and the message being sent.
|
||||
return;
|
||||
}
|
||||
messages.push(e.data);
|
||||
if (e.data == 'done')
|
||||
reply(messages);
|
||||
};
|
||||
c.postMessage('from worker');
|
||||
}
|
||||
|
||||
onmessage = e => handler(e, postMessage);
|
||||
|
||||
onconnect = e => {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = e => handler(e, msg => port.postMessage(msg));
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Creating BroadcastChannel in an opaque origin</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
async_test(t => {
|
||||
self.onmessage = t.step_func(e => {
|
||||
assert_equals(e.data, 'Created');
|
||||
t.done();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<iframe sandbox="allow-scripts" src="resources/sandboxed.html"></iframe>
|
||||
</body>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
|
||||
const SERVICE_WORKER_TEST_CHANNEL_NAME = 'service worker';
|
||||
const events = [];
|
||||
const c1 = new BroadcastChannel(SERVICE_WORKER_TEST_CHANNEL_NAME);
|
||||
const c2 = new BroadcastChannel(SERVICE_WORKER_TEST_CHANNEL_NAME);
|
||||
c1.onmessage = e => events.push(e);
|
||||
c2.onmessage = e => events.push(e);
|
||||
|
||||
function testCompletion(t) {
|
||||
return new Promise((resolve) => {
|
||||
c2.addEventListener("message", t.step_func(e => {
|
||||
if (e.data == 'from worker') {
|
||||
c2.postMessage('from c2');
|
||||
} else if (e.data == 'done') {
|
||||
assert_equals(events.length, 5);
|
||||
assert_equals(events[0].data, 'from worker');
|
||||
assert_equals(events[0].target, c1);
|
||||
assert_equals(events[1].data, 'from worker');
|
||||
assert_equals(events[1].target, c2);
|
||||
assert_equals(events[2].data, 'from c2');
|
||||
assert_equals(events[3].data, 'done');
|
||||
assert_equals(events[3].target, c1);
|
||||
assert_equals(events[4].data, 'done');
|
||||
assert_equals(events[4].target, c2);
|
||||
resolve();
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
|
||||
const testResults = testCompletion(t);
|
||||
const SCRIPT = "resources/service-worker.js";
|
||||
const SCOPE = "/webmessaging/broadcastchannel/resources/not-used/";
|
||||
|
||||
const reg = await navigator.serviceWorker.register(SCRIPT, {'scope': SCOPE});
|
||||
t.add_cleanup(() => reg.unregister());
|
||||
|
||||
return testResults;
|
||||
}, 'BroadcastChannel works in service workers');
|
||||
</script>
|
||||
@@ -0,0 +1,375 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('worker');
|
||||
let c2 = new BroadcastChannel('worker');
|
||||
let events = [];
|
||||
|
||||
c1.onmessage = e => events.push(e);
|
||||
c2.onmessage = e => events.push(e);
|
||||
|
||||
let doneCount = 0;
|
||||
c2.addEventListener('message', t.step_func(e => {
|
||||
if (e.data == 'from worker') {
|
||||
c2.postMessage('from c2');
|
||||
c1.postMessage('done');
|
||||
} else if (e.data == 'done') {
|
||||
assert_equals(events.length, 4);
|
||||
assert_equals(events[0].data, 'from worker');
|
||||
assert_equals(events[0].target, c1);
|
||||
assert_equals(events[1].data, 'from worker');
|
||||
assert_equals(events[1].target, c2);
|
||||
assert_equals(events[2].data, 'from c2');
|
||||
assert_equals(events[3].data, 'done');
|
||||
if (++doneCount == 2) t.done();
|
||||
}
|
||||
}));
|
||||
|
||||
let worker = new Worker('resources/worker.js');
|
||||
worker.onmessage = t.step_func(e => {
|
||||
assert_array_equals(e.data, ['from c2', 'done']);
|
||||
if (++doneCount == 2) t.done();
|
||||
});
|
||||
worker.postMessage({channel: 'worker'});
|
||||
|
||||
}, 'BroadcastChannel works in workers');
|
||||
|
||||
async_test(t => {
|
||||
let c1 = new BroadcastChannel('shared worker');
|
||||
let c2 = new BroadcastChannel('shared worker');
|
||||
let events = [];
|
||||
|
||||
c1.onmessage = e => events.push(e);
|
||||
c2.onmessage = e => events.push(e);
|
||||
|
||||
let doneCount = 0;
|
||||
c2.addEventListener('message', t.step_func(e => {
|
||||
if (e.data == 'from worker') {
|
||||
c2.postMessage('from c2');
|
||||
c1.postMessage('done');
|
||||
} else if (e.data == 'done') {
|
||||
assert_equals(events.length, 4);
|
||||
assert_equals(events[0].data, 'from worker');
|
||||
assert_equals(events[0].target, c1);
|
||||
assert_equals(events[1].data, 'from worker');
|
||||
assert_equals(events[1].target, c2);
|
||||
assert_equals(events[2].data, 'from c2');
|
||||
assert_equals(events[3].data, 'done');
|
||||
if (++doneCount == 2) t.done();
|
||||
}
|
||||
}));
|
||||
|
||||
let worker = new SharedWorker('resources/worker.js');
|
||||
worker.port.onmessage = t.step_func(e => {
|
||||
assert_array_equals(e.data, ['from c2', 'done']);
|
||||
if (++doneCount == 2) t.done();
|
||||
});
|
||||
worker.port.postMessage({channel: 'shared worker'});
|
||||
|
||||
}, 'BroadcastChannel works in shared workers');
|
||||
|
||||
async_test(t => {
|
||||
let c = new BroadcastChannel('worker-close');
|
||||
let events = [];
|
||||
|
||||
c.onmessage = e => events.push('c1: ' + e.data);
|
||||
|
||||
let worker = new Worker('resources/worker.js');
|
||||
worker.onmessage = t.step_func(e => {
|
||||
assert_array_equals(events,
|
||||
['c1: from worker', 'c2: ready', 'c2: echo'],
|
||||
'messages in document');
|
||||
assert_array_equals(e.data, ['done'], 'messages in worker');
|
||||
t.done();
|
||||
});
|
||||
worker.onmessagerror =
|
||||
t.unreached_func('Worker\'s onmessageerror handler called');
|
||||
|
||||
c.addEventListener('message', e => {
|
||||
if (e.data == 'from worker') {
|
||||
c.close();
|
||||
if (self.gc) self.gc();
|
||||
window.setTimeout(() => {
|
||||
let c2 = new BroadcastChannel('worker-close');
|
||||
c2.onmessage = e => {
|
||||
events.push('c2: ' + e.data);
|
||||
if (e.data === 'ready') {
|
||||
worker.postMessage({ping: 'echo'});
|
||||
} else {
|
||||
c2.postMessage('done');
|
||||
c2.close();
|
||||
}
|
||||
};
|
||||
// For some implementations there may be a race condition between
|
||||
// when the BroadcastChannel instance above is created / ready to
|
||||
// receive messages and when the worker calls postMessage on it's
|
||||
// BroadcastChannel instance. To avoid this, confirm that our
|
||||
// instance can receive a message before indicating to the other
|
||||
// thread that we are ready. For more details, see:
|
||||
// https://github.com/whatwg/html/issues/7267
|
||||
let c3 = new BroadcastChannel('worker-close');
|
||||
c3.postMessage('ready');
|
||||
c3.close();
|
||||
}, 1);
|
||||
}
|
||||
});
|
||||
|
||||
worker.postMessage({channel: 'worker-close'});
|
||||
t.add_cleanup(() => worker.terminate());
|
||||
|
||||
}, 'Closing and re-opening a channel works.');
|
||||
|
||||
async_test(t => {
|
||||
function workerCode() {
|
||||
close();
|
||||
try {
|
||||
var bc = new BroadcastChannel('worker-create-after-close');
|
||||
} catch (e) {
|
||||
postMessage(e);
|
||||
return;
|
||||
}
|
||||
postMessage(true);
|
||||
}
|
||||
|
||||
var workerBlob = new Blob(
|
||||
[workerCode.toString() + ';workerCode();'],
|
||||
{type: 'application/javascript'});
|
||||
|
||||
var w = new Worker(URL.createObjectURL(workerBlob));
|
||||
w.onmessage = t.step_func_done(function(e) {
|
||||
assert_equals(
|
||||
e.data, true,
|
||||
'BroadcastChannel creation in closed worker triggered exception: ' +
|
||||
e.data.message);
|
||||
});
|
||||
t.add_cleanup(() => w.terminate());
|
||||
}, 'BroadcastChannel created after a worker self.close()');
|
||||
|
||||
|
||||
function postMessageFromWorkerWorkerCode(workerName, channelName) {
|
||||
if (workerName === 'close-before-create-worker') {
|
||||
close();
|
||||
}
|
||||
let bc = new BroadcastChannel(channelName);
|
||||
if (workerName === 'close-after-create-worker') {
|
||||
close();
|
||||
}
|
||||
bc.postMessage(workerName + ' done');
|
||||
postMessage(true);
|
||||
}
|
||||
|
||||
function doPostMessageFromWorkerTest(t, workerName, channelName) {
|
||||
var bc = new BroadcastChannel(channelName);
|
||||
bc.onmessage = t.step_func_done(function(e) {
|
||||
assert_equals(
|
||||
e.data, 'done-worker done',
|
||||
'BroadcastChannel message should only be received from the second worker');
|
||||
});
|
||||
t.add_cleanup(() => bc.close());
|
||||
|
||||
var testMessageHandler = t.step_func(function(e) {
|
||||
assert_equals(
|
||||
e.data, true,
|
||||
'Worker sent postMessage indicating it sent a BroadcastChannel message');
|
||||
|
||||
var w = createWorker(
|
||||
postMessageFromWorkerWorkerCode, 'done-worker', channelName);
|
||||
t.add_cleanup(() => w.terminate());
|
||||
});
|
||||
createWorker(
|
||||
postMessageFromWorkerWorkerCode, workerName, channelName,
|
||||
testMessageHandler);
|
||||
|
||||
// To avoid calling t.step_timeout here, have the worker postMessage(true)
|
||||
// once it is finished and then we'll instantiate another worker that
|
||||
// performs the same test steps but doesn't close. By the time the
|
||||
// BroadcastChannel message in that worker gets sent successfully it should
|
||||
// be safe to assume that any BroadcastChannel messages from the previous
|
||||
// worker would have been sent if they were going to be.
|
||||
}
|
||||
|
||||
function createWorker(workerCode, workerName, channelName, handler = null) {
|
||||
var workerCodeStr = workerCode.toString() +
|
||||
`;${workerCode.name}("${workerName}", "${channelName}");`;
|
||||
var workerBlob = new Blob([workerCodeStr], {type: 'application/javascript'});
|
||||
var w = new Worker(URL.createObjectURL(workerBlob));
|
||||
if (handler !== null) {
|
||||
w.onmessage = handler;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-after-create-worker';
|
||||
const channelName = workerName + '-postmessage-from-worker';
|
||||
doPostMessageFromWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created before closing)');
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-before-create-worker';
|
||||
const channelName = workerName + '-postmessage-from-worker';
|
||||
doPostMessageFromWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created after closing)');
|
||||
|
||||
|
||||
function postMessageToWorkerWorkerCode(workerName, channelName) {
|
||||
self.addEventListener('message', () => {
|
||||
if (workerName === 'close-before-create-worker') {
|
||||
close();
|
||||
}
|
||||
try {
|
||||
let bc1 = new BroadcastChannel(channelName);
|
||||
bc1.onmessage = e => {
|
||||
if (e.data === 'ready') {
|
||||
postMessage(e.data);
|
||||
} else if (e.data === 'test') {
|
||||
postMessage(workerName + ' done');
|
||||
}
|
||||
};
|
||||
bc1.onmessageerror = () => {
|
||||
postMessage('onmessageerror called from worker BroadcastChannel');
|
||||
};
|
||||
if (workerName === 'close-after-create-worker') {
|
||||
close();
|
||||
}
|
||||
} catch (e) {
|
||||
postMessage(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (workerName === 'done-worker') {
|
||||
// For some implementations there may be a race condition between when
|
||||
// the BroadcastChannel instance above is created / ready to receive
|
||||
// messages and when the parent calls postMessage on it's
|
||||
// BroadcastChannel instance. To avoid this, confirm that our instance
|
||||
// can receive a message before indicating to the other thread that we
|
||||
// are ready. For more details, see:
|
||||
// https://github.com/whatwg/html/issues/7267
|
||||
let bc2 = new BroadcastChannel(channelName);
|
||||
bc2.postMessage('ready');
|
||||
bc2.close();
|
||||
} else {
|
||||
// Since the worker has closed, it's not expected that the
|
||||
// BroadcastChannel will receive messages (there's a separate test for
|
||||
// that), so just indicate directly that it's ready to test receiving
|
||||
// a message from the parent dispite the possibility of a race condition.
|
||||
postMessage('ready');
|
||||
}
|
||||
});
|
||||
self.addEventListener('messageerror', () => {
|
||||
postMessage('onmessageerror called from worker');
|
||||
});
|
||||
}
|
||||
|
||||
function doPostMessageToWorkerTest(t, workerName, channelName) {
|
||||
var bc = new BroadcastChannel(channelName);
|
||||
t.add_cleanup(() => bc.close());
|
||||
|
||||
var doneMessageHandler = t.step_func(function(e) {
|
||||
if (e.data === 'ready') {
|
||||
bc.postMessage('test');
|
||||
} else if (e.data === 'done-worker done') {
|
||||
t.done();
|
||||
} else {
|
||||
assert_unreached(
|
||||
'BroadcastChannel.postMessage triggered exception within second worker: ' +
|
||||
e.data.message);
|
||||
}
|
||||
});
|
||||
var testMessageHandler = t.step_func(function(e) {
|
||||
assert_equals(
|
||||
e.data, 'ready',
|
||||
'Worker sent postMessage indicating its BroadcastChannel instance is ready');
|
||||
bc.postMessage('test');
|
||||
|
||||
var doneWorker = createWorker(
|
||||
postMessageToWorkerWorkerCode, 'done-worker', channelName,
|
||||
doneMessageHandler);
|
||||
t.add_cleanup(() => {
|
||||
doneWorker.terminate();
|
||||
});
|
||||
doneWorker.postMessage('start');
|
||||
});
|
||||
var testWorker = createWorker(
|
||||
postMessageToWorkerWorkerCode, workerName, channelName,
|
||||
testMessageHandler);
|
||||
testWorker.postMessage('start');
|
||||
}
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-after-create-worker';
|
||||
const channelName = workerName + '-postmessage-to-worker';
|
||||
doPostMessageToWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created before closing)');
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-before-create-worker';
|
||||
const channelName = workerName + '-postmessage-to-worker';
|
||||
doPostMessageToWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created after closing)');
|
||||
|
||||
|
||||
function postMessageWithinWorkerWorkerCode(workerName, channelName) {
|
||||
if (workerName === 'close-before-create-worker') {
|
||||
close();
|
||||
}
|
||||
try {
|
||||
let bc1 = new BroadcastChannel(channelName);
|
||||
let bc2 = new BroadcastChannel(channelName);
|
||||
bc1.onmessage = e => {
|
||||
postMessage(workerName + ' done')
|
||||
};
|
||||
if (workerName === 'close-after-create-worker') {
|
||||
close();
|
||||
}
|
||||
bc2.postMessage(true);
|
||||
postMessage(true);
|
||||
} catch (e) {
|
||||
postMessage(e);
|
||||
}
|
||||
}
|
||||
|
||||
function doPostMessageWithinWorkerTest(t, workerName, channelName) {
|
||||
var doneMessageHandler = t.step_func(function(e) {
|
||||
if (e.data === true) {
|
||||
// Done worker has finished - no action needed
|
||||
} else if (e.data === 'done-worker done') {
|
||||
t.done();
|
||||
} else {
|
||||
assert_unreached(
|
||||
'BroadcastChannel.postMessage triggered exception within second worker: ' +
|
||||
e.data.message);
|
||||
}
|
||||
});
|
||||
var testMessageHandler = t.step_func(function(e) {
|
||||
assert_equals(
|
||||
e.data, true,
|
||||
'Worker indicated that the test procedures were executed successfully');
|
||||
|
||||
var w = createWorker(
|
||||
postMessageWithinWorkerWorkerCode, 'done-worker', channelName,
|
||||
doneMessageHandler);
|
||||
t.add_cleanup(() => w.terminate());
|
||||
});
|
||||
createWorker(
|
||||
postMessageWithinWorkerWorkerCode, workerName, channelName,
|
||||
testMessageHandler);
|
||||
}
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-after-create-worker';
|
||||
const channelName = workerName + '-postmessage-within-worker';
|
||||
doPostMessageWithinWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages within closed worker should be ignored (BCs created before closing)');
|
||||
|
||||
async_test(t => {
|
||||
const workerName = 'close-before-create-worker';
|
||||
const channelName = workerName + '-postmessage-within-worker';
|
||||
doPostMessageWithinWorkerTest(t, workerName, channelName);
|
||||
}, 'BroadcastChannel messages within closed worker should be ignored (BCs created after closing)');
|
||||
|
||||
</script>
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"basics.any.js": {
|
||||
"fail": {
|
||||
"unexpected": [
|
||||
"assert_equals: origin expected \"https://example.com\" but got \"\"",
|
||||
"assert_equals: target for event 0 expected object \"[object EventTarget]\" but got object \"[object EventTarget]\""
|
||||
]
|
||||
}
|
||||
},
|
||||
"interface.any.js": {
|
||||
"fail": {
|
||||
"unexpected": [
|
||||
"assert_throws_dom: function \"() => c.postMessage('')\" threw object \"Error: BroadcastChannel is closed.\" that is not a DOMException InvalidStateError: property \"code\" is equal to 0, expected 11",
|
||||
"assert_not_equals: got disallowed value undefined",
|
||||
"assert_throws_dom: function \"() => c.postMessage(new Symbol())\" threw object \"Error: BroadcastChannel is closed.\" that is not a DOMException InvalidStateError: property \"code\" is equal to 0, expected 11"
|
||||
]
|
||||
}
|
||||
},
|
||||
"origin.window.js": {
|
||||
"fail": {
|
||||
"expected": [
|
||||
"document is not defined"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const { WPTRunner } = require('../common/wpt');
|
||||
|
||||
const runner = new WPTRunner('webmessaging/broadcastchannel');
|
||||
|
||||
runner.setInitScript(`
|
||||
const { BroadcastChannel } = require('worker_threads');
|
||||
global.BroadcastChannel = BroadcastChannel;
|
||||
`);
|
||||
|
||||
runner.runJsTests();
|
||||
Reference in new issue
Block a user