fune/devtools/client/debugger/panel.js
Alexandre Poirot 4c2b722821 Bug 1775302 - [devtools] Remove WebReplay leftover "preview paused location". r=bomsy
Also stop passing `frame` to DebugLine which wasn't using this attribute.
This might prevent unnecessary component updates.

Differential Revision: https://phabricator.services.mozilla.com/D149895
2022-06-27 14:03:21 +00:00

280 lines
7.4 KiB
JavaScript

/* 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/>. */
"use strict";
const { MultiLocalizationHelper } = require("devtools/shared/l10n");
const {
FluentL10n,
} = require("devtools/client/shared/fluent-l10n/fluent-l10n");
loader.lazyRequireGetter(
this,
"openContentLink",
"devtools/client/shared/link",
true
);
loader.lazyRequireGetter(
this,
"features",
"devtools/client/debugger/src/utils/prefs",
true
);
loader.lazyRequireGetter(
this,
"registerStoreObserver",
"devtools/client/shared/redux/subscriber",
true
);
const DBG_STRINGS_URI = [
"devtools/client/locales/debugger.properties",
// These are used in the AppErrorBoundary component
"devtools/client/locales/startup.properties",
"devtools/client/locales/components.properties",
];
const L10N = new MultiLocalizationHelper(...DBG_STRINGS_URI);
async function getNodeFront(gripOrFront, toolbox) {
// Given a NodeFront
if ("actorID" in gripOrFront) {
return new Promise(resolve => resolve(gripOrFront));
}
const inspectorFront = await toolbox.target.getFront("inspector");
return inspectorFront.getNodeFrontFromNodeGrip(gripOrFront);
}
class DebuggerPanel {
constructor(iframeWindow, toolbox, commands) {
this.panelWin = iframeWindow;
this.panelWin.L10N = L10N;
this.toolbox = toolbox;
this.commands = commands;
}
async open() {
// whypaused-* strings are in devtools/shared as they're used in the PausedDebuggerOverlay as well
const fluentL10n = new FluentL10n();
await fluentL10n.init(["devtools/shared/debugger-paused-reasons.ftl"]);
const {
actions,
store,
selectors,
client,
} = await this.panelWin.Debugger.bootstrap({
commands: this.commands,
fluentBundles: fluentL10n.getBundles(),
resourceCommand: this.toolbox.resourceCommand,
workers: {
sourceMaps: this.toolbox.sourceMapService,
evaluationsParser: this.toolbox.parserService,
},
panel: this,
});
this._actions = actions;
this._store = store;
this._selectors = selectors;
this._client = client;
registerStoreObserver(this._store, this._onDebuggerStateChange.bind(this));
return this;
}
_onDebuggerStateChange(state, oldState) {
const { getCurrentThread } = this._selectors;
const currentThreadActorID = getCurrentThread(state);
if (
currentThreadActorID &&
currentThreadActorID !== getCurrentThread(oldState)
) {
const threadFront = this.commands.client.getFrontByID(
currentThreadActorID
);
this.toolbox.selectTarget(threadFront?.targetFront.actorID);
}
}
getVarsForTests() {
return {
store: this._store,
selectors: this._selectors,
actions: this._actions,
client: this._client,
};
}
_getState() {
return this._store.getState();
}
getToolboxStore() {
return this.toolbox.store;
}
openLink(url) {
openContentLink(url);
}
async openConsoleAndEvaluate(input) {
const { hud } = await this.toolbox.selectTool("webconsole");
hud.ui.wrapper.dispatchEvaluateExpression(input);
}
async openInspector() {
this.toolbox.selectTool("inspector");
}
async openElementInInspector(gripOrFront) {
const onSelectInspector = this.toolbox.selectTool("inspector");
const onGripNodeToFront = getNodeFront(gripOrFront, this.toolbox);
const [front, inspector] = await Promise.all([
onGripNodeToFront,
onSelectInspector,
]);
const onInspectorUpdated = inspector.once("inspector-updated");
const onNodeFrontSet = this.toolbox.selection.setNodeFront(front, {
reason: "debugger",
});
return Promise.all([onNodeFrontSet, onInspectorUpdated]);
}
highlightDomElement(gripOrFront) {
if (!this._highlight) {
const { highlight, unhighlight } = this.toolbox.getHighlighter();
this._highlight = highlight;
this._unhighlight = unhighlight;
}
return this._highlight(gripOrFront);
}
unHighlightDomElement() {
if (!this._unhighlight) {
return Promise.resolve();
}
return this._unhighlight();
}
/**
* Return the Frame Actor ID of the currently selected frame,
* or null if the debugger isn't paused.
*/
getSelectedFrameActorID() {
const thread = this._selectors.getCurrentThread(this._getState());
const selectedFrame = this._selectors.getSelectedFrame(
this._getState(),
thread
);
if (selectedFrame) {
return selectedFrame.id;
}
return null;
}
getMappedExpression(expression) {
return this._actions.getMappedExpression(expression);
}
/**
* Return the source-mapped variables for the current scope.
* @returns {{[String]: String} | null} A dictionary mapping original variable names to generated
* variable names if map scopes is enabled, otherwise null.
*/
getMappedVariables() {
if (!this._selectors.isMapScopesEnabled(this._getState())) {
return null;
}
const thread = this._selectors.getCurrentThread(this._getState());
return this._selectors.getSelectedScopeMappings(this._getState(), thread);
}
isPaused() {
const thread = this._selectors.getCurrentThread(this._getState());
return this._selectors.getIsPaused(this._getState(), thread);
}
selectSourceURL(url, line, column) {
const cx = this._selectors.getContext(this._getState());
return this._actions.selectSourceURL(cx, url, { line, column });
}
async selectWorker(workerDescriptorFront) {
const threadActorID = workerDescriptorFront.threadFront?.actorID;
const isThreadAvailable = this._selectors
.getThreads(this._getState())
.find(x => x.actor === threadActorID);
if (!features.windowlessServiceWorkers) {
console.error(
"Selecting a worker needs the pref debugger.features.windowless-service-workers set to true"
);
return;
}
if (!isThreadAvailable) {
console.error(`Worker ${threadActorID} is not available for debugging`);
return;
}
// select worker's thread
this.selectThread(threadActorID);
// select worker's source
const source = this.getSourceByURL(workerDescriptorFront._url);
await this.selectSource(source.id, 1, 1);
}
selectThread(threadActorID) {
const cx = this._selectors.getContext(this._getState());
this._actions.selectThread(cx, threadActorID);
}
async selectSource(sourceId, line, column) {
const cx = this._selectors.getContext(this._getState());
const location = { sourceId, line, column };
await this._actions.selectSource(cx, sourceId, location);
if (this._selectors.hasLogpoint(this._getState(), location)) {
this._actions.openConditionalPanel(location, true);
}
}
getSourceActorsForSource(sourceId) {
return this._selectors.getSourceActorsForSource(this._getState(), sourceId);
}
getSourceByActorId(sourceId) {
return this._selectors.getSourceByActorId(this._getState(), sourceId);
}
getSourceByURL(sourceURL) {
return this._selectors.getSourceByURL(this._getState(), sourceURL);
}
getSource(sourceId) {
return this._selectors.getSource(this._getState(), sourceId);
}
getLocationSource(location) {
return this._selectors.getLocationSource(this._getState(), location);
}
destroy() {
this.panelWin.Debugger.destroy();
this.emit("destroyed");
}
}
exports.DebuggerPanel = DebuggerPanel;