browser_dbg-javascript-tracer.js (10850B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 // Tests the Javascript Tracing feature. 6 7 "use strict"; 8 9 add_task(async function () { 10 const dbg = await initDebugger("doc-scripts.html"); 11 12 // This test covers the Web Console, whereas it is no longer the default output 13 await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-console"); 14 15 // Add an iframe before starting the tracer to later check for key event on it 16 const preExistingIframeBrowsingContext = await SpecialPowers.spawn( 17 gBrowser.selectedBrowser, 18 [], 19 async function () { 20 const iframe = content.document.createElement("iframe"); 21 iframe.src = `data:text/html,<input type="text" value="pre existing iframe" onkeydown="console.log('keydown')" />`; 22 content.document.body.appendChild(iframe); 23 await new Promise(resolve => (iframe.onload = resolve)); 24 return iframe.contentWindow.browsingContext; 25 } 26 ); 27 28 info("Enable the tracing"); 29 await toggleJsTracer(dbg.toolbox); 30 31 ok( 32 dbg.toolbox.splitConsole, 33 "Split console is automatically opened when tracing to the console" 34 ); 35 36 await hasConsoleMessage(dbg, "Started tracing to Web Console"); 37 38 invokeInTab("main"); 39 40 info("Wait for console messages for the whole trace"); 41 // `main` calls `foo` which calls `bar` 42 await hasConsoleMessage(dbg, "λ main"); 43 await hasConsoleMessage(dbg, "λ foo"); 44 await hasConsoleMessage(dbg, "λ bar"); 45 46 const linkEl = await waitForConsoleMessageLink( 47 dbg.toolbox, 48 "λ main", 49 "simple1.js:1:17" 50 ); 51 linkEl.click(); 52 info("Wait for the main function to be highlighted in the debugger"); 53 await waitForSelectedSource(dbg, "simple1.js"); 54 await waitForSelectedLocation(dbg, 1, 17); 55 56 // Trigger a click to verify we do trace DOM events 57 BrowserTestUtils.synthesizeMouseAtCenter( 58 "button", 59 {}, 60 gBrowser.selectedBrowser 61 ); 62 63 await hasConsoleMessage(dbg, "DOM | node.click"); 64 await hasConsoleMessage(dbg, "λ simple"); 65 66 const iframeBrowsingContext = await SpecialPowers.spawn( 67 gBrowser.selectedBrowser, 68 [], 69 async function () { 70 const iframe = content.document.createElement("iframe"); 71 iframe.src = `data:text/html,<input type="text" value="new iframe" onkeypress="console.log('keypress')" />`; 72 content.document.body.appendChild(iframe); 73 await new Promise(resolve => (iframe.onload = resolve)); 74 iframe.contentWindow.document.querySelector("input").focus(); 75 return iframe.contentWindow.browsingContext; 76 } 77 ); 78 79 await BrowserTestUtils.synthesizeKey("x", {}, iframeBrowsingContext); 80 await hasConsoleMessage(dbg, "DOM | node.keypress"); 81 await hasConsoleMessage(dbg, "λ onkeypress"); 82 83 await SpecialPowers.spawn( 84 preExistingIframeBrowsingContext, 85 [], 86 async function () { 87 content.document.querySelector("input").focus(); 88 } 89 ); 90 await BrowserTestUtils.synthesizeKey( 91 "x", 92 {}, 93 preExistingIframeBrowsingContext 94 ); 95 await hasConsoleMessage(dbg, "DOM | node.keydown"); 96 await hasConsoleMessage(dbg, "λ onkeydown"); 97 98 // Test Blackboxing 99 info("Clear the console from previous traces"); 100 const { hud } = await dbg.toolbox.getPanel("webconsole"); 101 hud.ui.clearOutput(); 102 await waitFor( 103 async () => !(await findConsoleMessages(dbg.toolbox, "λ main")).length, 104 "Wait for console to be cleared" 105 ); 106 107 info( 108 "Now blackbox only the source where main function is (simple1.js), but foo and bar are in another module" 109 ); 110 await clickElement(dbg, "blackbox"); 111 await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"); 112 113 info("Trigger some code from simple1 and simple2"); 114 invokeInTab("main"); 115 116 info("Only methods from simple2 are logged"); 117 await hasConsoleMessage(dbg, "λ foo"); 118 await hasConsoleMessage(dbg, "λ bar"); 119 is( 120 (await findConsoleMessages(dbg.toolbox, "λ main")).length, 121 0, 122 "Traces from simple1.js, related to main function are not logged" 123 ); 124 125 info("Revert blackboxing"); 126 await clickElement(dbg, "blackbox"); 127 await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"); 128 129 // Test Disabling tracing 130 info("Disable the tracing"); 131 await toggleJsTracer(dbg.toolbox); 132 133 invokeInTab("inline_script2"); 134 135 // Let some time for the tracer to appear if we failed disabling the tracing 136 await wait(1000); 137 138 const messages = await findConsoleMessages(dbg.toolbox, "inline_script2"); 139 is( 140 messages.length, 141 0, 142 "We stopped recording traces, an the function call isn't logged in the console" 143 ); 144 145 // Test Navigations 146 await navigate(dbg, "doc-sourcemaps2.html", "main.js", "main.min.js"); 147 148 info("Re-enable the tracing after navigation"); 149 await toggleJsTracer(dbg.toolbox); 150 151 invokeInTab("logMessage"); 152 153 // Test clicking on the function to open the precise related location 154 const linkEl2 = await waitForConsoleMessageLink( 155 dbg.toolbox, 156 "λ logMessage", 157 "main.js:4:3" 158 ); 159 linkEl2.click(); 160 161 info("Wait for the 'logMessage' function to be highlighted in the debugger"); 162 await waitForSelectedSource(dbg, "main.js"); 163 await waitForSelectedLocation(dbg, 4, 3); 164 ok(true, "The selected source and location is on the original file"); 165 166 await dbg.toolbox.closeToolbox(); 167 }); 168 169 add_task(async function testPersitentLogMethod() { 170 let dbg = await initDebugger("doc-scripts.html"); 171 172 is( 173 dbg.commands.tracerCommand.getTracingOptions().logMethod, 174 "console", 175 "By default traces are logged to the console" 176 ); 177 178 info("Change the log method to stdout"); 179 await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-stdout"); 180 181 await dbg.toolbox.closeToolbox(); 182 183 dbg = await initDebugger("doc-scripts.html"); 184 is( 185 dbg.commands.tracerCommand.getTracingOptions().logMethod, 186 "stdout", 187 "The new setting has been persisted" 188 ); 189 190 info("Reset back to the default value"); 191 await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-console"); 192 }); 193 194 add_task(async function testPageKeyShortcut() { 195 // Ensures that the key shortcut emitted in the content process bubbles up to the parent process 196 await pushPref("test.events.async.enabled", true); 197 198 // Fake DevTools being opened by a real user interaction. 199 // Tests are bypassing DevToolsStartup to open the tools by calling gDevTools directly. 200 // By doing so DevToolsStartup considers itself as uninitialized, 201 // whereas we want it to handle the key shortcut we trigger in this test. 202 const DevToolsStartup = Cc["@mozilla.org/devtools/startup-clh;1"].getService( 203 Ci.nsISupports 204 ).wrappedJSObject; 205 DevToolsStartup.initialized = true; 206 registerCleanupFunction(() => { 207 DevToolsStartup.initialized = false; 208 }); 209 210 const dbg = await initDebuggerWithAbsoluteURL("data:text/html,key-shortcut"); 211 212 const button = dbg.toolbox.doc.getElementById("command-button-jstracer"); 213 ok(!button.classList.contains("checked"), "The trace button is off on start"); 214 215 info( 216 "Focus the page in order to assert that the page keeps the focus when enabling the tracer" 217 ); 218 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 219 content.focus(); 220 }); 221 await waitFor( 222 () => Services.focus.focusedElement == gBrowser.selectedBrowser 223 ); 224 is( 225 Services.focus.focusedElement, 226 gBrowser.selectedBrowser, 227 "The tab is still focused before enabling tracing" 228 ); 229 230 info("Toggle ON the tracing via the key shortcut from the web page"); 231 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 232 EventUtils.synthesizeKey( 233 "VK_5", 234 { ctrlKey: true, shiftKey: true }, 235 content 236 ); 237 }); 238 239 info("Wait for tracing to be enabled"); 240 await waitFor(() => button.classList.contains("checked")); 241 242 is( 243 Services.focus.focusedElement, 244 gBrowser.selectedBrowser, 245 "The tab is still focused after enabling tracing" 246 ); 247 248 info("Toggle it back off, with the same shortcut"); 249 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 250 EventUtils.synthesizeKey( 251 "VK_5", 252 { ctrlKey: true, shiftKey: true }, 253 content 254 ); 255 }); 256 257 info("Wait for tracing to be disabled"); 258 await waitFor(() => !button.classList.contains("checked")); 259 }); 260 261 add_task(async function testPageKeyShortcutWithoutDebugger() { 262 // Ensures that the key shortcut emitted in the content process bubbles up to the parent process 263 await pushPref("test.events.async.enabled", true); 264 265 // Fake DevTools being opened by a real user interaction. 266 // Tests are bypassing DevToolsStartup to open the tools by calling gDevTools directly. 267 // By doing so DevToolsStartup considers itself as uninitialized, 268 // whereas we want it to handle the key shortcut we trigger in this test. 269 const DevToolsStartup = Cc["@mozilla.org/devtools/startup-clh;1"].getService( 270 Ci.nsISupports 271 ).wrappedJSObject; 272 DevToolsStartup.initialized = true; 273 registerCleanupFunction(() => { 274 DevToolsStartup.initialized = false; 275 }); 276 277 const toolbox = await openNewTabAndToolbox( 278 "data:text/html,tracer", 279 "webconsole" 280 ); 281 282 info( 283 "Focus the page in order to assert that the page keeps the focus when enabling the tracer" 284 ); 285 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 286 content.focus(); 287 }); 288 await waitFor( 289 () => Services.focus.focusedElement == gBrowser.selectedBrowser 290 ); 291 is( 292 Services.focus.focusedElement, 293 gBrowser.selectedBrowser, 294 "The tab is still focused before enabling tracing" 295 ); 296 297 info("Toggle ON the tracing via the key shortcut from the web page"); 298 const { resourceCommand } = toolbox.commands; 299 const { onResource: onTracingStateEnabled } = 300 await resourceCommand.waitForNextResource( 301 resourceCommand.TYPES.JSTRACER_STATE, 302 { 303 ignoreExistingResources: true, 304 predicate(resource) { 305 return resource.enabled; 306 }, 307 } 308 ); 309 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 310 EventUtils.synthesizeKey( 311 "VK_5", 312 { ctrlKey: true, shiftKey: true }, 313 content 314 ); 315 }); 316 info("Wait for tracing to be enabled"); 317 await onTracingStateEnabled; 318 319 info("Toggle it back off, with the same shortcut"); 320 const { onResource: onTracingStateDisabled } = 321 await resourceCommand.waitForNextResource( 322 resourceCommand.TYPES.JSTRACER_STATE, 323 { 324 ignoreExistingResources: true, 325 predicate(resource) { 326 return !resource.enabled; 327 }, 328 } 329 ); 330 await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { 331 EventUtils.synthesizeKey( 332 "VK_5", 333 { ctrlKey: true, shiftKey: true }, 334 content 335 ); 336 }); 337 338 info("Wait for tracing to be disabled"); 339 await onTracingStateDisabled; 340 });