browser_target_command_browser_workers.js (8242B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 // Test the TargetCommand API around workers 7 8 const FISSION_TEST_URL = URL_ROOT_SSL + "fission_document.html"; 9 const WORKER_FILE = "test_worker.js"; 10 const CHROME_WORKER_URL = CHROME_URL_ROOT + WORKER_FILE; 11 const SERVICE_WORKER_URL = URL_ROOT_SSL + "test_service_worker.js"; 12 13 add_task(async function () { 14 // Enabled fission's pref as the TargetCommand is almost disabled without it 15 await pushPref("devtools.browsertoolbox.scope", "everything"); 16 17 // Disable the preloaded process as it creates processes intermittently 18 // which forces the emission of RDP requests we aren't correctly waiting for. 19 await pushPref("dom.ipc.processPrelaunch.enabled", false); 20 21 const tab = await addTab(FISSION_TEST_URL); 22 23 info("Test TargetCommand against workers via the parent process target"); 24 25 // Instantiate a worker in the parent process 26 // eslint-disable-next-line no-unused-vars 27 const worker = new Worker(CHROME_WORKER_URL + "#simple-worker"); 28 // eslint-disable-next-line no-unused-vars 29 const sharedWorker = new SharedWorker(CHROME_WORKER_URL + "#shared-worker"); 30 31 const commands = await CommandsFactory.forMainProcess(); 32 const targetCommand = commands.targetCommand; 33 const { TYPES } = targetCommand; 34 await targetCommand.startListening(); 35 36 // Bug 1918267 - startListening may not collect all worker if any process is slow to respond (on tsan for example) 37 // So that we have to wait for the worker to be received by the TargetCommand 38 await waitFor(() => 39 targetCommand 40 .getAllTargets([TYPES.SHARED_WORKER]) 41 .some(target => target.url == CHROME_WORKER_URL + "#shared-worker") 42 ); 43 44 // Very naive sanity check against getAllTargets([workerType]) 45 info("Check that getAllTargets returned the expected targets"); 46 const workers = targetCommand.getAllTargets([TYPES.WORKER]); 47 const hasWorker = workers.find(workerTarget => { 48 return workerTarget.url == CHROME_WORKER_URL + "#simple-worker"; 49 }); 50 ok(hasWorker, "retrieve the target for the worker"); 51 52 const sharedWorkers = await targetCommand.getAllTargets([ 53 TYPES.SHARED_WORKER, 54 ]); 55 const hasSharedWorker = sharedWorkers.find(workerTarget => { 56 return workerTarget.url == CHROME_WORKER_URL + "#shared-worker"; 57 }); 58 ok(hasSharedWorker, "retrieve the target for the shared worker"); 59 60 const serviceWorkers = await targetCommand.getAllTargets([ 61 TYPES.SERVICE_WORKER, 62 ]); 63 const hasServiceWorker = serviceWorkers.find(workerTarget => { 64 return workerTarget.url == SERVICE_WORKER_URL; 65 }); 66 ok(hasServiceWorker, "retrieve the target for the service worker"); 67 68 info( 69 "Check that calling getAllTargets again return the same target instances" 70 ); 71 const workers2 = await targetCommand.getAllTargets([TYPES.WORKER]); 72 const sharedWorkers2 = await targetCommand.getAllTargets([ 73 TYPES.SHARED_WORKER, 74 ]); 75 const serviceWorkers2 = await targetCommand.getAllTargets([ 76 TYPES.SERVICE_WORKER, 77 ]); 78 is(workers2.length, workers.length, "retrieved the same number of workers"); 79 is( 80 sharedWorkers2.length, 81 sharedWorkers.length, 82 "retrieved the same number of shared workers" 83 ); 84 is( 85 serviceWorkers2.length, 86 serviceWorkers.length, 87 "retrieved the same number of service workers" 88 ); 89 90 workers.sort(sortFronts); 91 workers2.sort(sortFronts); 92 sharedWorkers.sort(sortFronts); 93 sharedWorkers2.sort(sortFronts); 94 serviceWorkers.sort(sortFronts); 95 serviceWorkers2.sort(sortFronts); 96 97 for (let i = 0; i < workers.length; i++) { 98 is(workers[i], workers2[i], `worker ${i} targets are the same`); 99 } 100 for (let i = 0; i < sharedWorkers2.length; i++) { 101 is( 102 sharedWorkers[i], 103 sharedWorkers2[i], 104 `shared worker ${i} targets are the same` 105 ); 106 } 107 for (let i = 0; i < serviceWorkers2.length; i++) { 108 is( 109 serviceWorkers[i], 110 serviceWorkers2[i], 111 `service worker ${i} targets are the same` 112 ); 113 } 114 115 info( 116 "Check that watchTargets will call the create callback for all existing workers" 117 ); 118 const targets = []; 119 const topLevelTarget = await commands.targetCommand.targetFront; 120 const onAvailable = async ({ targetFront }) => { 121 ok( 122 targetFront.targetType === TYPES.WORKER || 123 targetFront.targetType === TYPES.SHARED_WORKER || 124 targetFront.targetType === TYPES.SERVICE_WORKER, 125 "We are only notified about worker targets" 126 ); 127 ok( 128 targetFront == topLevelTarget 129 ? targetFront.isTopLevel 130 : !targetFront.isTopLevel, 131 "isTopLevel property is correct" 132 ); 133 targets.push(targetFront); 134 }; 135 await targetCommand.watchTargets({ 136 types: [TYPES.WORKER, TYPES.SHARED_WORKER, TYPES.SERVICE_WORKER], 137 onAvailable, 138 }); 139 is( 140 targets.length, 141 workers.length + sharedWorkers.length + serviceWorkers.length, 142 "retrieved the same number of workers via watchTargets" 143 ); 144 145 targets.sort(sortFronts); 146 const allWorkers = workers 147 .concat(sharedWorkers, serviceWorkers) 148 .sort(sortFronts); 149 150 for (let i = 0; i < allWorkers.length; i++) { 151 is( 152 allWorkers[i], 153 targets[i], 154 `worker ${i} targets are the same via watchTargets` 155 ); 156 } 157 158 targetCommand.unwatchTargets({ 159 types: [TYPES.WORKER, TYPES.SHARED_WORKER, TYPES.SERVICE_WORKER], 160 onAvailable, 161 }); 162 163 // Create a new worker and see if the worker target is reported 164 const onWorkerCreated = new Promise(resolve => { 165 const onAvailable2 = async ({ targetFront }) => { 166 if (targets.includes(targetFront)) { 167 return; 168 } 169 if (!targetFront.url.startsWith(CHROME_WORKER_URL)) { 170 // We might get unrelated workers eg for RemoteSettings. 171 return; 172 } 173 targetCommand.unwatchTargets({ 174 types: [TYPES.WORKER], 175 onAvailable: onAvailable2, 176 }); 177 resolve(targetFront); 178 }; 179 targetCommand.watchTargets({ 180 types: [TYPES.WORKER], 181 onAvailable: onAvailable2, 182 }); 183 }); 184 // eslint-disable-next-line no-unused-vars 185 const worker2 = new Worker(CHROME_WORKER_URL + "#second"); 186 info("Wait for the second worker to be created"); 187 const workerTarget = await onWorkerCreated; 188 189 is( 190 workerTarget.url, 191 CHROME_WORKER_URL + "#second", 192 "This worker target is about the new worker" 193 ); 194 is( 195 workerTarget.name, 196 "test_worker.js#second", 197 "The worker target has the expected name" 198 ); 199 200 const workers3 = await targetCommand.getAllTargets([TYPES.WORKER]); 201 const hasWorker2 = workers3.find( 202 ({ url }) => url == `${CHROME_WORKER_URL}#second` 203 ); 204 ok(hasWorker2, "retrieve the target for tab via getAllTargets"); 205 206 info( 207 "Check that terminating the worker does trigger the onDestroyed callback" 208 ); 209 const onWorkerDestroyed = new Promise(resolve => { 210 const emptyFn = () => {}; 211 const onDestroyed = ({ targetFront }) => { 212 if (!targetFront.url.startsWith(CHROME_WORKER_URL)) { 213 // We might get unrelated workers eg for RemoteSettings. 214 return; 215 } 216 targetCommand.unwatchTargets({ 217 types: [TYPES.WORKER], 218 onAvailable: emptyFn, 219 onDestroyed, 220 }); 221 resolve(targetFront); 222 }; 223 224 targetCommand.watchTargets({ 225 types: [TYPES.WORKER], 226 onAvailable: emptyFn, 227 onDestroyed, 228 }); 229 }); 230 worker2.terminate(); 231 const workerTargetFront = await onWorkerDestroyed; 232 ok(true, "onDestroyed was called when the worker was terminated"); 233 234 workerTargetFront.isTopLevel; 235 ok( 236 true, 237 "isTopLevel can be called on the target front after onDestroyed was called" 238 ); 239 240 workerTargetFront.name; 241 ok( 242 true, 243 "name can be accessed on the target front after onDestroyed was called" 244 ); 245 246 targetCommand.destroy(); 247 248 info("Unregister service workers so they don't appear in other tests."); 249 await unregisterAllServiceWorkers(commands.client); 250 251 await commands.destroy(); 252 253 await SpecialPowers.spawn(tab.linkedBrowser, [], async () => { 254 // registrationPromise is set by the test page. 255 const registration = await content.wrappedJSObject.registrationPromise; 256 registration.unregister(); 257 }); 258 }); 259 260 function sortFronts(f1, f2) { 261 return f1.actorID < f2.actorID; 262 }