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:
Jason Laster 2019-09-21 04:51:48 +00:00
Родитель d19841f79e
Коммит f13398b88f
9 изменённых файлов: 142 добавлений и 43 удалений

Просмотреть файл

@ -168,6 +168,14 @@ DebuggerPanel.prototype = {
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) {
const cx = this._selectors.getContext(this._getState());
const location = { sourceId, line, column };

Просмотреть файл

@ -30,3 +30,7 @@ export { toggleSkipPausing, setSkipPausing } from "./skipPausing";
export { toggleMapScopes } from "./mapScopes";
export { setExpandedScope } from "./expandScopes";
export { generateInlinePreview } from "./inlinePreview";
export {
previewPausedLocation,
clearPreviewPausedLocation,
} from "./previewPausedLocation";

Просмотреть файл

@ -19,6 +19,7 @@ CompiledModules(
'mapScopes.js',
'paused.js',
'pauseOnExceptions.js',
'previewPausedLocation.js',
'resumed.js',
'selectFrame.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
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 { MatchedLocations } from "../../reducers/file-search";
import type { TreeNode } from "../../utils/sources-tree/types";
@ -149,6 +155,13 @@ export type DebuggeeAction =
+type: "SELECT_THREAD",
+cx: Context,
+thread: ThreadId,
|}
| {|
+type: "PREVIEW_PAUSED_LOCATION",
+location: SourceLocation,
|}
| {|
+type: "CLEAR_PREVIEW_PAUSED_LOCATION",
|};
export type {

Просмотреть файл

@ -20,12 +20,13 @@ import {
getPauseReason,
getSourceWithContent,
getCurrentThread,
getPausePreviewLocation,
} from "../../selectors";
import type { Frame, Why, SourceWithContent } from "../../types";
import type { SourceLocation, Why, SourceWithContent } from "../../types";
type Props = {
frame: Frame,
location: SourceLocation,
why: Why,
source: ?SourceWithContent,
};
@ -35,42 +36,40 @@ type TextClasses = {
lineClass: string,
};
function isDocumentReady(source: ?SourceWithContent, frame) {
return (
frame && source && source.content && hasDocument(frame.location.sourceId)
);
function isDocumentReady(source: ?SourceWithContent, location) {
return location && source && source.content && hasDocument(location.sourceId);
}
export class DebugLine extends PureComponent<Props> {
debugExpression: null;
componentDidMount() {
const { why, frame, source } = this.props;
this.setDebugLine(why, frame, source);
const { why, location, source } = this.props;
this.setDebugLine(why, location, source);
}
componentWillUnmount() {
const { why, frame, source } = this.props;
this.clearDebugLine(why, frame, source);
const { why, location, source } = this.props;
this.clearDebugLine(why, location, source);
}
componentDidUpdate(prevProps: Props) {
const { why, frame, source } = this.props;
const { why, location, source } = this.props;
startOperation();
this.clearDebugLine(prevProps.why, prevProps.frame, prevProps.source);
this.setDebugLine(why, frame, source);
this.clearDebugLine(prevProps.why, prevProps.location, prevProps.source);
this.setDebugLine(why, location, source);
endOperation();
}
setDebugLine(why: Why, frame: Frame, source: ?SourceWithContent) {
if (!isDocumentReady(source, frame)) {
setDebugLine(why: Why, location: SourceLocation, source: ?SourceWithContent) {
if (!isDocumentReady(source, location)) {
return;
}
const sourceId = frame.location.sourceId;
const sourceId = location.sourceId;
const doc = getDocument(sourceId);
let { line, column } = toEditorPosition(frame.location);
let { line, column } = toEditorPosition(location);
let { markTextClass, lineClass } = this.getTextClasses(why);
doc.addLineClass(line, "line", lineClass);
@ -92,8 +91,12 @@ export class DebugLine extends PureComponent<Props> {
);
}
clearDebugLine(why: Why, frame: Frame, source: ?SourceWithContent) {
if (!isDocumentReady(source, frame)) {
clearDebugLine(
why: Why,
location: SourceLocation,
source: ?SourceWithContent
) {
if (!isDocumentReady(source, location)) {
return;
}
@ -101,15 +104,15 @@ export class DebugLine extends PureComponent<Props> {
this.debugExpression.clear();
}
const sourceId = frame.location.sourceId;
const { line } = toEditorPosition(frame.location);
const sourceId = location.sourceId;
const { line } = toEditorPosition(location);
const doc = getDocument(sourceId);
const { lineClass } = this.getTextClasses(why);
doc.removeLineClass(line, "line", lineClass);
}
getTextClasses(why: Why): TextClasses {
if (isException(why)) {
if (why && isException(why)) {
return {
markTextClass: "debug-expression-error",
lineClass: "new-debug-line-error",
@ -126,9 +129,12 @@ export class DebugLine extends PureComponent<Props> {
const mapStateToProps = state => {
const frame = getVisibleSelectedFrame(state);
const previewLocation = getPausePreviewLocation(state);
const location = previewLocation || (frame && frame.location);
return {
frame,
source: frame && getSourceWithContent(state, frame.location.sourceId),
location,
source: location && getSourceWithContent(state, location.sourceId),
why: getPauseReason(state, getCurrentThread(state)),
};
};

Просмотреть файл

@ -41,13 +41,11 @@ function generateDefaults(editor, overrides) {
};
}
function createFrame(line) {
function createLocation(line) {
return {
location: {
sourceId: "foo",
line,
column: 2,
},
sourceId: "foo",
line,
column: 2,
};
}
@ -80,9 +78,9 @@ describe("DebugLine Component", () => {
},
});
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.addLineClass.mock.calls).toEqual([
@ -107,10 +105,10 @@ describe("DebugLine Component", () => {
const firstLine = 2;
const secondLine = 2;
component.setProps({ ...props, frame: createFrame(firstLine) });
component.setProps({ ...props, location: createLocation(firstLine) });
component.setProps({
...props,
frame: createFrame(secondLine),
frame: createLocation(secondLine),
});
expect(doc.removeLineClass.mock.calls).toEqual([
@ -143,9 +141,9 @@ describe("DebugLine Component", () => {
it("should not set the debug line", () => {
const { component, props, doc } = render({ frame: null });
const line = 2;
const frame = createFrame(line);
const location = createLocation(line);
component.setProps({ ...props, frame });
component.setProps({ ...props, location });
expect(doc.removeLineClass).not.toHaveBeenCalled();
});
});

Просмотреть файл

@ -3,7 +3,7 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
// @flow
/* eslint complexity: ["error", 30]*/
/* eslint complexity: ["error", 35]*/
/**
* Pause reducer
@ -29,6 +29,7 @@ import type {
Context,
ThreadContext,
Previews,
SourceLocation,
} from "../types";
export type Command =
@ -96,6 +97,7 @@ export type PauseState = {
mapScopes: boolean,
shouldPauseOnExceptions: boolean,
shouldPauseOnCaughtExceptions: boolean,
previewLocation: ?SourceLocation,
};
function createPauseState(thread: ThreadId = "UnknownThread") {
@ -109,6 +111,7 @@ function createPauseState(thread: ThreadId = "UnknownThread") {
isPaused: false,
pauseCounter: 0,
},
previewLocation: null,
threads: {},
canRewind: false,
skipPausing: prefs.skipPausing,
@ -191,6 +194,7 @@ function update(
state = {
...state,
previewLocation: null,
threadcx: {
...state.threadcx,
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": {
const { selectedFrameId, frames } = action;
return updateThreadState({ frames, selectedFrameId });
@ -677,4 +689,8 @@ export function getLastExpandedScopes(state: State, thread: ThreadId) {
return getThreadPauseState(state.pause, thread).lastExpandedScopes;
}
export function getPausePreviewLocation(state: State) {
return state.pause.previewLocation;
}
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
@ -341,11 +348,6 @@ class WebReplayPlayer extends Component {
}
}
async clearPreviewLocation() {
const dbg = await this.toolbox.loadTool("jsdebugger");
dbg.clearPreviewPausedLocation();
}
unhighlightConsoleMessage() {
if (this.hoveredMessage) {
this.hoveredMessage.classList.remove("highlight");
@ -374,9 +376,20 @@ class WebReplayPlayer extends Component {
}
onMessageMouseEnter(message) {
this.previewLocation(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) {
if (!e.altKey) {
return;
@ -403,6 +416,7 @@ class WebReplayPlayer extends Component {
onPlayerMouseLeave() {
this.unhighlightConsoleMessage();
this.clearPreviewLocation();
return this.threadFront.paintCurrentPoint();
}
@ -614,7 +628,7 @@ class WebReplayPlayer extends Component {
e.stopPropagation();
this.seek(message.executionPoint);
},
onMouseEnter: () => this.onMessageMouseEnter(message.executionPoint),
onMouseEnter: () => this.onMessageMouseEnter(message),
});
}