зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1754407 - [devtools] Use a distinct reducer for source text content and make source objects immutables. r=perftest-reviewers,devtools-reviewers,bomsy,AlexandruIonescu
This helps working on debugger performance as source objects are now immutable and so won't trigger selector updates. This also stop updating the object/map that contains all the text contents. Differential Revision: https://phabricator.services.mozilla.com/D138261
This commit is contained in:
Родитель
3976d30b56
Коммит
a736d67240
|
@ -4,7 +4,8 @@
|
|||
|
||||
import {
|
||||
hasInScopeLines,
|
||||
getSourceWithContent,
|
||||
getSource,
|
||||
getSourceTextContent,
|
||||
getVisibleSelectedFrame,
|
||||
} from "../../selectors";
|
||||
|
||||
|
@ -28,7 +29,8 @@ function getOutOfScopeLines(outOfScopeLocations) {
|
|||
}
|
||||
|
||||
async function getInScopeLines(cx, location, { dispatch, getState, parser }) {
|
||||
const source = getSourceWithContent(getState(), location.sourceId);
|
||||
const source = getSource(getState(), location.sourceId);
|
||||
const sourceTextContent = getSourceTextContent(getState(), source.id);
|
||||
|
||||
let locations = null;
|
||||
if (location.line && source && !source.isWasm) {
|
||||
|
@ -37,9 +39,9 @@ async function getInScopeLines(cx, location, { dispatch, getState, parser }) {
|
|||
|
||||
const linesOutOfScope = getOutOfScopeLines(locations);
|
||||
const sourceNumLines =
|
||||
!source.content || !isFulfilled(source.content)
|
||||
!sourceTextContent || !isFulfilled(sourceTextContent)
|
||||
? 0
|
||||
: getSourceLineCount(source.content.value);
|
||||
: getSourceLineCount(sourceTextContent.value);
|
||||
|
||||
const noLinesOutOfScope =
|
||||
linesOutOfScope == null || linesOutOfScope.size == 0;
|
||||
|
@ -72,9 +74,12 @@ export function setInScopeLines(cx) {
|
|||
}
|
||||
|
||||
const { location } = visibleFrame;
|
||||
const { content } = getSourceWithContent(getState(), location.sourceId);
|
||||
const sourceTextContent = getSourceTextContent(
|
||||
getState(),
|
||||
location.sourceId
|
||||
);
|
||||
|
||||
if (hasInScopeLines(getState(), location) || !content) {
|
||||
if (hasInScopeLines(getState(), location) || !sourceTextContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,8 @@ import { renderWasmText } from "../utils/wasm";
|
|||
import { getMatches } from "../workers/search";
|
||||
|
||||
import {
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSourceId,
|
||||
getSelectedSourceTextContent,
|
||||
getFileSearchModifiers,
|
||||
getFileSearchQuery,
|
||||
getFileSearchResults,
|
||||
|
@ -29,8 +30,8 @@ import { isFulfilled } from "../utils/async-value";
|
|||
|
||||
export function doSearch(cx, query, editor) {
|
||||
return ({ getState, dispatch }) => {
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSource || !selectedSource.content) {
|
||||
const sourceTextContent = getSelectedSourceTextContent(getState());
|
||||
if (!sourceTextContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -41,10 +42,11 @@ export function doSearch(cx, query, editor) {
|
|||
|
||||
export function doSearchForHighlight(query, editor, line, ch) {
|
||||
return async ({ getState, dispatch }) => {
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
if (!selectedSource?.content) {
|
||||
const sourceTextContent = getSelectedSourceTextContent(getState());
|
||||
if (!sourceTextContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(searchContentsForHighlight(query, editor, line, ch));
|
||||
};
|
||||
}
|
||||
|
@ -81,18 +83,17 @@ export function updateSearchResults(cx, characterIndex, line, matches) {
|
|||
export function searchContents(cx, query, editor, focusFirstResult = true) {
|
||||
return async ({ getState, dispatch }) => {
|
||||
const modifiers = getFileSearchModifiers(getState());
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
const sourceTextContent = getSelectedSourceTextContent(getState());
|
||||
|
||||
if (
|
||||
!editor ||
|
||||
!selectedSource ||
|
||||
!selectedSource.content ||
|
||||
!isFulfilled(selectedSource.content) ||
|
||||
!sourceTextContent ||
|
||||
!isFulfilled(sourceTextContent) ||
|
||||
!modifiers
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const selectedContent = selectedSource.content.value;
|
||||
const selectedContent = sourceTextContent.value;
|
||||
|
||||
const ctx = { ed: editor, cm: editor.codeMirror };
|
||||
|
||||
|
@ -103,7 +104,8 @@ export function searchContents(cx, query, editor, focusFirstResult = true) {
|
|||
|
||||
let text;
|
||||
if (selectedContent.type === "wasm") {
|
||||
text = renderWasmText(selectedSource.id, selectedContent).join("\n");
|
||||
const selectedSourceId = getSelectedSourceId(getState());
|
||||
text = renderWasmText(selectedSourceId, selectedContent).join("\n");
|
||||
} else {
|
||||
text = selectedContent.value;
|
||||
}
|
||||
|
@ -124,15 +126,9 @@ export function searchContents(cx, query, editor, focusFirstResult = true) {
|
|||
export function searchContentsForHighlight(query, editor, line, ch) {
|
||||
return async ({ getState, dispatch }) => {
|
||||
const modifiers = getFileSearchModifiers(getState());
|
||||
const selectedSource = getSelectedSourceWithContent(getState());
|
||||
const sourceTextContent = getSelectedSourceTextContent(getState());
|
||||
|
||||
if (
|
||||
!query ||
|
||||
!editor ||
|
||||
!selectedSource ||
|
||||
!selectedSource.content ||
|
||||
!modifiers
|
||||
) {
|
||||
if (!query || !editor || !sourceTextContent || !modifiers) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { PROMISE } from "../utils/middleware/promise";
|
|||
import {
|
||||
getSource,
|
||||
getSourceFromId,
|
||||
getSourceWithContent,
|
||||
getSourceTextContent,
|
||||
getSourceContent,
|
||||
getGeneratedSource,
|
||||
getSourcesEpoch,
|
||||
|
@ -137,9 +137,9 @@ export const loadSourceText = memoizeableAction("loadSourceText", {
|
|||
return null;
|
||||
}
|
||||
|
||||
const { content } = getSourceWithContent(getState(), source.id);
|
||||
if (!content || content.state === "pending") {
|
||||
return content;
|
||||
const sourceTextContent = getSourceTextContent(getState(), source.id);
|
||||
if (!sourceTextContent || sourceTextContent.state === "pending") {
|
||||
return sourceTextContent;
|
||||
}
|
||||
|
||||
// This currently swallows source-load-failure since we return fulfilled
|
||||
|
|
|
@ -34,7 +34,7 @@ import {
|
|||
getPendingSelectedLocation,
|
||||
getPendingBreakpointsForSource,
|
||||
getContext,
|
||||
isSourceLoadingOrLoaded,
|
||||
getSourceTextContent,
|
||||
} from "../../selectors";
|
||||
|
||||
import { prefs } from "../../utils/prefs";
|
||||
|
@ -290,7 +290,7 @@ export function newGeneratedSources(sourceResources) {
|
|||
// when the HTML file has started loading
|
||||
if (
|
||||
isInlineScript(newSourceActor) &&
|
||||
isSourceLoadingOrLoaded(getState(), newSourceActor.source)
|
||||
getSourceTextContent(getState(), newSourceActor.source) != null
|
||||
) {
|
||||
dispatch(setBreakableLines(cx, newSourceActor.source)).catch(error => {
|
||||
if (!(error instanceof ContextError)) {
|
||||
|
|
|
@ -33,7 +33,7 @@ import {
|
|||
canPrettyPrintSource,
|
||||
getIsCurrentThreadPaused,
|
||||
getSourceFromId,
|
||||
getSourceWithContent,
|
||||
getSourceTextContent,
|
||||
tabExists,
|
||||
} from "../../selectors";
|
||||
|
||||
|
@ -145,14 +145,14 @@ export function selectLocation(cx, location, { keepContext = true } = {}) {
|
|||
return;
|
||||
}
|
||||
|
||||
const sourceWithContent = getSourceWithContent(getState(), source.id);
|
||||
const sourceTextContent = getSourceTextContent(getState(), source.id);
|
||||
|
||||
if (
|
||||
keepContext &&
|
||||
prefs.autoPrettyPrint &&
|
||||
!getPrettySource(getState(), loadedSource.id) &&
|
||||
canPrettyPrintSource(getState(), loadedSource.id) &&
|
||||
isMinified(sourceWithContent)
|
||||
isMinified(source, sourceTextContent)
|
||||
) {
|
||||
await dispatch(togglePrettyPrint(cx, loadedSource.id));
|
||||
dispatch(closeTab(cx, loadedSource));
|
||||
|
|
|
@ -17,39 +17,43 @@ import { connect } from "../../utils/connect";
|
|||
import {
|
||||
getVisibleSelectedFrame,
|
||||
getPauseReason,
|
||||
getSourceWithContent,
|
||||
getSourceTextContent,
|
||||
getCurrentThread,
|
||||
getPausePreviewLocation,
|
||||
} from "../../selectors";
|
||||
|
||||
function isDocumentReady(source, location) {
|
||||
return location && source && source.content && hasDocument(location.sourceId);
|
||||
function isDocumentReady(location, sourceTextContent) {
|
||||
return location && sourceTextContent && hasDocument(location.sourceId);
|
||||
}
|
||||
|
||||
export class DebugLine extends PureComponent {
|
||||
debugExpression;
|
||||
|
||||
componentDidMount() {
|
||||
const { why, location, source } = this.props;
|
||||
this.setDebugLine(why, location, source);
|
||||
const { why, location, sourceTextContent } = this.props;
|
||||
this.setDebugLine(why, location, sourceTextContent);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
const { why, location, source } = this.props;
|
||||
this.clearDebugLine(why, location, source);
|
||||
const { why, location, sourceTextContent } = this.props;
|
||||
this.clearDebugLine(why, location, sourceTextContent);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { why, location, source } = this.props;
|
||||
const { why, location, sourceTextContent } = this.props;
|
||||
|
||||
startOperation();
|
||||
this.clearDebugLine(prevProps.why, prevProps.location, prevProps.source);
|
||||
this.setDebugLine(why, location, source);
|
||||
this.clearDebugLine(
|
||||
prevProps.why,
|
||||
prevProps.location,
|
||||
prevProps.sourceTextContent
|
||||
);
|
||||
this.setDebugLine(why, location, sourceTextContent);
|
||||
endOperation();
|
||||
}
|
||||
|
||||
setDebugLine(why, location, source) {
|
||||
if (!location || !isDocumentReady(source, location)) {
|
||||
setDebugLine(why, location, sourceTextContent) {
|
||||
if (!location || !isDocumentReady(location, sourceTextContent)) {
|
||||
return;
|
||||
}
|
||||
const { sourceId } = location;
|
||||
|
@ -77,8 +81,8 @@ export class DebugLine extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
clearDebugLine(why, location, source) {
|
||||
if (!location || !isDocumentReady(source, location)) {
|
||||
clearDebugLine(why, location, sourceTextContent) {
|
||||
if (!location || !isDocumentReady(location, sourceTextContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -115,7 +119,8 @@ const mapStateToProps = state => {
|
|||
return {
|
||||
frame,
|
||||
location,
|
||||
source: location && getSourceWithContent(state, location.sourceId),
|
||||
sourceTextContent:
|
||||
location && getSourceTextContent(state, location.sourceId),
|
||||
why: getPauseReason(state, getCurrentThread(state)),
|
||||
};
|
||||
};
|
||||
|
|
|
@ -7,7 +7,8 @@ import { connect } from "../../utils/connect";
|
|||
import classnames from "classnames";
|
||||
import actions from "../../actions";
|
||||
import {
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSource,
|
||||
getSelectedSourceTextContent,
|
||||
getPrettySource,
|
||||
getPaneCollapse,
|
||||
getContext,
|
||||
|
@ -59,13 +60,14 @@ class SourceFooter extends PureComponent {
|
|||
selectedSource,
|
||||
canPrettyPrint,
|
||||
togglePrettyPrint,
|
||||
sourceLoaded,
|
||||
} = this.props;
|
||||
|
||||
if (!selectedSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedSource.content && selectedSource.isPrettyPrinted) {
|
||||
if (!sourceLoaded && selectedSource.isPrettyPrinted) {
|
||||
return (
|
||||
<div className="action" key="pretty-loader">
|
||||
<AccessibleImage className="loader spin" />
|
||||
|
@ -78,7 +80,6 @@ class SourceFooter extends PureComponent {
|
|||
}
|
||||
|
||||
const tooltip = L10N.getStr("sourceTabs.prettyPrint");
|
||||
const sourceLoaded = !!selectedSource.content;
|
||||
|
||||
const type = "prettyPrint";
|
||||
return (
|
||||
|
@ -98,8 +99,7 @@ class SourceFooter extends PureComponent {
|
|||
}
|
||||
|
||||
blackBoxButton() {
|
||||
const { cx, selectedSource, toggleBlackBox } = this.props;
|
||||
const sourceLoaded = selectedSource?.content;
|
||||
const { cx, selectedSource, toggleBlackBox, sourceLoaded } = this.props;
|
||||
|
||||
if (!selectedSource) {
|
||||
return;
|
||||
|
@ -235,11 +235,13 @@ class SourceFooter extends PureComponent {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const selectedSource = getSelectedSource(state);
|
||||
const sourceTextContent = getSelectedSourceTextContent(state);
|
||||
|
||||
return {
|
||||
cx: getContext(state),
|
||||
selectedSource,
|
||||
sourceLoaded: !!sourceTextContent,
|
||||
mappedSource: getGeneratedSource(state, selectedSource),
|
||||
prettySource: getPrettySource(
|
||||
state,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { connect } from "../../utils/connect";
|
|||
import {
|
||||
getVisibleSelectedFrame,
|
||||
getSelectedLocation,
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSourceTextContent,
|
||||
getPauseCommand,
|
||||
getCurrentThread,
|
||||
} from "../../selectors";
|
||||
|
@ -26,11 +26,10 @@ function isDebugLine(selectedFrame, selectedLocation) {
|
|||
);
|
||||
}
|
||||
|
||||
function isDocumentReady(selectedSource, selectedLocation) {
|
||||
function isDocumentReady(selectedLocation, selectedSourceTextContent) {
|
||||
return (
|
||||
selectedLocation &&
|
||||
selectedSource &&
|
||||
selectedSource.content &&
|
||||
selectedSourceTextContent &&
|
||||
hasDocument(selectedLocation.sourceId)
|
||||
);
|
||||
}
|
||||
|
@ -40,8 +39,11 @@ export class HighlightLine extends Component {
|
|||
previousEditorLine = null;
|
||||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const { selectedLocation, selectedSource } = nextProps;
|
||||
return this.shouldSetHighlightLine(selectedLocation, selectedSource);
|
||||
const { selectedLocation, selectedSourceTextContent } = nextProps;
|
||||
return this.shouldSetHighlightLine(
|
||||
selectedLocation,
|
||||
selectedSourceTextContent
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
|
@ -52,11 +54,11 @@ export class HighlightLine extends Component {
|
|||
this.completeHighlightLine(null);
|
||||
}
|
||||
|
||||
shouldSetHighlightLine(selectedLocation, selectedSource) {
|
||||
shouldSetHighlightLine(selectedLocation, selectedSourceTextContent) {
|
||||
const { sourceId, line } = selectedLocation;
|
||||
const editorLine = toEditorLine(sourceId, line);
|
||||
|
||||
if (!isDocumentReady(selectedSource, selectedLocation)) {
|
||||
if (!isDocumentReady(selectedLocation, selectedSourceTextContent)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -72,7 +74,7 @@ export class HighlightLine extends Component {
|
|||
pauseCommand,
|
||||
selectedLocation,
|
||||
selectedFrame,
|
||||
selectedSource,
|
||||
selectedSourceTextContent,
|
||||
} = this.props;
|
||||
if (pauseCommand) {
|
||||
this.isStepping = true;
|
||||
|
@ -82,16 +84,22 @@ export class HighlightLine extends Component {
|
|||
if (prevProps) {
|
||||
this.clearHighlightLine(
|
||||
prevProps.selectedLocation,
|
||||
prevProps.selectedSource
|
||||
prevProps.selectedSourceTextContent
|
||||
);
|
||||
}
|
||||
this.setHighlightLine(selectedLocation, selectedFrame, selectedSource);
|
||||
this.setHighlightLine(
|
||||
selectedLocation,
|
||||
selectedFrame,
|
||||
selectedSourceTextContent
|
||||
);
|
||||
endOperation();
|
||||
}
|
||||
|
||||
setHighlightLine(selectedLocation, selectedFrame, selectedSource) {
|
||||
setHighlightLine(selectedLocation, selectedFrame, selectedSourceTextContent) {
|
||||
const { sourceId, line } = selectedLocation;
|
||||
if (!this.shouldSetHighlightLine(selectedLocation, selectedSource)) {
|
||||
if (
|
||||
!this.shouldSetHighlightLine(selectedLocation, selectedSourceTextContent)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -129,8 +137,8 @@ export class HighlightLine extends Component {
|
|||
);
|
||||
}
|
||||
|
||||
clearHighlightLine(selectedLocation, selectedSource) {
|
||||
if (!isDocumentReady(selectedSource, selectedLocation)) {
|
||||
clearHighlightLine(selectedLocation, selectedSourceTextContent) {
|
||||
if (!isDocumentReady(selectedLocation, selectedSourceTextContent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -155,6 +163,6 @@ export default connect(state => {
|
|||
pauseCommand: getPauseCommand(state, getCurrentThread(state)),
|
||||
selectedFrame: getVisibleSelectedFrame(state),
|
||||
selectedLocation,
|
||||
selectedSource: getSelectedSourceWithContent(state),
|
||||
selectedSourceTextContent: getSelectedSourceTextContent(state),
|
||||
};
|
||||
})(HighlightLine);
|
||||
|
|
|
@ -28,7 +28,8 @@ import {
|
|||
import {
|
||||
getActiveSearch,
|
||||
getSelectedLocation,
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSource,
|
||||
getSelectedSourceTextContent,
|
||||
getConditionalPanelLocation,
|
||||
getSymbols,
|
||||
getIsCurrentThreadPaused,
|
||||
|
@ -382,6 +383,7 @@ class Editor extends PureComponent {
|
|||
const {
|
||||
cx,
|
||||
selectedSource,
|
||||
selectedSourceTextContent,
|
||||
breakpointActions,
|
||||
editorActions,
|
||||
isPaused,
|
||||
|
@ -412,7 +414,7 @@ class Editor extends PureComponent {
|
|||
if (target.classList.contains("CodeMirror-linenumber")) {
|
||||
const lineText = getLineText(
|
||||
sourceId,
|
||||
selectedSource.content,
|
||||
selectedSourceTextContent,
|
||||
line
|
||||
).trim();
|
||||
|
||||
|
@ -516,20 +518,24 @@ class Editor extends PureComponent {
|
|||
}
|
||||
|
||||
shouldScrollToLocation(nextProps, editor) {
|
||||
const { selectedLocation, selectedSource } = this.props;
|
||||
const {
|
||||
selectedLocation,
|
||||
selectedSource,
|
||||
selectedSourceTextContent,
|
||||
} = this.props;
|
||||
if (
|
||||
!editor ||
|
||||
!nextProps.selectedSource ||
|
||||
!nextProps.selectedLocation ||
|
||||
!nextProps.selectedLocation.line ||
|
||||
!nextProps.selectedSource.content
|
||||
!nextProps.selectedSourceTextContent
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const isFirstLoad =
|
||||
(!selectedSource || !selectedSource.content) &&
|
||||
nextProps.selectedSource.content;
|
||||
(!selectedSource || !selectedSourceTextContent) &&
|
||||
nextProps.selectedSourceTextContent;
|
||||
const locationChanged = selectedLocation !== nextProps.selectedLocation;
|
||||
const symbolsChanged = nextProps.symbols != this.props.symbols;
|
||||
|
||||
|
@ -566,7 +572,7 @@ class Editor extends PureComponent {
|
|||
}
|
||||
|
||||
setText(props, editor) {
|
||||
const { selectedSource, symbols } = props;
|
||||
const { selectedSource, selectedSourceTextContent, symbols } = props;
|
||||
|
||||
if (!editor) {
|
||||
return;
|
||||
|
@ -577,12 +583,12 @@ class Editor extends PureComponent {
|
|||
return this.clearEditor();
|
||||
}
|
||||
|
||||
if (!selectedSource.content) {
|
||||
if (!selectedSourceTextContent?.value) {
|
||||
return showLoading(editor);
|
||||
}
|
||||
|
||||
if (selectedSource.content.state === "rejected") {
|
||||
let { value } = selectedSource.content;
|
||||
if (selectedSourceTextContent.state === "rejected") {
|
||||
let { value } = selectedSourceTextContent;
|
||||
if (typeof value !== "string") {
|
||||
value = "Unexpected source error";
|
||||
}
|
||||
|
@ -593,7 +599,7 @@ class Editor extends PureComponent {
|
|||
return showSourceText(
|
||||
editor,
|
||||
selectedSource,
|
||||
selectedSource.content.value,
|
||||
selectedSourceTextContent.value,
|
||||
symbols
|
||||
);
|
||||
}
|
||||
|
@ -712,12 +718,13 @@ Editor.contextTypes = {
|
|||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const selectedSource = getSelectedSource(state);
|
||||
|
||||
return {
|
||||
cx: getThreadContext(state),
|
||||
selectedLocation: getSelectedLocation(state),
|
||||
selectedSource,
|
||||
selectedSourceTextContent: getSelectedSourceTextContent(state),
|
||||
searchOn: getActiveSearch(state) === "file",
|
||||
conditionalPanelLocation: getConditionalPanelLocation(state),
|
||||
symbols: getSymbols(state, selectedSource),
|
||||
|
|
|
@ -8,9 +8,7 @@ import { shallow } from "enzyme";
|
|||
|
||||
import DebugLine from "../DebugLine";
|
||||
|
||||
import * as asyncValue from "../../../utils/async-value";
|
||||
import { createSourceObject } from "../../../utils/test-head";
|
||||
import { setDocument, toEditorLine } from "../../../utils/editor";
|
||||
import { setDocument } from "../../../utils/editor";
|
||||
|
||||
function createMockDocument(clear) {
|
||||
const doc = {
|
||||
|
@ -30,10 +28,7 @@ function generateDefaults(editor, overrides) {
|
|||
why: { type: "breakpoint" },
|
||||
},
|
||||
frame: null,
|
||||
source: {
|
||||
...createSourceObject("foo"),
|
||||
content: null,
|
||||
},
|
||||
sourceTextContent: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
@ -52,7 +47,7 @@ function render(overrides = {}) {
|
|||
const props = generateDefaults(editor, overrides);
|
||||
|
||||
const doc = createMockDocument(clear);
|
||||
setDocument(props.source.id, doc);
|
||||
setDocument("foo", doc);
|
||||
|
||||
const component = shallow(<DebugLine.WrappedComponent {...props} />, {
|
||||
lifecycleExperimental: true,
|
||||
|
@ -62,77 +57,6 @@ function render(overrides = {}) {
|
|||
|
||||
describe("DebugLine Component", () => {
|
||||
describe("pausing at the first location", () => {
|
||||
it("should show a new debug line", async () => {
|
||||
const { component, props, doc } = render({
|
||||
source: {
|
||||
...createSourceObject("foo"),
|
||||
content: asyncValue.fulfilled({
|
||||
type: "text",
|
||||
value: "",
|
||||
contentType: undefined,
|
||||
}),
|
||||
},
|
||||
});
|
||||
const line = 2;
|
||||
const location = createLocation(line);
|
||||
|
||||
component.setProps({ ...props, location });
|
||||
|
||||
expect(doc.removeLineClass.mock.calls).toEqual([]);
|
||||
expect(doc.addLineClass.mock.calls).toEqual([
|
||||
[toEditorLine("foo", line), "wrapClass", "new-debug-line"],
|
||||
]);
|
||||
});
|
||||
|
||||
describe("pausing at a new location", () => {
|
||||
it("should replace the first debug line", async () => {
|
||||
const { props, component, clear, doc } = render({
|
||||
source: {
|
||||
...createSourceObject("foo"),
|
||||
content: asyncValue.fulfilled({
|
||||
type: "text",
|
||||
value: "",
|
||||
contentType: undefined,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
component.instance().debugExpression = { clear: jest.fn() };
|
||||
const firstLine = 2;
|
||||
const secondLine = 2;
|
||||
|
||||
component.setProps({ ...props, location: createLocation(firstLine) });
|
||||
component.setProps({
|
||||
...props,
|
||||
frame: createLocation(secondLine),
|
||||
});
|
||||
|
||||
expect(doc.removeLineClass.mock.calls).toEqual([
|
||||
[toEditorLine("foo", firstLine), "wrapClass", "new-debug-line"],
|
||||
]);
|
||||
|
||||
expect(doc.addLineClass.mock.calls).toEqual([
|
||||
[toEditorLine("foo", firstLine), "wrapClass", "new-debug-line"],
|
||||
[toEditorLine("foo", secondLine), "wrapClass", "new-debug-line"],
|
||||
]);
|
||||
|
||||
expect(doc.markText.mock.calls).toEqual([
|
||||
[
|
||||
{ ch: 2, line: toEditorLine("foo", firstLine) },
|
||||
{ ch: null, line: toEditorLine("foo", firstLine) },
|
||||
{ className: "debug-expression to-line-end" },
|
||||
],
|
||||
[
|
||||
{ ch: 2, line: toEditorLine("foo", secondLine) },
|
||||
{ ch: null, line: toEditorLine("foo", secondLine) },
|
||||
{ className: "debug-expression to-line-end" },
|
||||
],
|
||||
]);
|
||||
|
||||
expect(clear.mock.calls).toEqual([[]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("when there is no selected frame", () => {
|
||||
it("should not set the debug line", () => {
|
||||
const { component, props, doc } = render({ frame: null });
|
||||
|
|
|
@ -14,7 +14,8 @@ import { findFunctionText } from "../../utils/function";
|
|||
|
||||
import actions from "../../actions";
|
||||
import {
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSource,
|
||||
getSelectedSourceTextContent,
|
||||
getSymbols,
|
||||
getCursorPosition,
|
||||
getContext,
|
||||
|
@ -319,17 +320,23 @@ export class Outline extends Component {
|
|||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const selectedSource = getSelectedSourceWithContent(state);
|
||||
const selectedSource = getSelectedSource(state);
|
||||
const symbols = selectedSource ? getSymbols(state, selectedSource) : null;
|
||||
|
||||
return {
|
||||
cx: getContext(state),
|
||||
symbols,
|
||||
selectedSource: selectedSource,
|
||||
selectedSource,
|
||||
cursorPosition: getCursorPosition(state),
|
||||
getFunctionText: line => {
|
||||
if (selectedSource) {
|
||||
return findFunctionText(line, selectedSource, symbols);
|
||||
const selectedSourceTextContent = getSelectedSourceTextContent(state);
|
||||
return findFunctionText(
|
||||
line,
|
||||
selectedSource,
|
||||
selectedSourceTextContent,
|
||||
symbols
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
|
|
@ -204,10 +204,7 @@ describe("Frames", () => {
|
|||
source2: [],
|
||||
};
|
||||
|
||||
const sources = insertResources(createInitial(), [
|
||||
{ ...source1, content: null },
|
||||
{ ...source2, content: null },
|
||||
]);
|
||||
const sources = insertResources(createInitial(), [source1, source2]);
|
||||
|
||||
const processedFrames = formatCallStackFrames(
|
||||
frames,
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
import expressions, { initialExpressionState } from "./expressions";
|
||||
import sourceActors from "./source-actors";
|
||||
import sources, { initialSourcesState } from "./sources";
|
||||
import sourcesContent, { initialSourcesContentState } from "./sources-content";
|
||||
import tabs, { initialTabState } from "./tabs";
|
||||
import breakpoints, { initialBreakpointsState } from "./breakpoints";
|
||||
import pendingBreakpoints from "./pending-breakpoints";
|
||||
|
@ -34,9 +35,15 @@ import { objectInspector } from "devtools/client/shared/components/reps/index";
|
|||
|
||||
import { createInitial } from "../utils/resource";
|
||||
|
||||
/**
|
||||
* Note that this is only used by jest tests.
|
||||
*
|
||||
* Production is using loadInitialState() in main.js
|
||||
*/
|
||||
export function initialState() {
|
||||
return {
|
||||
sources: initialSourcesState(),
|
||||
sourcesContent: initialSourcesContentState(),
|
||||
expressions: initialExpressionState(),
|
||||
sourceActors: createInitial(),
|
||||
tabs: initialTabState(),
|
||||
|
@ -61,6 +68,7 @@ export function initialState() {
|
|||
export default {
|
||||
expressions,
|
||||
sourceActors,
|
||||
sourcesContent,
|
||||
sources,
|
||||
tabs,
|
||||
breakpoints,
|
||||
|
|
|
@ -22,6 +22,7 @@ CompiledModules(
|
|||
"source-actors.js",
|
||||
"source-tree.js",
|
||||
"sources.js",
|
||||
"sources-content.js",
|
||||
"tabs.js",
|
||||
"threads.js",
|
||||
"ui.js",
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
/**
|
||||
* Sources content reducer.
|
||||
*
|
||||
* This store the textual content for each source.
|
||||
*/
|
||||
|
||||
import { pending, fulfilled, rejected } from "../utils/async-value";
|
||||
|
||||
export function initialSourcesContentState() {
|
||||
return {
|
||||
/**
|
||||
* Text content of all the sources.
|
||||
* This is large data, so this is only fetched on-demand for a subset of sources.
|
||||
* This state attribute is mutable in order to avoid cloning this possibly large map
|
||||
* on each new source. But selectors are never based on the map. Instead they only
|
||||
* query elements of the map.
|
||||
*
|
||||
* Map(source id => AsyncValue<String>)
|
||||
*/
|
||||
mutableTextContentMap: new Map(),
|
||||
|
||||
/**
|
||||
* Incremental number that is bumped each time we navigate to a new page.
|
||||
*
|
||||
* This is used to better handle async race condition where we mix previous page data
|
||||
* with the new page. As sources are keyed by URL we may easily conflate the two page loads data.
|
||||
*/
|
||||
epoch: 1,
|
||||
};
|
||||
}
|
||||
|
||||
function update(state = initialSourcesContentState(), action) {
|
||||
switch (action.type) {
|
||||
case "LOAD_SOURCE_TEXT":
|
||||
return updateSourceTextContent(state, action);
|
||||
|
||||
case "NAVIGATE":
|
||||
return {
|
||||
...initialSourcesContentState(),
|
||||
epoch: state.epoch + 1,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a source's loaded text content.
|
||||
*/
|
||||
function updateSourceTextContent(state, action) {
|
||||
// If there was a navigation between the time the action was started and
|
||||
// completed, we don't want to update the store.
|
||||
if (action.epoch !== state.epoch) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let content;
|
||||
if (action.status === "start") {
|
||||
content = pending();
|
||||
} else if (action.status === "error") {
|
||||
content = rejected(action.error);
|
||||
} else if (typeof action.value.text === "string") {
|
||||
content = fulfilled({
|
||||
type: "text",
|
||||
value: action.value.text,
|
||||
contentType: action.value.contentType,
|
||||
});
|
||||
} else {
|
||||
content = fulfilled({
|
||||
type: "wasm",
|
||||
value: action.value.text,
|
||||
});
|
||||
}
|
||||
|
||||
state.mutableTextContentMap.set(action.sourceId, content);
|
||||
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
|
||||
export default update;
|
|
@ -16,7 +16,6 @@ import {
|
|||
getResource,
|
||||
getResourceIds,
|
||||
} from "../utils/resource";
|
||||
import { pending, fulfilled, rejected } from "../utils/async-value";
|
||||
import { prefs } from "../utils/prefs";
|
||||
|
||||
export function initialSourcesState(state) {
|
||||
|
@ -25,7 +24,6 @@ export function initialSourcesState(state) {
|
|||
* All currently available sources.
|
||||
*
|
||||
* See create.js: `createSourceObject` method for the description of stored objects.
|
||||
* This reducers will add an extra `content` attribute which is the source text for each source.
|
||||
*/
|
||||
sources: createInitial(),
|
||||
|
||||
|
@ -68,14 +66,6 @@ export function initialSourcesState(state) {
|
|||
breakpointPositions: {},
|
||||
breakableLines: {},
|
||||
|
||||
/**
|
||||
* Incremental number that is bumped each time we navigate to a new page.
|
||||
*
|
||||
* This is used to better handle async race condition where we mix previous page data
|
||||
* with the new page. As sources are keyed by URL we may easily conflate the two page loads data.
|
||||
*/
|
||||
epoch: 1,
|
||||
|
||||
/**
|
||||
* The actual currently selected location.
|
||||
* Only set if the related source is already registered in the sources reducer.
|
||||
|
@ -178,9 +168,6 @@ function update(state = initialSourcesState(), action) {
|
|||
prefs.pendingSelectedLocation = location;
|
||||
return { ...state, pendingSelectedLocation: location };
|
||||
|
||||
case "LOAD_SOURCE_TEXT":
|
||||
return updateLoadedState(state, action);
|
||||
|
||||
case "BLACKBOX":
|
||||
if (action.status === "done") {
|
||||
const { blackboxSources } = action.value;
|
||||
|
@ -218,11 +205,9 @@ function update(state = initialSourcesState(), action) {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
case "NAVIGATE":
|
||||
return {
|
||||
...initialSourcesState(state),
|
||||
epoch: state.epoch + 1,
|
||||
};
|
||||
return initialSourcesState(state);
|
||||
}
|
||||
|
||||
return state;
|
||||
|
@ -242,13 +227,7 @@ function addSources(state, sources) {
|
|||
plainUrls: { ...state.plainUrls },
|
||||
};
|
||||
|
||||
state.sources = insertResources(
|
||||
state.sources,
|
||||
sources.map(source => ({
|
||||
...source,
|
||||
content: null,
|
||||
}))
|
||||
);
|
||||
state.sources = insertResources(state.sources, sources);
|
||||
|
||||
for (const source of sources) {
|
||||
// 1. Update the source url map
|
||||
|
@ -388,47 +367,6 @@ function updateRootRelativeValues(
|
|||
return state;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a source's loaded text content.
|
||||
*/
|
||||
function updateLoadedState(state, action) {
|
||||
const { sourceId } = action;
|
||||
|
||||
// If there was a navigation between the time the action was started and
|
||||
// completed, we don't want to update the store.
|
||||
if (action.epoch !== state.epoch || !hasResource(state.sources, sourceId)) {
|
||||
return state;
|
||||
}
|
||||
|
||||
let content;
|
||||
if (action.status === "start") {
|
||||
content = pending();
|
||||
} else if (action.status === "error") {
|
||||
content = rejected(action.error);
|
||||
} else if (typeof action.value.text === "string") {
|
||||
content = fulfilled({
|
||||
type: "text",
|
||||
value: action.value.text,
|
||||
contentType: action.value.contentType,
|
||||
});
|
||||
} else {
|
||||
content = fulfilled({
|
||||
type: "wasm",
|
||||
value: action.value.text,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
sources: updateResources(state.sources, [
|
||||
{
|
||||
id: sourceId,
|
||||
content,
|
||||
},
|
||||
]),
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the "isBlackBoxed" property on the source objects
|
||||
*/
|
||||
|
@ -463,7 +401,7 @@ function updateBlackboxRangesForSourceUrl(
|
|||
) {
|
||||
if (shouldBlackBox) {
|
||||
// If newRanges is an empty array, it would mean we are blackboxing the whole
|
||||
// source. To do that lets reset the contentto an empty array.
|
||||
// source. To do that lets reset the content to an empty array.
|
||||
if (!newRanges.length) {
|
||||
currentRanges[url] = [];
|
||||
} else {
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import {
|
||||
getSelectedSource,
|
||||
getBreakpointPositionsForLine,
|
||||
} from "../selectors/sources";
|
||||
import { getBreakpointsList } from "../selectors/breakpoints";
|
||||
import { getSelectedSource, getBreakpointPositionsForLine } from "./sources";
|
||||
import { getBreakpointsList } from "./breakpoints";
|
||||
import { isGenerated } from "../utils/source";
|
||||
|
||||
function getColumn(column, selectedSource) {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { createSelector } from "reselect";
|
||||
import { getSelectedSource, getSourceFromId } from "../selectors/sources";
|
||||
import { getBreakpointsList } from "../selectors/breakpoints";
|
||||
import { getSelectedSource, getSourceFromId } from "./sources";
|
||||
import { getBreakpointsList } from "./breakpoints";
|
||||
import { getFilename } from "../utils/source";
|
||||
import { getSelectedLocation } from "../utils/selected-location";
|
||||
import { sortSelectedBreakpoints } from "../utils/breakpoint";
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
getSelectedSource,
|
||||
getSourceInSources,
|
||||
getBlackBoxRanges,
|
||||
} from "../selectors/sources";
|
||||
} from "./sources";
|
||||
import { getCurrentThreadFrames } from "./pause";
|
||||
import { annotateFrames } from "../utils/pause/frames";
|
||||
import { isFrameBlackBoxed } from "../utils/source";
|
||||
|
|
|
@ -25,6 +25,7 @@ export * from "./project-text-search";
|
|||
export * from "./quick-open";
|
||||
export * from "./source-actors";
|
||||
export * from "./source-tree";
|
||||
export * from "./sources-content";
|
||||
export * from "./sources";
|
||||
export * from "./tabs";
|
||||
export * from "./threads";
|
||||
|
|
|
@ -25,6 +25,7 @@ CompiledModules(
|
|||
"quick-open.js",
|
||||
"source-actors.js",
|
||||
"source-tree.js",
|
||||
"sources-content.js",
|
||||
"sources.js",
|
||||
"tabs.js",
|
||||
"threads.js",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { getThreadPauseState } from "../reducers/pause";
|
||||
import { getSelectedSourceId, getSelectedLocation } from "../selectors/sources";
|
||||
import { getSelectedSourceId, getSelectedLocation } from "./sources";
|
||||
|
||||
import { isGeneratedId } from "devtools-source-map";
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||
|
||||
import { asSettled } from "../utils/async-value";
|
||||
|
||||
import { getSelectedSource } from "../selectors/sources";
|
||||
|
||||
export function getSourceTextContent(state, id) {
|
||||
return state.sourcesContent.mutableTextContentMap.get(id);
|
||||
}
|
||||
|
||||
export function getSourceContent(state, id) {
|
||||
const content = getSourceTextContent(state, id);
|
||||
return asSettled(content);
|
||||
}
|
||||
|
||||
export function getSelectedSourceTextContent(state) {
|
||||
const source = getSelectedSource(state);
|
||||
if (!source) return null;
|
||||
return getSourceTextContent(state, source.id);
|
||||
}
|
||||
|
||||
export function isSourceLoadingOrLoaded(state, sourceId) {
|
||||
const content = getSourceTextContent(state, sourceId);
|
||||
return content != null;
|
||||
}
|
||||
|
||||
export function getSourcesEpoch(state) {
|
||||
return state.sourcesContent.epoch;
|
||||
}
|
|
@ -24,7 +24,7 @@ import {
|
|||
import { stripQuery } from "../utils/url";
|
||||
|
||||
import { findPosition } from "../utils/breakpoint/breakpointPositions";
|
||||
import { asSettled, isFulfilled } from "../utils/async-value";
|
||||
import { isFulfilled } from "../utils/async-value";
|
||||
|
||||
import { originalToGeneratedId } from "devtools-source-map";
|
||||
import { prefs } from "../utils/prefs";
|
||||
|
@ -34,20 +34,12 @@ import {
|
|||
getSourceActor,
|
||||
getSourceActors,
|
||||
getBreakableLinesForSourceActors,
|
||||
} from "../selectors/source-actors";
|
||||
import { getAllThreads } from "../selectors/threads";
|
||||
} from "./source-actors";
|
||||
import { getSourceTextContent } from "./sources-content";
|
||||
import { getAllThreads } from "./threads";
|
||||
|
||||
// This is used by tabs selectors
|
||||
export const resourceAsSourceBase = memoizeResourceShallow(
|
||||
({ content, ...source }) => source
|
||||
);
|
||||
|
||||
const resourceAsSourceWithContent = memoizeResourceShallow(
|
||||
({ content, ...source }) => ({
|
||||
...source,
|
||||
content: asSettled(content),
|
||||
})
|
||||
);
|
||||
export const resourceAsSourceBase = memoizeResourceShallow(source => source);
|
||||
|
||||
export function getSourceInSources(sources, id) {
|
||||
return hasResource(sources, id)
|
||||
|
@ -79,9 +71,7 @@ function getSourcesByURLInSources(sources, urls, url) {
|
|||
if (!url || !urls[url]) {
|
||||
return [];
|
||||
}
|
||||
return urls[url].map(id =>
|
||||
getMappedResource(sources, id, resourceAsSourceBase)
|
||||
);
|
||||
return urls[url].map(id => getSourceInSources(sources, id));
|
||||
}
|
||||
|
||||
function getSourcesByURL(state, url) {
|
||||
|
@ -179,10 +169,6 @@ export function getSources(state) {
|
|||
return state.sources.sources;
|
||||
}
|
||||
|
||||
export function getSourcesEpoch(state) {
|
||||
return state.sources.epoch;
|
||||
}
|
||||
|
||||
function getUrls(state) {
|
||||
return state.sources.urls;
|
||||
}
|
||||
|
@ -230,30 +216,6 @@ export const getSelectedSource = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
export const getSelectedSourceWithContent = createSelector(
|
||||
getSelectedLocation,
|
||||
getSources,
|
||||
(selectedLocation, sources) => {
|
||||
const source =
|
||||
selectedLocation &&
|
||||
getSourceInSources(sources, selectedLocation.sourceId);
|
||||
return source
|
||||
? getMappedResource(sources, source.id, resourceAsSourceWithContent)
|
||||
: null;
|
||||
}
|
||||
);
|
||||
export function getSourceWithContent(state, id) {
|
||||
return getMappedResource(
|
||||
state.sources.sources,
|
||||
id,
|
||||
resourceAsSourceWithContent
|
||||
);
|
||||
}
|
||||
export function getSourceContent(state, id) {
|
||||
const { content } = getResource(state.sources.sources, id);
|
||||
return asSettled(content);
|
||||
}
|
||||
|
||||
// This is used by tests and pause reducers
|
||||
export function getSelectedSourceId(state) {
|
||||
const source = getSelectedSource(state);
|
||||
|
@ -300,7 +262,7 @@ const queryAllDisplayedSources = makeShallowQuery({
|
|||
});
|
||||
|
||||
function getAllDisplayedSources(state) {
|
||||
return queryAllDisplayedSources(state.sources.sources, {
|
||||
return queryAllDisplayedSources(getSources(state), {
|
||||
sourcesWithUrls: state.sources.sourcesWithUrls,
|
||||
projectDirectoryRoot: state.sources.projectDirectoryRoot,
|
||||
chromeAndExtensionsEnabled: state.sources.chromeAndExtensionsEnabled,
|
||||
|
@ -324,7 +286,7 @@ const getDisplayedSourceIDs = createSelector(
|
|||
);
|
||||
|
||||
export const getDisplayedSources = createSelector(
|
||||
state => state.sources.sources,
|
||||
getSources,
|
||||
getDisplayedSourceIDs,
|
||||
(sources, idsByThread) => {
|
||||
const result = {};
|
||||
|
@ -382,7 +344,7 @@ export function isSourceWithMap(state, id) {
|
|||
}
|
||||
|
||||
export function canPrettyPrintSource(state, id) {
|
||||
const source = getSourceWithContent(state, id);
|
||||
const source = getSource(state, id);
|
||||
if (
|
||||
!source ||
|
||||
isPretty(source) ||
|
||||
|
@ -392,8 +354,8 @@ export function canPrettyPrintSource(state, id) {
|
|||
return false;
|
||||
}
|
||||
|
||||
const sourceContent =
|
||||
source.content && isFulfilled(source.content) ? source.content.value : null;
|
||||
const content = getSourceTextContent(state, id);
|
||||
const sourceContent = content && isFulfilled(content) ? content.value : null;
|
||||
|
||||
if (!sourceContent || !isJavaScript(source, sourceContent)) {
|
||||
return false;
|
||||
|
@ -457,11 +419,6 @@ export const getSelectedBreakableLines = createSelector(
|
|||
breakableLines => new Set(breakableLines || [])
|
||||
);
|
||||
|
||||
export function isSourceLoadingOrLoaded(state, sourceId) {
|
||||
const { content } = getResource(state.sources.sources, sourceId);
|
||||
return content !== null;
|
||||
}
|
||||
|
||||
export function getBlackBoxRanges(state) {
|
||||
return state.sources.blackboxedRanges;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
getSpecificSourceByURL,
|
||||
getSources,
|
||||
resourceAsSourceBase,
|
||||
} from "../selectors/sources";
|
||||
} from "./sources";
|
||||
import { isOriginalId } from "devtools-source-map";
|
||||
import { isSimilarTab } from "../utils/tabs";
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ describe("visible column breakpoints", () => {
|
|||
pausePoints,
|
||||
breakpoints,
|
||||
viewport,
|
||||
source
|
||||
source,
|
||||
source.content
|
||||
);
|
||||
expect(columnBps).toMatchSnapshot();
|
||||
});
|
||||
|
@ -74,7 +75,8 @@ describe("visible column breakpoints", () => {
|
|||
pausePoints,
|
||||
breakpoints,
|
||||
viewport,
|
||||
source
|
||||
source,
|
||||
source.content
|
||||
);
|
||||
expect(columnBps).toMatchSnapshot();
|
||||
});
|
||||
|
@ -91,7 +93,8 @@ describe("visible column breakpoints", () => {
|
|||
pausePoints,
|
||||
breakpoints,
|
||||
viewport,
|
||||
source
|
||||
source,
|
||||
source.content
|
||||
);
|
||||
expect(columnBps).toMatchSnapshot();
|
||||
});
|
||||
|
@ -108,7 +111,8 @@ describe("visible column breakpoints", () => {
|
|||
pausePoints,
|
||||
breakpoints,
|
||||
viewport,
|
||||
source
|
||||
source,
|
||||
source.content
|
||||
);
|
||||
expect(columnBps).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
import { createSelector } from "reselect";
|
||||
|
||||
import { getBreakpointsList } from "./breakpoints";
|
||||
import { getSelectedSource } from "../selectors/sources";
|
||||
import { getSelectedSource } from "./sources";
|
||||
|
||||
import { sortSelectedBreakpoints } from "../utils/breakpoint";
|
||||
import { getSelectedLocation } from "../utils/selected-location";
|
||||
|
|
|
@ -8,10 +8,10 @@ import {
|
|||
getViewport,
|
||||
getSource,
|
||||
getSelectedSource,
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSourceTextContent,
|
||||
getBreakpointPositions,
|
||||
getBreakpointPositionsForSource,
|
||||
} from "../selectors";
|
||||
} from "./index";
|
||||
import { getVisibleBreakpoints } from "./visibleBreakpoints";
|
||||
import { getSelectedLocation } from "../utils/selected-location";
|
||||
import { sortSelectedLocations } from "../utils/location";
|
||||
|
@ -126,7 +126,8 @@ export function getColumnBreakpoints(
|
|||
positions,
|
||||
breakpoints,
|
||||
viewport,
|
||||
selectedSource
|
||||
selectedSource,
|
||||
selectedSourceTextContent
|
||||
) {
|
||||
if (!positions || !selectedSource) {
|
||||
return [];
|
||||
|
@ -140,7 +141,11 @@ export function getColumnBreakpoints(
|
|||
const breakpointMap = groupBreakpoints(breakpoints, selectedSource);
|
||||
positions = filterByLineCount(positions, selectedSource);
|
||||
positions = filterVisible(positions, selectedSource, viewport);
|
||||
positions = filterInLine(positions, selectedSource, selectedSource.content);
|
||||
positions = filterInLine(
|
||||
positions,
|
||||
selectedSource,
|
||||
selectedSourceTextContent
|
||||
);
|
||||
positions = filterByBreakpoints(positions, selectedSource, breakpointMap);
|
||||
|
||||
return formatPositions(positions, selectedSource, breakpointMap);
|
||||
|
@ -167,7 +172,8 @@ export const visibleColumnBreakpoints = createSelector(
|
|||
getVisibleBreakpointPositions,
|
||||
getVisibleBreakpoints,
|
||||
getViewport,
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSource,
|
||||
getSelectedSourceTextContent,
|
||||
getColumnBreakpoints
|
||||
);
|
||||
|
||||
|
|
|
@ -122,7 +122,7 @@ function setMode(editor, source, content, symbols) {
|
|||
// Disable modes for minified files with 1+ million characters Bug 1569829
|
||||
if (
|
||||
content.type === "text" &&
|
||||
isMinified(source) &&
|
||||
isMinified(source, content) &&
|
||||
content.value.length > 1000000
|
||||
) {
|
||||
return;
|
||||
|
|
|
@ -6,7 +6,7 @@ import { isFulfilled } from "./async-value";
|
|||
import { findClosestFunction } from "./ast";
|
||||
import { correctIndentation } from "./indentation";
|
||||
|
||||
export function findFunctionText(line, source, symbols) {
|
||||
export function findFunctionText(line, source, sourceTextContent, symbols) {
|
||||
const func = findClosestFunction(symbols, {
|
||||
sourceId: source.id,
|
||||
line,
|
||||
|
@ -16,9 +16,9 @@ export function findFunctionText(line, source, symbols) {
|
|||
if (
|
||||
source.isWasm ||
|
||||
!func ||
|
||||
!source.content ||
|
||||
!isFulfilled(source.content) ||
|
||||
source.content.value.type !== "text"
|
||||
!sourceTextContent ||
|
||||
!isFulfilled(sourceTextContent) ||
|
||||
sourceTextContent.value.type !== "text"
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ export function findFunctionText(line, source, symbols) {
|
|||
const {
|
||||
location: { start, end },
|
||||
} = func;
|
||||
const lines = source.content.value.value.split("\n");
|
||||
const lines = sourceTextContent.value.value.split("\n");
|
||||
const firstLine = lines[start.line - 1].slice(start.column);
|
||||
const lastLine = lines[end.line - 1].slice(0, end.column);
|
||||
const middle = lines.slice(start.line, end.line - 1);
|
||||
|
|
|
@ -10,20 +10,20 @@ const INDENT_COUNT_THRESHOLD = 5;
|
|||
const CHARACTER_LIMIT = 250;
|
||||
const _minifiedCache = new Map();
|
||||
|
||||
export function isMinified(source) {
|
||||
export function isMinified(source, sourceTextContent) {
|
||||
if (_minifiedCache.has(source.id)) {
|
||||
return _minifiedCache.get(source.id);
|
||||
}
|
||||
|
||||
if (
|
||||
!source.content ||
|
||||
!isFulfilled(source.content) ||
|
||||
source.content.value.type !== "text"
|
||||
!sourceTextContent ||
|
||||
!isFulfilled(sourceTextContent) ||
|
||||
sourceTextContent.value.type !== "text"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let text = source.content.value.value;
|
||||
let text = sourceTextContent.value.value;
|
||||
|
||||
let lineEndIndex = 0;
|
||||
let lineStartIndex = 0;
|
||||
|
|
|
@ -12,7 +12,7 @@ describe("function", () => {
|
|||
it("finds function", () => {
|
||||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
const text = findFunctionText(14, source, symbols);
|
||||
const text = findFunctionText(14, source, source.content, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -20,7 +20,7 @@ describe("function", () => {
|
|||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
const text = findFunctionText(13, source, symbols);
|
||||
const text = findFunctionText(13, source, source.content, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe("function", () => {
|
|||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
const text = findFunctionText(15, source, symbols);
|
||||
const text = findFunctionText(15, source, source.content, symbols);
|
||||
|
||||
// TODO: we should try and match the closing bracket.
|
||||
expect(text).toEqual(null);
|
||||
|
@ -38,7 +38,7 @@ describe("function", () => {
|
|||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
const text = findFunctionText(29, source, symbols);
|
||||
const text = findFunctionText(29, source, source.content, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -46,7 +46,7 @@ describe("function", () => {
|
|||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
const text = findFunctionText(33, source, symbols);
|
||||
const text = findFunctionText(33, source, source.content, symbols);
|
||||
expect(text).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -54,7 +54,7 @@ describe("function", () => {
|
|||
const source = populateOriginalSource("func");
|
||||
const symbols = getSymbols(source.id);
|
||||
|
||||
const text = findFunctionText(20, source, symbols);
|
||||
const text = findFunctionText(20, source, source.content, symbols);
|
||||
expect(text).toEqual(null);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,6 +13,6 @@ describe("isMinified", () => {
|
|||
undefined,
|
||||
"function base(boo) {\n}"
|
||||
);
|
||||
expect(isMinified(sourceWithContent)).toBe(true);
|
||||
expect(isMinified(sourceWithContent, sourceWithContent.content)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,17 +37,13 @@ function getSourceContent(name, type = "js") {
|
|||
}
|
||||
|
||||
export function getSource(name, type) {
|
||||
return getSourceWithContent(name, type);
|
||||
}
|
||||
|
||||
export function getSourceWithContent(name, type) {
|
||||
const { value: text, contentType } = getSourceContent(name, type);
|
||||
|
||||
return makeMockSourceAndContent(undefined, name, contentType, text);
|
||||
}
|
||||
|
||||
export function populateSource(name, type) {
|
||||
const { content, ...source } = getSourceWithContent(name, type);
|
||||
const { content, ...source } = getSource(name, type);
|
||||
setSource({
|
||||
id: source.id,
|
||||
text: content.value,
|
||||
|
|
|
@ -19,7 +19,7 @@ add_task(async function testBreakableLinesOverReloads() {
|
|||
);
|
||||
|
||||
info("Assert breakable lines of the first html page load");
|
||||
await assertBreakableLines(dbg, "index.html", 20, [[17], [18]]);
|
||||
await assertBreakableLines(dbg, "index.html", 22, [[17], [18]]);
|
||||
|
||||
info("Assert breakable lines of the first original source file, original.js");
|
||||
// The length of original.js is longer than the test file
|
||||
|
@ -74,11 +74,8 @@ function shouldLineBeBreakable(breakableLines, line) {
|
|||
|
||||
async function assertBreakableLines(dbg, file, numberOfLines, breakableLines) {
|
||||
await selectSource(dbg, file);
|
||||
const editorLines = dbg.win.document.querySelectorAll(
|
||||
".CodeMirror-lines .CodeMirror-code > div"
|
||||
);
|
||||
is(
|
||||
editorLines.length,
|
||||
getCM(dbg).lineCount(),
|
||||
numberOfLines,
|
||||
`We show the expected number of lines in CodeMirror for ${file}`
|
||||
);
|
||||
|
|
|
@ -60,7 +60,7 @@ async function invokeAndAssertBreakpoints(dbg) {
|
|||
|
||||
function assertPauseLocation(dbg, line, url = "event-breakpoints.js") {
|
||||
const { location } = dbg.selectors.getVisibleSelectedFrame();
|
||||
const selectedSource = dbg.selectors.getSelectedSourceWithContent();
|
||||
const selectedSource = dbg.selectors.getSelectedSource();
|
||||
|
||||
is(location.sourceId, selectedSource.id, `Correct selected sourceId`);
|
||||
ok(selectedSource.url.includes(url), "Correct url");
|
||||
|
|
|
@ -13,8 +13,9 @@ add_task(async function() {
|
|||
await selectSource(dbg, "pretty.js", 4, 8);
|
||||
|
||||
prettyPrint(dbg);
|
||||
info("Wait for a second tab to be displayed with the pretty printed source");
|
||||
await waitForTabCounts(dbg, 2);
|
||||
await waitForElementWithSelector(dbg, selectors.prettyPrintLoader);
|
||||
info("Wait for the pretty printed source to be selected on a different line");
|
||||
await waitForSelectedLocation(dbg, 5);
|
||||
});
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ add_task(async function() {
|
|||
// We should be paused at the first line of simple-worker.js
|
||||
// Each worker has its own sources, so we have to retrieve the new source,
|
||||
// which has just been opened on pause
|
||||
const workerSource2 = dbg.selectors.getSelectedSourceWithContent();
|
||||
const workerSource2 = dbg.selectors.getSelectedSource();
|
||||
assertPausedAtSourceAndLine(dbg, workerSource2.id, 1);
|
||||
// We have to remove the first breakpoint, set on the fist worker.
|
||||
// The first worker is loaded on the html page load.
|
||||
|
@ -40,7 +40,7 @@ add_task(async function() {
|
|||
await waitForPaused(dbg, "simple-worker.js");
|
||||
|
||||
// We should be paused in the message listener in simple-worker.js
|
||||
const workerSource3 = dbg.selectors.getSelectedSourceWithContent();
|
||||
const workerSource3 = dbg.selectors.getSelectedSource();
|
||||
assertPausedAtSourceAndLine(dbg, workerSource3.id, 10);
|
||||
await removeBreakpoint(dbg, workerSource2.id, 10, 2);
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ add_task(async function() {
|
|||
await dbg.actions.breakOnNext(getThreadContext(dbg));
|
||||
await waitForPaused(dbg, "simple-worker.js");
|
||||
threadIsSelected(dbg, 2);
|
||||
const workerSource2 = dbg.selectors.getSelectedSourceWithContent();
|
||||
const workerSource2 = dbg.selectors.getSelectedSource();
|
||||
assertPausedAtSourceAndLine(dbg, workerSource2.id, 3);
|
||||
|
||||
info("Add a watch expression and view the value");
|
||||
|
@ -90,7 +90,7 @@ add_task(async function() {
|
|||
await dbg.actions.selectThread(getContext(dbg), thread2);
|
||||
threadIsSelected(dbg, 3);
|
||||
await waitForPaused(dbg);
|
||||
const workerSource3 = dbg.selectors.getSelectedSourceWithContent();
|
||||
const workerSource3 = dbg.selectors.getSelectedSource();
|
||||
assertPausedAtSourceAndLine(dbg, workerSource3.id, 10);
|
||||
|
||||
info("StepOver in second worker and not the first");
|
||||
|
|
|
@ -21,7 +21,7 @@ add_task(async function() {
|
|||
await onRemoved;
|
||||
|
||||
// We should be paused at the first line of simple-worker.js
|
||||
const workerSource2 = dbg.selectors.getSelectedSourceWithContent();
|
||||
const workerSource2 = dbg.selectors.getSelectedSource();
|
||||
assertPausedAtSourceAndLine(dbg, workerSource2.id, 11);
|
||||
|
||||
await toggleNode(dbg, "var_array");
|
||||
|
|
|
@ -197,7 +197,8 @@ function waitForSelectedLocation(dbg, line, column) {
|
|||
*/
|
||||
function waitForSelectedSource(dbg, sourceOrUrl) {
|
||||
const {
|
||||
getSelectedSourceWithContent,
|
||||
getSelectedSource,
|
||||
getSelectedSourceTextContent,
|
||||
hasSymbols,
|
||||
getBreakableLines,
|
||||
} = dbg.selectors;
|
||||
|
@ -205,8 +206,9 @@ function waitForSelectedSource(dbg, sourceOrUrl) {
|
|||
return waitForState(
|
||||
dbg,
|
||||
state => {
|
||||
const source = getSelectedSourceWithContent() || {};
|
||||
if (!source.content) {
|
||||
const source = getSelectedSource() || {};
|
||||
const sourceTextContent = getSelectedSourceTextContent();
|
||||
if (!sourceTextContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -259,8 +261,9 @@ function assertPausedLocation(dbg) {
|
|||
function assertDebugLine(dbg, line, column) {
|
||||
// Check the debug line
|
||||
const lineInfo = getCM(dbg).lineInfo(line - 1);
|
||||
const source = dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
if (source && !source.content) {
|
||||
const source = dbg.selectors.getSelectedSource();
|
||||
const sourceTextContent = dbg.selectors.getSelectedSourceTextContent();
|
||||
if (source && !sourceTextContent) {
|
||||
const url = source.url;
|
||||
ok(
|
||||
false,
|
||||
|
@ -538,9 +541,10 @@ function isSelectedFrameSelected(dbg, state) {
|
|||
// Make sure the source text is completely loaded for the
|
||||
// source we are paused in.
|
||||
const sourceId = frame.location.sourceId;
|
||||
const source = dbg.selectors.getSelectedSourceWithContent() || {};
|
||||
const source = dbg.selectors.getSelectedSource();
|
||||
const sourceTextContent = dbg.selectors.getSelectedSourceTextContent();
|
||||
|
||||
if (!source || !source.content) {
|
||||
if (!source || !sourceTextContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1475,7 +1479,6 @@ const selectors = {
|
|||
replayNext: ".replay-next.active",
|
||||
toggleBreakpoints: ".breakpoints-toggle",
|
||||
prettyPrintButton: ".source-footer .prettyPrint",
|
||||
prettyPrintLoader: ".source-footer .spin",
|
||||
sourceMapLink: ".source-footer .mapped-source",
|
||||
sourcesFooter: ".sources-panel .source-footer",
|
||||
editorFooter: ".editor-pane .source-footer",
|
||||
|
|
|
@ -179,8 +179,14 @@ function selectSource(dbg, url) {
|
|||
return waitForState(
|
||||
dbg,
|
||||
state => {
|
||||
const source = dbg.selectors.getSelectedSourceWithContent(state);
|
||||
if (!source || !source.content) {
|
||||
const source = dbg.selectors.getSelectedSource(state);
|
||||
if (!source) {
|
||||
return false;
|
||||
}
|
||||
const sourceTextContent = dbg.selectors.getSelectedSourceTextContent(
|
||||
state
|
||||
);
|
||||
if (!sourceTextContent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче