forked from mirrors/gecko-dev
Bug 1828647 - [devtools] Stop automatically unblackboxing a source when a breakpoint is added to a blackboxed source r=devtools-reviewers,nchevobbe
There is a bit of complexity around blackboxing, let's avoid side effects based of blackboxing. #### Highlights of this patch - This removes auto unblackboxing a source when a breakpoint is added. This should also fix Bug 1828585. This will benefit the work around blackboxing sources on the x_google_ignoreList. - Breakpoints created on ignored lines or in an ignored source are disabled by default - Breakpoints are disabled once the line it is on gets ignored. - Disabled breakpoints on ignored lines cannot be enabled until the line is unignored. - Disabled breakpoints are enabled once the line is unignored. Differential Revision: https://phabricator.services.mozilla.com/D177459
This commit is contained in:
parent
5c2d7ec950
commit
116486a521
26 changed files with 550 additions and 183 deletions
|
|
@ -15,6 +15,7 @@ import {
|
|||
getBreakpointsList,
|
||||
getPendingBreakpointList,
|
||||
isMapScopesEnabled,
|
||||
getBlackBoxRanges,
|
||||
} from "../../selectors";
|
||||
|
||||
import { setBreakpointPositions } from "./breakpointPositions";
|
||||
|
|
@ -23,7 +24,7 @@ import { setSkipPausing } from "../pause/skipPausing";
|
|||
import { PROMISE } from "../utils/middleware/promise";
|
||||
import { recordEvent } from "../../utils/telemetry";
|
||||
import { comparePosition } from "../../utils/location";
|
||||
import { getTextAtPosition } from "../../utils/source";
|
||||
import { getTextAtPosition, isLineBlackboxed } from "../../utils/source";
|
||||
import { getMappedScopesForLocation } from "../pause/mapScopes";
|
||||
import { validateNavigateContext } from "../../utils/context";
|
||||
|
||||
|
|
@ -81,7 +82,15 @@ export function enableBreakpoint(cx, initialBreakpoint) {
|
|||
return thunkArgs => {
|
||||
const { dispatch, getState, client } = thunkArgs;
|
||||
const breakpoint = getBreakpoint(getState(), initialBreakpoint.location);
|
||||
if (!breakpoint || !breakpoint.disabled) {
|
||||
const blackboxedRanges = getBlackBoxRanges(getState());
|
||||
if (
|
||||
!breakpoint ||
|
||||
!breakpoint.disabled ||
|
||||
isLineBlackboxed(
|
||||
blackboxedRanges[breakpoint.location.source.url],
|
||||
breakpoint.location.line
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,10 +12,12 @@ import {
|
|||
originalToGeneratedId,
|
||||
} from "devtools/client/shared/source-map-loader/index";
|
||||
import { recordEvent } from "../../utils/telemetry";
|
||||
import { toggleBreakpoints } from "../breakpoints";
|
||||
import {
|
||||
getSourceActorsForSource,
|
||||
isSourceBlackBoxed,
|
||||
getBlackBoxRanges,
|
||||
getBreakpointsForSource,
|
||||
} from "../../selectors";
|
||||
|
||||
async function _blackboxSourceActorsForSource(
|
||||
|
|
@ -93,6 +95,12 @@ export function toggleBlackBox(cx, source, shouldBlackBox, ranges = []) {
|
|||
// source. To do that lets reset the content to an empty array.
|
||||
if (!ranges.length) {
|
||||
dispatch({ type: "BLACKBOX_WHOLE_SOURCES", sources: [source] });
|
||||
await toggleBreakpointsInBlackboxedSources({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable: true,
|
||||
sources: [source],
|
||||
});
|
||||
} else {
|
||||
const currentRanges = getBlackBoxRanges(getState())[source.url] || [];
|
||||
ranges = ranges.filter(newRange => {
|
||||
|
|
@ -106,6 +114,13 @@ export function toggleBlackBox(cx, source, shouldBlackBox, ranges = []) {
|
|||
return duplicate == -1;
|
||||
});
|
||||
dispatch({ type: "BLACKBOX_SOURCE_RANGES", source, ranges });
|
||||
await toggleBreakpointsInRangesForBlackboxedSource({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable: true,
|
||||
source,
|
||||
ranges,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// if there are no ranges to blackbox, then we are unblackboxing
|
||||
|
|
@ -113,16 +128,57 @@ export function toggleBlackBox(cx, source, shouldBlackBox, ranges = []) {
|
|||
// eslint-disable-next-line no-lonely-if
|
||||
if (!ranges.length) {
|
||||
dispatch({ type: "UNBLACKBOX_WHOLE_SOURCES", sources: [source] });
|
||||
toggleBreakpointsInBlackboxedSources({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable: false,
|
||||
sources: [source],
|
||||
});
|
||||
} else {
|
||||
dispatch({ type: "UNBLACKBOX_SOURCE_RANGES", source, ranges });
|
||||
const blackboxRanges = getBlackBoxRanges(getState());
|
||||
if (!blackboxRanges[source.url].length) {
|
||||
dispatch({ type: "UNBLACKBOX_WHOLE_SOURCES", sources: [source] });
|
||||
}
|
||||
await toggleBreakpointsInRangesForBlackboxedSource({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable: false,
|
||||
source,
|
||||
ranges,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function toggleBreakpointsInRangesForBlackboxedSource({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable,
|
||||
source,
|
||||
ranges,
|
||||
}) {
|
||||
const { dispatch, getState } = thunkArgs;
|
||||
for (const range of ranges) {
|
||||
const breakpoints = getBreakpointsForSource(getState(), source.id, range);
|
||||
await dispatch(toggleBreakpoints(cx, shouldDisable, breakpoints));
|
||||
}
|
||||
}
|
||||
|
||||
async function toggleBreakpointsInBlackboxedSources({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable,
|
||||
sources,
|
||||
}) {
|
||||
const { dispatch, getState } = thunkArgs;
|
||||
for (const source of sources) {
|
||||
const breakpoints = getBreakpointsForSource(getState(), source.id);
|
||||
await dispatch(toggleBreakpoints(cx, shouldDisable, breakpoints));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Blackboxes a group of sources together
|
||||
*
|
||||
|
|
@ -157,5 +213,11 @@ export function blackBoxSources(cx, sourcesToBlackBox, shouldBlackBox) {
|
|||
: "UNBLACKBOX_WHOLE_SOURCES",
|
||||
sources,
|
||||
});
|
||||
await toggleBreakpointsInBlackboxedSources({
|
||||
thunkArgs,
|
||||
cx,
|
||||
shouldDisable: shouldBlackBox,
|
||||
sources,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { connect } from "../../utils/connect";
|
|||
import PropTypes from "prop-types";
|
||||
import { Component } from "react";
|
||||
import { toEditorLine, fromEditorLine } from "../../utils/editor";
|
||||
import { isLineBlackboxed } from "../../utils/source";
|
||||
import { getBlackBoxRanges, getSelectedSource } from "../../selectors";
|
||||
import { isWasm } from "../../utils/wasm";
|
||||
|
||||
|
|
@ -80,7 +81,7 @@ class BlackboxLines extends Component {
|
|||
sourceIsWasm
|
||||
);
|
||||
|
||||
if (this.isLineBlackboxed(blackboxedRangesForSelectedSource, line)) {
|
||||
if (isLineBlackboxed(blackboxedRangesForSelectedSource, line)) {
|
||||
this.setBlackboxLine(editor, lineHandle);
|
||||
} else {
|
||||
this.clearBlackboxLine(editor, lineHandle);
|
||||
|
|
@ -95,12 +96,6 @@ class BlackboxLines extends Component {
|
|||
this.clearAllBlackboxLines(this.props.editor);
|
||||
}
|
||||
|
||||
isLineBlackboxed(ranges, line) {
|
||||
return !!ranges.find(
|
||||
range => line >= range.start.line && line <= range.end.line
|
||||
);
|
||||
}
|
||||
|
||||
clearAllBlackboxLines(editor) {
|
||||
editor.codeMirror.operation(() => {
|
||||
editor.codeMirror.eachLine(lineHandle => {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class Breakpoint extends PureComponent {
|
|||
editor: PropTypes.object.isRequired,
|
||||
editorActions: PropTypes.object.isRequired,
|
||||
selectedSource: PropTypes.object,
|
||||
blackboxedRangesForSelectedSource: PropTypes.array,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -95,14 +96,26 @@ class Breakpoint extends PureComponent {
|
|||
};
|
||||
|
||||
onContextMenu = event => {
|
||||
const { cx, breakpoint, selectedSource, breakpointActions } = this.props;
|
||||
const {
|
||||
cx,
|
||||
breakpoint,
|
||||
selectedSource,
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource,
|
||||
} = this.props;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
const selectedLocation = getSelectedLocation(breakpoint, selectedSource);
|
||||
|
||||
showMenu(
|
||||
event,
|
||||
breakpointItems(cx, breakpoint, selectedLocation, breakpointActions)
|
||||
breakpointItems(
|
||||
cx,
|
||||
breakpoint,
|
||||
selectedLocation,
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,11 @@ import PropTypes from "prop-types";
|
|||
import React, { Component } from "react";
|
||||
import Breakpoint from "./Breakpoint";
|
||||
|
||||
import { getSelectedSource, getFirstVisibleBreakpoints } from "../../selectors";
|
||||
import {
|
||||
getSelectedSource,
|
||||
getFirstVisibleBreakpoints,
|
||||
getBlackBoxRanges,
|
||||
} from "../../selectors";
|
||||
import { makeBreakpointId } from "../../utils/breakpoint";
|
||||
import { connect } from "../../utils/connect";
|
||||
import { breakpointItemActions } from "./menus/breakpoints";
|
||||
|
|
@ -21,6 +25,7 @@ class Breakpoints extends Component {
|
|||
breakpointActions: PropTypes.object,
|
||||
editorActions: PropTypes.object,
|
||||
selectedSource: PropTypes.object,
|
||||
blackboxedRanges: PropTypes.object,
|
||||
};
|
||||
}
|
||||
render() {
|
||||
|
|
@ -31,6 +36,7 @@ class Breakpoints extends Component {
|
|||
editor,
|
||||
breakpointActions,
|
||||
editorActions,
|
||||
blackboxedRanges,
|
||||
} = this.props;
|
||||
|
||||
if (!selectedSource || !breakpoints) {
|
||||
|
|
@ -46,6 +52,9 @@ class Breakpoints extends Component {
|
|||
key={makeBreakpointId(bp.location)}
|
||||
breakpoint={bp}
|
||||
selectedSource={selectedSource}
|
||||
blackboxedRangesForSelectedSource={
|
||||
blackboxedRanges[selectedSource.url]
|
||||
}
|
||||
editor={editor}
|
||||
breakpointActions={breakpointActions}
|
||||
editorActions={editorActions}
|
||||
|
|
@ -63,6 +72,7 @@ export default connect(
|
|||
// breakpoint marker represents only the first breakpoint
|
||||
breakpoints: getFirstVisibleBreakpoints(state),
|
||||
selectedSource: getSelectedSource(state),
|
||||
blackboxedRanges: getBlackBoxRanges(state),
|
||||
}),
|
||||
dispatch => ({
|
||||
breakpointActions: breakpointItemActions(dispatch),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { bindActionCreators } from "redux";
|
|||
import ReactDOM from "react-dom";
|
||||
import { connect } from "../../utils/connect";
|
||||
|
||||
import { getLineText } from "./../../utils/source";
|
||||
import { getLineText, isLineBlackboxed } from "./../../utils/source";
|
||||
import { createLocation } from "./../../utils/location";
|
||||
import { features } from "../../utils/prefs";
|
||||
import { getIndentation } from "../../utils/indentation";
|
||||
|
|
@ -483,8 +483,8 @@ class Editor extends PureComponent {
|
|||
closeConditionalPanel,
|
||||
addBreakpointAtLine,
|
||||
continueToHere,
|
||||
toggleBlackBox,
|
||||
breakableLines,
|
||||
blackboxedRanges,
|
||||
} = this.props;
|
||||
|
||||
// ignore right clicks in the gutter
|
||||
|
|
@ -492,11 +492,6 @@ class Editor extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
// if user clicks gutter to set breakpoint on blackboxed source, un-blackbox the source.
|
||||
if (this.props.selectedSourceIsBlackBoxed) {
|
||||
toggleBlackBox(cx, selectedSource);
|
||||
}
|
||||
|
||||
if (conditionalPanelLocation) {
|
||||
closeConditionalPanel();
|
||||
return;
|
||||
|
|
@ -525,7 +520,13 @@ class Editor extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
addBreakpointAtLine(cx, sourceLine, ev.altKey, ev.shiftKey);
|
||||
addBreakpointAtLine(
|
||||
cx,
|
||||
sourceLine,
|
||||
ev.altKey,
|
||||
ev.shiftKey ||
|
||||
isLineBlackboxed(blackboxedRanges[selectedSource.url], sourceLine)
|
||||
);
|
||||
};
|
||||
|
||||
onGutterContextMenu = event => {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import actions from "../../../actions";
|
|||
import { bindActionCreators } from "redux";
|
||||
import { features } from "../../../utils/prefs";
|
||||
import { formatKeyShortcut } from "../../../utils/text";
|
||||
import { isLineBlackboxed } from "../../../utils/source";
|
||||
|
||||
export const addBreakpointItem = (cx, location, breakpointActions) => ({
|
||||
id: "node-menu-add-breakpoint",
|
||||
|
|
@ -86,11 +87,15 @@ export const logPointItem = (breakpoint, location, breakpointActions) => {
|
|||
export const toggleDisabledBreakpointItem = (
|
||||
cx,
|
||||
breakpoint,
|
||||
breakpointActions
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
) => {
|
||||
return {
|
||||
accesskey: L10N.getStr("editor.disableBreakpoint.accesskey"),
|
||||
disabled: false,
|
||||
disabled: isLineBlackboxed(
|
||||
blackboxedRangesForSelectedSource,
|
||||
breakpoint.location.line
|
||||
),
|
||||
click: () => breakpointActions.toggleDisabledBreakpoint(cx, breakpoint),
|
||||
...(breakpoint.disabled
|
||||
? {
|
||||
|
|
@ -138,11 +143,17 @@ export function breakpointItems(
|
|||
cx,
|
||||
breakpoint,
|
||||
selectedLocation,
|
||||
breakpointActions
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
) {
|
||||
const items = [
|
||||
removeBreakpointItem(cx, breakpoint, breakpointActions),
|
||||
toggleDisabledBreakpointItem(cx, breakpoint, breakpointActions),
|
||||
toggleDisabledBreakpointItem(
|
||||
cx,
|
||||
breakpoint,
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
),
|
||||
];
|
||||
|
||||
if (breakpoint.originalText.startsWith("debugger")) {
|
||||
|
|
@ -161,7 +172,12 @@ export function breakpointItems(
|
|||
{ type: "separator" },
|
||||
removeBreakpointsOnLineItem(cx, selectedLocation, breakpointActions),
|
||||
breakpoint.disabled
|
||||
? enableBreakpointsOnLineItem(cx, selectedLocation, breakpointActions)
|
||||
? enableBreakpointsOnLineItem(
|
||||
cx,
|
||||
selectedLocation,
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
)
|
||||
: disableBreakpointsOnLineItem(cx, selectedLocation, breakpointActions),
|
||||
{ type: "separator" }
|
||||
);
|
||||
|
|
@ -216,12 +232,13 @@ export const removeBreakpointsOnLineItem = (
|
|||
export const enableBreakpointsOnLineItem = (
|
||||
cx,
|
||||
location,
|
||||
breakpointActions
|
||||
breakpointActions,
|
||||
blackboxedRangesForSelectedSource
|
||||
) => ({
|
||||
id: "node-menu-remove-breakpoints-on-line",
|
||||
label: L10N.getStr("breakpointMenuItem.enableAllAtLine.label"),
|
||||
accesskey: L10N.getStr("breakpointMenuItem.enableAllAtLine.accesskey"),
|
||||
disabled: false,
|
||||
disabled: isLineBlackboxed(blackboxedRangesForSelectedSource, location.line),
|
||||
click: () =>
|
||||
breakpointActions.enableBreakpointsAtLine(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -211,9 +211,6 @@ const blackBoxLinesMenuItem = (
|
|||
},
|
||||
};
|
||||
|
||||
// removes the current selection
|
||||
codeMirror.replaceSelection(codeMirror.getSelection(), "start");
|
||||
|
||||
editorActions.toggleBlackBox(
|
||||
cx,
|
||||
selectedSource,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ function generateDefaults(overrides) {
|
|||
setGutterMarker: jest.fn(),
|
||||
},
|
||||
},
|
||||
blackboxedRanges: {},
|
||||
cx: {},
|
||||
breakpointActions: {},
|
||||
editorActions: {},
|
||||
breakpoints: matchingBreakpoints,
|
||||
...overrides,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ exports[`Breakpoints Component should render breakpoints with columns 1`] = `
|
|||
},
|
||||
}
|
||||
}
|
||||
breakpointActions={Object {}}
|
||||
cx={Object {}}
|
||||
editor={
|
||||
Object {
|
||||
"codeMirror": Object {
|
||||
|
|
@ -18,6 +20,7 @@ exports[`Breakpoints Component should render breakpoints with columns 1`] = `
|
|||
},
|
||||
}
|
||||
}
|
||||
editorActions={Object {}}
|
||||
key="server1.conn1.child1/source1:undefined:2"
|
||||
selectedSource={
|
||||
Object {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import { CloseButton } from "../../shared/Button";
|
|||
|
||||
import { getSelectedText, makeBreakpointId } from "../../../utils/breakpoint";
|
||||
import { getSelectedLocation } from "../../../utils/selected-location";
|
||||
import { isLineBlackboxed } from "../../../utils/source";
|
||||
|
||||
import {
|
||||
getBreakpointsList,
|
||||
|
|
@ -38,6 +39,7 @@ class Breakpoint extends PureComponent {
|
|||
selectSpecificLocation: PropTypes.func.isRequired,
|
||||
selectedSource: PropTypes.object,
|
||||
source: PropTypes.object.isRequired,
|
||||
blackboxedRangesForSource: PropTypes.array.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -117,7 +119,7 @@ class Breakpoint extends PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { breakpoint, editor } = this.props;
|
||||
const { breakpoint, editor, blackboxedRangesForSource } = this.props;
|
||||
const text = this.getBreakpointText();
|
||||
const labelId = `${breakpoint.id}-label`;
|
||||
|
||||
|
|
@ -139,6 +141,10 @@ class Breakpoint extends PureComponent {
|
|||
type="checkbox"
|
||||
className="breakpoint-checkbox"
|
||||
checked={!breakpoint.disabled}
|
||||
disabled={isLineBlackboxed(
|
||||
blackboxedRangesForSource,
|
||||
breakpoint.location.line
|
||||
)}
|
||||
onChange={this.handleBreakpointCheckbox}
|
||||
onClick={ev => ev.stopPropagation()}
|
||||
aria-labelledby={labelId}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import { buildMenu, showMenu } from "../../../context-menu/menu";
|
||||
import { getSelectedLocation } from "../../../utils/selected-location";
|
||||
import { isLineBlackboxed } from "../../../utils/source";
|
||||
import { features } from "../../../utils/prefs";
|
||||
import { formatKeyShortcut } from "../../../utils/text";
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ export default function showContextMenu(props) {
|
|||
setBreakpointOptions,
|
||||
openConditionalPanel,
|
||||
contextMenuEvent,
|
||||
blackboxedRangesForSource,
|
||||
} = props;
|
||||
|
||||
contextMenuEvent.preventDefault();
|
||||
|
|
@ -126,7 +128,10 @@ export default function showContextMenu(props) {
|
|||
id: "node-menu-enable-self",
|
||||
label: enableSelfLabel,
|
||||
accesskey: enableSelfKey,
|
||||
disabled: false,
|
||||
disabled: isLineBlackboxed(
|
||||
blackboxedRangesForSource,
|
||||
breakpoint.location.line
|
||||
),
|
||||
click: () => {
|
||||
toggleDisabledBreakpoint(cx, breakpoint);
|
||||
},
|
||||
|
|
@ -136,7 +141,10 @@ export default function showContextMenu(props) {
|
|||
id: "node-menu-enable-all",
|
||||
label: enableAllLabel,
|
||||
accesskey: enableAllKey,
|
||||
disabled: false,
|
||||
disabled: isLineBlackboxed(
|
||||
blackboxedRangesForSource,
|
||||
breakpoint.location.line
|
||||
),
|
||||
click: () => toggleAllBreakpoints(cx, false),
|
||||
};
|
||||
|
||||
|
|
@ -144,7 +152,10 @@ export default function showContextMenu(props) {
|
|||
id: "node-menu-enable-others",
|
||||
label: enableOthersLabel,
|
||||
accesskey: enableOthersKey,
|
||||
disabled: false,
|
||||
disabled: isLineBlackboxed(
|
||||
blackboxedRangesForSource,
|
||||
breakpoint.location.line
|
||||
),
|
||||
click: () => toggleBreakpoints(cx, false, otherDisabledBreakpoints),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,11 @@ import { createHeadlessEditor } from "../../../utils/editor/create-editor";
|
|||
|
||||
import { makeBreakpointId } from "../../../utils/breakpoint";
|
||||
|
||||
import { getSelectedSource, getBreakpointSources } from "../../../selectors";
|
||||
import {
|
||||
getSelectedSource,
|
||||
getBreakpointSources,
|
||||
getBlackBoxRanges,
|
||||
} from "../../../selectors";
|
||||
|
||||
const classnames = require("devtools/client/shared/classnames.js");
|
||||
|
||||
|
|
@ -31,6 +35,7 @@ class Breakpoints extends Component {
|
|||
selectedSource: PropTypes.object,
|
||||
shouldPauseOnCaughtExceptions: PropTypes.bool.isRequired,
|
||||
shouldPauseOnExceptions: PropTypes.bool.isRequired,
|
||||
blackboxedRanges: PropTypes.array.isRequired,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -91,7 +96,7 @@ class Breakpoints extends Component {
|
|||
}
|
||||
|
||||
renderBreakpoints() {
|
||||
const { breakpointSources, selectedSource } = this.props;
|
||||
const { breakpointSources, selectedSource, blackboxedRanges } = this.props;
|
||||
if (!breakpointSources.length) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -112,6 +117,7 @@ class Breakpoints extends Component {
|
|||
<Breakpoint
|
||||
breakpoint={breakpoint}
|
||||
source={source}
|
||||
blackboxedRangesForSource={blackboxedRanges[source.url]}
|
||||
selectedSource={selectedSource}
|
||||
editor={editor}
|
||||
key={makeBreakpointId(
|
||||
|
|
@ -138,6 +144,7 @@ class Breakpoints extends Component {
|
|||
const mapStateToProps = state => ({
|
||||
breakpointSources: getBreakpointSources(state),
|
||||
selectedSource: getSelectedSource(state),
|
||||
blackboxedRanges: getBlackBoxRanges(state),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,13 @@ function generateDefaults(overrides = {}, breakpointOverrides = {}) {
|
|||
const breakpoint = makeBreakpoint(breakpointOverrides);
|
||||
const selectedSource = createSourceObject("foo");
|
||||
return {
|
||||
cx: {},
|
||||
disableBreakpoint: () => {},
|
||||
enableBreakpoint: () => {},
|
||||
openConditionalPanel: () => {},
|
||||
removeBreakpoint: () => {},
|
||||
selectSpecificLocation: () => {},
|
||||
blackboxedRangesForSource: [],
|
||||
source,
|
||||
breakpoint,
|
||||
selectedSource,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ exports[`Breakpoint disabled 1`] = `
|
|||
aria-labelledby="1-label"
|
||||
checked={false}
|
||||
className="breakpoint-checkbox"
|
||||
disabled={true}
|
||||
id={1}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
|
|
@ -56,6 +57,7 @@ exports[`Breakpoint paused at a different 1`] = `
|
|||
aria-labelledby="1-label"
|
||||
checked={true}
|
||||
className="breakpoint-checkbox"
|
||||
disabled={true}
|
||||
id={1}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
|
|
@ -101,6 +103,7 @@ exports[`Breakpoint paused at a generatedLocation 1`] = `
|
|||
aria-labelledby="1-label"
|
||||
checked={true}
|
||||
className="breakpoint-checkbox"
|
||||
disabled={true}
|
||||
id={1}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
|
|
@ -146,6 +149,7 @@ exports[`Breakpoint paused at an original location 1`] = `
|
|||
aria-labelledby="1-label"
|
||||
checked={true}
|
||||
className="breakpoint-checkbox"
|
||||
disabled={true}
|
||||
id={1}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
|
|
@ -191,6 +195,7 @@ exports[`Breakpoint simple 1`] = `
|
|||
aria-labelledby="1-label"
|
||||
checked={true}
|
||||
className="breakpoint-checkbox"
|
||||
disabled={true}
|
||||
id={1}
|
||||
onChange={[Function]}
|
||||
onClick={[Function]}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,15 @@ function generateDefaultState(propsOverride) {
|
|||
text: 'URL contains ""',
|
||||
},
|
||||
],
|
||||
enableXHRBreakpoint: () => {},
|
||||
disableXHRBreakpoint: () => {},
|
||||
updateXHRBreakpoint: () => {},
|
||||
removeXHRBreakpoint: () => {},
|
||||
setXHRBreakpoint: () => {},
|
||||
togglePauseOnAny: () => {},
|
||||
showInput: false,
|
||||
shouldPauseOnAny: false,
|
||||
onXHRAdded: () => {},
|
||||
...propsOverride,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,15 @@
|
|||
|
||||
exports[`XHR Breakpoints should render with 0 expressions passed from props 1`] = `
|
||||
<XHRBreakpoints
|
||||
disableXHRBreakpoint={[Function]}
|
||||
enableXHRBreakpoint={[Function]}
|
||||
onXHRAdded={[Function]}
|
||||
removeXHRBreakpoint={[Function]}
|
||||
setXHRBreakpoint={[Function]}
|
||||
shouldPauseOnAny={false}
|
||||
showInput={false}
|
||||
togglePauseOnAny={[Function]}
|
||||
updateXHRBreakpoint={[Function]}
|
||||
xhrBreakpoints={
|
||||
Array [
|
||||
Object {
|
||||
|
|
@ -19,6 +28,7 @@ exports[`XHR Breakpoints should render with 0 expressions passed from props 1`]
|
|||
>
|
||||
<ExceptionOption
|
||||
className="breakpoints-exceptions"
|
||||
isChecked={false}
|
||||
label="Pause on any URL"
|
||||
onChange={[Function]}
|
||||
>
|
||||
|
|
@ -132,6 +142,15 @@ exports[`XHR Breakpoints should render with 0 expressions passed from props 1`]
|
|||
|
||||
exports[`XHR Breakpoints should render with 8 expressions passed from props 1`] = `
|
||||
<XHRBreakpoints
|
||||
disableXHRBreakpoint={[Function]}
|
||||
enableXHRBreakpoint={[Function]}
|
||||
onXHRAdded={[Function]}
|
||||
removeXHRBreakpoint={[Function]}
|
||||
setXHRBreakpoint={[Function]}
|
||||
shouldPauseOnAny={false}
|
||||
showInput={false}
|
||||
togglePauseOnAny={[Function]}
|
||||
updateXHRBreakpoint={[Function]}
|
||||
xhrBreakpoints={
|
||||
Array [
|
||||
Object {
|
||||
|
|
@ -205,6 +224,7 @@ exports[`XHR Breakpoints should render with 8 expressions passed from props 1`]
|
|||
>
|
||||
<ExceptionOption
|
||||
className="breakpoints-exceptions"
|
||||
isChecked={false}
|
||||
label="Pause on any URL"
|
||||
onChange={[Function]}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ export function initialBreakpointsState(xhrBreakpoints = []) {
|
|||
return {
|
||||
breakpoints: {},
|
||||
xhrBreakpoints,
|
||||
breakpointsDisabled: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@
|
|||
import { createSelector } from "reselect";
|
||||
import { getSelectedSource } from "./sources";
|
||||
import { getBreakpointsList } from "./breakpoints";
|
||||
import { getBlackBoxRanges } from "./source-blackbox";
|
||||
import { getFilename } from "../utils/source";
|
||||
import { getSelectedLocation } from "../utils/selected-location";
|
||||
|
||||
|
|
@ -18,8 +17,7 @@ import { getSelectedLocation } from "../utils/selected-location";
|
|||
export const getBreakpointSources = createSelector(
|
||||
getBreakpointsList,
|
||||
getSelectedSource,
|
||||
getBlackBoxRanges,
|
||||
(breakpoints, selectedSource, blackBoxRanges) => {
|
||||
(breakpoints, selectedSource) => {
|
||||
const visibleBreakpoints = breakpoints.filter(
|
||||
bp =>
|
||||
!bp.options.hidden &&
|
||||
|
|
@ -33,11 +31,6 @@ export const getBreakpointSources = createSelector(
|
|||
const location = getSelectedLocation(breakpoint, selectedSource);
|
||||
const { source } = location;
|
||||
|
||||
// Ignore any blackboxed sources.
|
||||
if (blackBoxRanges[source.url]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We may have more than one breakpoint per source,
|
||||
// so use the map to have a unique entry per source.
|
||||
if (!sources.has(source)) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,14 @@ export function getBreakpoint(state, location) {
|
|||
return breakpoints[makeBreakpointId(location)];
|
||||
}
|
||||
|
||||
export function getBreakpointsForSource(state, sourceId, line) {
|
||||
/**
|
||||
* Gets the breakpoints on a line or within a range of lines
|
||||
* @param {Object} state
|
||||
* @param {Number} sourceId
|
||||
* @param {Number|Object} lines - line or an object with a start and end range of lines
|
||||
* @returns {Array} breakpoints
|
||||
*/
|
||||
export function getBreakpointsForSource(state, sourceId, lines) {
|
||||
if (!sourceId) {
|
||||
return [];
|
||||
}
|
||||
|
|
@ -39,7 +46,16 @@ export function getBreakpointsForSource(state, sourceId, line) {
|
|||
const breakpoints = getBreakpointsList(state);
|
||||
return breakpoints.filter(bp => {
|
||||
const location = isGeneratedSource ? bp.generatedLocation : bp.location;
|
||||
return location.sourceId === sourceId && (!line || line == location.line);
|
||||
|
||||
if (lines) {
|
||||
const isOnLineOrWithinRange =
|
||||
typeof lines == "number"
|
||||
? location.line == lines
|
||||
: location.line >= lines.start.line &&
|
||||
location.line <= lines.end.line;
|
||||
return location.sourceId === sourceId && isOnLineOrWithinRange;
|
||||
}
|
||||
return location.sourceId === sourceId;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,6 +116,26 @@ export function findBlackBoxRange(source, blackboxedRanges, lineRange) {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a source line is blackboxed
|
||||
* @param {Array} ranges - Line ranges that are blackboxed
|
||||
* @param {Number} line
|
||||
* @returns boolean
|
||||
*/
|
||||
export function isLineBlackboxed(ranges, line) {
|
||||
if (!ranges) {
|
||||
return false;
|
||||
}
|
||||
// If the whole source is ignored , then the line is
|
||||
// ignored.
|
||||
if (!ranges.length) {
|
||||
return true;
|
||||
}
|
||||
return !!ranges.find(
|
||||
range => line >= range.start.line && line <= range.end.line
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified url and/or content type are specific to
|
||||
* javascript files.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ add_task(async function testBlackBoxOnMultipleFiles() {
|
|||
"Ignore files outside this directory"
|
||||
);
|
||||
selectContextMenuItem(dbg, NODE_SELECTORS.nodeBlackBoxAllInside);
|
||||
await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES");
|
||||
await waitForBlackboxCount(dbg, 1);
|
||||
await waitForRequestsToSettle(dbg);
|
||||
|
||||
|
|
@ -86,8 +87,11 @@ add_task(async function testBlackBoxOnMultipleFiles() {
|
|||
"Unignore files outside this directory"
|
||||
);
|
||||
selectContextMenuItem(dbg, NODE_SELECTORS.nodeUnBlackBoxAllOutside);
|
||||
await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES");
|
||||
await waitForBlackboxCount(dbg, 0);
|
||||
await waitForRequestsToSettle(dbg);
|
||||
info("Wait for any breakpoints in the source to get enabled");
|
||||
await waitForDispatch(dbg.store, "SET_BREAKPOINT");
|
||||
|
||||
assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.nestedSource, false);
|
||||
assertSourceNodeIsBlackBoxed(dbg, SOURCE_FILES.codeReload1, false);
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ add_task(async function testBlackBoxOnReload() {
|
|||
assertNotPaused(dbg);
|
||||
|
||||
info("Ignoring line 2 using the gutter context menu");
|
||||
await openContextMenu(dbg, "gutter", 2);
|
||||
await openContextMenuInDebugger(dbg, "gutter", 2);
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-line");
|
||||
|
||||
info("Ignoring line 7 to 9 using the editor context menu");
|
||||
|
|
@ -141,7 +141,7 @@ add_task(async function testBlackBoxOnToolboxRestart() {
|
|||
await onReloaded;
|
||||
|
||||
info("Ignoring line 2 using the gutter context menu");
|
||||
await openContextMenu(dbg, "gutter", 2);
|
||||
await openContextMenuInDebugger(dbg, "gutter", 2);
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-line");
|
||||
|
||||
await reloadBrowser();
|
||||
|
|
@ -194,7 +194,7 @@ async function testBlackBoxSource(dbg, source) {
|
|||
info(
|
||||
"Blackbox the whole simple4.js source file using the editor context menu"
|
||||
);
|
||||
await openContextMenu(dbg, "CodeMirrorLines");
|
||||
await openContextMenuInDebugger(dbg, "CodeMirrorLines");
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox");
|
||||
|
||||
info("Assert that all lines in the source are styled correctly");
|
||||
|
|
@ -392,7 +392,7 @@ async function testBlackBoxMultipleLines(dbg, source) {
|
|||
|
||||
async function testBlackBoxSingleLine(dbg, source) {
|
||||
info("Black box line 2 of funcA() with the debugger statement");
|
||||
await openContextMenu(dbg, "gutter", 2);
|
||||
await openContextMenuInDebugger(dbg, "gutter", 2);
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-line");
|
||||
|
||||
await assertEditorBlackBoxBoxContextMenuItems(dbg, {
|
||||
|
|
@ -421,7 +421,7 @@ async function testBlackBoxSingleLine(dbg, source) {
|
|||
});
|
||||
|
||||
info("Black box line 4 of funcC() with the debugger statement");
|
||||
await openContextMenu(dbg, "gutter", 4);
|
||||
await openContextMenuInDebugger(dbg, "gutter", 4);
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-line");
|
||||
|
||||
info("Assert that the ignored line 4 is styled correctly");
|
||||
|
|
@ -441,7 +441,7 @@ async function testBlackBoxSingleLine(dbg, source) {
|
|||
|
||||
info("Un-blackbox line 2 of funcA()");
|
||||
selectEditorLines(dbg, 2, 2);
|
||||
await openContextMenu(dbg, "CodeMirrorLines");
|
||||
await openContextMenuInDebugger(dbg, "CodeMirrorLines");
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-line");
|
||||
|
||||
await assertEditorBlackBoxBoxContextMenuItems(dbg, {
|
||||
|
|
@ -511,7 +511,11 @@ async function assertGutterBlackBoxBoxContextMenuItems(dbg, testFixtures) {
|
|||
info(
|
||||
"Asserts that the gutter context menu items when clicking on the gutter of a blackboxed line"
|
||||
);
|
||||
const popup = await openContextMenu(dbg, "gutter", blackboxedLine);
|
||||
const popup = await openContextMenuInDebugger(
|
||||
dbg,
|
||||
"gutter",
|
||||
blackboxedLine
|
||||
);
|
||||
// When the whole source is blackboxed the the gutter visually shows `ignore line`
|
||||
// but it is disabled indicating that individual lines cannot be nonBlackboxed.
|
||||
const item =
|
||||
|
|
@ -527,7 +531,11 @@ async function assertGutterBlackBoxBoxContextMenuItems(dbg, testFixtures) {
|
|||
info(
|
||||
"Asserts that the gutter context menu items when clicking on the gutter of a nonBlackboxed line"
|
||||
);
|
||||
const popup = await openContextMenu(dbg, "gutter", nonBlackBoxedLine);
|
||||
const popup = await openContextMenuInDebugger(
|
||||
dbg,
|
||||
"gutter",
|
||||
nonBlackBoxedLine
|
||||
);
|
||||
const item = contextMenuItems.ignoreLine;
|
||||
await assertContextMenuLabel(dbg, item.selector, item.label);
|
||||
|
||||
|
|
@ -713,61 +721,6 @@ async function assertEditorBlackBoxBoxContextMenuItems(dbg, testFixtures) {
|
|||
}
|
||||
}
|
||||
|
||||
async function selectEditorLinesAndOpenContextMenu(dbg, lines) {
|
||||
const { startLine, endLine } = lines;
|
||||
const elementName = "line";
|
||||
if (!endLine) {
|
||||
await clickElement(dbg, elementName, startLine);
|
||||
} else {
|
||||
getCM(dbg).setSelection(
|
||||
{ line: startLine - 1, ch: 0 },
|
||||
{ line: endLine, ch: 0 }
|
||||
);
|
||||
}
|
||||
return openContextMenu(dbg, elementName, startLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the debugger editor context menu in either codemirror or the
|
||||
* the debugger gutter.
|
||||
* @param {Object} dbg
|
||||
* @param {String} elementName
|
||||
* The element to select
|
||||
* @param {Number} line
|
||||
* The line to open the context menu on.
|
||||
*/
|
||||
async function openContextMenu(dbg, elementName, line) {
|
||||
const waitForOpen = waitForContextMenu(dbg);
|
||||
info(`Open ${elementName} context menu on line ${line || ""}`);
|
||||
rightClickElement(dbg, elementName, line);
|
||||
return waitForOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the specific black box context menu item
|
||||
* @param {Object} dbg
|
||||
* @param {String} itemName
|
||||
* The name of the context menu item.
|
||||
*/
|
||||
async function selectBlackBoxContextMenuItem(dbg, itemName) {
|
||||
let wait = null;
|
||||
if (itemName == "blackbox-line" || itemName == "blackbox-lines") {
|
||||
wait = Promise.any([
|
||||
waitForDispatch(dbg.store, "BLACKBOX_SOURCE_RANGES"),
|
||||
waitForDispatch(dbg.store, "UNBLACKBOX_SOURCE_RANGES"),
|
||||
]);
|
||||
} else if (itemName == "blackbox") {
|
||||
wait = Promise.any([
|
||||
waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"),
|
||||
waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"),
|
||||
]);
|
||||
}
|
||||
|
||||
info(`Select the ${itemName} context menu item`);
|
||||
selectContextMenuItem(dbg, `#node-menu-${itemName}`);
|
||||
return wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a range of lines
|
||||
* @param {Object} dbg
|
||||
|
|
@ -780,64 +733,3 @@ function selectEditorLines(dbg, startLine, endLine) {
|
|||
{ line: endLine, ch: 0 }
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Asserts that the styling for ignored lines are applied
|
||||
* @param {Object} dbg
|
||||
* @param {Object} options
|
||||
* lines {null | Number | Number[]} [lines] Line(s) to assert.
|
||||
* - If null is passed, the assertion is on all the blackboxed lines
|
||||
* - If a line number is passed, the assertion is on the specified line
|
||||
* - If an array (start and end lines) is passed, the assertion is on the multiple lines seelected
|
||||
* hasBlackboxedLinesClass
|
||||
* If `true` assert that style exist, else assert that style does not exist
|
||||
*/
|
||||
function assertIgnoredStyleInSourceLines(
|
||||
dbg,
|
||||
{ lines, hasBlackboxedLinesClass }
|
||||
) {
|
||||
if (lines) {
|
||||
if (!lines[1]) {
|
||||
// Single line ignored
|
||||
const element = findElement(dbg, "line", lines[0]);
|
||||
const hasStyle = hasBlackboxedLinesClass
|
||||
? element.parentNode.classList.contains("blackboxed-line")
|
||||
: !element.parentNode.classList.contains("blackboxed-line");
|
||||
ok(
|
||||
hasStyle,
|
||||
`Line ${lines[0]} ${
|
||||
hasBlackboxedLinesClass ? "does not have" : "has"
|
||||
} ignored styling`
|
||||
);
|
||||
} else {
|
||||
// Multiple lines ignored
|
||||
let currentLine = lines[0];
|
||||
while (currentLine <= lines[1]) {
|
||||
const element = findElement(dbg, "line", currentLine);
|
||||
const hasStyle = hasBlackboxedLinesClass
|
||||
? element.parentNode.classList.contains("blackboxed-line")
|
||||
: !element.parentNode.classList.contains("blackboxed-line");
|
||||
ok(
|
||||
hasStyle,
|
||||
`Line ${currentLine} ${
|
||||
hasBlackboxedLinesClass ? "does not have" : "has"
|
||||
} ignored styling`
|
||||
);
|
||||
currentLine = currentLine + 1;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const codeLines = findAllElementsWithSelector(
|
||||
dbg,
|
||||
".CodeMirror-code .CodeMirror-line"
|
||||
);
|
||||
const blackboxedLines = findAllElementsWithSelector(
|
||||
dbg,
|
||||
".CodeMirror-code .blackboxed-line"
|
||||
);
|
||||
is(
|
||||
hasBlackboxedLinesClass ? codeLines.length : 0,
|
||||
blackboxedLines.length,
|
||||
`${blackboxedLines.length} of ${codeLines.length} lines are blackboxed`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,3 +114,77 @@ add_task(async function testBreakpointsListForOriginalFiles() {
|
|||
|
||||
await removeBreakpoint(dbg, source.id, 5);
|
||||
});
|
||||
|
||||
add_task(async function testBreakpointsListForIgnoredLines() {
|
||||
await pushPref("devtools.debugger.features.blackbox-lines", true);
|
||||
const dbg = await initDebugger("doc-sourcemaps.html", "entry.js");
|
||||
|
||||
info("Add breakpoint to the entry.js (original source)");
|
||||
await selectSource(dbg, "entry.js");
|
||||
await addBreakpoint(dbg, "entry.js", 5);
|
||||
|
||||
info("Ignoring line 5 to 6 which has a breakpoint already set");
|
||||
await selectEditorLinesAndOpenContextMenu(dbg, {
|
||||
startLine: 5,
|
||||
endLine: 6,
|
||||
});
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-lines");
|
||||
|
||||
info("Assert the breakpoint on the ignored line");
|
||||
let breakpointItems = findAllElements(dbg, "breakpointItems");
|
||||
is(
|
||||
breakpointItems[0].textContent,
|
||||
"output(times2(1));5",
|
||||
"The info displayed for the 1st breakpoint is correct"
|
||||
);
|
||||
const firstBreakpointCheck = breakpointItems[0].querySelector("input");
|
||||
ok(
|
||||
firstBreakpointCheck.disabled,
|
||||
"The first breakpoint checkbox on an ignored line is disabled"
|
||||
);
|
||||
ok(
|
||||
!firstBreakpointCheck.checked,
|
||||
"The first breakpoint on an ignored line is not checked"
|
||||
);
|
||||
|
||||
info("Ignoring line 8 to 9 which currently has not breakpoint");
|
||||
await selectEditorLinesAndOpenContextMenu(dbg, {
|
||||
startLine: 8,
|
||||
endLine: 9,
|
||||
});
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-lines");
|
||||
|
||||
await addBreakpointViaGutter(dbg, 9);
|
||||
|
||||
breakpointItems = findAllElements(dbg, "breakpointItems");
|
||||
is(
|
||||
breakpointItems[1].textContent,
|
||||
"output(times2(3));9",
|
||||
"The info displayed for the 2nd breakpoint is correct"
|
||||
);
|
||||
const secondBreakpointCheck = breakpointItems[1].querySelector("input");
|
||||
ok(
|
||||
secondBreakpointCheck.disabled,
|
||||
"The second breakpoint checkbox on an ignored line is disabled"
|
||||
);
|
||||
ok(
|
||||
!secondBreakpointCheck.checked,
|
||||
"The second breakpoint on an ignored line is not checked"
|
||||
);
|
||||
|
||||
await clickElement(dbg, "blackbox");
|
||||
await waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES");
|
||||
|
||||
info("Assert that both breakpoints are now enabled");
|
||||
breakpointItems = findAllElements(dbg, "breakpointItems");
|
||||
[...breakpointItems].forEach(breakpointItem => {
|
||||
const check = breakpointItem.querySelector("input");
|
||||
ok(
|
||||
!check.disabled,
|
||||
"The breakpoint checkbox on the unignored line is enabled"
|
||||
);
|
||||
ok(check.checked, "The breakpoint on the unignored line is checked");
|
||||
});
|
||||
|
||||
await dbg.toolbox.closeToolbox();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
// rejections and make bug 1512742 permafail.
|
||||
PromiseTestUtils.allowMatchingRejectionsGlobally(/NS_ERROR_NOT_INITIALIZED/);
|
||||
|
||||
add_task(async function() {
|
||||
add_task(async function testGutterBreakpoints() {
|
||||
const dbg = await initDebugger("doc-scripts.html", "simple1.js");
|
||||
const source = findSource(dbg, "simple1.js");
|
||||
|
||||
|
|
@ -30,32 +30,117 @@ add_task(async function() {
|
|||
await assertNoBreakpoint(dbg, 4);
|
||||
});
|
||||
|
||||
add_task(async function() {
|
||||
info("Ensure clicking on gutter to add breakpoint will un-blackbox source");
|
||||
add_task(async function testGutterBreakpointsInIgnoredSource() {
|
||||
await pushPref("devtools.debugger.features.blackbox-lines", true);
|
||||
info(
|
||||
"Ensure clicking on gutter to add breakpoint should not un-ignore source"
|
||||
);
|
||||
const dbg = await initDebugger("doc-sourcemaps3.html");
|
||||
dbg.actions.toggleMapScopes();
|
||||
await waitForSources(dbg, "bundle.js", "sorted.js", "test.js");
|
||||
|
||||
info("blackbox the source");
|
||||
const sortedSrc = findSource(dbg, "sorted.js");
|
||||
await selectSource(dbg, sortedSrc);
|
||||
info("Ignore the source");
|
||||
await selectSource(dbg, findSource(dbg, "sorted.js"));
|
||||
await clickElement(dbg, "blackbox");
|
||||
await waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES");
|
||||
|
||||
// invoke test
|
||||
invokeInTab("test");
|
||||
// should not pause
|
||||
|
||||
// wait to make sure no pause has occured
|
||||
await wait(1000);
|
||||
assertNotPaused(dbg);
|
||||
|
||||
info("ensure gutter breakpoint gets set with click");
|
||||
info("Ensure gutter breakpoint gets set with click");
|
||||
await clickGutter(dbg, 4);
|
||||
await waitForDispatch(dbg.store, "SET_BREAKPOINT");
|
||||
|
||||
info("Assert that the `Enable breakpoint` context menu item is disabled");
|
||||
const popup = await openContextMenuInDebugger(dbg, "gutter", 4);
|
||||
await assertContextMenuItemDisabled(
|
||||
dbg,
|
||||
"#node-menu-enable-breakpoint",
|
||||
true
|
||||
);
|
||||
await closeContextMenu(dbg, popup);
|
||||
|
||||
is(dbg.selectors.getBreakpointCount(), 1, "One breakpoint exists");
|
||||
await assertBreakpoint(dbg, 4);
|
||||
is(
|
||||
findBreakpoint(dbg, "sorted.js", 4).disabled,
|
||||
true,
|
||||
"The breakpoint in the ignored source looks disabled"
|
||||
);
|
||||
|
||||
// click on test
|
||||
invokeInTab("test");
|
||||
// verify pause at breakpoint.
|
||||
await waitForPaused(dbg);
|
||||
ok(true, "source is un-blackboxed");
|
||||
|
||||
await wait(1000);
|
||||
assertNotPaused(dbg);
|
||||
|
||||
ok(true, "source is still blackboxed");
|
||||
});
|
||||
|
||||
add_task(async function testGutterBreakpointsForSourceWithIgnoredLines() {
|
||||
await pushPref("devtools.debugger.features.blackbox-lines", true);
|
||||
info(
|
||||
"Asserts that adding a breakpoint to the gutter of an un-ignored line does not un-ignore other ranges in the source"
|
||||
);
|
||||
const dbg = await initDebugger("doc-sourcemaps3.html");
|
||||
await waitForSources(dbg, "bundle.js", "sorted.js", "test.js");
|
||||
|
||||
await selectSource(dbg, findSource(dbg, "sorted.js"));
|
||||
|
||||
info("Ignoring line 17 to 21 using the editor context menu");
|
||||
await selectEditorLinesAndOpenContextMenu(dbg, {
|
||||
startLine: 17,
|
||||
endLine: 21,
|
||||
});
|
||||
await selectBlackBoxContextMenuItem(dbg, "blackbox-lines");
|
||||
|
||||
info("Set breakpoint on an un-ignored line");
|
||||
await clickGutter(dbg, 4);
|
||||
await waitForDispatch(dbg.store, "SET_BREAKPOINT");
|
||||
|
||||
info("Assert that the `Disable breakpoint` context menu item is enabled");
|
||||
const popup = await openContextMenuInDebugger(dbg, "gutter", 4);
|
||||
await assertContextMenuItemDisabled(
|
||||
dbg,
|
||||
"#node-menu-disable-breakpoint",
|
||||
false
|
||||
);
|
||||
await closeContextMenu(dbg, popup);
|
||||
|
||||
info("Assert that the lines 17 to 21 are still ignored");
|
||||
assertIgnoredStyleInSourceLines(dbg, {
|
||||
lines: [17, 21],
|
||||
hasBlackboxedLinesClass: true,
|
||||
});
|
||||
|
||||
info(
|
||||
"Asserts that adding a breakpoint to the gutter of an ignored line creates a disabled breakpoint"
|
||||
);
|
||||
info("Set breakpoint on an ignored line");
|
||||
await clickGutter(dbg, 19);
|
||||
await waitForDispatch(dbg.store, "SET_BREAKPOINT");
|
||||
|
||||
info("Assert that the `Enable breakpoint` context menu item is disabled");
|
||||
const popup2 = await openContextMenuInDebugger(dbg, "gutter", 19);
|
||||
await assertContextMenuItemDisabled(
|
||||
dbg,
|
||||
"#node-menu-enable-breakpoint",
|
||||
true
|
||||
);
|
||||
await closeContextMenu(dbg, popup2);
|
||||
|
||||
info("Assert that the breakpoint on the line 19 is visually disabled");
|
||||
is(
|
||||
findBreakpoint(dbg, "sorted.js", 19).disabled,
|
||||
true,
|
||||
"The breakpoint on an ignored line is disabled"
|
||||
);
|
||||
});
|
||||
|
||||
async function assertContextMenuItemDisabled(dbg, selector, expectedState) {
|
||||
const item = await waitFor(() => findContextMenu(dbg, selector));
|
||||
is(item.disabled, expectedState, "The context menu item is disabled");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1475,6 +1475,89 @@ async function getEditorLineEl(dbg, line) {
|
|||
return el;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the debugger editor context menu in either codemirror or the
|
||||
* the debugger gutter.
|
||||
* @param {Object} dbg
|
||||
* @param {String} elementName
|
||||
* The element to select
|
||||
* @param {Number} line
|
||||
* The line to open the context menu on.
|
||||
*/
|
||||
async function openContextMenuInDebugger(dbg, elementName, line) {
|
||||
const waitForOpen = waitForContextMenu(dbg);
|
||||
info(`Open ${elementName} context menu on line ${line || ""}`);
|
||||
rightClickElement(dbg, elementName, line);
|
||||
return waitForOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a range of lines in the editor and open the contextmenu
|
||||
* @param {Object} dbg
|
||||
* @param {Object} lines
|
||||
* @returns
|
||||
*/
|
||||
async function selectEditorLinesAndOpenContextMenu(dbg, lines) {
|
||||
const { startLine, endLine } = lines;
|
||||
const elementName = "line";
|
||||
if (!endLine) {
|
||||
await clickElement(dbg, elementName, startLine);
|
||||
} else {
|
||||
getCM(dbg).setSelection(
|
||||
{ line: startLine - 1, ch: 0 },
|
||||
{ line: endLine, ch: 0 }
|
||||
);
|
||||
}
|
||||
return openContextMenuInDebugger(dbg, elementName, startLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts that the styling for ignored lines are applied
|
||||
* @param {Object} dbg
|
||||
* @param {Object} options
|
||||
* lines {null | Number[]} [lines] Line(s) to assert.
|
||||
* - If null is passed, the assertion is on all the blackboxed lines
|
||||
* - If an array of one item (start line) is passed, the assertion is on the specified line
|
||||
* - If an array (start and end lines) is passed, the assertion is on the multiple lines seelected
|
||||
* hasBlackboxedLinesClass
|
||||
* If `true` assert that style exist, else assert that style does not exist
|
||||
*/
|
||||
function assertIgnoredStyleInSourceLines(
|
||||
dbg,
|
||||
{ lines, hasBlackboxedLinesClass }
|
||||
) {
|
||||
if (lines) {
|
||||
let currentLine = lines[0];
|
||||
do {
|
||||
const element = findElement(dbg, "line", currentLine);
|
||||
const hasStyle = hasBlackboxedLinesClass
|
||||
? element.parentNode.classList.contains("blackboxed-line")
|
||||
: !element.parentNode.classList.contains("blackboxed-line");
|
||||
ok(
|
||||
hasStyle,
|
||||
`Line ${currentLine} ${
|
||||
hasBlackboxedLinesClass ? "does not have" : "has"
|
||||
} ignored styling`
|
||||
);
|
||||
currentLine = currentLine + 1;
|
||||
} while (currentLine <= lines[1]);
|
||||
} else {
|
||||
const codeLines = findAllElementsWithSelector(
|
||||
dbg,
|
||||
".CodeMirror-code .CodeMirror-line"
|
||||
);
|
||||
const blackboxedLines = findAllElementsWithSelector(
|
||||
dbg,
|
||||
".CodeMirror-code .blackboxed-line"
|
||||
);
|
||||
is(
|
||||
hasBlackboxedLinesClass ? codeLines.length : 0,
|
||||
blackboxedLines.length,
|
||||
`${blackboxedLines.length} of ${codeLines.length} lines are blackboxed`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert the text content on the line matches what is
|
||||
* expected.
|
||||
|
|
@ -2596,3 +2679,28 @@ if (protocolHandler.hasSubstitution("testing-common")) {
|
|||
PromiseTestUtils.allowMatchingRejectionsGlobally(/Connection closed/);
|
||||
this.PromiseTestUtils = PromiseTestUtils;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the specific black box context menu item
|
||||
* @param {Object} dbg
|
||||
* @param {String} itemName
|
||||
* The name of the context menu item.
|
||||
*/
|
||||
async function selectBlackBoxContextMenuItem(dbg, itemName) {
|
||||
let wait = null;
|
||||
if (itemName == "blackbox-line" || itemName == "blackbox-lines") {
|
||||
wait = Promise.any([
|
||||
waitForDispatch(dbg.store, "BLACKBOX_SOURCE_RANGES"),
|
||||
waitForDispatch(dbg.store, "UNBLACKBOX_SOURCE_RANGES"),
|
||||
]);
|
||||
} else if (itemName == "blackbox") {
|
||||
wait = Promise.any([
|
||||
waitForDispatch(dbg.store, "BLACKBOX_WHOLE_SOURCES"),
|
||||
waitForDispatch(dbg.store, "UNBLACKBOX_WHOLE_SOURCES"),
|
||||
]);
|
||||
}
|
||||
|
||||
info(`Select the ${itemName} context menu item`);
|
||||
selectContextMenuItem(dbg, `#node-menu-${itemName}`);
|
||||
return wait;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue