diff options
Diffstat (limited to 'devtools/client/debugger')
36 files changed, 487 insertions, 134 deletions
diff --git a/devtools/client/debugger/index.html b/devtools/client/debugger/index.html index 0a7ef58d8e..108fe5a653 100644 --- a/devtools/client/debugger/index.html +++ b/devtools/client/debugger/index.html @@ -55,8 +55,8 @@ ></script> <script> try { - const { BrowserLoader } = ChromeUtils.import( - "resource://devtools/shared/loader/browser-loader.js" + const { BrowserLoader } = ChromeUtils.importESModule( + "resource://devtools/shared/loader/browser-loader.sys.mjs" ); const { require } = BrowserLoader({ baseURI: "resource://devtools/client/debugger", diff --git a/devtools/client/debugger/src/.eslintrc.js b/devtools/client/debugger/src/.eslintrc.js index ec6d77b504..67f317ea28 100644 --- a/devtools/client/debugger/src/.eslintrc.js +++ b/devtools/client/debugger/src/.eslintrc.js @@ -251,8 +251,9 @@ module.exports = { // Disallow unreachable statements after a return, throw, continue, or break // statement. "no-unreachable": 2, - // Disallow global and local variables that arent used, but allow unused function arguments. - "no-unused-vars": [2, { vars: "all", args: "none" }], + // Disallow global and local variables that arent used. Allow unused function arguments + // that are prefixed with `_`. + "no-unused-vars": [2, { vars: "all", argsIgnorePattern: "^_" }], // Allow using variables before they are defined. "no-use-before-define": 0, // We use var-only-at-top-level instead of no-var as we allow top level diff --git a/devtools/client/debugger/src/actions/context-menus/editor.js b/devtools/client/debugger/src/actions/context-menus/editor.js index 1cad1e2131..4785c963cb 100644 --- a/devtools/client/debugger/src/actions/context-menus/editor.js +++ b/devtools/client/debugger/src/actions/context-menus/editor.js @@ -73,7 +73,7 @@ export function showEditorContextMenu(event, editor, location) { }; } -export function showEditorGutterContextMenu(event, editor, location, lineText) { +export function showEditorGutterContextMenu(event, line, location, lineText) { return async ({ dispatch, getState }) => { const { source } = location; const state = getState(); @@ -90,7 +90,7 @@ export function showEditorGutterContextMenu(event, editor, location, lineText) { { type: "separator" }, blackBoxLineMenuItem( source, - editor, + line, blackboxedRanges, isSourceOnIgnoreList, location.line, @@ -178,7 +178,7 @@ const blackBoxMenuItem = ( const blackBoxLineMenuItem = ( selectedSource, - editor, + { from, to }, blackboxedRanges, isSourceOnIgnoreList, // the clickedLine is passed when the context menu @@ -187,10 +187,6 @@ const blackBoxLineMenuItem = ( clickedLine = null, dispatch ) => { - const { codeMirror } = editor; - const from = codeMirror.getCursor("from"); - const to = codeMirror.getCursor("to"); - const startLine = clickedLine ?? toSourceLine(selectedSource.id, from.line); const endLine = clickedLine ?? toSourceLine(selectedSource.id, to.line); @@ -251,16 +247,12 @@ const blackBoxLineMenuItem = ( const blackBoxLinesMenuItem = ( selectedSource, - editor, + { from, to }, blackboxedRanges, isSourceOnIgnoreList, clickedLine, dispatch ) => { - const { codeMirror } = editor; - const from = codeMirror.getCursor("from"); - const to = codeMirror.getCursor("to"); - const startLine = toSourceLine(selectedSource.id, from.line); const endLine = toSourceLine(selectedSource.id, to.line); @@ -408,7 +400,10 @@ function editorMenuItems({ items.push( blackBoxSourceLinesMenuItem( source, - editor, + { + from: editor.codeMirror.getCursor("from"), + to: editor.codeMirror.getCursor("to"), + }, blackboxedRanges, isSourceOnIgnoreList, null, diff --git a/devtools/client/debugger/src/components/Editor/Breakpoint.js b/devtools/client/debugger/src/components/Editor/Breakpoint.js index 4559a20289..40de6d47c7 100644 --- a/devtools/client/debugger/src/components/Editor/Breakpoint.js +++ b/devtools/client/debugger/src/components/Editor/Breakpoint.js @@ -59,7 +59,7 @@ class Breakpoint extends PureComponent { selectedSource, } = this.props; - // ignore right clicks + // ignore ctrl + click and right clicks when clicking on the breakpoint if ((event.ctrlKey && event.button === 0) || event.button === 2) { return; } diff --git a/devtools/client/debugger/src/components/Editor/Breakpoints.css b/devtools/client/debugger/src/components/Editor/Breakpoints.css index 1269f73f82..92121e0f46 100644 --- a/devtools/client/debugger/src/components/Editor/Breakpoints.css +++ b/devtools/client/debugger/src/components/Editor/Breakpoints.css @@ -36,7 +36,7 @@ pointer-events: none; } -.editor-wrapper :not(.empty-line, .new-breakpoint) +.editor-wrapper :not(.empty-line, .new-breakpoint .cm6-gutter-breakpoint) > .CodeMirror-gutter-wrapper > .CodeMirror-linenumber:hover::after { content: ""; @@ -55,6 +55,27 @@ mask-position: right; } +/* Codemirror 6*/ +.cm6-gutter-breakpoint .breakpoint-marker svg { + fill: var(--breakpoint-fill); + stroke: var(--breakpoint-stroke); + height: 14px; + position: absolute; + right: -8px; + z-index: -99; +} + +/* set the linenumber white when there is a breakpoint */ +.editor-wrapper:not(.skip-pausing) .new-breakpoint .CodeMirror-gutter-wrapper .CodeMirror-linenumber, + .editor-wrapper:not(.skip-pausing) .cm6-gutter-breakpoint { + color: white; +} + +/* move the breakpoint below the other gutter elements */ +.new-breakpoint .CodeMirror-gutter-elt:nth-child(2) { + z-index: 0; +} + .editor.new-breakpoint svg { fill: var(--breakpoint-fill); stroke: var(--breakpoint-stroke); @@ -85,7 +106,9 @@ } .editor.new-breakpoint.breakpoint-disabled svg, -.blackboxed-line .editor.new-breakpoint svg { +.blackboxed-line .editor.new-breakpoint svg, +.cm6-gutter-breakpoint .breakpoint-marker.breakpoint-disabled svg, +.cm6-gutter-breakpoint.blackboxed-line .breakpoint-marker svg { fill-opacity: var(--breakpoint-disabled-opacity); stroke-opacity: var(--breakpoint-disabled-opacity); } diff --git a/devtools/client/debugger/src/components/Editor/Breakpoints.js b/devtools/client/debugger/src/components/Editor/Breakpoints.js index 6d1d088f11..ac3fe0890c 100644 --- a/devtools/client/debugger/src/components/Editor/Breakpoints.js +++ b/devtools/client/debugger/src/components/Editor/Breakpoints.js @@ -11,9 +11,31 @@ import { getSelectedSource, getFirstVisibleBreakpoints, } from "../../selectors/index"; +import { getSelectedLocation } from "../../utils/selected-location"; import { makeBreakpointId } from "../../utils/breakpoint/index"; import { connect } from "devtools/client/shared/vendor/react-redux"; +import { fromEditorLine } from "../../utils/editor/index"; import actions from "../../actions/index"; +import { features } from "../../utils/prefs"; +const classnames = require("resource://devtools/client/shared/classnames.js"); + +const isMacOS = Services.appinfo.OS === "Darwin"; + +const breakpointSvg = document.createElement("div"); +const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); +svg.setAttribute("viewBox", "0 0 60 15"); +svg.setAttribute("width", 60); +svg.setAttribute("height", 15); + +const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); +path.setAttributeNS( + null, + "d", + "M53.07.5H1.5c-.54 0-1 .46-1 1v12c0 .54.46 1 1 1h51.57c.58 0 1.15-.26 1.53-.7l4.7-6.3-4.7-6.3c-.38-.44-.95-.7-1.53-.7z" +); + +svg.appendChild(path); +breakpointSvg.appendChild(svg); class Breakpoints extends Component { static get propTypes() { @@ -27,6 +49,93 @@ class Breakpoints extends Component { showEditorEditBreakpointContextMenu: PropTypes.func, }; } + + constructor(props) { + super(props); + } + + componentDidUpdate() { + const { selectedSource, breakpoints, editor } = this.props; + + // Only for codemirror 6 + if (!features.codemirrorNext) { + return; + } + + if (!selectedSource || !breakpoints || !editor) { + return; + } + + const markers = [ + { + id: "gutter-breakpoint-marker", + lineClassName: "cm6-gutter-breakpoint", + condition: line => { + const lineNumber = fromEditorLine(selectedSource.id, line); + return breakpoints.some(bp => bp.location.line === lineNumber); + }, + createLineElementNode: line => { + const lineNumber = fromEditorLine(selectedSource.id, line); + const breakpoint = breakpoints.find( + bp => bp.location.line === lineNumber + ); + + const breakpointNode = breakpointSvg.cloneNode(true); + breakpointNode.appendChild(document.createTextNode(lineNumber)); + breakpointNode.className = classnames("breakpoint-marker", { + "breakpoint-disabled": breakpoint.disabled, + "has-condition": breakpoint?.options.condition, + "has-log": breakpoint?.options.logValue, + }); + breakpointNode.onclick = event => this.onClick(event, breakpoint); + breakpointNode.oncontextmenu = event => + this.onContextMenu(event, breakpoint); + return breakpointNode; + }, + }, + ]; + editor.setLineGutterMarkers(markers); + } + + onClick = (event, breakpoint) => { + const { + continueToHere, + toggleBreakpointsAtLine, + removeBreakpointsAtLine, + selectedSource, + } = this.props; + + event.stopPropagation(); + event.preventDefault(); + + // ignore right clicks when clicking on the breakpoint + if (event.button === 2) { + return; + } + + const selectedLocation = getSelectedLocation(breakpoint, selectedSource); + const ctrlOrCmd = isMacOS ? event.metaKey : event.ctrlKey; + + if (ctrlOrCmd) { + continueToHere(selectedLocation); + return; + } + + if (event.shiftKey) { + toggleBreakpointsAtLine(!breakpoint.disabled, selectedLocation.line); + return; + } + + removeBreakpointsAtLine(selectedLocation.source, selectedLocation.line); + }; + + onContextMenu = (event, breakpoint) => { + event.stopPropagation(); + event.preventDefault(); + + this.props.showEditorEditBreakpointContextMenu(event, breakpoint); + }; + render() { const { breakpoints, @@ -41,6 +150,11 @@ class Breakpoints extends Component { if (!selectedSource || !breakpoints) { return null; } + + if (features.codemirrorNext) { + return null; + } + return div( null, breakpoints.map(breakpoint => { diff --git a/devtools/client/debugger/src/components/Editor/DebugLine.js b/devtools/client/debugger/src/components/Editor/DebugLine.js index 1b8e59ba64..61caae69a5 100644 --- a/devtools/client/debugger/src/components/Editor/DebugLine.js +++ b/devtools/client/debugger/src/components/Editor/DebugLine.js @@ -6,6 +6,7 @@ import { PureComponent } from "devtools/client/shared/vendor/react"; import PropTypes from "devtools/client/shared/vendor/react-prop-types"; import { toEditorPosition, + fromEditorLine, getDocument, hasDocument, startOperation, @@ -21,12 +22,16 @@ import { getSourceTextContent, getCurrentThread, } from "../../selectors/index"; +import { isWasm } from "../../utils/wasm"; +import { features } from "../../utils/prefs"; export class DebugLine extends PureComponent { debugExpression; static get propTypes() { return { + editor: PropTypes.object, + selectedSource: PropTypes.object, location: PropTypes.object, why: PropTypes.object, }; @@ -34,21 +39,62 @@ export class DebugLine extends PureComponent { componentDidMount() { const { why, location } = this.props; + if (features.codemirrorNext) { + return; + } this.setDebugLine(why, location); } componentWillUnmount() { const { why, location } = this.props; + if (features.codemirrorNext) { + return; + } this.clearDebugLine(why, location); } componentDidUpdate(prevProps) { - const { why, location } = this.props; - - startOperation(); - this.clearDebugLine(prevProps.why, prevProps.location); - this.setDebugLine(why, location); - endOperation(); + const { why, location, editor, selectedSource } = this.props; + + if (features.codemirrorNext) { + if (!selectedSource) { + return; + } + + if ( + prevProps.location == this.props.location && + prevProps.selectedSource?.id == selectedSource?.id + ) { + return; + } + + const { lineClass } = this.getTextClasses(why); + // Remove the debug line marker when no longer paused, or the selected source + // is no longer the source where the pause occured. + if (!location || location.source.id !== selectedSource.id) { + editor.removeLineContentMarker("debug-line-marker"); + } else { + const isSourceWasm = isWasm(selectedSource.id); + editor.setLineContentMarker({ + id: "debug-line-marker", + lineClassName: lineClass, + condition(line) { + const lineNumber = fromEditorLine( + selectedSource.id, + line, + isSourceWasm + ); + const editorLocation = toEditorPosition(location); + return editorLocation.line == lineNumber; + }, + }); + } + } else { + startOperation(); + this.clearDebugLine(prevProps.why, prevProps.location); + this.setDebugLine(why, location); + endOperation(); + } } setDebugLine(why, location) { @@ -125,7 +171,10 @@ const mapStateToProps = state => { return {}; } const sourceTextContent = getSourceTextContent(state, location); - if (!isDocumentReady(location, sourceTextContent)) { + if ( + !features.codemirrorNext && + !isDocumentReady(location, sourceTextContent) + ) { return {}; } return { diff --git a/devtools/client/debugger/src/components/Editor/Editor.css b/devtools/client/debugger/src/components/Editor/Editor.css index 0c48da019e..f28833747d 100644 --- a/devtools/client/debugger/src/components/Editor/Editor.css +++ b/devtools/client/debugger/src/components/Editor/Editor.css @@ -80,19 +80,6 @@ html[dir="rtl"] .editor-mount { line-height: var(--theme-code-line-height); } -/* set the linenumber white when there is a breakpoint */ -.editor-wrapper:not(.skip-pausing) - .new-breakpoint - .CodeMirror-gutter-wrapper - .CodeMirror-linenumber { - color: white; -} - -/* move the breakpoint below the other gutter elements */ -.new-breakpoint .CodeMirror-gutter-elt:nth-child(2) { - z-index: 0; -} - .theme-dark .editor-wrapper .CodeMirror-line .cm-comment { color: var(--theme-comment); } @@ -134,7 +121,9 @@ html[dir="rtl"] .editor-mount { background-color: var(--debug-expression-error-background); } -.new-debug-line > .CodeMirror-line { +.new-debug-line > .CodeMirror-line, +/* For CM6 */ +.cm-editor .cm-line.new-debug-line { background-color: transparent !important; outline: var(--debug-line-border) solid 1px; } @@ -145,7 +134,9 @@ html[dir="rtl"] .editor-mount { display: none; } -.new-debug-line-error > .CodeMirror-line { +.new-debug-line-error > .CodeMirror-line, +/* For CM6 */ +.cm-editor .cm-line.new-debug-line-error { background-color: var(--debug-expression-error-background) !important; outline: var(--debug-line-error-border) solid 1px; } @@ -196,10 +187,6 @@ html[dir="rtl"] .editor-mount { border-left: none; } -.editor-wrapper .CodeMirror-foldgutter .CodeMirror-guttermarker-subtle { - visibility: visible; -} - .editor-wrapper .CodeMirror-foldgutter .CodeMirror-linenumber { text-align: left; padding: 0 0 0 2px; diff --git a/devtools/client/debugger/src/components/Editor/Exceptions.js b/devtools/client/debugger/src/components/Editor/Exceptions.js index 2fb183f135..217ec40be6 100644 --- a/devtools/client/debugger/src/components/Editor/Exceptions.js +++ b/devtools/client/debugger/src/components/Editor/Exceptions.js @@ -6,25 +6,72 @@ import React, { Component } from "devtools/client/shared/vendor/react"; import PropTypes from "devtools/client/shared/vendor/react-prop-types"; import { connect } from "devtools/client/shared/vendor/react-redux"; +import { + toEditorPosition, + fromEditorLine, + getDocument, +} from "../../utils/editor/index"; +import { createLocation } from "../../utils/location"; + +import { features } from "../../utils/prefs"; + import Exception from "./Exception"; import { getSelectedSource, getSelectedSourceExceptions, } from "../../selectors/index"; -import { getDocument } from "../../utils/editor/index"; class Exceptions extends Component { static get propTypes() { return { exceptions: PropTypes.array, selectedSource: PropTypes.object, + editor: PropTypes.object, }; } + componentDidUpdate() { + const { exceptions, selectedSource, editor } = this.props; + + if (!features.codemirrorNext) { + return; + } + + if (!selectedSource || !editor || !exceptions.length) { + return; + } + + editor.setLineContentMarker({ + id: "line-exception-marker", + lineClassName: "line-exception", + condition: line => { + const lineNumber = fromEditorLine(selectedSource.id, line); + + const exception = exceptions.find(e => e.lineNumber == lineNumber); + if (!exception) { + return false; + } + const exceptionLocation = createLocation({ + source: selectedSource, + line: exception.lineNumber, + // Exceptions are reported with column being 1-based + // while the frontend uses 0-based column. + column: exception.columnNumber - 1, + }); + const editorLocation = toEditorPosition(exceptionLocation); + return editorLocation.line == lineNumber; + }, + }); + } + render() { const { exceptions, selectedSource } = this.props; + if (features.codemirrorNext) { + return null; + } + if (!selectedSource || !exceptions.length) { return null; } diff --git a/devtools/client/debugger/src/components/Editor/index.js b/devtools/client/debugger/src/components/Editor/index.js index ae9bde7657..e21e05c11a 100644 --- a/devtools/client/debugger/src/components/Editor/index.js +++ b/devtools/client/debugger/src/components/Editor/index.js @@ -12,6 +12,7 @@ import { connect } from "devtools/client/shared/vendor/react-redux"; import { getLineText, isLineBlackboxed } from "./../../utils/source"; import { createLocation } from "./../../utils/location"; import { getIndentation } from "../../utils/indentation"; +import { isWasm } from "../../utils/wasm"; import { features } from "../../utils/prefs"; import { @@ -50,6 +51,7 @@ import Exceptions from "./Exceptions"; import BlackboxLines from "./BlackboxLines"; import { + fromEditorLine, showSourceText, setDocument, resetLineNumberFormat, @@ -68,7 +70,11 @@ import { endOperation, } from "../../utils/editor/index"; -import { resizeToggleButton, resizeBreakpointGutter } from "../../utils/ui"; +import { + resizeToggleButton, + getLineNumberWidth, + resizeBreakpointGutter, +} from "../../utils/ui"; const { debounce } = require("resource://devtools/shared/debounce.js"); const classnames = require("resource://devtools/client/shared/classnames.js"); @@ -169,7 +175,7 @@ class Editor extends PureComponent { if (this.props.selectedSource != nextProps.selectedSource) { this.props.updateViewport(); resizeBreakpointGutter(editor.codeMirror); - resizeToggleButton(editor.codeMirror); + resizeToggleButton(getLineNumberWidth(editor.codeMirror)); } } else { // For codemirror 6 @@ -180,6 +186,12 @@ class Editor extends PureComponent { } } + onEditorUpdated(v) { + if (v.docChanged || v.geometryChanged) { + resizeToggleButton(v.view.dom.querySelector(".cm-gutters").clientWidth); + } + } + setupEditor() { const editor = getEditor(features.codemirrorNext); @@ -216,32 +228,18 @@ class Editor extends PureComponent { codeMirrorWrapper.addEventListener("keydown", e => this.onKeyDown(e)); codeMirrorWrapper.addEventListener("click", e => this.onClick(e)); codeMirrorWrapper.addEventListener("mouseover", onMouseOver(codeMirror)); - - const toggleFoldMarkerVisibility = () => { - if (node instanceof HTMLElement) { - node - .querySelectorAll(".CodeMirror-guttermarker-subtle") - .forEach(elem => { - elem.classList.toggle("visible"); - }); - } - }; - - const codeMirrorGutter = codeMirror.getGutterElement(); - codeMirrorGutter.addEventListener( - "mouseleave", - toggleFoldMarkerVisibility - ); - codeMirrorGutter.addEventListener( - "mouseenter", - toggleFoldMarkerVisibility - ); codeMirrorWrapper.addEventListener("contextmenu", event => this.openMenu(event) ); codeMirror.on("scroll", this.onEditorScroll); this.onEditorScroll(); + } else { + 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), + }); } this.setState({ editor }); return editor; @@ -280,6 +278,69 @@ class Editor extends PureComponent { } }; + componentDidUpdate(prevProps) { + const { + selectedSource, + blackboxedRanges, + isSourceOnIgnoreList, + breakableLines, + } = this.props; + const { editor } = this.state; + + if (!selectedSource || !editor) { + return; + } + + // Sets the breakables lines for codemirror 6 + if (features.codemirrorNext) { + const shouldUpdateBreakableLines = + prevProps.breakableLines.size !== this.props.breakableLines.size || + prevProps.selectedSource?.id !== selectedSource.id; + + const isSourceWasm = isWasm(selectedSource.id); + + if (shouldUpdateBreakableLines) { + editor.setLineGutterMarkers([ + { + id: "empty-line-marker", + lineClassName: "empty-line", + condition: line => { + const lineNumber = fromEditorLine( + selectedSource.id, + line, + isSourceWasm + ); + return !breakableLines.has(lineNumber); + }, + }, + ]); + } + + function condition(line) { + const lineNumber = fromEditorLine(selectedSource.id, line); + + return isLineBlackboxed( + blackboxedRanges[selectedSource.url], + lineNumber, + isSourceOnIgnoreList + ); + } + + editor.setLineGutterMarkers([ + { + id: "blackboxed-line-gutter-marker", + lineClassName: "blackboxed-line", + condition, + }, + ]); + editor.setLineContentMarker({ + id: "blackboxed-line-marker", + lineClassName: "blackboxed-line", + condition, + }); + } + } + componentWillUnmount() { if (!features.codemirrorNext) { const { editor } = this.state; @@ -396,8 +457,9 @@ class Editor extends PureComponent { e.preventDefault(); } }; - - openMenu(event) { + // Note: The line is optional, if not passed (as is likely for codemirror 6) + // it fallsback to lineAtHeight. + openMenu(event, line) { event.stopPropagation(); event.preventDefault(); @@ -421,13 +483,19 @@ class Editor extends PureComponent { const target = event.target; const { id: sourceId } = selectedSource; - const line = lineAtHeight(editor, sourceId, event); + line = line ?? lineAtHeight(editor, sourceId, event); if (typeof line != "number") { return; } - if (target.classList.contains("CodeMirror-linenumber")) { + if ( + // handles codemirror 6 + (target.classList.contains("cm-gutterElement") && + target.closest(".cm-gutter.cm-lineNumbers")) || + // handles codemirror 5 + target.classList.contains("CodeMirror-linenumber") + ) { const location = createLocation({ line, column: undefined, @@ -440,7 +508,14 @@ class Editor extends PureComponent { line ).trim(); - this.props.showEditorGutterContextMenu(event, editor, location, lineText); + const lineObject = { from: { line }, to: { line } }; + + this.props.showEditorGutterContextMenu( + event, + lineObject, + location, + lineText + ); return; } @@ -542,10 +617,6 @@ class Editor extends PureComponent { ); }; - onGutterContextMenu = event => { - this.openMenu(event); - }; - onClick(e) { const { selectedSource, updateCursorPosition, jumpToMappedLocation } = this.props; @@ -717,6 +788,18 @@ class Editor extends PureComponent { } = this.props; const { editor } = this.state; + if (features.codemirrorNext) { + return React.createElement( + React.Fragment, + null, + React.createElement(Breakpoints, { + editor, + }), + React.createElement(DebugLine, { editor, selectedSource }), + React.createElement(Exceptions, { editor }) + ); + } + if (!selectedSource || !editor || !getDocument(selectedSource.id)) { return null; } diff --git a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js index 1f5e08cd7e..767df21bf6 100644 --- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js +++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js @@ -17,7 +17,6 @@ import { getSelectedLocation } from "../../../utils/selected-location"; import { createHeadlessEditor } from "../../../utils/editor/create-editor"; import { makeBreakpointId } from "../../../utils/breakpoint/index"; -import { features } from "../../../utils/prefs"; import { getSelectedSource, @@ -154,7 +153,7 @@ class Breakpoints extends Component { className: "pane", }, this.renderExceptionsOptions(), - !features.codemirrorNext ? this.renderBreakpoints() : null + this.renderBreakpoints() ); } } diff --git a/devtools/client/debugger/src/components/shared/Button/styles/CommandBarButton.css b/devtools/client/debugger/src/components/shared/Button/styles/CommandBarButton.css index 12e53e6fc5..a04f080291 100644 --- a/devtools/client/debugger/src/components/shared/Button/styles/CommandBarButton.css +++ b/devtools/client/debugger/src/components/shared/Button/styles/CommandBarButton.css @@ -14,6 +14,7 @@ min-width: 30px; /* Adjust outline so it's not clipped */ outline-offset: -3px; + flex-shrink: 0; } .command-bar-button:disabled { diff --git a/devtools/client/debugger/src/utils/editor/index.js b/devtools/client/debugger/src/utils/editor/index.js index d12e2f29f1..e729388acf 100644 --- a/devtools/client/debugger/src/utils/editor/index.js +++ b/devtools/client/debugger/src/utils/editor/index.js @@ -11,6 +11,7 @@ import { createEditor } from "./create-editor"; import { isWasm, lineToWasmOffset, wasmOffsetToLine } from "../wasm"; import { createLocation } from "../location"; +import { features } from "../prefs"; let editor; @@ -65,6 +66,10 @@ export function toEditorLine(sourceId, lineOrOffset) { return wasmOffsetToLine(sourceId, lineOrOffset) || 0; } + if (features.codemirrorNext) { + return lineOrOffset; + } + return lineOrOffset ? lineOrOffset - 1 : 1; } @@ -73,6 +78,10 @@ export function fromEditorLine(sourceId, line, sourceIsWasm) { return lineToWasmOffset(sourceId, line) || 0; } + if (features.codemirrorNext) { + return line; + } + return line + 1; } @@ -88,7 +97,13 @@ export function toEditorPosition(location) { } export function toSourceLine(sourceId, line) { - return isWasm(sourceId) ? lineToWasmOffset(sourceId, line) : line + 1; + if (isWasm(sourceId)) { + return lineToWasmOffset(sourceId, line); + } + if (features.codemirrorNext) { + return line; + } + return line + 1; } export function scrollToPosition(codeMirror, line, column) { diff --git a/devtools/client/debugger/src/utils/editor/source-documents.js b/devtools/client/debugger/src/utils/editor/source-documents.js index 53ee4f2f35..cc9f44d82a 100644 --- a/devtools/client/debugger/src/utils/editor/source-documents.js +++ b/devtools/client/debugger/src/utils/editor/source-documents.js @@ -4,7 +4,11 @@ import { isWasm, getWasmLineNumberFormatter, renderWasmText } from "../wasm"; import { isMinified } from "../isMinified"; -import { resizeBreakpointGutter, resizeToggleButton } from "../ui"; +import { + resizeBreakpointGutter, + resizeToggleButton, + getLineNumberWidth, +} from "../ui"; import { javascriptLikeExtensions } from "../source"; const sourceDocs = new Map(); @@ -39,7 +43,7 @@ export function resetLineNumberFormat(editor) { const cm = editor.codeMirror; cm.setOption("lineNumberFormatter", number => number); resizeBreakpointGutter(cm); - resizeToggleButton(cm); + resizeToggleButton(getLineNumberWidth(cm)); } function updateLineNumberFormat(editor, sourceId) { @@ -51,7 +55,7 @@ function updateLineNumberFormat(editor, sourceId) { const lineNumberFormatter = getWasmLineNumberFormatter(sourceId); cm.setOption("lineNumberFormatter", lineNumberFormatter); resizeBreakpointGutter(cm); - resizeToggleButton(cm); + resizeToggleButton(getLineNumberWidth(cm)); } const contentTypeModeMap = new Map([ diff --git a/devtools/client/debugger/src/utils/editor/tokens.js b/devtools/client/debugger/src/utils/editor/tokens.js index f8783c02fe..3c6875f9cd 100644 --- a/devtools/client/debugger/src/utils/editor/tokens.js +++ b/devtools/client/debugger/src/utils/editor/tokens.js @@ -50,6 +50,8 @@ function _isInvalidTarget(target) { target.closest(".CodeMirror-widget") || // exclude in-line "empty" space, as well as the gutter target.matches(".CodeMirror-line, .CodeMirror-gutter-elt") || + // exclude items that are not in a line + !target.closest(".CodeMirror-line") || target.getBoundingClientRect().top == 0 ) { return true; diff --git a/devtools/client/debugger/src/utils/ui.js b/devtools/client/debugger/src/utils/ui.js index eab5bb1e07..3ad221f465 100644 --- a/devtools/client/debugger/src/utils/ui.js +++ b/devtools/client/debugger/src/utils/ui.js @@ -38,11 +38,11 @@ export function resizeBreakpointGutter(editor) { * Forces the left toggle button in source header to be the same size * as the line numbers gutter. */ -export function resizeToggleButton(editor) { +export function resizeToggleButton(newSize) { const toggleButton = document.querySelector( - ".source-header .toggle-button-start" + ".source-header .toggle-button.start" ); if (toggleButton) { - toggleButton.style.width = `${getLineNumberWidth(editor)}px`; + toggleButton.style.width = `${newSize}px`; } } diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js index eba02919d3..8a7ac18df4 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-breakpoints-popup.js @@ -70,8 +70,7 @@ add_task(async function testPausedByBreakpoint() { info("Open the popup in order to be able to set a breakpoint"); const firstPopupBrowsingContext = await openPopup(POPUP_URL); - await waitForSource(dbg, POPUP_URL); - const source = findSource(dbg, POPUP_URL); + let source = await waitForSource(dbg, POPUP_URL); await selectSource(dbg, source); await addBreakpoint(dbg, source, 4); @@ -88,7 +87,7 @@ add_task(async function testPausedByBreakpoint() { "The popup is really paused" ); - await waitForSource(dbg, POPUP_URL); + source = await waitForSource(dbg, POPUP_URL); assertPausedAtSourceAndLine(dbg, source.id, 4); await resume(dbg); @@ -131,8 +130,7 @@ add_task(async function testPausedInTwoPopups() { const browser = gBrowser.selectedBrowser; const popupBrowsingContext = await openPopup(POPUP_URL); - await waitForSource(dbg, POPUP_URL); - const source = findSource(dbg, POPUP_URL); + const source = await waitForSource(dbg, POPUP_URL); await selectSource(dbg, source); await addBreakpoint(dbg, source, 4); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js b/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js index d461918d7a..4d8720d395 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-call-stack.js @@ -19,6 +19,8 @@ add_task(async function () { const button = toggleButton(dbg); ok(!button, "toggle button shouldn't be there"); + + await resume(dbg); }); add_task(async function () { diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js index 4c669766b0..caa122eb24 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-async.js @@ -9,7 +9,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); Services.prefs.setBoolPref( "devtools.debugger.features.map-await-expression", true diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js b/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js index 26529a75cd..085b048fb6 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console-map-bindings.js @@ -5,7 +5,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); const dbg = await initDebugger("doc-strict.html"); await getSplitConsole(dbg); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-console.js b/devtools/client/debugger/test/mochitest/browser_dbg-console.js index a725f7de43..73a5780cc6 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-console.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-console.js @@ -5,7 +5,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); const dbg = await initDebugger( "doc-script-switching.html", "script-switching-01.js" diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js index e26ca0493b..de10014069 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-dom-mutation-breakpoints.js @@ -146,9 +146,7 @@ add_task(async function () { await resume(dbg); info("Blackboxing the source prevents debugger pause"); - await waitForSource(dbg, "dom-mutation.original.js"); - - const source = findSource(dbg, "dom-mutation.original.js"); + const source = await waitForSource(dbg, "dom-mutation.original.js"); await selectSource(dbg, source); await clickElement(dbg, "blackbox"); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js b/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js index 93123187e1..f9b299991f 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-es-module-worker.js @@ -40,7 +40,7 @@ add_task(async function () { is(threads.length, 1, "Got the page and the worker threads"); is(threads[0].name, WORKER_URL, "Thread name is correct"); - const source = findSource(dbg, "worker.js"); + const source = await waitForSource(dbg, "worker.js"); await selectSource(dbg, source); await addBreakpoint(dbg, source, 3); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js index 6ce0acbc77..e857a38654 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-events.js @@ -9,7 +9,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); const dbg = await initDebugger( "doc-event-breakpoints.html", "event-breakpoints.js" diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js index 67886672b6..521e59b270 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-point-mapping.js @@ -9,7 +9,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); await pushPref("devtools.debugger.map-scopes-enabled", true); const dbg = await initDebugger("doc-sourcemaps3.html", "test.js"); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js index aa52904d31..3475d3f41d 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-points-workers.js @@ -9,7 +9,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); const dbg = await initDebugger("doc-windowless-workers.html"); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js b/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js index d265caa0ad..ea3fecf1ec 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-log-points.js @@ -9,7 +9,7 @@ "use strict"; add_task(async function () { - Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true); + Services.prefs.setBoolPref("devtools.toolbox.splitconsole.open", true); const dbg = await initDebugger( "doc-script-switching.html", "script-switching-01.js" diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js index 8d3771cae9..223f401c73 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-pretty-print-paused-anonymous.js @@ -22,8 +22,7 @@ add_task(async function () { const prettyEvaluatedSourceFilename = evaluatedSourceId.split("/").at(-1) + ":formatted"; - await waitForSource(dbg, prettyEvaluatedSourceFilename); - const prettySource = findSource(dbg, prettyEvaluatedSourceFilename); + const prettySource = await waitForSource(dbg, prettyEvaluatedSourceFilename); info("Check that the script was pretty-printed as expected"); const { value: prettySourceValue } = findSourceContent(dbg, prettySource); @@ -67,8 +66,7 @@ document.addEventListener('click', e => { const prettyEvalSourceFilename = evalSourceId.split("/").at(-1) + ":formatted"; - await waitForSource(dbg, prettyEvalSourceFilename); - const prettyEvalSource = findSource(dbg, prettyEvalSourceFilename); + const prettyEvalSource = await waitForSource(dbg, prettyEvalSourceFilename); info("Check that the script was pretty-printed as expected"); const { value: prettyEvalSourceValue } = findSourceContent( @@ -113,8 +111,7 @@ setTimeout( const prettyNewFunctionSourceFilename = newFunctionSourceId.split("/").at(-1) + ":formatted"; - await waitForSource(dbg, prettyNewFunctionSourceFilename); - const prettyNewFunctionSource = findSource( + const prettyNewFunctionSource = await waitForSource( dbg, prettyNewFunctionSourceFilename ); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-preview.js b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js index 532854548c..b7bb67c7d8 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-preview.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-preview.js @@ -158,6 +158,46 @@ async function testHoveringInvalidTargetTokens(dbg) { is(raceResult, "TIMEOUT", "No popup was displayed over the inline preview"); await resume(dbg); + + info("Test hovering element not in a line"); + await getDebuggerSplitConsole(dbg); + const { hud } = dbg.toolbox.getPanel("webconsole"); + evaluateExpressionInConsole( + hud, + ` + a = 1; + debugger; + b = 2;` + ); + await waitForPaused(dbg); + await dbg.toolbox.toggleSplitConsole(); + + resetCursorPositionToTopLeftCorner(dbg); + + const racePromiseLines = Promise.any([ + waitForElement(dbg, "previewPopup"), + wait(500).then(() => "TIMEOUT_LINES"), + ]); + // We don't want to use hoverToken, as it synthesize the event at the center of the element, + // which wouldn't reproduce the original issue we want to check + EventUtils.synthesizeMouse( + findElementWithSelector(dbg, ".CodeMirror-lines"), + 0, + 0, + { + type: "mousemove", + }, + dbg.win + ); + is( + await racePromiseLines, + "TIMEOUT_LINES", + "No popup was displayed over the .CodeMirror-lines element" + ); + + // Resume and select back the main JS file that is used by the other assertions + await resume(dbg); + await selectSource(dbg, "preview.js"); } async function assertNoPreviews(dbg, expression, line, column) { diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js b/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js index edca4e2b80..82fbd911ac 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-source-pragma.js @@ -13,8 +13,7 @@ add_task(async function () { const dbg = await initDebugger("doc-source-pragma.html"); // The sourceURL pragma didn't rename the source - await waitForSource(dbg, "source-pragma.js"); - const source = findSource(dbg, "source-pragma.js"); + const source = await waitForSource(dbg, "source-pragma.js"); const actors = dbg.selectors.getSourceActorsForSource(source.id); is(actors.length, 1, "have a single actor"); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js b/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js index 0dc81605c9..2f07390c48 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-state-based-panels.js @@ -149,8 +149,9 @@ add_task(async function testBreakpointsPanePersistOnPauseToggle() { is(getPaneElements(dbg).length, 1, "Breakpoint pane is closed"); info("Check event listener breakpoints log box"); - + const wait = waitForDispatch(dbg.store, "TOGGLE_EVENT_LISTENERS"); await clickElement(dbg, "logEventsCheckbox"); + await wait; is(getPaneElements(dbg).length, 1, "Breakpoint pane is still closed"); }); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js index 53d66c6f12..85dff42104 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-step-in-navigate.js @@ -24,11 +24,10 @@ add_task(async function () { await addBreakpoint(dbg, "long.js", 1); // Navigation should clear the stepping state - const reloaded = reload(dbg); + const reloaded = reload(dbg, "simple2.js"); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, findSource(dbg, "long.js").id, 1); await resume(dbg); await reloaded; - await waitForSource(dbg, "simple3.js"); }); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers-reload.js b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers-reload.js index 1638cb9722..9256f91c2f 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers-reload.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers-reload.js @@ -17,8 +17,7 @@ add_task(async function () { const dbg = await initDebugger("doc-service-workers.html"); invokeInTab("registerWorker"); - await waitForSource(dbg, "service-worker.sjs"); - const workerSource = findSource(dbg, "service-worker.sjs"); + const workerSource = await waitForSource(dbg, "service-worker.sjs"); await reload(dbg, "service-worker.sjs"); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js index f2e20f4541..c89541871b 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-service-workers.js @@ -22,8 +22,7 @@ add_task(async function () { ); invokeInTab("registerWorker"); - await waitForSource(dbg, "service-worker.sjs"); - const workerSource = findSource(dbg, "service-worker.sjs"); + const workerSource = await waitForSource(dbg, "service-worker.sjs"); await addBreakpoint(dbg, "service-worker.sjs", 13); @@ -52,8 +51,7 @@ add_task(async function () { // The test page will immediately fetch from the service worker if registered. const onReloaded = reload(dbg); - await waitForSource(dbg, "service-worker.sjs"); - const workerSource = findSource(dbg, "service-worker.sjs"); + const workerSource = await waitForSource(dbg, "service-worker.sjs"); await waitForPaused(dbg); assertPausedAtSourceAndLine(dbg, workerSource.id, 13); @@ -140,8 +138,7 @@ add_task(async function () { invokeInTab("registerWorker"); await checkAdditionalThreadCount(dbg, 1); - await waitForSource(dbg, "service-worker.sjs"); - const workerSource = findSource(dbg, "service-worker.sjs"); + const workerSource = await waitForSource(dbg, "service-worker.sjs"); await waitForBreakpointCount(dbg, 1); await waitForPaused(dbg); diff --git a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers.js b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers.js index ffe362b44f..49decefd1c 100644 --- a/devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers.js +++ b/devtools/client/debugger/test/mochitest/browser_dbg-windowless-workers.js @@ -10,7 +10,10 @@ add_task(async function () { await pushPref("devtools.debugger.threads-visible", true); - const dbg = await initDebugger("doc-windowless-workers.html"); + const dbg = await initDebugger( + "doc-windowless-workers.html", + "simple-worker.js" + ); const mainThread = dbg.toolbox.threadFront.actor; await waitForThreadCount(dbg, 2); @@ -21,8 +24,6 @@ add_task(async function () { const mainThreadSource = findSource(dbg, "doc-windowless-workers.html"); - await waitForSource(dbg, "simple-worker.js"); - info("Pause in the main thread"); assertNotPaused(dbg); await dbg.actions.breakOnNext(); diff --git a/devtools/client/debugger/test/mochitest/shared-head.js b/devtools/client/debugger/test/mochitest/shared-head.js index b2a1c7c3d7..fe3ab0171c 100644 --- a/devtools/client/debugger/test/mochitest/shared-head.js +++ b/devtools/client/debugger/test/mochitest/shared-head.js @@ -68,25 +68,27 @@ const DEBUGGER_L10N = new LocalizationHelper( /** * Waits for `predicate()` to be true. `state` is the redux app state. * - * @memberof mochitest/waits * @param {Object} dbg * @param {Function} predicate + * @param {String} msg * @return {Promise} - * @static */ -function waitForState(dbg, predicate, msg) { +function waitForState(dbg, predicate, msg = "") { return new Promise(resolve => { - info(`Waiting for state change: ${msg || ""}`); - if (predicate(dbg.store.getState())) { - info(`Finished waiting for state change: ${msg || ""}`); - resolve(); + info(`Waiting for state change: ${msg}`); + let result = predicate(dbg.store.getState()); + if (result) { + info( + `--> The state was immediately correct (should rather do an immediate assertion?)` + ); + resolve(result); return; } const unsubscribe = dbg.store.subscribe(() => { - const result = predicate(dbg.store.getState()); + result = predicate(dbg.store.getState()); if (result) { - info(`Finished waiting for state change: ${msg || ""}`); + info(`Finished waiting for state change: ${msg}`); unsubscribe(); resolve(result); } |