diff options
Diffstat (limited to 'devtools/client/debugger')
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"); +} |