browser_target_command_watchTargets.js (6516B)
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's `watchTargets` function 7 8 const TEST_URL = 9 "data:text/html;charset=utf-8," + encodeURIComponent(`<div id="test"></div>`); 10 11 add_task(async function () { 12 // Enabled fission's pref as the TargetCommand is almost disabled without it 13 await pushPref("devtools.browsertoolbox.scope", "everything"); 14 // Disable the preloaded process as it gets created lazily and may interfere 15 // with process count assertions 16 await pushPref("dom.ipc.processPrelaunch.enabled", false); 17 // This preference helps destroying the content process when we close the tab 18 await pushPref("dom.ipc.keepProcessesAlive.web", 1); 19 20 await testWatchTargets(); 21 await testThrowingInOnAvailable(); 22 }); 23 24 async function testWatchTargets() { 25 info("Test TargetCommand watchTargets function"); 26 27 const commands = await CommandsFactory.forMainProcess(); 28 const targetCommand = commands.targetCommand; 29 const { TYPES } = targetCommand; 30 31 await targetCommand.startListening(); 32 33 // Note that ppmm also includes the parent process, which is considered as a frame rather than a process 34 const originalProcessesCount = Services.ppmm.childCount - 1; 35 36 info( 37 "Check that onAvailable is called for processes already created *before* the call to watchTargets" 38 ); 39 const targets = new Set(); 40 const topLevelTarget = targetCommand.targetFront; 41 const onAvailable = ({ targetFront }) => { 42 if (targets.has(targetFront)) { 43 ok(false, "The same target is notified multiple times via onAvailable"); 44 } 45 is( 46 targetFront.targetType, 47 TYPES.PROCESS, 48 "We are only notified about process targets" 49 ); 50 ok( 51 targetFront == topLevelTarget 52 ? targetFront.isTopLevel 53 : !targetFront.isTopLevel, 54 "isTopLevel property is correct" 55 ); 56 targets.add(targetFront); 57 }; 58 const onDestroyed = ({ targetFront }) => { 59 if (!targets.has(targetFront)) { 60 ok( 61 false, 62 "A target is declared destroyed via onDestroyed without being notified via onAvailable" 63 ); 64 } 65 is( 66 targetFront.targetType, 67 TYPES.PROCESS, 68 "We are only notified about process targets" 69 ); 70 ok( 71 !targetFront.isTopLevel, 72 "We are not notified about the top level target destruction" 73 ); 74 targets.delete(targetFront); 75 }; 76 await targetCommand.watchTargets({ 77 types: [TYPES.PROCESS], 78 onAvailable, 79 onDestroyed, 80 }); 81 is( 82 targets.size, 83 originalProcessesCount, 84 "retrieved the expected number of processes via watchTargets" 85 ); 86 // Start from 1 in order to ignore the parent process target, which is considered as a frame rather than a process 87 for (let i = 1; i < Services.ppmm.childCount; i++) { 88 const process = Services.ppmm.getChildAt(i); 89 const hasTargetWithSamePID = [...targets].find( 90 processTarget => processTarget.targetForm.processID == process.osPid 91 ); 92 ok( 93 hasTargetWithSamePID, 94 `Process with PID ${process.osPid} has been reported via onAvailable` 95 ); 96 } 97 98 info( 99 "Check that onAvailable is called for processes created *after* the call to watchTargets" 100 ); 101 const previousTargets = new Set(targets); 102 const onProcessCreated = new Promise(resolve => { 103 const onAvailable2 = ({ targetFront }) => { 104 if (previousTargets.has(targetFront)) { 105 return; 106 } 107 targetCommand.unwatchTargets({ 108 types: [TYPES.PROCESS], 109 onAvailable: onAvailable2, 110 }); 111 resolve(targetFront); 112 }; 113 targetCommand.watchTargets({ 114 types: [TYPES.PROCESS], 115 onAvailable: onAvailable2, 116 }); 117 }); 118 const tab1 = await BrowserTestUtils.openNewForegroundTab({ 119 gBrowser, 120 url: TEST_URL, 121 forceNewProcess: true, 122 }); 123 const createdTarget = await onProcessCreated; 124 125 // For some reason, creating a new tab purges processes created from previous tests 126 // so it is not reasonable to assert the side of `targets` as it may be lower than expected. 127 ok(targets.has(createdTarget), "The new tab process is in the list"); 128 129 const processCountAfterTabOpen = targets.size; 130 131 // Assert that onDestroyed is called for destroyed processes 132 const onProcessDestroyed = new Promise(resolve => { 133 const onAvailable3 = () => {}; 134 const onDestroyed3 = ({ targetFront }) => { 135 resolve(targetFront); 136 targetCommand.unwatchTargets({ 137 types: [TYPES.PROCESS], 138 onAvailable: onAvailable3, 139 onDestroyed: onDestroyed3, 140 }); 141 }; 142 targetCommand.watchTargets({ 143 types: [TYPES.PROCESS], 144 onAvailable: onAvailable3, 145 onDestroyed: onDestroyed3, 146 }); 147 }); 148 149 BrowserTestUtils.removeTab(tab1); 150 151 const destroyedTarget = await onProcessDestroyed; 152 is( 153 targets.size, 154 processCountAfterTabOpen - 1, 155 "The closed tab's process has been reported as destroyed" 156 ); 157 ok( 158 !targets.has(destroyedTarget), 159 "The destroyed target is no longer in the list" 160 ); 161 is( 162 destroyedTarget, 163 createdTarget, 164 "The destroyed target is the one that has been reported as created" 165 ); 166 167 targetCommand.unwatchTargets({ 168 types: [TYPES.PROCESS], 169 onAvailable, 170 onDestroyed, 171 }); 172 173 targetCommand.destroy(); 174 175 await commands.destroy(); 176 } 177 178 async function testThrowingInOnAvailable() { 179 info( 180 "Test TargetCommand watchTargets function when an exception is thrown in onAvailable callback" 181 ); 182 183 const commands = await CommandsFactory.forMainProcess(); 184 const targetCommand = commands.targetCommand; 185 const { TYPES } = targetCommand; 186 187 await targetCommand.startListening(); 188 189 // Note that ppmm also includes the parent process, which is considered as a frame rather than a process 190 const originalProcessesCount = Services.ppmm.childCount - 1; 191 192 info( 193 "Check that onAvailable is called for processes already created *before* the call to watchTargets" 194 ); 195 const targets = new Set(); 196 let thrown = false; 197 const onAvailable = ({ targetFront }) => { 198 if (!thrown) { 199 thrown = true; 200 throw new Error("Force an exception when processing the first target"); 201 } 202 targets.add(targetFront); 203 }; 204 await targetCommand.watchTargets({ types: [TYPES.PROCESS], onAvailable }); 205 is( 206 targets.size, 207 originalProcessesCount - 1, 208 "retrieved the expected number of processes via onAvailable. All but the first one where we have thrown." 209 ); 210 211 targetCommand.destroy(); 212 213 await commands.destroy(); 214 }