summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/webmessaging/broadcastchannel/workers.html
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/workers.html
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/workers.html')
-rw-r--r--testing/web-platform/tests/webmessaging/broadcastchannel/workers.html375
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>