diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /testing/web-platform/tests/webmessaging/broadcastchannel/workers.html | |
parent | Initial commit. (diff) | |
download | thunderbird-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/workers.html')
-rw-r--r-- | testing/web-platform/tests/webmessaging/broadcastchannel/workers.html | 375 |
1 files changed, 375 insertions, 0 deletions
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> |