diff --git a/webmessaging/broadcastchannel/detached-iframe.tentative.html b/webmessaging/broadcastchannel/detached-iframe.html
similarity index 100%
rename from webmessaging/broadcastchannel/detached-iframe.tentative.html
rename to webmessaging/broadcastchannel/detached-iframe.html
diff --git a/webmessaging/broadcastchannel/workers.html b/webmessaging/broadcastchannel/workers.html
index 16fea069309f573..8b55492f3cffb35 100644
--- a/webmessaging/broadcastchannel/workers.html
+++ b/webmessaging/broadcastchannel/workers.html
@@ -123,4 +123,253 @@
}, '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)');
+
diff --git a/webmessaging/broadcastchannel/workers.tentative.html b/webmessaging/broadcastchannel/workers.tentative.html
deleted file mode 100644
index e72fa365fd78613..000000000000000
--- a/webmessaging/broadcastchannel/workers.tentative.html
+++ /dev/null
@@ -1,256 +0,0 @@
-
-
-
-
-