/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at . */ // Tests the Javascript Tracing feature. "use strict"; add_task(async function () { // This is preffed off for now, so ensure turning it on await pushPref("devtools.debugger.features.javascript-tracing", true); const dbg = await initDebugger("doc-scripts.html"); await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () { // Register a global event listener to cover listeners set to DOM Element as well as on the global // This may regress the DOM Events panel to show more than one entry for the click event. content.eval(`window.onclick = () => {};`); }); info("Force the log method to be the debugger sidebar"); await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-debugger-sidebar"); info("Enable the tracing"); await toggleJsTracer(dbg.toolbox); is( dbg.selectors.getSelectedPrimaryPaneTab(), "tracer", "The tracer sidebar is automatically shown on start" ); const argumentSearchInput = findElementWithSelector( dbg, `#tracer-tab-panel .call-tree-container input` ); is( argumentSearchInput.disabled, true, "The input to search by values is disabled" ); info("Toggle off and on in order to record with values"); await toggleJsTracer(dbg.toolbox); info("Enable values recording"); await toggleJsTracerMenuItem(dbg, "#jstracer-menu-item-log-values"); await toggleJsTracer(dbg.toolbox); const topLevelThreadActorID = dbg.toolbox.commands.targetCommand.targetFront.threadFront.actorID; info("Wait for tracing to be enabled"); await waitForState(dbg, () => { return dbg.selectors.getIsThreadCurrentlyTracing(topLevelThreadActorID); }); is( argumentSearchInput.disabled, false, "The input to search by values is no longer disabled" ); let tracerMessage = findElementWithSelector( dbg, "#tracer-tab-panel .tracer-message" ); is(tracerMessage.textContent, "Waiting for the first JavaScript executions"); invokeInTab("main"); info("Wait for the call tree to appear in the tracer panel"); const tracerTree = await waitForElementWithSelector( dbg, "#tracer-tab-panel .tree" ); info("Wait for the expected traces to appear in the call tree"); let traces = await waitFor(() => { const elements = tracerTree.querySelectorAll(".trace-line"); if (elements.length == 3) { return elements; } return false; }); is(traces[0].textContent, "λ main simple1.js:1:16"); is(traces[1].textContent, "λ foo simple2.js:1:12"); is(traces[2].textContent, "λ bar simple2.js:3:4"); ok( !findElement(dbg, "tracedLine"), "Before selecting any trace, no line is highlighted in CodeMirror" ); info("Select the trace for the call to `foo`"); EventUtils.synthesizeMouseAtCenter(traces[1], {}, dbg.win); let focusedTrace = await waitFor( () => tracerTree.querySelector(".tree-node.focused .trace-line"), "Wait for the line to be focused in the tracer panel" ); is(focusedTrace, traces[1], "The clicked trace is now focused"); await waitFor( () => findElement(dbg, "tracedLine"), "Wait for the traced line to be highlighted in CodeMirror" ); ok( findElement(dbg, "tracedLine"), "When a trace is selected, the line is highlighted in CodeMirror" ); // Naive sanity checks for inlines previews const inlinePreviews = [ { identifier: "x:", value: "1", }, { identifier: "y:", value: "2", }, ]; await waitForAllElements(dbg, "inlinePreviewLabels", inlinePreviews.length); const labels = findAllElements(dbg, "inlinePreviewLabels"); const values = findAllElements(dbg, "inlinePreviewValues"); let index = 0; const fnName = "foo"; for (const { identifier, value } of inlinePreviews) { is( labels[index].innerText, identifier, `${identifier} in ${fnName} has correct inline preview label` ); is( values[index].innerText, value, `${identifier} in ${fnName} has correct inline preview value` ); index++; } // Naive sanity checks for popup previews on hovering { const { element: popupEl, tokenEl } = await tryHovering( dbg, 1, 14, "previewPopup" ); is(popupEl.querySelector(".objectBox")?.textContent, "1"); await closePreviewForToken(dbg, tokenEl, "previewPopup"); } { const { element: popupEl, tokenEl } = await tryHovering( dbg, 1, 17, "previewPopup" ); is(popupEl.querySelector(".objectBox")?.textContent, "2"); await closePreviewForToken(dbg, tokenEl, "previewPopup"); } let focusedPausedFrame = findElementWithSelector( dbg, ".frames .frame.selected" ); ok(!focusedPausedFrame, "Before pausing, there is no selected paused frame"); info("Trigger a breakpoint"); const onResumed = SpecialPowers.spawn( gBrowser.selectedBrowser, [], async function () { content.eval("debugger;"); } ); await waitForPaused(dbg); await waitForSelectedLocation(dbg, 1, 1); focusedPausedFrame = findElementWithSelector(dbg, ".frames .frame.selected"); ok( !!focusedPausedFrame, "When paused, a frame is selected in the call stack panel" ); focusedTrace = tracerTree.querySelector(".tree-node.focused .trace-line"); is(focusedTrace, null, "When pausing, there is no trace selected anymore"); info("Re select the tracer frame while being paused"); EventUtils.synthesizeMouseAtCenter(traces[1], {}, dbg.win); await waitForSelectedLocation(dbg, 1, 13); focusedPausedFrame = findElementWithSelector(dbg, ".frames .frame.selected"); ok( !focusedPausedFrame, "While paused, if we select a tracer frame, the paused frame is no longer highlighted in the call stack panel" ); const highlightedPausedFrame = findElementWithSelector( dbg, ".frames .frame.inactive" ); ok( !!highlightedPausedFrame, "But it is still highlighted as inactive with a grey background" ); ok( findElement(dbg, "tracedLine"), "When a trace is selected, while being paused, the line is highlighted as traced in CodeMirror" ); ok( !findElement(dbg, "pausedLine"), "The traced line is not highlighted as paused" ); await resume(dbg); await onResumed; ok( findElement(dbg, "tracedLine"), "After resuming, the traced line is still highlighted in CodeMirror" ); // Trigger a click in the content page to verify we do trace DOM events BrowserTestUtils.synthesizeMouseAtCenter( "button", {}, gBrowser.selectedBrowser ); const [nodeClickTrace, globalClickTrace] = await waitFor(() => { const elts = tracerTree.querySelectorAll(".tracer-dom-event"); if (elts.length == 2) { return elts; } return false; }); // This is the listener set on the