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 @@ - - - - -