summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger')
-rw-r--r--devtools/client/debugger/src/actions/context-menus/editor.js25
-rw-r--r--devtools/client/debugger/src/components/Editor/Breakpoints.css8
-rw-r--r--devtools/client/debugger/src/components/Editor/ConditionalPanel.css5
-rw-r--r--devtools/client/debugger/src/components/Editor/ConditionalPanel.js72
-rw-r--r--devtools/client/debugger/src/components/Editor/Editor.css4
-rw-r--r--devtools/client/debugger/src/components/Editor/Footer.js2
-rw-r--r--devtools/client/debugger/src/components/Editor/HighlightLine.js132
-rw-r--r--devtools/client/debugger/src/components/Editor/HighlightLines.js44
-rw-r--r--devtools/client/debugger/src/components/Editor/InlinePreviews.js88
-rw-r--r--devtools/client/debugger/src/components/Editor/index.js116
-rw-r--r--devtools/client/debugger/src/utils/editor/index.js44
-rw-r--r--devtools/client/debugger/src/utils/editor/tests/editor.spec.js12
-rw-r--r--devtools/client/debugger/src/utils/source.js9
-rw-r--r--devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js10
-rw-r--r--devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js45
-rw-r--r--devtools/client/debugger/test/mochitest/examples/event-breakpoints.js7
16 files changed, 440 insertions, 183 deletions
diff --git a/devtools/client/debugger/src/actions/context-menus/editor.js b/devtools/client/debugger/src/actions/context-menus/editor.js
index 4785c963cb..2b82628f1a 100644
--- a/devtools/client/debugger/src/actions/context-menus/editor.js
+++ b/devtools/client/debugger/src/actions/context-menus/editor.js
@@ -39,7 +39,7 @@ import { toggleBlackBox } from "../../actions/sources/blackbox";
import { addExpression } from "../../actions/expressions";
import { evaluateInConsole } from "../../actions/toolbox";
-export function showEditorContextMenu(event, editor, location) {
+export function showEditorContextMenu(event, editor, lineObject, location) {
return async ({ dispatch, getState }) => {
const { source } = location;
const state = getState();
@@ -63,9 +63,9 @@ export function showEditorContextMenu(event, editor, location) {
location,
isPaused,
editorWrappingEnabled,
- selectionText: editor.codeMirror.getSelection().trim(),
- isTextSelected: editor.codeMirror.somethingSelected(),
- editor,
+ selectionText: editor.getSelectedText(),
+ isTextSelected: editor.isTextSelected(),
+ lineObject,
isSourceOnIgnoreList,
dispatch,
})
@@ -339,7 +339,7 @@ function editorMenuItems({
isTextSelected,
isPaused,
editorWrappingEnabled,
- editor,
+ lineObject,
isSourceOnIgnoreList,
dispatch,
}) {
@@ -368,14 +368,8 @@ function editorMenuItems({
blackBoxMenuItem(source, blackboxedRanges, isSourceOnIgnoreList, dispatch)
);
- const startLine = toSourceLine(
- source.id,
- editor.codeMirror.getCursor("from").line
- );
- const endLine = toSourceLine(
- source.id,
- editor.codeMirror.getCursor("to").line
- );
+ const startLine = toSourceLine(source.id, lineObject.from.line);
+ const endLine = toSourceLine(source.id, lineObject.to.line);
// Find any blackbox ranges that exist for the selected lines
const blackboxRange = findBlackBoxRange(source, blackboxedRanges, {
@@ -400,10 +394,7 @@ function editorMenuItems({
items.push(
blackBoxSourceLinesMenuItem(
source,
- {
- from: editor.codeMirror.getCursor("from"),
- to: editor.codeMirror.getCursor("to"),
- },
+ lineObject,
blackboxedRanges,
isSourceOnIgnoreList,
null,
diff --git a/devtools/client/debugger/src/components/Editor/Breakpoints.css b/devtools/client/debugger/src/components/Editor/Breakpoints.css
index 92121e0f46..4983688031 100644
--- a/devtools/client/debugger/src/components/Editor/Breakpoints.css
+++ b/devtools/client/debugger/src/components/Editor/Breakpoints.css
@@ -95,12 +95,16 @@
right: -16px;
}
-.new-breakpoint.has-condition .CodeMirror-gutter-wrapper svg {
+.new-breakpoint.has-condition .CodeMirror-gutter-wrapper svg,
+/* Codemirror 6*/
+.cm6-gutter-breakpoint .breakpoint-marker.has-condition svg {
fill: var(--breakpoint-condition-fill);
stroke: var(--breakpoint-condition-stroke);
}
-.new-breakpoint.has-log .CodeMirror-gutter-wrapper svg {
+.new-breakpoint.has-log .CodeMirror-gutter-wrapper svg,
+/* Codemirror 6*/
+.cm6-gutter-breakpoint .breakpoint-marker.has-log svg {
fill: var(--logpoint-fill);
stroke: var(--logpoint-stroke);
}
diff --git a/devtools/client/debugger/src/components/Editor/ConditionalPanel.css b/devtools/client/debugger/src/components/Editor/ConditionalPanel.css
index 4ce8dbcd8c..1aeac91604 100644
--- a/devtools/client/debugger/src/components/Editor/ConditionalPanel.css
+++ b/devtools/client/debugger/src/components/Editor/ConditionalPanel.css
@@ -37,3 +37,8 @@
/* Match the color of the placeholder text to existing inputs in the Debugger */
color: var(--theme-text-color-alt);
}
+
+/* Removing the line padding for Codemirror 6 */
+.cm-line:has(div.conditional-breakpoint-panel) {
+ padding: 0;
+}
diff --git a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
index 97876f2f00..4e6f0b58ea 100644
--- a/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
+++ b/devtools/client/debugger/src/components/Editor/ConditionalPanel.js
@@ -11,7 +11,8 @@ import ReactDOM from "devtools/client/shared/vendor/react-dom";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import { connect } from "devtools/client/shared/vendor/react-redux";
import { toEditorLine } from "../../utils/editor/index";
-import { prefs } from "../../utils/prefs";
+import { createEditor } from "../../utils/editor/create-editor";
+import { prefs, features } from "../../utils/prefs";
import actions from "../../actions/index";
import {
@@ -21,6 +22,7 @@ import {
} from "../../selectors/index";
const classnames = require("resource://devtools/client/shared/classnames.js");
+const CONDITIONAL_BP_MARKER = "conditional-breakpoint-panel-marker";
function addNewLine(doc) {
const cursor = doc.getCursor();
@@ -49,6 +51,7 @@ export class ConditionalPanel extends PureComponent {
log: PropTypes.bool.isRequired,
openConditionalPanel: PropTypes.func.isRequired,
setBreakpointOptions: PropTypes.func.isRequired,
+ selectedSource: PropTypes.object.isRequired,
};
}
@@ -111,17 +114,53 @@ export class ConditionalPanel extends PureComponent {
}
};
+ showConditionalPanel(prevProps) {
+ const { location, editor, breakpoint, selectedSource } = this.props;
+ // When breakpoint is removed
+ if (prevProps?.breakpoint && !breakpoint) {
+ editor.removeLineContentMarker(CONDITIONAL_BP_MARKER);
+ return;
+ }
+ if (selectedSource.id !== location.source.id) {
+ editor.removeLineContentMarker(CONDITIONAL_BP_MARKER);
+ return;
+ }
+ const editorLine = toEditorLine(location.source.id, location.line || 0);
+ editor.setLineContentMarker({
+ id: CONDITIONAL_BP_MARKER,
+ condition: line => line == editorLine,
+ createLineElementNode: () => {
+ // Create a Codemirror 5 editor for the breakpoint panel
+ // TODO: Switch to use Codemirror 6 version Bug 1890205
+ const breakpointPanelEditor = createEditor();
+ breakpointPanelEditor.appendToLocalElement(
+ document.createElement("div")
+ );
+ return this.renderConditionalPanel(this.props, breakpointPanelEditor);
+ },
+ });
+ }
+
// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
UNSAFE_componentWillMount() {
- return this.renderToWidget(this.props);
+ if (features.codemirrorNext) {
+ this.showConditionalPanel();
+ } else {
+ this.renderToWidget(this.props);
+ }
}
// FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=1774507
UNSAFE_componentWillUpdate() {
- return this.clearConditionalPanel();
+ if (!features.codemirrorNext) {
+ this.clearConditionalPanel();
+ }
}
- componentDidUpdate() {
+ componentDidUpdate(prevProps) {
+ if (features.codemirrorNext) {
+ this.showConditionalPanel(prevProps);
+ }
this.keepFocusOnInput();
}
@@ -129,7 +168,12 @@ export class ConditionalPanel extends PureComponent {
// This is called if CodeMirror is re-initializing itself before the
// user closes the conditional panel. Clear the widget, and re-render it
// as soon as this component gets remounted
- return this.clearConditionalPanel();
+ const { editor } = this.props;
+ if (features.codemirrorNext) {
+ editor.removeLineContentMarker(CONDITIONAL_BP_MARKER);
+ } else {
+ this.clearConditionalPanel();
+ }
}
renderToWidget(props) {
@@ -141,7 +185,7 @@ export class ConditionalPanel extends PureComponent {
const editorLine = toEditorLine(location.source.id, location.line || 0);
this.cbPanel = editor.codeMirror.addLineWidget(
editorLine,
- this.renderConditionalPanel(props),
+ this.renderConditionalPanel(props, editor),
{
coverGutter: true,
noHScroll: true,
@@ -168,8 +212,8 @@ export class ConditionalPanel extends PureComponent {
}
}
- createEditor = input => {
- const { log, editor, closeConditionalPanel } = this.props;
+ createEditor = (input, editor) => {
+ const { log, closeConditionalPanel } = this.props;
const codeMirror = editor.CodeMirror.fromTextArea(input, {
mode: "javascript",
theme: "mozilla",
@@ -189,8 +233,12 @@ export class ConditionalPanel extends PureComponent {
codeMirror.on("blur", (cm, e) => {
if (
- e?.relatedTarget &&
- e.relatedTarget.closest(".conditional-breakpoint-panel")
+ // if there is no event
+ // or if the focus is the conditional panel
+ // do not close the conditional panel
+ !e ||
+ (e?.relatedTarget &&
+ e.relatedTarget.closest(".conditional-breakpoint-panel"))
) {
return;
}
@@ -217,7 +265,7 @@ export class ConditionalPanel extends PureComponent {
return log ? options.logValue : options.condition;
}
- renderConditionalPanel(props) {
+ renderConditionalPanel(props, editor) {
const { log } = props;
const defaultValue = this.getDefaultValue();
@@ -239,7 +287,7 @@ export class ConditionalPanel extends PureComponent {
),
textarea({
defaultValue,
- ref: input => this.createEditor(input),
+ ref: input => this.createEditor(input, editor),
})
),
panel
diff --git a/devtools/client/debugger/src/components/Editor/Editor.css b/devtools/client/debugger/src/components/Editor/Editor.css
index f28833747d..d2f55ed6db 100644
--- a/devtools/client/debugger/src/components/Editor/Editor.css
+++ b/devtools/client/debugger/src/components/Editor/Editor.css
@@ -146,7 +146,9 @@ html[dir="rtl"] .editor-mount {
.new-debug-line-error .CodeMirror-activeline-background {
display: none;
}
-.highlight-line .CodeMirror-line {
+.highlight-line .CodeMirror-line,
+/* For CM6 */
+.cm-editor .cm-line.highlight-line {
animation-name: fade-highlight-out;
animation-duration: var(--highlight-line-duration);
animation-timing-function: ease-out;
diff --git a/devtools/client/debugger/src/components/Editor/Footer.js b/devtools/client/debugger/src/components/Editor/Footer.js
index 69c7b52b68..278bc1dad7 100644
--- a/devtools/client/debugger/src/components/Editor/Footer.js
+++ b/devtools/client/debugger/src/components/Editor/Footer.js
@@ -459,8 +459,6 @@ const mapStateToProps = state => {
: null,
isSourceActorWithSourceMap: isSourceActorWithSourceMapProp,
- sourceMapURL: selectedLocation?.sourceActor.sourceMapURL,
-
areSourceMapsEnabled: areSourceMapsEnabledProp,
shouldSelectOriginalLocation: getShouldSelectOriginalLocation(state),
};
diff --git a/devtools/client/debugger/src/components/Editor/HighlightLine.js b/devtools/client/debugger/src/components/Editor/HighlightLine.js
index 8639128905..0df0fa482e 100644
--- a/devtools/client/debugger/src/components/Editor/HighlightLine.js
+++ b/devtools/client/debugger/src/components/Editor/HighlightLine.js
@@ -2,10 +2,20 @@
* 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/>. */
+/**
+ * Uses of this panel are:-
+ * - Highlight line when source is opened using view source links from other panels
+ * - Highlight line with function or class from an Outline search result selection
+ * - Highlight line from a Quick open panel search result selection
+ * - Highlight the last selected line when a source is selected
+ * - Highlight the breakpoint line when the breakpoint is selected
+ */
+
import { Component } from "devtools/client/shared/vendor/react";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import {
toEditorLine,
+ fromEditorLine,
endOperation,
startOperation,
} from "../../utils/editor/index";
@@ -20,6 +30,7 @@ import {
getCurrentThread,
getShouldHighlightSelectedLocation,
} from "../../selectors/index";
+import { features } from "../../utils/prefs";
function isDebugLine(selectedFrame, selectedLocation) {
if (!selectedFrame) {
@@ -32,14 +43,6 @@ function isDebugLine(selectedFrame, selectedLocation) {
);
}
-function isDocumentReady(selectedLocation, selectedSourceTextContent) {
- return (
- selectedLocation &&
- selectedSourceTextContent &&
- hasDocument(selectedLocation.source.id)
- );
-}
-
export class HighlightLine extends Component {
isStepping = false;
previousEditorLine = null;
@@ -56,30 +59,34 @@ export class HighlightLine extends Component {
selectedFrame: PropTypes.object,
selectedLocation: PropTypes.object.isRequired,
selectedSourceTextContent: PropTypes.object.isRequired,
+ shouldHighlightSelectedLocation: PropTypes.func.isRequired,
+ editor: PropTypes.object,
};
}
shouldComponentUpdate(nextProps) {
- const { selectedLocation, selectedSourceTextContent } = nextProps;
- return this.shouldSetHighlightLine(
- selectedLocation,
- selectedSourceTextContent
- );
+ return this.shouldSetHighlightLine(nextProps);
}
componentDidUpdate(prevProps) {
- this.completeHighlightLine(prevProps);
+ this.highlightLine(prevProps);
}
componentDidMount() {
- this.completeHighlightLine(null);
+ this.highlightLine(null);
}
- shouldSetHighlightLine(selectedLocation, selectedSourceTextContent) {
- const { line } = selectedLocation;
- const editorLine = toEditorLine(selectedLocation.source.id, line);
+ shouldSetHighlightLine({ selectedLocation, selectedSourceTextContent }) {
+ const editorLine = toEditorLine(
+ selectedLocation.source.id,
+ selectedLocation.line
+ );
- if (!isDocumentReady(selectedLocation, selectedSourceTextContent)) {
+ if (
+ !selectedLocation ||
+ !selectedSourceTextContent ||
+ (!features.codemirrorNext && !hasDocument(selectedLocation.source.id))
+ ) {
return false;
}
@@ -90,58 +97,61 @@ export class HighlightLine extends Component {
return true;
}
- completeHighlightLine(prevProps) {
- const {
- pauseCommand,
- selectedLocation,
- selectedFrame,
- selectedSourceTextContent,
- shouldHighlightSelectedLocation,
- } = this.props;
+ highlightLine(prevProps) {
+ const { pauseCommand, shouldHighlightSelectedLocation } = this.props;
if (pauseCommand) {
this.isStepping = true;
}
- startOperation();
+ if (!features.codemirrorNext) {
+ startOperation();
+ }
if (prevProps) {
- this.clearHighlightLine(
- prevProps.selectedLocation,
- prevProps.selectedSourceTextContent
- );
+ this.clearHighlightLine(prevProps);
}
if (shouldHighlightSelectedLocation) {
- this.setHighlightLine(
- selectedLocation,
- selectedFrame,
- selectedSourceTextContent
- );
+ this.setHighlightLine();
+ }
+ if (!features.codemirrorNext) {
+ endOperation();
}
- endOperation();
}
- setHighlightLine(selectedLocation, selectedFrame, selectedSourceTextContent) {
- const { line } = selectedLocation;
- if (
- !this.shouldSetHighlightLine(selectedLocation, selectedSourceTextContent)
- ) {
+ setHighlightLine() {
+ const { selectedLocation, selectedFrame, editor } = this.props;
+ if (!this.shouldSetHighlightLine(this.props)) {
return;
}
this.isStepping = false;
const sourceId = selectedLocation.source.id;
- const editorLine = toEditorLine(sourceId, line);
+ const editorLine = toEditorLine(sourceId, selectedLocation.line);
this.previousEditorLine = editorLine;
- if (!line || isDebugLine(selectedFrame, selectedLocation)) {
+ if (
+ !selectedLocation.line ||
+ isDebugLine(selectedFrame, selectedLocation)
+ ) {
return;
}
- const doc = getDocument(sourceId);
- doc.addLineClass(editorLine, "wrap", "highlight-line");
- this.resetHighlightLine(doc, editorLine);
+ if (features.codemirrorNext) {
+ editor.setLineContentMarker({
+ id: "highlight-line-marker",
+ lineClassName: "highlight-line",
+ condition(line) {
+ const lineNumber = fromEditorLine(sourceId, line);
+ return selectedLocation.line == lineNumber;
+ },
+ });
+ } else {
+ const doc = getDocument(sourceId);
+ doc.addLineClass(editorLine, "wrap", "highlight-line");
+ }
+ this.clearHighlightLineAfterDuration();
}
- resetHighlightLine(doc, editorLine) {
+ clearHighlightLineAfterDuration() {
const editorWrapper = document.querySelector(".editor-wrapper");
if (editorWrapper === null) {
@@ -155,20 +165,28 @@ export class HighlightLine extends Component {
10
);
- setTimeout(
- () => doc && doc.removeLineClass(editorLine, "wrap", "highlight-line"),
- duration
- );
+ setTimeout(() => this.clearHighlightLine(this.props), duration);
}
- clearHighlightLine(selectedLocation, selectedSourceTextContent) {
- if (!isDocumentReady(selectedLocation, selectedSourceTextContent)) {
+ clearHighlightLine({ selectedLocation, selectedSourceTextContent }) {
+ if (!selectedLocation || !selectedSourceTextContent) {
+ return;
+ }
+
+ if (features.codemirrorNext) {
+ const { editor } = this.props;
+ if (editor) {
+ editor.removeLineContentMarker("highlight-line-marker");
+ }
+ return;
+ }
+
+ if (!hasDocument(selectedLocation.source.id)) {
return;
}
- const { line } = selectedLocation;
const sourceId = selectedLocation.source.id;
- const editorLine = toEditorLine(sourceId, line);
+ const editorLine = toEditorLine(sourceId, selectedLocation.line);
const doc = getDocument(sourceId);
doc.removeLineClass(editorLine, "wrap", "highlight-line");
}
diff --git a/devtools/client/debugger/src/components/Editor/HighlightLines.js b/devtools/client/debugger/src/components/Editor/HighlightLines.js
index e34a86aba9..e62125e5f8 100644
--- a/devtools/client/debugger/src/components/Editor/HighlightLines.js
+++ b/devtools/client/debugger/src/components/Editor/HighlightLines.js
@@ -2,8 +2,15 @@
* 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/>. */
+/**
+ * Uses of this panel are:-
+ * - Highlighting lines of a function selected to be copied using the "Copy function" context menu in the Outline panel
+ */
+
import { Component } from "devtools/client/shared/vendor/react";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
+import { fromEditorLine } from "../../utils/editor/index";
+import { features } from "../../utils/prefs";
class HighlightLines extends Component {
static get propTypes() {
@@ -33,9 +40,19 @@ class HighlightLines extends Component {
clearHighlightRange() {
const { range, editor } = this.props;
- const { codeMirror } = editor;
+ if (!range) {
+ return;
+ }
- if (!range || !codeMirror) {
+ if (features.codemirrorNext) {
+ if (editor) {
+ editor.removeLineContentMarker("multi-highlight-line-marker");
+ }
+ return;
+ }
+
+ const { codeMirror } = editor;
+ if (!codeMirror) {
return;
}
@@ -50,14 +67,31 @@ class HighlightLines extends Component {
highlightLineRange = () => {
const { range, editor } = this.props;
- const { codeMirror } = editor;
+ if (!range) {
+ return;
+ }
- if (!range || !codeMirror) {
+ if (features.codemirrorNext) {
+ // TODO: Fix scrolling into view if its out Bug 1894725
+ if (editor) {
+ editor.setLineContentMarker({
+ id: "multi-highlight-line-marker",
+ lineClassName: "highlight-lines",
+ condition(line) {
+ const lineNumber = fromEditorLine(null, line);
+ return lineNumber >= range.start && lineNumber <= range.end;
+ },
+ });
+ }
return;
}
- const { start, end } = range;
+ const { codeMirror } = editor;
+ if (!codeMirror) {
+ return;
+ }
+ const { start, end } = range;
codeMirror.operation(() => {
editor.alignLine(start);
for (let line = start - 1; line < end; line++) {
diff --git a/devtools/client/debugger/src/components/Editor/InlinePreviews.js b/devtools/client/debugger/src/components/Editor/InlinePreviews.js
index ba8b08669a..56c5f5021f 100644
--- a/devtools/client/debugger/src/components/Editor/InlinePreviews.js
+++ b/devtools/client/debugger/src/components/Editor/InlinePreviews.js
@@ -3,9 +3,13 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
import React, { Component } from "devtools/client/shared/vendor/react";
+import ReactDOM from "devtools/client/shared/vendor/react-dom";
+
+import actions from "../../actions/index";
import { div } from "devtools/client/shared/vendor/react-dom-factories";
import PropTypes from "devtools/client/shared/vendor/react-prop-types";
import InlinePreviewRow from "./InlinePreviewRow";
+import InlinePreview from "./InlinePreview";
import { connect } from "devtools/client/shared/vendor/react-redux";
import {
getSelectedFrame,
@@ -13,6 +17,8 @@ import {
getInlinePreviews,
} from "../../selectors/index";
+import { features } from "../../utils/prefs";
+
function hasPreviews(previews) {
return !!previews && !!Object.keys(previews).length;
}
@@ -31,9 +37,85 @@ class InlinePreviews extends Component {
return hasPreviews(previews);
}
+ componentDidMount() {
+ this.renderInlinePreviewMarker();
+ }
+
+ componentDidUpdate() {
+ this.renderInlinePreviewMarker();
+ }
+
+ renderInlinePreviewMarker() {
+ const {
+ editor,
+ selectedFrame,
+ selectedSource,
+ previews,
+ openElementInInspector,
+ highlightDomElement,
+ unHighlightDomElement,
+ } = this.props;
+
+ if (!features.codemirrorNext) {
+ return;
+ }
+
+ if (
+ !editor ||
+ !selectedFrame ||
+ selectedFrame.location.source.id !== selectedSource.id ||
+ !hasPreviews(previews)
+ ) {
+ editor.removeLineContentMarker("inline-preview-marker");
+ return;
+ }
+ editor.setLineContentMarker({
+ id: "inline-preview-marker",
+ condition: line => {
+ // CM6 line is 1-based unlike CM5 which is 0-based.
+ return !!previews[line - 1];
+ },
+ createLineElementNode: line => {
+ const widgetNode = document.createElement("div");
+ widgetNode.className = "inline-preview";
+
+ ReactDOM.render(
+ React.createElement(
+ React.Fragment,
+ null,
+ previews[line - 1].map(preview =>
+ React.createElement(InlinePreview, {
+ line,
+ key: `${line}-${preview.name}`,
+ variable: preview.name,
+ value: preview.value,
+ openElementInInspector,
+ highlightDomElement,
+ unHighlightDomElement,
+ })
+ )
+ ),
+ widgetNode
+ );
+ return widgetNode;
+ },
+ });
+ }
+
+ componentWillUnmount() {
+ if (!features.codemirrorNext) {
+ return;
+ }
+ this.props.editor.removeLineContentMarker("inline-preview-marker");
+ }
+
render() {
const { editor, selectedFrame, selectedSource, previews } = this.props;
+ if (features.codemirrorNext) {
+ return null;
+ }
+
// Render only if currently open file is the one where debugger is paused
if (
!selectedFrame ||
@@ -77,4 +159,8 @@ const mapStateToProps = state => {
};
};
-export default connect(mapStateToProps)(InlinePreviews);
+export default connect(mapStateToProps, {
+ openElementInInspector: actions.openElementInInspectorCommand,
+ highlightDomElement: actions.highlightDomElement,
+ unHighlightDomElement: actions.unHighlightDomElement,
+})(InlinePreviews);
diff --git a/devtools/client/debugger/src/components/Editor/index.js b/devtools/client/debugger/src/components/Editor/index.js
index e21e05c11a..38040e3314 100644
--- a/devtools/client/debugger/src/components/Editor/index.js
+++ b/devtools/client/debugger/src/components/Editor/index.js
@@ -61,7 +61,6 @@ import {
lineAtHeight,
toSourceLine,
getDocument,
- scrollToPosition,
toEditorPosition,
getSourceLocationFromMouseEvent,
hasDocument,
@@ -149,15 +148,15 @@ class Editor extends PureComponent {
this.props.selectedSourceTextContent?.value ||
nextProps.symbols !== this.props.symbols;
+ const shouldScroll =
+ nextProps.selectedLocation &&
+ this.shouldScrollToLocation(nextProps, editor);
+
if (!features.codemirrorNext) {
const shouldUpdateSize =
nextProps.startPanelSize !== this.props.startPanelSize ||
nextProps.endPanelSize !== this.props.endPanelSize;
- const shouldScroll =
- nextProps.selectedLocation &&
- this.shouldScrollToLocation(nextProps, editor);
-
if (shouldUpdateText || shouldUpdateSize || shouldScroll) {
startOperation();
if (shouldUpdateText) {
@@ -183,6 +182,10 @@ class Editor extends PureComponent {
if (shouldUpdateText) {
this.setText(nextProps, editor);
}
+
+ if (shouldScroll) {
+ this.scrollToLocation(nextProps, editor);
+ }
}
}
@@ -238,7 +241,12 @@ class Editor extends PureComponent {
editor.setUpdateListener(this.onEditorUpdated);
editor.setGutterEventListeners({
click: (event, cm, line) => this.onGutterClick(cm, line, null, event),
- contextmenu: (event, cm, line) => this.openMenu(event, line, true),
+ contextmenu: (event, cm, line) => this.openMenu(event, line),
+ });
+ editor.setContentEventListeners({
+ click: (event, cm, line, column) => this.onClick(event, line, column),
+ contextmenu: (event, cm, line, column) =>
+ this.openMenu(event, line, column),
});
}
this.setState({ editor });
@@ -278,7 +286,7 @@ class Editor extends PureComponent {
}
};
- componentDidUpdate(prevProps) {
+ componentDidUpdate(prevProps, prevState) {
const {
selectedSource,
blackboxedRanges,
@@ -295,7 +303,9 @@ class Editor extends PureComponent {
if (features.codemirrorNext) {
const shouldUpdateBreakableLines =
prevProps.breakableLines.size !== this.props.breakableLines.size ||
- prevProps.selectedSource?.id !== selectedSource.id;
+ prevProps.selectedSource?.id !== selectedSource.id ||
+ // Make sure we update after the editor has loaded
+ (!prevState.editor && !!editor);
const isSourceWasm = isWasm(selectedSource.id);
@@ -459,7 +469,7 @@ class Editor extends PureComponent {
};
// Note: The line is optional, if not passed (as is likely for codemirror 6)
// it fallsback to lineAtHeight.
- openMenu(event, line) {
+ openMenu(event, line, ch) {
event.stopPropagation();
event.preventDefault();
@@ -508,7 +518,7 @@ class Editor extends PureComponent {
line
).trim();
- const lineObject = { from: { line }, to: { line } };
+ const lineObject = { from: { line, ch }, to: { line, ch } };
this.props.showEditorGutterContextMenu(
event,
@@ -523,13 +533,23 @@ class Editor extends PureComponent {
return;
}
- const location = getSourceLocationFromMouseEvent(
- editor,
- selectedSource,
- event
- );
+ let location;
+ if (features.codemirrorNext) {
+ location = createLocation({
+ source: selectedSource,
+ line: fromEditorLine(
+ selectedSource.id,
+ line,
+ isWasm(selectedSource.id)
+ ),
+ column: isWasm(selectedSource.id) ? 0 : ch + 1,
+ });
+ } else {
+ location = getSourceLocationFromMouseEvent(editor, selectedSource, event);
+ }
- this.props.showEditorContextMenu(event, editor, location);
+ const lineObject = editor.getSelectionCursor();
+ this.props.showEditorContextMenu(event, editor, lineObject, location);
}
/**
@@ -617,16 +637,29 @@ class Editor extends PureComponent {
);
};
- onClick(e) {
+ onClick(e, line, ch) {
const { selectedSource, updateCursorPosition, jumpToMappedLocation } =
this.props;
if (selectedSource) {
- const sourceLocation = getSourceLocationFromMouseEvent(
- this.state.editor,
- selectedSource,
- e
- );
+ let sourceLocation;
+ if (features.codemirrorNext) {
+ sourceLocation = createLocation({
+ source: selectedSource,
+ line: fromEditorLine(
+ selectedSource.id,
+ line,
+ isWasm(selectedSource.id)
+ ),
+ column: isWasm(selectedSource.id) ? 0 : ch + 1,
+ });
+ } else {
+ sourceLocation = getSourceLocationFromMouseEvent(
+ this.state.editor,
+ selectedSource,
+ e
+ );
+ }
if (e.metaKey && e.altKey) {
jumpToMappedLocation(sourceLocation);
@@ -664,8 +697,7 @@ class Editor extends PureComponent {
const lineText = doc.getLine(line);
column = Math.max(column, getIndentation(lineText));
}
-
- scrollToPosition(editor.codeMirror, line, column);
+ editor.scrollTo(line, column);
}
setText(props, editor) {
@@ -774,6 +806,7 @@ class Editor extends PureComponent {
};
}
+ // eslint-disable-next-line complexity
renderItems() {
const {
selectedSource,
@@ -788,19 +821,44 @@ class Editor extends PureComponent {
} = this.props;
const { editor } = this.state;
+ if (!selectedSource || !editor) {
+ return null;
+ }
+
if (features.codemirrorNext) {
return React.createElement(
React.Fragment,
null,
- React.createElement(Breakpoints, {
- editor,
- }),
+ React.createElement(Breakpoints, { editor }),
React.createElement(DebugLine, { editor, selectedSource }),
- React.createElement(Exceptions, { editor })
+ React.createElement(HighlightLine, { editor }),
+ React.createElement(Exceptions, { editor }),
+ conditionalPanelLocation
+ ? React.createElement(ConditionalPanel, {
+ editor,
+ selectedSource,
+ })
+ : null,
+ isPaused &&
+ inlinePreviewEnabled &&
+ (!selectedSource.isOriginal ||
+ selectedSource.isPrettyPrinted ||
+ mapScopesEnabled)
+ ? React.createElement(InlinePreviews, {
+ editor,
+ selectedSource,
+ })
+ : null,
+ highlightedLineRange
+ ? React.createElement(HighlightLines, {
+ editor,
+ range: highlightedLineRange,
+ })
+ : null
);
}
- if (!selectedSource || !editor || !getDocument(selectedSource.id)) {
+ if (!getDocument(selectedSource.id)) {
return null;
}
return div(
diff --git a/devtools/client/debugger/src/utils/editor/index.js b/devtools/client/debugger/src/utils/editor/index.js
index e729388acf..3146581fdd 100644
--- a/devtools/client/debugger/src/utils/editor/index.js
+++ b/devtools/client/debugger/src/utils/editor/index.js
@@ -106,50 +106,6 @@ export function toSourceLine(sourceId, line) {
return line + 1;
}
-export function scrollToPosition(codeMirror, line, column) {
- // For all cases where these are on the first line and column,
- // avoid the possibly slow computation of cursor location on large bundles.
- if (!line && !column) {
- codeMirror.scrollTo(0, 0);
- return;
- }
-
- const { top, left } = codeMirror.charCoords({ line, ch: column }, "local");
-
- if (!isVisible(codeMirror, top, left)) {
- const scroller = codeMirror.getScrollerElement();
- const centeredX = Math.max(left - scroller.offsetWidth / 2, 0);
- const centeredY = Math.max(top - scroller.offsetHeight / 2, 0);
-
- codeMirror.scrollTo(centeredX, centeredY);
- }
-}
-
-function isVisible(codeMirror, top, left) {
- function withinBounds(x, min, max) {
- return x >= min && x <= max;
- }
-
- const scrollArea = codeMirror.getScrollInfo();
- const charWidth = codeMirror.defaultCharWidth();
- const fontHeight = codeMirror.defaultTextHeight();
- const { scrollTop, scrollLeft } = codeMirror.doc;
-
- const inXView = withinBounds(
- left,
- scrollLeft,
- scrollLeft + (scrollArea.clientWidth - 30) - charWidth
- );
-
- const inYView = withinBounds(
- top,
- scrollTop,
- scrollTop + scrollArea.clientHeight - fontHeight
- );
-
- return inXView && inYView;
-}
-
export function getLocationsInViewport(
{ codeMirror },
// Offset represents an allowance of characters or lines offscreen to improve
diff --git a/devtools/client/debugger/src/utils/editor/tests/editor.spec.js b/devtools/client/debugger/src/utils/editor/tests/editor.spec.js
index b3fcad17ff..3917adaba1 100644
--- a/devtools/client/debugger/src/utils/editor/tests/editor.spec.js
+++ b/devtools/client/debugger/src/utils/editor/tests/editor.spec.js
@@ -6,7 +6,6 @@ import {
toEditorLine,
toEditorPosition,
toSourceLine,
- scrollToPosition,
markText,
lineAtHeight,
getSourceLocationFromMouseEvent,
@@ -82,17 +81,6 @@ const codeMirror = {
const editor = { codeMirror };
-describe("scrollToPosition", () => {
- it("calls codemirror APIs charCoords, getScrollerElement, scrollTo", () => {
- scrollToPosition(codeMirror, 60, 123);
- expect(codeMirror.charCoords).toHaveBeenCalledWith(
- { line: 60, ch: 123 },
- "local"
- );
- expect(codeMirror.scrollTo).toHaveBeenCalledWith(0, 50);
- });
-});
-
describe("markText", () => {
it("calls codemirror API markText & returns marker", () => {
const loc = {
diff --git a/devtools/client/debugger/src/utils/source.js b/devtools/client/debugger/src/utils/source.js
index 91a02778e2..1a2b453ec2 100644
--- a/devtools/client/debugger/src/utils/source.js
+++ b/devtools/client/debugger/src/utils/source.js
@@ -209,6 +209,15 @@ function resolveFileURL(
}
export function getFormattedSourceId(id) {
+ if (typeof id != "string") {
+ console.error(
+ "Expected source id to be a string, got",
+ typeof id,
+ " | id:",
+ id
+ );
+ return "";
+ }
return id.substring(id.lastIndexOf("/") + 1);
}
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
index 1065674186..49b3d3ffce 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-event-breakpoints.js
@@ -7,6 +7,7 @@
add_task(async function () {
await pushPref("dom.element.invokers.enabled", true);
await pushPref("dom.element.popover.enabled", true);
+ await pushPref("dom.events.textevent.enabled", true);
const dbg = await initDebugger(
"doc-event-breakpoints.html",
@@ -151,6 +152,15 @@ add_task(async function () {
assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 63);
await resume(dbg);
+ info("Test textInput");
+ await toggleEventBreakpoint(dbg, "Keyboard", "event.keyboard.textInput");
+ invokeOnElement("#focus-text", "focus");
+ EventUtils.sendChar("N");
+ await waitForPaused(dbg);
+ assertPausedAtSourceAndLine(dbg, eventBreakpointsSource.id, 98);
+ await resume(dbg);
+ await toggleEventBreakpoint(dbg, "Keyboard", "event.keyboard.textInput");
+
info(`Check that breakpoint can be set on "scrollend"`);
await toggleEventBreakpoint(dbg, "Control", "event.control.scrollend");
diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js b/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js
index bfa2447474..29335af768 100644
--- a/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-javascript-tracer.js
@@ -12,6 +12,19 @@ add_task(async function () {
const dbg = await initDebugger("doc-scripts.html");
+ // Add an iframe before starting the tracer to later check for key event on it
+ const preExistingIframeBrowsingContext = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ async function () {
+ const iframe = content.document.createElement("iframe");
+ iframe.src = `data:text/html,<input type="text" value="pre existing iframe" onkeydown="console.log('keydown')" />`;
+ content.document.body.appendChild(iframe);
+ await new Promise(resolve => (iframe.onload = resolve));
+ return iframe.contentWindow.browsingContext;
+ }
+ );
+
info("Enable the tracing");
await clickElement(dbg, "trace");
@@ -55,6 +68,38 @@ add_task(async function () {
await hasConsoleMessage(dbg, "DOM | click");
await hasConsoleMessage(dbg, "λ simple");
+ const iframeBrowsingContext = await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [],
+ async function () {
+ const iframe = content.document.createElement("iframe");
+ iframe.src = `data:text/html,<input type="text" value="new iframe" onkeypress="console.log('keypress')" />`;
+ content.document.body.appendChild(iframe);
+ await new Promise(resolve => (iframe.onload = resolve));
+ iframe.contentWindow.document.querySelector("input").focus();
+ return iframe.contentWindow.browsingContext;
+ }
+ );
+
+ await BrowserTestUtils.synthesizeKey("x", {}, iframeBrowsingContext);
+ await hasConsoleMessage(dbg, "DOM | keypress");
+ await hasConsoleMessage(dbg, "λ onkeypress");
+
+ await SpecialPowers.spawn(
+ preExistingIframeBrowsingContext,
+ [],
+ async function () {
+ content.document.querySelector("input").focus();
+ }
+ );
+ await BrowserTestUtils.synthesizeKey(
+ "x",
+ {},
+ preExistingIframeBrowsingContext
+ );
+ await hasConsoleMessage(dbg, "DOM | keydown");
+ await hasConsoleMessage(dbg, "λ onkeydown");
+
// Test Blackboxing
info("Clear the console from previous traces");
const { hud } = await dbg.toolbox.getPanel("webconsole");
diff --git a/devtools/client/debugger/test/mochitest/examples/event-breakpoints.js b/devtools/client/debugger/test/mochitest/examples/event-breakpoints.js
index f9aa16b858..3d8163a8c4 100644
--- a/devtools/client/debugger/test/mochitest/examples/event-breakpoints.js
+++ b/devtools/client/debugger/test/mochitest/examples/event-breakpoints.js
@@ -91,4 +91,9 @@ function onBeforeToggle(event) {
popover.addEventListener("toggle", onToggle);
function onToggle(event) {
console.log("toggle", event);
-} \ No newline at end of file
+}
+
+document.getElementById("focus-text").addEventListener("textInput", onTextInput);
+function onTextInput() {
+ console.log("textInput");
+}