зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1580334 - Hovering on the timeline should update the debugger.
Differential Revision: https://phabricator.services.mozilla.com/D46550 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
d19841f79e
Коммит
f13398b88f
|
@ -168,6 +168,14 @@ DebuggerPanel.prototype = {
|
||||||
return this._actions.selectSourceURL(cx, url, { line, column });
|
return this._actions.selectSourceURL(cx, url, { line, column });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
previewPausedLocation(location) {
|
||||||
|
return this._actions.previewPausedLocation(location);
|
||||||
|
},
|
||||||
|
|
||||||
|
clearPreviewPausedLocation() {
|
||||||
|
return this._actions.clearPreviewPausedLocation();
|
||||||
|
},
|
||||||
|
|
||||||
async selectSource(sourceId, line, column) {
|
async selectSource(sourceId, line, column) {
|
||||||
const cx = this._selectors.getContext(this._getState());
|
const cx = this._selectors.getContext(this._getState());
|
||||||
const location = { sourceId, line, column };
|
const location = { sourceId, line, column };
|
||||||
|
|
|
@ -30,3 +30,7 @@ export { toggleSkipPausing, setSkipPausing } from "./skipPausing";
|
||||||
export { toggleMapScopes } from "./mapScopes";
|
export { toggleMapScopes } from "./mapScopes";
|
||||||
export { setExpandedScope } from "./expandScopes";
|
export { setExpandedScope } from "./expandScopes";
|
||||||
export { generateInlinePreview } from "./inlinePreview";
|
export { generateInlinePreview } from "./inlinePreview";
|
||||||
|
export {
|
||||||
|
previewPausedLocation,
|
||||||
|
clearPreviewPausedLocation,
|
||||||
|
} from "./previewPausedLocation";
|
||||||
|
|
|
@ -19,6 +19,7 @@ CompiledModules(
|
||||||
'mapScopes.js',
|
'mapScopes.js',
|
||||||
'paused.js',
|
'paused.js',
|
||||||
'pauseOnExceptions.js',
|
'pauseOnExceptions.js',
|
||||||
|
'previewPausedLocation.js',
|
||||||
'resumed.js',
|
'resumed.js',
|
||||||
'selectFrame.js',
|
'selectFrame.js',
|
||||||
'skipPausing.js',
|
'skipPausing.js',
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
/* 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/>. */
|
||||||
|
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import { selectLocation } from "../sources";
|
||||||
|
import { getContext, getSourceByURL } from "../../selectors";
|
||||||
|
import type { ThunkArgs } from "../types";
|
||||||
|
|
||||||
|
type Location = {
|
||||||
|
sourceUrl: string,
|
||||||
|
column: number,
|
||||||
|
line: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function previewPausedLocation(location: Location) {
|
||||||
|
return ({ dispatch, getState }: ThunkArgs) => {
|
||||||
|
const cx = getContext(getState());
|
||||||
|
const source = getSourceByURL(getState(), location.sourceUrl);
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceLocation = { ...location, sourceId: source.id };
|
||||||
|
dispatch(selectLocation(cx, sourceLocation));
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: "PREVIEW_PAUSED_LOCATION",
|
||||||
|
location: sourceLocation,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearPreviewPausedLocation() {
|
||||||
|
return {
|
||||||
|
type: "CLEAR_PREVIEW_PAUSED_LOCATION",
|
||||||
|
};
|
||||||
|
}
|
|
@ -5,7 +5,13 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import typeof SourceMaps from "devtools-source-map";
|
import typeof SourceMaps from "devtools-source-map";
|
||||||
import type { ThreadList, Thread, Context, ThreadId } from "../../types";
|
import type {
|
||||||
|
ThreadList,
|
||||||
|
Thread,
|
||||||
|
Context,
|
||||||
|
ThreadId,
|
||||||
|
SourceLocation,
|
||||||
|
} from "../../types";
|
||||||
import type { State } from "../../reducers/types";
|
import type { State } from "../../reducers/types";
|
||||||
import type { MatchedLocations } from "../../reducers/file-search";
|
import type { MatchedLocations } from "../../reducers/file-search";
|
||||||
import type { TreeNode } from "../../utils/sources-tree/types";
|
import type { TreeNode } from "../../utils/sources-tree/types";
|
||||||
|
@ -149,6 +155,13 @@ export type DebuggeeAction =
|
||||||
+type: "SELECT_THREAD",
|
+type: "SELECT_THREAD",
|
||||||
+cx: Context,
|
+cx: Context,
|
||||||
+thread: ThreadId,
|
+thread: ThreadId,
|
||||||
|
|}
|
||||||
|
| {|
|
||||||
|
+type: "PREVIEW_PAUSED_LOCATION",
|
||||||
|
+location: SourceLocation,
|
||||||
|
|}
|
||||||
|
| {|
|
||||||
|
+type: "CLEAR_PREVIEW_PAUSED_LOCATION",
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
|
|
|
@ -20,12 +20,13 @@ import {
|
||||||
getPauseReason,
|
getPauseReason,
|
||||||
getSourceWithContent,
|
getSourceWithContent,
|
||||||
getCurrentThread,
|
getCurrentThread,
|
||||||
|
getPausePreviewLocation,
|
||||||
} from "../../selectors";
|
} from "../../selectors";
|
||||||
|
|
||||||
import type { Frame, Why, SourceWithContent } from "../../types";
|
import type { SourceLocation, Why, SourceWithContent } from "../../types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
frame: Frame,
|
location: SourceLocation,
|
||||||
why: Why,
|
why: Why,
|
||||||
source: ?SourceWithContent,
|
source: ?SourceWithContent,
|
||||||
};
|
};
|
||||||
|
@ -35,42 +36,40 @@ type TextClasses = {
|
||||||
lineClass: string,
|
lineClass: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
function isDocumentReady(source: ?SourceWithContent, frame) {
|
function isDocumentReady(source: ?SourceWithContent, location) {
|
||||||
return (
|
return location && source && source.content && hasDocument(location.sourceId);
|
||||||
frame && source && source.content && hasDocument(frame.location.sourceId)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DebugLine extends PureComponent<Props> {
|
export class DebugLine extends PureComponent<Props> {
|
||||||
debugExpression: null;
|
debugExpression: null;
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const { why, frame, source } = this.props;
|
const { why, location, source } = this.props;
|
||||||
this.setDebugLine(why, frame, source);
|
this.setDebugLine(why, location, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
const { why, frame, source } = this.props;
|
const { why, location, source } = this.props;
|
||||||
this.clearDebugLine(why, frame, source);
|
this.clearDebugLine(why, location, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps: Props) {
|
componentDidUpdate(prevProps: Props) {
|
||||||
const { why, frame, source } = this.props;
|
const { why, location, source } = this.props;
|
||||||
|
|
||||||
startOperation();
|
startOperation();
|
||||||
this.clearDebugLine(prevProps.why, prevProps.frame, prevProps.source);
|
this.clearDebugLine(prevProps.why, prevProps.location, prevProps.source);
|
||||||
this.setDebugLine(why, frame, source);
|
this.setDebugLine(why, location, source);
|
||||||
endOperation();
|
endOperation();
|
||||||
}
|
}
|
||||||
|
|
||||||
setDebugLine(why: Why, frame: Frame, source: ?SourceWithContent) {
|
setDebugLine(why: Why, location: SourceLocation, source: ?SourceWithContent) {
|
||||||
if (!isDocumentReady(source, frame)) {
|
if (!isDocumentReady(source, location)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const sourceId = frame.location.sourceId;
|
const sourceId = location.sourceId;
|
||||||
const doc = getDocument(sourceId);
|
const doc = getDocument(sourceId);
|
||||||
|
|
||||||
let { line, column } = toEditorPosition(frame.location);
|
let { line, column } = toEditorPosition(location);
|
||||||
let { markTextClass, lineClass } = this.getTextClasses(why);
|
let { markTextClass, lineClass } = this.getTextClasses(why);
|
||||||
doc.addLineClass(line, "line", lineClass);
|
doc.addLineClass(line, "line", lineClass);
|
||||||
|
|
||||||
|
@ -92,8 +91,12 @@ export class DebugLine extends PureComponent<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
clearDebugLine(why: Why, frame: Frame, source: ?SourceWithContent) {
|
clearDebugLine(
|
||||||
if (!isDocumentReady(source, frame)) {
|
why: Why,
|
||||||
|
location: SourceLocation,
|
||||||
|
source: ?SourceWithContent
|
||||||
|
) {
|
||||||
|
if (!isDocumentReady(source, location)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,15 +104,15 @@ export class DebugLine extends PureComponent<Props> {
|
||||||
this.debugExpression.clear();
|
this.debugExpression.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceId = frame.location.sourceId;
|
const sourceId = location.sourceId;
|
||||||
const { line } = toEditorPosition(frame.location);
|
const { line } = toEditorPosition(location);
|
||||||
const doc = getDocument(sourceId);
|
const doc = getDocument(sourceId);
|
||||||
const { lineClass } = this.getTextClasses(why);
|
const { lineClass } = this.getTextClasses(why);
|
||||||
doc.removeLineClass(line, "line", lineClass);
|
doc.removeLineClass(line, "line", lineClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
getTextClasses(why: Why): TextClasses {
|
getTextClasses(why: Why): TextClasses {
|
||||||
if (isException(why)) {
|
if (why && isException(why)) {
|
||||||
return {
|
return {
|
||||||
markTextClass: "debug-expression-error",
|
markTextClass: "debug-expression-error",
|
||||||
lineClass: "new-debug-line-error",
|
lineClass: "new-debug-line-error",
|
||||||
|
@ -126,9 +129,12 @@ export class DebugLine extends PureComponent<Props> {
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const frame = getVisibleSelectedFrame(state);
|
const frame = getVisibleSelectedFrame(state);
|
||||||
|
const previewLocation = getPausePreviewLocation(state);
|
||||||
|
const location = previewLocation || (frame && frame.location);
|
||||||
return {
|
return {
|
||||||
frame,
|
frame,
|
||||||
source: frame && getSourceWithContent(state, frame.location.sourceId),
|
location,
|
||||||
|
source: location && getSourceWithContent(state, location.sourceId),
|
||||||
why: getPauseReason(state, getCurrentThread(state)),
|
why: getPauseReason(state, getCurrentThread(state)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,13 +41,11 @@ function generateDefaults(editor, overrides) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFrame(line) {
|
function createLocation(line) {
|
||||||
return {
|
return {
|
||||||
location: {
|
|
||||||
sourceId: "foo",
|
sourceId: "foo",
|
||||||
line,
|
line,
|
||||||
column: 2,
|
column: 2,
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,9 +78,9 @@ describe("DebugLine Component", () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const line = 2;
|
const line = 2;
|
||||||
const frame = createFrame(line);
|
const location = createLocation(line);
|
||||||
|
|
||||||
component.setProps({ ...props, frame });
|
component.setProps({ ...props, location });
|
||||||
|
|
||||||
expect(doc.removeLineClass.mock.calls).toEqual([]);
|
expect(doc.removeLineClass.mock.calls).toEqual([]);
|
||||||
expect(doc.addLineClass.mock.calls).toEqual([
|
expect(doc.addLineClass.mock.calls).toEqual([
|
||||||
|
@ -107,10 +105,10 @@ describe("DebugLine Component", () => {
|
||||||
const firstLine = 2;
|
const firstLine = 2;
|
||||||
const secondLine = 2;
|
const secondLine = 2;
|
||||||
|
|
||||||
component.setProps({ ...props, frame: createFrame(firstLine) });
|
component.setProps({ ...props, location: createLocation(firstLine) });
|
||||||
component.setProps({
|
component.setProps({
|
||||||
...props,
|
...props,
|
||||||
frame: createFrame(secondLine),
|
frame: createLocation(secondLine),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(doc.removeLineClass.mock.calls).toEqual([
|
expect(doc.removeLineClass.mock.calls).toEqual([
|
||||||
|
@ -143,9 +141,9 @@ describe("DebugLine Component", () => {
|
||||||
it("should not set the debug line", () => {
|
it("should not set the debug line", () => {
|
||||||
const { component, props, doc } = render({ frame: null });
|
const { component, props, doc } = render({ frame: null });
|
||||||
const line = 2;
|
const line = 2;
|
||||||
const frame = createFrame(line);
|
const location = createLocation(line);
|
||||||
|
|
||||||
component.setProps({ ...props, frame });
|
component.setProps({ ...props, location });
|
||||||
expect(doc.removeLineClass).not.toHaveBeenCalled();
|
expect(doc.removeLineClass).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
|
||||||
|
|
||||||
// @flow
|
// @flow
|
||||||
/* eslint complexity: ["error", 30]*/
|
/* eslint complexity: ["error", 35]*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pause reducer
|
* Pause reducer
|
||||||
|
@ -29,6 +29,7 @@ import type {
|
||||||
Context,
|
Context,
|
||||||
ThreadContext,
|
ThreadContext,
|
||||||
Previews,
|
Previews,
|
||||||
|
SourceLocation,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
export type Command =
|
export type Command =
|
||||||
|
@ -96,6 +97,7 @@ export type PauseState = {
|
||||||
mapScopes: boolean,
|
mapScopes: boolean,
|
||||||
shouldPauseOnExceptions: boolean,
|
shouldPauseOnExceptions: boolean,
|
||||||
shouldPauseOnCaughtExceptions: boolean,
|
shouldPauseOnCaughtExceptions: boolean,
|
||||||
|
previewLocation: ?SourceLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
function createPauseState(thread: ThreadId = "UnknownThread") {
|
function createPauseState(thread: ThreadId = "UnknownThread") {
|
||||||
|
@ -109,6 +111,7 @@ function createPauseState(thread: ThreadId = "UnknownThread") {
|
||||||
isPaused: false,
|
isPaused: false,
|
||||||
pauseCounter: 0,
|
pauseCounter: 0,
|
||||||
},
|
},
|
||||||
|
previewLocation: null,
|
||||||
threads: {},
|
threads: {},
|
||||||
canRewind: false,
|
canRewind: false,
|
||||||
skipPausing: prefs.skipPausing,
|
skipPausing: prefs.skipPausing,
|
||||||
|
@ -191,6 +194,7 @@ function update(
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
...state,
|
...state,
|
||||||
|
previewLocation: null,
|
||||||
threadcx: {
|
threadcx: {
|
||||||
...state.threadcx,
|
...state.threadcx,
|
||||||
pauseCounter: state.threadcx.pauseCounter + 1,
|
pauseCounter: state.threadcx.pauseCounter + 1,
|
||||||
|
@ -207,6 +211,14 @@ function update(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "PREVIEW_PAUSED_LOCATION": {
|
||||||
|
return { ...state, previewLocation: action.location };
|
||||||
|
}
|
||||||
|
|
||||||
|
case "CLEAR_PREVIEW_PAUSED_LOCATION": {
|
||||||
|
return { ...state, previewLocation: null };
|
||||||
|
}
|
||||||
|
|
||||||
case "MAP_FRAMES": {
|
case "MAP_FRAMES": {
|
||||||
const { selectedFrameId, frames } = action;
|
const { selectedFrameId, frames } = action;
|
||||||
return updateThreadState({ frames, selectedFrameId });
|
return updateThreadState({ frames, selectedFrameId });
|
||||||
|
@ -677,4 +689,8 @@ export function getLastExpandedScopes(state: State, thread: ThreadId) {
|
||||||
return getThreadPauseState(state.pause, thread).lastExpandedScopes;
|
return getThreadPauseState(state.pause, thread).lastExpandedScopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPausePreviewLocation(state: State) {
|
||||||
|
return state.pause.previewLocation;
|
||||||
|
}
|
||||||
|
|
||||||
export default update;
|
export default update;
|
||||||
|
|
|
@ -101,6 +101,13 @@ function sameLocation(m1, m2) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMessageLocation(message) {
|
||||||
|
const {
|
||||||
|
frame: { source, line, column },
|
||||||
|
} = message;
|
||||||
|
return { sourceUrl: source, line, column };
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* The player has 4 valid states
|
* The player has 4 valid states
|
||||||
|
@ -341,11 +348,6 @@ class WebReplayPlayer extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async clearPreviewLocation() {
|
|
||||||
const dbg = await this.toolbox.loadTool("jsdebugger");
|
|
||||||
dbg.clearPreviewPausedLocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
unhighlightConsoleMessage() {
|
unhighlightConsoleMessage() {
|
||||||
if (this.hoveredMessage) {
|
if (this.hoveredMessage) {
|
||||||
this.hoveredMessage.classList.remove("highlight");
|
this.hoveredMessage.classList.remove("highlight");
|
||||||
|
@ -374,9 +376,20 @@ class WebReplayPlayer extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
onMessageMouseEnter(message) {
|
onMessageMouseEnter(message) {
|
||||||
|
this.previewLocation(message);
|
||||||
this.showMessage(message);
|
this.showMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async previewLocation(closestMessage) {
|
||||||
|
const dbg = await this.toolbox.loadTool("jsdebugger");
|
||||||
|
dbg.previewPausedLocation(getMessageLocation(closestMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearPreviewLocation() {
|
||||||
|
const dbg = await this.toolbox.loadTool("jsdebugger");
|
||||||
|
dbg.clearPreviewPausedLocation();
|
||||||
|
}
|
||||||
|
|
||||||
onProgressBarClick(e) {
|
onProgressBarClick(e) {
|
||||||
if (!e.altKey) {
|
if (!e.altKey) {
|
||||||
return;
|
return;
|
||||||
|
@ -403,6 +416,7 @@ class WebReplayPlayer extends Component {
|
||||||
|
|
||||||
onPlayerMouseLeave() {
|
onPlayerMouseLeave() {
|
||||||
this.unhighlightConsoleMessage();
|
this.unhighlightConsoleMessage();
|
||||||
|
this.clearPreviewLocation();
|
||||||
return this.threadFront.paintCurrentPoint();
|
return this.threadFront.paintCurrentPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +628,7 @@ class WebReplayPlayer extends Component {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
this.seek(message.executionPoint);
|
this.seek(message.executionPoint);
|
||||||
},
|
},
|
||||||
onMouseEnter: () => this.onMessageMouseEnter(message.executionPoint),
|
onMouseEnter: () => this.onMessageMouseEnter(message),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче