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 }); 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),
}); });
} }