зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1870710 - [devtools] Optionally trigger the tracer only on next mousedown or keydown. r=devtools-reviewers,fluent-reviewers,flod,nchevobbe
Differential Revision: https://phabricator.services.mozilla.com/D196830
This commit is contained in:
Родитель
acd7b3af58
Коммит
b2321bf866
|
@ -240,6 +240,12 @@ export function toggleJavascriptTracingValues() {
|
|||
};
|
||||
}
|
||||
|
||||
export function toggleJavascriptTracingOnNextInteraction() {
|
||||
return {
|
||||
type: "TOGGLE_JAVASCRIPT_TRACING_ON_NEXT_INTERACTION",
|
||||
};
|
||||
}
|
||||
|
||||
export function setHideOrShowIgnoredSources(shouldHide) {
|
||||
return ({ dispatch, getState }) => {
|
||||
dispatch({ type: "HIDE_IGNORED_SOURCES", shouldHide });
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
getIsThreadCurrentlyTracing,
|
||||
getJavascriptTracingLogMethod,
|
||||
getJavascriptTracingValues,
|
||||
getJavascriptTracingOnNextInteraction,
|
||||
} from "../../selectors";
|
||||
import { formatKeyShortcut } from "../../utils/text";
|
||||
import actions from "../../actions";
|
||||
|
@ -119,6 +120,7 @@ class CommandBar extends Component {
|
|||
toggleTracing: PropTypes.func.isRequired,
|
||||
logMethod: PropTypes.string.isRequired,
|
||||
logValues: PropTypes.bool.isRequired,
|
||||
traceOnNextInteraction: PropTypes.bool.isRequired,
|
||||
setJavascriptTracingLogMethod: PropTypes.func.isRequired,
|
||||
setHideOrShowIgnoredSources: PropTypes.func.isRequired,
|
||||
toggleSourceMapIgnoreList: PropTypes.func.isRequired,
|
||||
|
@ -254,6 +256,15 @@ class CommandBar extends Component {
|
|||
this.props.toggleJavascriptTracingValues();
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "debugger-trace-menu-item-next-interaction",
|
||||
label: L10N.getStr("traceOnNextInteraction"),
|
||||
type: "checkbox",
|
||||
checked: this.props.traceOnNextInteraction,
|
||||
click: () => {
|
||||
this.props.toggleJavascriptTracingOnNextInteraction();
|
||||
},
|
||||
},
|
||||
];
|
||||
showMenu(event, items);
|
||||
},
|
||||
|
@ -423,12 +434,15 @@ const mapStateToProps = state => ({
|
|||
isTracingEnabled: getIsThreadCurrentlyTracing(state, getCurrentThread(state)),
|
||||
logMethod: getJavascriptTracingLogMethod(state),
|
||||
logValues: getJavascriptTracingValues(state),
|
||||
traceOnNextInteraction: getJavascriptTracingOnNextInteraction(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
toggleTracing: actions.toggleTracing,
|
||||
setJavascriptTracingLogMethod: actions.setJavascriptTracingLogMethod,
|
||||
toggleJavascriptTracingValues: actions.toggleJavascriptTracingValues,
|
||||
toggleJavascriptTracingOnNextInteraction:
|
||||
actions.toggleJavascriptTracingOnNextInteraction,
|
||||
resume: actions.resume,
|
||||
stepIn: actions.stepIn,
|
||||
stepOut: actions.stepOut,
|
||||
|
|
|
@ -37,6 +37,7 @@ export const initialUIState = ({ supportsDebuggerStatementIgnore } = {}) => ({
|
|||
javascriptEnabled: true,
|
||||
javascriptTracingLogMethod: prefs.javascriptTracingLogMethod,
|
||||
javascriptTracingValues: prefs.javascriptTracingValues,
|
||||
javascriptTracingOnNextInteraction: prefs.javascriptTracingOnNextInteraction,
|
||||
mutableSearchOptions: prefs.searchOptions || {
|
||||
[searchKeys.FILE_SEARCH]: {
|
||||
regexMatch: false,
|
||||
|
@ -173,6 +174,16 @@ function update(state = initialUIState(), action) {
|
|||
};
|
||||
}
|
||||
|
||||
case "TOGGLE_JAVASCRIPT_TRACING_ON_NEXT_INTERACTION": {
|
||||
prefs.javascriptTracingOnNextInteraction =
|
||||
!prefs.javascriptTracingOnNextInteraction;
|
||||
return {
|
||||
...state,
|
||||
javascriptTracingOnNextInteraction:
|
||||
prefs.javascriptTracingOnNextInteraction,
|
||||
};
|
||||
}
|
||||
|
||||
case "SET_SEARCH_OPTIONS": {
|
||||
state.mutableSearchOptions[action.searchKey] = {
|
||||
...state.mutableSearchOptions[action.searchKey],
|
||||
|
|
|
@ -76,6 +76,10 @@ export function getJavascriptTracingValues(state) {
|
|||
return state.ui.javascriptTracingValues;
|
||||
}
|
||||
|
||||
export function getJavascriptTracingOnNextInteraction(state) {
|
||||
return state.ui.javascriptTracingOnNextInteraction;
|
||||
}
|
||||
|
||||
export function getSearchOptions(state, searchKey) {
|
||||
return state.ui.mutableSearchOptions[searchKey];
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ if (isNode()) {
|
|||
pref("devtools.debugger.log-event-breakpoints", false);
|
||||
pref("devtools.debugger.javascript-tracing-log-method", "console");
|
||||
pref("devtools.debugger.javascript-tracing-values", false);
|
||||
pref("devtools.debugger.javascript-tracing-on-next-interaction", false);
|
||||
pref("devtools.debugger.hide-ignored-sources", false);
|
||||
pref("devtools.debugger.source-map-ignore-list-enabled", true);
|
||||
pref("devtools.debugger.features.wasm", true);
|
||||
|
@ -106,6 +107,10 @@ export const prefs = new PrefsHelper("devtools", {
|
|||
"debugger.javascript-tracing-log-method",
|
||||
],
|
||||
javascriptTracingValues: ["Bool", "debugger.javascript-tracing-values"],
|
||||
javascriptTracingOnNextInteraction: [
|
||||
"Bool",
|
||||
"debugger.javascript-tracing-on-next-interaction",
|
||||
],
|
||||
hideIgnoredSources: ["Bool", "debugger.hide-ignored-sources"],
|
||||
sourceMapIgnoreListEnabled: [
|
||||
"Bool",
|
||||
|
|
|
@ -325,7 +325,7 @@ add_task(async function testTracingValues() {
|
|||
"data:text/html," + encodeURIComponent(`<script>${jsCode}</script>`)
|
||||
);
|
||||
|
||||
await openContextMenuInDebugger(dbg, "trace", 4);
|
||||
await openContextMenuInDebugger(dbg, "trace");
|
||||
const toggled = waitForDispatch(
|
||||
dbg.store,
|
||||
"TOGGLE_JAVASCRIPT_TRACING_VALUES"
|
||||
|
@ -357,3 +357,67 @@ add_task(async function testTracingValues() {
|
|||
// Reset the log values setting
|
||||
Services.prefs.clearUserPref("devtools.debugger.javascript-tracing-values");
|
||||
});
|
||||
|
||||
add_task(async function testTracingOnNextInteraction() {
|
||||
await pushPref("devtools.debugger.features.javascript-tracing", true);
|
||||
// Cover tracing only on next user interaction
|
||||
const jsCode = `function foo() {}; window.addEventListener("mousedown", function onmousedown(){}, { capture: true }); window.onclick = function onclick() {};`;
|
||||
const dbg = await initDebuggerWithAbsoluteURL(
|
||||
"data:text/html," +
|
||||
encodeURIComponent(`<script>${jsCode}</script><body></body>`)
|
||||
);
|
||||
|
||||
await openContextMenuInDebugger(dbg, "trace");
|
||||
const toggled = waitForDispatch(
|
||||
dbg.store,
|
||||
"TOGGLE_JAVASCRIPT_TRACING_ON_NEXT_INTERACTION"
|
||||
);
|
||||
selectContextMenuItem(dbg, `#debugger-trace-menu-item-next-interaction`);
|
||||
await toggled;
|
||||
ok(true, "Toggled the trace on next interaction");
|
||||
|
||||
await clickElement(dbg, "trace");
|
||||
|
||||
const topLevelThreadActorID =
|
||||
dbg.toolbox.commands.targetCommand.targetFront.threadFront.actorID;
|
||||
info("Wait for tracing to be enabled");
|
||||
await waitForState(dbg, state => {
|
||||
return dbg.selectors.getIsThreadCurrentlyTracing(topLevelThreadActorID);
|
||||
});
|
||||
|
||||
invokeInTab("foo");
|
||||
|
||||
// Let a change to have the tracer to regress and log foo call
|
||||
await wait(500);
|
||||
|
||||
is(
|
||||
(await findConsoleMessages(dbg.toolbox, "λ foo")).length,
|
||||
0,
|
||||
"The tracer did not log the function call before trigerring the click event"
|
||||
);
|
||||
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(
|
||||
"body",
|
||||
{},
|
||||
gBrowser.selectedBrowser
|
||||
);
|
||||
|
||||
await hasConsoleMessage(dbg, "λ onmousedown");
|
||||
await hasConsoleMessage(dbg, "λ onclick");
|
||||
|
||||
is(
|
||||
(await findConsoleMessages(dbg.toolbox, "λ foo")).length,
|
||||
0,
|
||||
"Even after the click, the code called before the click is still not logged"
|
||||
);
|
||||
|
||||
// But if we call this code again, now it should be logged
|
||||
invokeInTab("foo");
|
||||
await hasConsoleMessage(dbg, "λ foo");
|
||||
ok(true, "foo was traced as expected");
|
||||
|
||||
// Reset the trace on next interaction setting
|
||||
Services.prefs.clearUserPref(
|
||||
"devtools.debugger.javascript-tracing-on-next-interaction"
|
||||
);
|
||||
});
|
||||
|
|
|
@ -148,6 +148,11 @@ traceInStdout=Trace in the stdout
|
|||
# as well as returned values (only for JS function calls, but not native function calls)
|
||||
traceValues=Log function arguments and returned values
|
||||
|
||||
# LOCALIZATION NOTE (traceOnNextLoad): The label that is displayed in the context menu
|
||||
# of the trace button, which is in the top of the debugger right sidebar.
|
||||
# This is used to automatically start the tracing on next user interaction (mousedown/keydown)
|
||||
traceOnNextInteraction=Trace only on next user interaction (mousedown/keydown)
|
||||
|
||||
# LOCALIZATION NOTE (resumeButtonTooltip): The label that is displayed on the pause
|
||||
# button when the debugger is in a paused state.
|
||||
resumeButtonTooltip=Resume %S
|
||||
|
|
|
@ -12,6 +12,7 @@ const TEST_URI = `data:text/html;charset=utf-8,<!DOCTYPE html>
|
|||
<h1>Testing trace command</h1>
|
||||
<script>
|
||||
function main() {}
|
||||
function someNoise() {}
|
||||
</script>
|
||||
</div>
|
||||
<div><p></p></div>
|
||||
|
@ -43,11 +44,19 @@ add_task(async function () {
|
|||
// Instead the frontend log a message as a console API message.
|
||||
msg = await evaluateExpressionInConsole(
|
||||
hud,
|
||||
":trace --logMethod console --prefix foo --values",
|
||||
":trace --logMethod console --prefix foo --values --on-next-interaction",
|
||||
"console-api"
|
||||
);
|
||||
is(msg.textContent.trim(), "Started tracing to Web Console");
|
||||
|
||||
info("Trigger some code before the user interaction");
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
content.wrappedJSObject.someNoise();
|
||||
});
|
||||
|
||||
info("Simulate a user interaction by trigerring a key event on the page");
|
||||
await BrowserTestUtils.synthesizeKey("a", {}, gBrowser.selectedBrowser);
|
||||
|
||||
info("Trigger some code to log some traces");
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
content.wrappedJSObject.main("arg", 2);
|
||||
|
@ -57,6 +66,19 @@ add_task(async function () {
|
|||
await waitFor(
|
||||
() => !!findTracerMessages(hud, `foo: interpreter⟶λ main("arg", 2)`).length
|
||||
);
|
||||
is(
|
||||
findTracerMessages(hud, `someNoise`).length,
|
||||
0,
|
||||
"The code running before the key press should not be traced"
|
||||
);
|
||||
|
||||
// But now that the tracer is active, we will be able to log this call to someNoise
|
||||
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
|
||||
content.wrappedJSObject.someNoise();
|
||||
});
|
||||
await waitFor(
|
||||
() => !!findTracerMessages(hud, `foo: interpreter⟶λ someNoise()`).length
|
||||
);
|
||||
|
||||
info("Test toggling the tracer OFF");
|
||||
msg = await evaluateExpressionInConsole(hud, ":trace", "console-api");
|
||||
|
|
|
@ -132,6 +132,8 @@ class TracerActor extends Actor {
|
|||
traceDOMEvents: true,
|
||||
// Enable tracing function arguments as well as returned values
|
||||
traceValues: !!options.traceValues,
|
||||
// Enable tracing only on next user interaction
|
||||
traceOnNextInteraction: !!options.traceOnNextInteraction,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
# 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
# These strings aren't translated and are meant to be used for experimental commands
|
||||
# which may frequently update their documentations
|
||||
|
||||
# Usage string for :trace command
|
||||
webconsole-commands-usage-trace3 =
|
||||
:trace
|
||||
|
||||
Toggles the JavaScript tracer
|
||||
|
||||
It supports the following arguments:
|
||||
--logMethod to be set to ‘console’ for logging to the web console (the default), or ‘stdout’ for logging to the standard output,
|
||||
--values Optional flag to be passed to log function call arguments as well as returned values (when returned frames are enabled).
|
||||
--on-next-interaction Optional flag, when set, the tracer will only start on next mousedown or keydown event.
|
||||
--prefix Optional string which will be logged in front of all the trace logs,
|
||||
--help or --usage to show this message.
|
|
@ -12,11 +12,17 @@ loader.lazyRequireGetter(
|
|||
);
|
||||
|
||||
loader.lazyGetter(this, "l10n", () => {
|
||||
return new Localization(["devtools/shared/webconsole-commands.ftl"], true);
|
||||
return new Localization(
|
||||
[
|
||||
"devtools/shared/webconsole-commands.ftl",
|
||||
"devtools/server/actors/webconsole/commands/experimental-commands.ftl",
|
||||
],
|
||||
true
|
||||
);
|
||||
});
|
||||
const USAGE_STRING_MAPPING = {
|
||||
block: "webconsole-commands-usage-block",
|
||||
trace: "webconsole-commands-usage-trace2",
|
||||
trace: "webconsole-commands-usage-trace3",
|
||||
unblock: "webconsole-commands-usage-unblock",
|
||||
};
|
||||
|
||||
|
@ -872,6 +878,7 @@ WebConsoleCommandsManager.register({
|
|||
logMethod,
|
||||
prefix: args.prefix || null,
|
||||
traceValues: !!args.values,
|
||||
traceOnNextInteraction: args["on-next-interaction"] || null,
|
||||
});
|
||||
|
||||
owner.helperResult = {
|
||||
|
@ -880,5 +887,5 @@ WebConsoleCommandsManager.register({
|
|||
logMethod,
|
||||
};
|
||||
},
|
||||
validArguments: ["logMethod", "prefix", "values"],
|
||||
validArguments: ["logMethod", "prefix", "values", "on-next-interaction"],
|
||||
});
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#filter substitution
|
||||
# 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 http://mozilla.org/MPL/2.0/.
|
||||
|
||||
[localization] @AB_CD@.jar:
|
||||
# This is a workaround to ship a fluent file not visible to localizers for experimental features.
|
||||
devtools/server/actors/webconsole/commands/experimental-commands.ftl (actors/webconsole/commands/experimental-commands.ftl)
|
|
@ -15,6 +15,8 @@ DIRS += [
|
|||
"tracer",
|
||||
]
|
||||
|
||||
JAR_MANIFESTS += ["jar.mn"]
|
||||
|
||||
if CONFIG["MOZ_BUILD_APP"] != "mobile/android":
|
||||
BROWSER_CHROME_MANIFESTS += ["tests/browser/browser.toml"]
|
||||
|
||||
|
|
|
@ -92,6 +92,9 @@ const customLazy = {
|
|||
* @param {Boolean} options.traceValues
|
||||
* Optional setting to enable tracing all function call values as well,
|
||||
* as returned values (when we do log returned frames).
|
||||
* @param {Boolean} options.traceOnNextInteraction
|
||||
* Optional setting to enable when the tracing should only start when the
|
||||
* use starts interacting with the page. i.e. on next keydown or mousedown.
|
||||
*/
|
||||
class JavaScriptTracer {
|
||||
constructor(options) {
|
||||
|
@ -120,15 +123,40 @@ class JavaScriptTracer {
|
|||
: dump;
|
||||
}
|
||||
|
||||
this.traceDOMEvents = !!options.traceDOMEvents;
|
||||
this.traceValues = !!options.traceValues;
|
||||
|
||||
if (options.traceOnNextInteraction) {
|
||||
this.abortController = new AbortController();
|
||||
const listener = () => {
|
||||
this.abortController.abort();
|
||||
// Avoid tracing if the users asked to stop tracing.
|
||||
if (this.dbg) {
|
||||
this.#startTracing();
|
||||
}
|
||||
};
|
||||
const eventOptions = {
|
||||
signal: this.abortController.signal,
|
||||
capture: true,
|
||||
};
|
||||
// Register the event listener on the Chrome Event Handler in order to receive the event first.
|
||||
const eventHandler = this.tracedGlobal.docShell.chromeEventHandler;
|
||||
eventHandler.addEventListener("mousedown", listener, eventOptions);
|
||||
eventHandler.addEventListener("keydown", listener, eventOptions);
|
||||
} else {
|
||||
this.#startTracing();
|
||||
}
|
||||
|
||||
// In any case, we consider the tracing as started
|
||||
this.notifyToggle(true);
|
||||
}
|
||||
|
||||
#startTracing() {
|
||||
this.dbg.onEnterFrame = this.onEnterFrame;
|
||||
|
||||
this.traceDOMEvents = !!options.traceDOMEvents;
|
||||
if (this.traceDOMEvents) {
|
||||
this.startTracingDOMEvents();
|
||||
}
|
||||
this.traceValues = !!options.traceValues;
|
||||
|
||||
this.notifyToggle(true);
|
||||
}
|
||||
|
||||
startTracingDOMEvents() {
|
||||
|
@ -141,9 +169,11 @@ class JavaScriptTracer {
|
|||
}
|
||||
|
||||
stopTracingDOMEvents() {
|
||||
this.debuggerNotificationObserver.removeListener(this.eventListener);
|
||||
this.debuggerNotificationObserver.disconnect(this.tracedGlobal);
|
||||
this.debuggerNotificationObserver = null;
|
||||
if (this.debuggerNotificationObserver) {
|
||||
this.debuggerNotificationObserver.removeListener(this.eventListener);
|
||||
this.debuggerNotificationObserver.disconnect(this.tracedGlobal);
|
||||
this.debuggerNotificationObserver = null;
|
||||
}
|
||||
this.currentDOMEvent = null;
|
||||
}
|
||||
|
||||
|
@ -186,6 +216,10 @@ class JavaScriptTracer {
|
|||
this.dbg = null;
|
||||
this.depth = 0;
|
||||
this.options = null;
|
||||
if (this.abortController) {
|
||||
this.abortController.abort();
|
||||
this.abortController = null;
|
||||
}
|
||||
|
||||
if (this.traceDOMEvents) {
|
||||
this.stopTracingDOMEvents();
|
||||
|
|
|
@ -38,6 +38,11 @@ class TracerCommand {
|
|||
false
|
||||
);
|
||||
|
||||
const traceOnNextInteraction = Services.prefs.getBoolPref(
|
||||
"devtools.debugger.javascript-tracing-on-next-interaction",
|
||||
false
|
||||
);
|
||||
|
||||
const targets = this.#targetCommand.getAllTargets(
|
||||
this.#targetCommand.ALL_TYPES
|
||||
);
|
||||
|
@ -51,7 +56,10 @@ class TracerCommand {
|
|||
}
|
||||
|
||||
if (this.#isTracing) {
|
||||
return tracerFront.startTracing(logMethod, { traceValues });
|
||||
return tracerFront.startTracing(logMethod, {
|
||||
traceValues,
|
||||
traceOnNextInteraction,
|
||||
});
|
||||
}
|
||||
return tracerFront.stopTracing();
|
||||
})
|
||||
|
|
|
@ -22,15 +22,3 @@ webconsole-commands-usage-unblock =
|
|||
Stop blocking network requests
|
||||
|
||||
It accepts only one argument, the exact same string previously passed to :block.
|
||||
|
||||
# Usage string for :trace command
|
||||
webconsole-commands-usage-trace2 =
|
||||
:trace
|
||||
|
||||
Toggles the JavaScript tracer
|
||||
|
||||
It supports the following arguments:
|
||||
--logMethod to be set to ‘console’ for logging to the web console (the default), or ‘stdout’ for logging to the standard output,
|
||||
--values Optional flag to be passed to log function call arguments as well as returned values (when returned frames are enabled).
|
||||
--prefix Optional string which will be logged in front of all the trace logs,
|
||||
--help or --usage to show this message.
|
||||
|
|
Загрузка…
Ссылка в новой задаче