%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
<!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>