/* 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