1<!DOCTYPE html> 2<meta charset=utf-8> 3<script src="/resources/testharness.js"></script> 4<script src="/resources/testharnessreport.js"></script> 5<script> 6 7async_test(t => { 8 let c1 = new BroadcastChannel('worker'); 9 let c2 = new BroadcastChannel('worker'); 10 let events = []; 11 12 c1.onmessage = e => events.push(e); 13 c2.onmessage = e => events.push(e); 14 15 let doneCount = 0; 16 c2.addEventListener('message', t.step_func(e => { 17 if (e.data == 'from worker') { 18 c2.postMessage('from c2'); 19 c1.postMessage('done'); 20 } else if (e.data == 'done') { 21 assert_equals(events.length, 4); 22 assert_equals(events[0].data, 'from worker'); 23 assert_equals(events[0].target, c1); 24 assert_equals(events[1].data, 'from worker'); 25 assert_equals(events[1].target, c2); 26 assert_equals(events[2].data, 'from c2'); 27 assert_equals(events[3].data, 'done'); 28 if (++doneCount == 2) t.done(); 29 } 30 })); 31 32 let worker = new Worker('resources/worker.js'); 33 worker.onmessage = t.step_func(e => { 34 assert_array_equals(e.data, ['from c2', 'done']); 35 if (++doneCount == 2) t.done(); 36 }); 37 worker.postMessage({channel: 'worker'}); 38 39 }, 'BroadcastChannel works in workers'); 40 41async_test(t => { 42 let c1 = new BroadcastChannel('shared worker'); 43 let c2 = new BroadcastChannel('shared worker'); 44 let events = []; 45 46 c1.onmessage = e => events.push(e); 47 c2.onmessage = e => events.push(e); 48 49 let doneCount = 0; 50 c2.addEventListener('message', t.step_func(e => { 51 if (e.data == 'from worker') { 52 c2.postMessage('from c2'); 53 c1.postMessage('done'); 54 } else if (e.data == 'done') { 55 assert_equals(events.length, 4); 56 assert_equals(events[0].data, 'from worker'); 57 assert_equals(events[0].target, c1); 58 assert_equals(events[1].data, 'from worker'); 59 assert_equals(events[1].target, c2); 60 assert_equals(events[2].data, 'from c2'); 61 assert_equals(events[3].data, 'done'); 62 if (++doneCount == 2) t.done(); 63 } 64 })); 65 66 let worker = new SharedWorker('resources/worker.js'); 67 worker.port.onmessage = t.step_func(e => { 68 assert_array_equals(e.data, ['from c2', 'done']); 69 if (++doneCount == 2) t.done(); 70 }); 71 worker.port.postMessage({channel: 'shared worker'}); 72 73 }, 'BroadcastChannel works in shared workers'); 74 75async_test(t => { 76 let c = new BroadcastChannel('worker-close'); 77 let events = []; 78 79 c.onmessage = e => events.push('c1: ' + e.data); 80 81 let worker = new Worker('resources/worker.js'); 82 worker.onmessage = t.step_func(e => { 83 assert_array_equals(events, 84 ['c1: from worker', 'c2: ready', 'c2: echo'], 85 'messages in document'); 86 assert_array_equals(e.data, ['done'], 'messages in worker'); 87 t.done(); 88 }); 89 worker.onmessagerror = 90 t.unreached_func('Worker\'s onmessageerror handler called'); 91 92 c.addEventListener('message', e => { 93 if (e.data == 'from worker') { 94 c.close(); 95 if (self.gc) self.gc(); 96 window.setTimeout(() => { 97 let c2 = new BroadcastChannel('worker-close'); 98 c2.onmessage = e => { 99 events.push('c2: ' + e.data); 100 if (e.data === 'ready') { 101 worker.postMessage({ping: 'echo'}); 102 } else { 103 c2.postMessage('done'); 104 c2.close(); 105 } 106 }; 107 // For some implementations there may be a race condition between 108 // when the BroadcastChannel instance above is created / ready to 109 // receive messages and when the worker calls postMessage on it's 110 // BroadcastChannel instance. To avoid this, confirm that our 111 // instance can receive a message before indicating to the other 112 // thread that we are ready. For more details, see: 113 // https://github.com/whatwg/html/issues/7267 114 let c3 = new BroadcastChannel('worker-close'); 115 c3.postMessage('ready'); 116 c3.close(); 117 }, 1); 118 } 119 }); 120 121 worker.postMessage({channel: 'worker-close'}); 122 t.add_cleanup(() => worker.terminate()); 123 124 }, 'Closing and re-opening a channel works.'); 125 126async_test(t => { 127 function workerCode() { 128 close(); 129 try { 130 var bc = new BroadcastChannel('worker-create-after-close'); 131 } catch (e) { 132 postMessage(e); 133 return; 134 } 135 postMessage(true); 136 } 137 138 var workerBlob = new Blob( 139 [workerCode.toString() + ';workerCode();'], 140 {type: 'application/javascript'}); 141 142 var w = new Worker(URL.createObjectURL(workerBlob)); 143 w.onmessage = t.step_func_done(function(e) { 144 assert_equals( 145 e.data, true, 146 'BroadcastChannel creation in closed worker triggered exception: ' + 147 e.data.message); 148 }); 149 t.add_cleanup(() => w.terminate()); 150}, 'BroadcastChannel created after a worker self.close()'); 151 152 153function postMessageFromWorkerWorkerCode(workerName, channelName) { 154 if (workerName === 'close-before-create-worker') { 155 close(); 156 } 157 let bc = new BroadcastChannel(channelName); 158 if (workerName === 'close-after-create-worker') { 159 close(); 160 } 161 bc.postMessage(workerName + ' done'); 162 postMessage(true); 163} 164 165function doPostMessageFromWorkerTest(t, workerName, channelName) { 166 var bc = new BroadcastChannel(channelName); 167 bc.onmessage = t.step_func_done(function(e) { 168 assert_equals( 169 e.data, 'done-worker done', 170 'BroadcastChannel message should only be received from the second worker'); 171 }); 172 t.add_cleanup(() => bc.close()); 173 174 var testMessageHandler = t.step_func(function(e) { 175 assert_equals( 176 e.data, true, 177 'Worker sent postMessage indicating it sent a BroadcastChannel message'); 178 179 var w = createWorker( 180 postMessageFromWorkerWorkerCode, 'done-worker', channelName); 181 t.add_cleanup(() => w.terminate()); 182 }); 183 createWorker( 184 postMessageFromWorkerWorkerCode, workerName, channelName, 185 testMessageHandler); 186 187 // To avoid calling t.step_timeout here, have the worker postMessage(true) 188 // once it is finished and then we'll instantiate another worker that 189 // performs the same test steps but doesn't close. By the time the 190 // BroadcastChannel message in that worker gets sent successfully it should 191 // be safe to assume that any BroadcastChannel messages from the previous 192 // worker would have been sent if they were going to be. 193} 194 195function createWorker(workerCode, workerName, channelName, handler = null) { 196 var workerCodeStr = workerCode.toString() + 197 `;${workerCode.name}("${workerName}", "${channelName}");`; 198 var workerBlob = new Blob([workerCodeStr], {type: 'application/javascript'}); 199 var w = new Worker(URL.createObjectURL(workerBlob)); 200 if (handler !== null) { 201 w.onmessage = handler; 202 } 203 return w; 204} 205 206async_test(t => { 207 const workerName = 'close-after-create-worker'; 208 const channelName = workerName + '-postmessage-from-worker'; 209 doPostMessageFromWorkerTest(t, workerName, channelName); 210}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created before closing)'); 211 212async_test(t => { 213 const workerName = 'close-before-create-worker'; 214 const channelName = workerName + '-postmessage-from-worker'; 215 doPostMessageFromWorkerTest(t, workerName, channelName); 216}, 'BroadcastChannel messages from closed worker to parent should be ignored (BC created after closing)'); 217 218 219function postMessageToWorkerWorkerCode(workerName, channelName) { 220 self.addEventListener('message', () => { 221 if (workerName === 'close-before-create-worker') { 222 close(); 223 } 224 try { 225 let bc1 = new BroadcastChannel(channelName); 226 bc1.onmessage = e => { 227 if (e.data === 'ready') { 228 postMessage(e.data); 229 } else if (e.data === 'test') { 230 postMessage(workerName + ' done'); 231 } 232 }; 233 bc1.onmessageerror = () => { 234 postMessage('onmessageerror called from worker BroadcastChannel'); 235 }; 236 if (workerName === 'close-after-create-worker') { 237 close(); 238 } 239 } catch (e) { 240 postMessage(e); 241 return; 242 } 243 244 if (workerName === 'done-worker') { 245 // For some implementations there may be a race condition between when 246 // the BroadcastChannel instance above is created / ready to receive 247 // messages and when the parent calls postMessage on it's 248 // BroadcastChannel instance. To avoid this, confirm that our instance 249 // can receive a message before indicating to the other thread that we 250 // are ready. For more details, see: 251 // https://github.com/whatwg/html/issues/7267 252 let bc2 = new BroadcastChannel(channelName); 253 bc2.postMessage('ready'); 254 bc2.close(); 255 } else { 256 // Since the worker has closed, it's not expected that the 257 // BroadcastChannel will receive messages (there's a separate test for 258 // that), so just indicate directly that it's ready to test receiving 259 // a message from the parent dispite the possibility of a race condition. 260 postMessage('ready'); 261 } 262 }); 263 self.addEventListener('messageerror', () => { 264 postMessage('onmessageerror called from worker'); 265 }); 266} 267 268function doPostMessageToWorkerTest(t, workerName, channelName) { 269 var bc = new BroadcastChannel(channelName); 270 t.add_cleanup(() => bc.close()); 271 272 var doneMessageHandler = t.step_func(function(e) { 273 if (e.data === 'ready') { 274 bc.postMessage('test'); 275 } else if (e.data === 'done-worker done') { 276 t.done(); 277 } else { 278 assert_unreached( 279 'BroadcastChannel.postMessage triggered exception within second worker: ' + 280 e.data.message); 281 } 282 }); 283 var testMessageHandler = t.step_func(function(e) { 284 assert_equals( 285 e.data, 'ready', 286 'Worker sent postMessage indicating its BroadcastChannel instance is ready'); 287 bc.postMessage('test'); 288 289 var doneWorker = createWorker( 290 postMessageToWorkerWorkerCode, 'done-worker', channelName, 291 doneMessageHandler); 292 t.add_cleanup(() => { 293 doneWorker.terminate(); 294 }); 295 doneWorker.postMessage('start'); 296 }); 297 var testWorker = createWorker( 298 postMessageToWorkerWorkerCode, workerName, channelName, 299 testMessageHandler); 300 testWorker.postMessage('start'); 301} 302 303async_test(t => { 304 const workerName = 'close-after-create-worker'; 305 const channelName = workerName + '-postmessage-to-worker'; 306 doPostMessageToWorkerTest(t, workerName, channelName); 307}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created before closing)'); 308 309async_test(t => { 310 const workerName = 'close-before-create-worker'; 311 const channelName = workerName + '-postmessage-to-worker'; 312 doPostMessageToWorkerTest(t, workerName, channelName); 313}, 'BroadcastChannel messages from parent to closed worker should be ignored (BC created after closing)'); 314 315 316function postMessageWithinWorkerWorkerCode(workerName, channelName) { 317 if (workerName === 'close-before-create-worker') { 318 close(); 319 } 320 try { 321 let bc1 = new BroadcastChannel(channelName); 322 let bc2 = new BroadcastChannel(channelName); 323 bc1.onmessage = e => { 324 postMessage(workerName + ' done') 325 }; 326 if (workerName === 'close-after-create-worker') { 327 close(); 328 } 329 bc2.postMessage(true); 330 postMessage(true); 331 } catch (e) { 332 postMessage(e); 333 } 334} 335 336function doPostMessageWithinWorkerTest(t, workerName, channelName) { 337 var doneMessageHandler = t.step_func(function(e) { 338 if (e.data === true) { 339 // Done worker has finished - no action needed 340 } else if (e.data === 'done-worker done') { 341 t.done(); 342 } else { 343 assert_unreached( 344 'BroadcastChannel.postMessage triggered exception within second worker: ' + 345 e.data.message); 346 } 347 }); 348 var testMessageHandler = t.step_func(function(e) { 349 assert_equals( 350 e.data, true, 351 'Worker indicated that the test procedures were executed successfully'); 352 353 var w = createWorker( 354 postMessageWithinWorkerWorkerCode, 'done-worker', channelName, 355 doneMessageHandler); 356 t.add_cleanup(() => w.terminate()); 357 }); 358 createWorker( 359 postMessageWithinWorkerWorkerCode, workerName, channelName, 360 testMessageHandler); 361} 362 363async_test(t => { 364 const workerName = 'close-after-create-worker'; 365 const channelName = workerName + '-postmessage-within-worker'; 366 doPostMessageWithinWorkerTest(t, workerName, channelName); 367}, 'BroadcastChannel messages within closed worker should be ignored (BCs created before closing)'); 368 369async_test(t => { 370 const workerName = 'close-before-create-worker'; 371 const channelName = workerName + '-postmessage-within-worker'; 372 doPostMessageWithinWorkerTest(t, workerName, channelName); 373}, 'BroadcastChannel messages within closed worker should be ignored (BCs created after closing)'); 374 375</script> 376