summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webmessaging/broadcastchannel
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/webmessaging/broadcastchannel
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/webmessaging/broadcastchannel')
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/basics.any.js128
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/blobs.html82
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/cross-origin.html38
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/cross-partition.https.tentative.html356
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/detached-iframe.html174
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/interface.any.js65
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/opaque-origin.html193
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/ordering.html116
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/origin.window.js10
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/cross-origin.html15
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/ordering.html78
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/origin.html8
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/sandboxed.html10
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/service-worker.js15
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/resources/worker.js37
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/sandbox.html16
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/service-worker.https.html47
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/workers.html375
18 files changed, 1763 insertions, 0 deletions
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/basics.any.js b/testing/web-platform/tests/webmessaging/broadcastchannel/basics.any.js
new file mode 100644
index 0000000000..eec09d65a3
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/basics.any.js
@@ -0,0 +1,128 @@
+test(function() {
+ assert_throws_js(
+ TypeError,
+ () => BroadcastChannel(""),
+ "Calling BroadcastChannel constructor without 'new' must throw"
+ );
+}, "BroadcastChannel constructor called as normal function");
+
+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');
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/blobs.html b/testing/web-platform/tests/webmessaging/broadcastchannel/blobs.html
new file mode 100644
index 0000000000..ab5096b63c
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/blobs.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/gc.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'])});
+ })();
+ garbageCollect();
+ }, '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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/cross-origin.html b/testing/web-platform/tests/webmessaging/broadcastchannel/cross-origin.html
new file mode 100644
index 0000000000..ee4b2f21c8
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/cross-origin.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/cross-partition.https.tentative.html b/testing/web-platform/tests/webmessaging/broadcastchannel/cross-partition.https.tentative.html
new file mode 100644
index 0000000000..4e91da5546
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/cross-partition.https.tentative.html
@@ -0,0 +1,356 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<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 importScript / newPopup / newIframe -->
+<script src="/html/anonymous-iframe/resources/common.js"></script>
+<body>
+<script>
+
+const bc_postmessage_js = (channel_name, message, done_queue_name) => `
+ const bc = new BroadcastChannel("${channel_name}");
+ bc.postMessage("${message}");
+ send("${done_queue_name}", "done");
+`;
+
+const add_iframe_js = (iframe_origin, response_queue_uuid) => `
+ const importScript = ${importScript};
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ await importScript("/common/utils.js");
+ send("${response_queue_uuid}", newIframe("${iframe_origin}"));
+`;
+
+const same_site_origin = get_host_info().HTTPS_ORIGIN;
+const cross_site_origin = get_host_info().HTTPS_NOTSAMESITE_ORIGIN;
+
+async function create_test_iframes(t, response_queue_uuid) {
+
+ // Create a same-origin iframe in a cross-site popup.
+ const not_same_site_popup_uuid = newPopup(t, cross_site_origin);
+ send(not_same_site_popup_uuid,
+ add_iframe_js(same_site_origin, response_queue_uuid));
+ 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, same_site_origin);
+ send(same_origin_popup_uuid,
+ add_iframe_js(same_site_origin, response_queue_uuid));
+ const iframe_2_uuid = await receive(response_queue_uuid);
+
+ return [iframe_1_uuid, iframe_2_uuid];
+}
+
+promise_test(t => {
+ return new Promise(async (resolve, reject) => {
+ const response_queue_uuid = token();
+
+ const [iframe_1_uuid, iframe_2_uuid] =
+ await create_test_iframes(t, response_queue_uuid);
+
+ const channel_name = token();
+ const bc = new BroadcastChannel(channel_name);
+ bc.onmessage = resolve;
+
+ // 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,
+ bc_postmessage_js(channel_name, "iframe1 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger iframe1 BroadcastChannel message creation");
+ }
+
+ // 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,
+ bc_postmessage_js(channel_name, "iframe2 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger iframe2 BroadcastChannel message creation");
+ }
+
+ }).then(event => {
+ assert_equals(event.data, "iframe2 msg");
+ });
+
+}, "BroadcastChannel messages aren't received from a cross-partition iframe");
+
+// Optional Test: Checking for partitioned BroadcastChannels in an A->B->A
+// (nested-iframe with cross-site ancestor chain) scenario.
+promise_test(t => {
+ return new Promise(async (resolve, reject) => {
+ const response_queue_uuid = token();
+
+ const [cross_site_iframe_uuid, all_same_parent_iframe_uuid] =
+ await create_test_iframes(t, response_queue_uuid);
+
+ // Create a same-origin iframe in a cross-site iframe in a same-site popup.
+ // Create the same-site child iframe of the cross-site iframe (A->B->A).
+ send(cross_site_iframe_uuid,
+ add_iframe_js(same_site_origin, response_queue_uuid));
+ const same_site_iframe_uuid = await receive(response_queue_uuid);
+
+ // Create a same-origin iframe in a same-site iframe in a same-site popup.
+ // Create the same-site child iframe of the same-site parent iframe (A->A->A).
+ send(all_same_parent_iframe_uuid,
+ add_iframe_js(same_site_origin, response_queue_uuid));
+ const all_same_child_iframe_uuid = await receive(response_queue_uuid);
+
+ const channel_name = token();
+ const bc = new BroadcastChannel(channel_name);
+ bc.onmessage = resolve;
+
+ // Instruct the A->B->A child 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(same_site_iframe_uuid,
+ bc_postmessage_js(channel_name, "iframe1 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger A->B->A BroadcastChannel message creation");
+ }
+
+ // Now instruct the A->A->A child iframe to send a BC message. By
+ // the time we send the script to execute, send the BC message,
+ // and 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(all_same_child_iframe_uuid,
+ bc_postmessage_js(channel_name, "iframe2 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger A->A->A BroadcastChannel message creation");
+ }
+
+ }).then(event => {
+ assert_equals(event.data, "iframe2 msg");
+ });
+
+}, "BroadcastChannel messages aren't received from a nested iframe with a cross-site ancestor");
+
+const newWorker = (origin) => {
+ const worker_token = token();
+ const worker_url = origin + executor_worker_path + `&uuid=${worker_token}`;
+ const worker = new Worker(worker_url);
+ return worker_token;
+}
+
+promise_test(t => {
+ return new Promise(async (resolve, reject) => {
+ const response_queue_uuid = token();
+
+ const create_worker_js = (origin) => `
+ const importScript = ${importScript};
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ await importScript("/common/utils.js");
+ const newWorker = ${newWorker};
+ send("${response_queue_uuid}", newWorker("${origin}"));
+ `;
+
+ const [iframe_1_uuid, iframe_2_uuid] =
+ await create_test_iframes(t, response_queue_uuid);
+
+ // Create a dedicated worker in the cross-top-level-site iframe.
+ send(iframe_1_uuid, create_worker_js(same_site_origin));
+ const worker_1_uuid = await receive(response_queue_uuid);
+
+ // Create a dedicated worker in the same-top-level-site iframe.
+ send(iframe_2_uuid, create_worker_js(same_site_origin));
+ const worker_2_uuid = await receive(response_queue_uuid);
+
+ const channel_name = token();
+ const bc = new BroadcastChannel(channel_name);
+ bc.onmessage = async (e) => {
+ await send(worker_1_uuid, "self.close();");
+ await send(worker_2_uuid, "self.close();");
+ resolve(e);
+ };
+
+ // Instruct the not-same-top-level-site worker to send a message on the BC
+ // channel we are listening on. This message should not be received since
+ // the worker should be in a different partition.
+ send(worker_1_uuid,
+ bc_postmessage_js(channel_name, "worker1 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker1 BroadcastChannel message creation");
+ }
+
+ // Now instruct the same-top-level-site worker 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 worker would have
+ // been delivered if it was going to be.
+ send(worker_2_uuid,
+ bc_postmessage_js(channel_name, "worker2 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker2 BroadcastChannel message creation");
+ }
+
+ }).then(event => {
+ assert_equals(event.data, "worker2 msg");
+ });
+
+}, "BroadcastChannel messages aren't received from a cross-partition dedicated worker");
+
+const newSharedWorker = (origin) => {
+ const worker_token = token();
+ const worker_url = origin + executor_worker_path + `&uuid=${worker_token}`;
+ const worker = new SharedWorker(worker_url, worker_token);
+ return worker_token;
+}
+
+promise_test(t => {
+ return new Promise(async (resolve, reject) => {
+ const response_queue_uuid = token();
+
+ const create_worker_js = (origin) => `
+ const importScript = ${importScript};
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ await importScript("/common/utils.js");
+ const newSharedWorker = ${newSharedWorker};
+ send("${response_queue_uuid}", newSharedWorker("${origin}"));
+ `;
+
+ const [iframe_1_uuid, iframe_2_uuid] =
+ await create_test_iframes(t, response_queue_uuid);
+
+ // Create a shared worker in the cross-top-level-site iframe.
+ send(iframe_1_uuid, create_worker_js(same_site_origin));
+ const worker_1_uuid = await receive(response_queue_uuid);
+
+ // Create a shared worker in the same-top-level-site iframe.
+ send(iframe_2_uuid, create_worker_js(same_site_origin));
+ const worker_2_uuid = await receive(response_queue_uuid);
+
+ const channel_name = token();
+ const bc = new BroadcastChannel(channel_name);
+ bc.onmessage = async (e) => {
+ await send(worker_1_uuid, "self.close();");
+ await send(worker_2_uuid, "self.close();");
+ resolve(e);
+ };
+
+ // Instruct the not-same-top-level-site worker to send a message on the BC
+ // channel we are listening on. This message should not be received since
+ // the worker should be in a different partition.
+ send(worker_1_uuid,
+ bc_postmessage_js(channel_name, "worker1 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker1 BroadcastChannel message creation");
+ }
+
+ // Now instruct the same-top-level-site worker 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 worker would have
+ // been delivered if it was going to be.
+ send(worker_2_uuid,
+ bc_postmessage_js(channel_name, "worker2 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker2 BroadcastChannel message creation");
+ }
+
+ }).then(event => {
+ assert_equals(event.data, "worker2 msg");
+ });
+
+}, "BroadcastChannel messages aren't received from a cross-partition shared worker");
+
+const newServiceWorker = async (origin) => {
+ const worker_token = token();
+ const worker_url = origin + executor_service_worker_path +
+ `&uuid=${worker_token}`;
+ const worker_url_path = executor_service_worker_path.substring(0,
+ executor_service_worker_path.lastIndexOf('/'));
+ const scope = worker_url_path + "/not-used/";
+ const reg = await navigator.serviceWorker.register(worker_url,
+ {'scope': scope});
+ return worker_token;
+}
+
+promise_test(t => {
+ return new Promise(async (resolve, reject) => {
+ const response_queue_uuid = token();
+
+ const create_worker_js = (origin) => `
+ const importScript = ${importScript};
+ await importScript("/html/cross-origin-embedder-policy/credentialless" +
+ "/resources/common.js");
+ await importScript("/html/anonymous-iframe/resources/common.js");
+ await importScript("/common/utils.js");
+ const newServiceWorker = ${newServiceWorker};
+ send("${response_queue_uuid}", await newServiceWorker("${origin}"));
+ `;
+
+ const [iframe_1_uuid, iframe_2_uuid] =
+ await create_test_iframes(t, response_queue_uuid);
+
+ // Create a service worker in the cross-top-level-site iframe.
+ send(iframe_1_uuid, create_worker_js(same_site_origin));
+ var worker_1_uuid = await receive(response_queue_uuid);
+
+ const channel_name = token();
+ const bc = new BroadcastChannel(channel_name);
+ bc.onmessage = async (e) => {
+ if (worker_1_uuid) {
+ await send(worker_1_uuid, "self.registration.unregister();");
+ }
+ if (worker_2_uuid) {
+ await send(worker_2_uuid, "self.registration.unregister();");
+ }
+ resolve(e);
+ };
+
+ // Instruct the not-same-top-level-site worker to send a message on the BC
+ // channel we are listening on. This message should not be received since
+ // the worker should be in a different partition.
+ send(worker_1_uuid,
+ bc_postmessage_js(channel_name, "worker1 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker1 BroadcastChannel message creation");
+ }
+
+ await send(worker_1_uuid, "await self.registration.unregister();");
+ worker_1_uuid = undefined;
+
+ // Create a service worker in the same-top-level-site iframe. Note that
+ // if service workers are unpartitioned then this new service worker would
+ // replace the one created above. This is why we wait to create the second
+ // service worker until after we are done with the first one.
+ send(iframe_2_uuid, create_worker_js(same_site_origin));
+ var worker_2_uuid = await receive(response_queue_uuid);
+
+ // Now instruct the same-top-level-site worker 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 worker would have
+ // been delivered if it was going to be.
+ send(worker_2_uuid,
+ bc_postmessage_js(channel_name, "worker2 msg", response_queue_uuid));
+ if (await receive(response_queue_uuid) != "done") {
+ reject("Unable to trigger worker2 BroadcastChannel message creation");
+ }
+
+ await send(worker_2_uuid, "await self.registration.unregister();");
+ worker_2_uuid = undefined;
+
+ }).then(event => {
+ assert_equals(event.data, "worker2 msg");
+ });
+
+}, "BroadcastChannel messages aren't received from a cross-partition service worker");
+
+</script>
+</body>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/detached-iframe.html b/testing/web-platform/tests/webmessaging/broadcastchannel/detached-iframe.html
new file mode 100644
index 0000000000..b9b06c3a46
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/detached-iframe.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/interface.any.js b/testing/web-platform/tests/webmessaging/broadcastchannel/interface.any.js
new file mode 100644
index 0000000000..35e09d34b4
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/interface.any.js
@@ -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');
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/opaque-origin.html b/testing/web-platform/tests/webmessaging/broadcastchannel/opaque-origin.html
new file mode 100644
index 0000000000..e09d935244
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/opaque-origin.html
@@ -0,0 +1,193 @@
+<!doctype html>
+<html>
+<head>
+<meta charset=utf-8>
+<meta name="timeout" content="long">
+<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});
+ t.add_cleanup(() => { document.body.removeChild(ifr) });
+ document.body.appendChild(ifr);
+ });
+}, "Opaque origin should be serialized to \"null\"");
+
+
+const iframe_src = (channel_name, iframe_name) => `data:text/html,<script>
+let bc2 = new BroadcastChannel("${channel_name}");
+bc2.onmessage = (e) => {
+ if (e.data == "from-${iframe_name}") {
+ parent.postMessage("${iframe_name}-done", "*");
+ } else {
+ parent.postMessage("fail", "*");
+ }
+};
+let bc3 = new BroadcastChannel("${channel_name}");
+bc3.postMessage("from-${iframe_name}");
+</script>`;
+
+promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const channel_name = "opaque-origin-test-2";
+ const bc1 = new BroadcastChannel(channel_name);
+ bc1.onmessage = t.unreached_func("Received message from an opaque origin");
+
+ // We'll create an iframe and have it send a BroadcastChannel message
+ // between two instances. Once the message is received, it will postMessage
+ // back and we'll repeat this with another iframe. If the first
+ // BroadcastChannel message is received by `bc1`, or if the second
+ // BroadcastChannel message is received by `bc1` or `bc2` in the first
+ // iframe, then the test should fail.
+
+ window.addEventListener("message", e => {
+ if(e.data == "iframe1-done") {
+ let iframe2 = document.createElement("iframe");
+ iframe2.src = iframe_src(channel_name, "iframe2");
+ t.add_cleanup(() => { document.body.removeChild(iframe2) });
+ document.body.appendChild(iframe2);
+ } else if(e.data == "iframe2-done") {
+ resolve();
+ } else if(e.data == "fail") {
+ reject("One opaque origin received a message from the other");
+ } else {
+ reject("An unexpected error occurred");
+ }
+ });
+
+ let iframe1 = document.createElement("iframe");
+ iframe1.src = iframe_src(channel_name, "iframe1");
+ t.add_cleanup(() => { document.body.removeChild(iframe1) });
+ document.body.appendChild(iframe1);
+ });
+}, "BroadcastChannel messages from opaque origins should be self-contained");
+
+const data_url_worker_src = (channel_name, worker_name) => {
+ const source = `
+const handler = (reply) => {
+ let bc2 = new BroadcastChannel("${channel_name}");
+ bc2.onmessage = (e) => {
+ if (e.data == "from-${worker_name}") {
+ reply("${worker_name}-done");
+ } else {
+ reply("fail");
+ }
+ };
+ let bc3 = new BroadcastChannel("${channel_name}");
+ bc3.postMessage("from-${worker_name}");
+};
+// For dedicated workers:
+self.addEventListener("message", () => handler(self.postMessage));
+// For shared workers:
+self.addEventListener("connect", (e) => {
+ var port = e.ports[0];
+ port.onmessage = () => handler(msg => port.postMessage(msg));
+ port.start();
+
+});
+`;
+ return "data:,".concat(encodeURIComponent(source));
+}
+
+promise_test(t => {
+ return new Promise((resolve, reject) => {
+ const channel_name = "opaque-origin-test-3";
+ const bc1 = new BroadcastChannel(channel_name);
+ bc1.onmessage = e => { reject("Received message from an opaque origin"); };
+
+ // Same as the previous test but with data URL dedicated workers (which
+ // should have opaque origins per the HTML spec).
+ const worker_name_prefix = "data-url-dedicated-worker";
+ const worker_1_name = `${worker_name_prefix}-1`;
+ const worker_2_name = `${worker_name_prefix}-2`;
+
+ const handler = e => {
+ if(e.data == `${worker_1_name}-done`) {
+ const worker2 = new Worker(data_url_worker_src(channel_name, worker_2_name));
+ t.add_cleanup(() => worker2.terminate());
+ worker2.addEventListener("message", handler);
+ worker2.postMessage("go!");
+ } else if(e.data == `${worker_2_name}-done`) {
+ resolve();
+ } else if(e.data == "fail") {
+ reject("One opaque origin received a message from the other");
+ } else {
+ reject("An unexpected error occurred");
+ }
+ };
+
+ let worker1 = new Worker(data_url_worker_src(channel_name, worker_1_name));
+ t.add_cleanup(() => worker1.terminate());
+ worker1.addEventListener("message", handler);
+ worker1.postMessage("go!");
+ });
+}, "BroadcastChannel messages from data URL dedicated workers should be self-contained");
+
+promise_test(() => {
+ return new Promise((resolve, reject) => {
+ const channel_name = "opaque-origin-test-4";
+ const bc1 = new BroadcastChannel(channel_name);
+
+ // Same as the previous test but with data URL shared workers (which
+ // should have opaque origins per the HTML spec).
+ const worker_name_prefix = "data-url-shared-worker";
+ const worker_1_name = `${worker_name_prefix}-1`;
+ const worker_2_name = `${worker_name_prefix}-2`;
+
+ const handler = e => {
+ if (e.data == `${worker_1_name}-done`) {
+ const worker_script = data_url_worker_src(channel_name, worker_2_name);
+ const worker2 = new SharedWorker(worker_script, worker_2_name);
+ worker2.port.addEventListener("message", handler);
+ worker2.port.start();
+ worker2.port.postMessage("go!");
+ } else if(e.data == `${worker_2_name}-done`) {
+ resolve();
+ } else if(e.data == "fail") {
+ reject("One opaque origin received a message from the other");
+ } else {
+ reject("An unexpected error occurred");
+ }
+ };
+
+ bc1.onmessage = e => {
+ if (e.data == "go!") {
+ const worker_script = data_url_worker_src(channel_name, worker_1_name);
+ const worker1 = new SharedWorker(worker_script, worker_1_name);
+ worker1.port.addEventListener("message", handler);
+ worker1.port.start();
+ worker1.port.postMessage("go!");
+ } else {
+ reject("Received message from an opaque origin");
+ }
+ };
+
+ // Ensure that the BroadcastChannel instance above can receive messages
+ // before we create the first shared worker.
+ const bc2 = new BroadcastChannel(channel_name);
+ bc2.postMessage("go!");
+ });
+}, "BroadcastChannel messages from data URL shared workers should be self-contained");
+//-->
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/ordering.html b/testing/web-platform/tests/webmessaging/broadcastchannel/ordering.html
new file mode 100644
index 0000000000..2d521b9e0c
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/ordering.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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/origin.window.js b/testing/web-platform/tests/webmessaging/broadcastchannel/origin.window.js
new file mode 100644
index 0000000000..7e9d602af1
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/origin.window.js
@@ -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");
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/cross-origin.html b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/cross-origin.html
new file mode 100644
index 0000000000..5078b6fc8e
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/cross-origin.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/ordering.html b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/ordering.html
new file mode 100644
index 0000000000..b7f12d865a
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/ordering.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/origin.html b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/origin.html
new file mode 100644
index 0000000000..f57d582bbb
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/origin.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/sandboxed.html b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/sandboxed.html
new file mode 100644
index 0000000000..e32962cdfd
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/sandboxed.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/service-worker.js b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/service-worker.js
new file mode 100644
index 0000000000..a3d17b9c65
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/service-worker.js
@@ -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);
+});
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/resources/worker.js b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/worker.js
new file mode 100644
index 0000000000..ee2d51a254
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/resources/worker.js
@@ -0,0 +1,37 @@
+importScripts("/common/gc.js");
+
+var c;
+
+async 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)});
+ })();
+ await garbageCollect();
+ }
+ 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));
+};
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/sandbox.html b/testing/web-platform/tests/webmessaging/broadcastchannel/sandbox.html
new file mode 100644
index 0000000000..aedf3c0d6f
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/sandbox.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/service-worker.https.html b/testing/web-platform/tests/webmessaging/broadcastchannel/service-worker.https.html
new file mode 100644
index 0000000000..d605434ae1
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/service-worker.https.html
@@ -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>
diff --git a/testing/web-platform/tests/webmessaging/broadcastchannel/workers.html b/testing/web-platform/tests/webmessaging/broadcastchannel/workers.html
new file mode 100644
index 0000000000..8b55492f3c
--- /dev/null
+++ b/testing/web-platform/tests/webmessaging/broadcastchannel/workers.html
@@ -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>