Skip to content

Commit ce9e4bf

Browse files
authored
Feature/history reducer 🎉 (firefox-devtools#5106)
* working replay with nice styling 🎉 * finish up test for now * cleanup from rebase
1 parent 4f7001f commit ce9e4bf

19 files changed

Lines changed: 521 additions & 5 deletions

File tree

assets/images/back.svg

Lines changed: 6 additions & 0 deletions
Loading

assets/images/forward.svg

Lines changed: 6 additions & 0 deletions
Loading

assets/panel/debugger.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,14 @@ pauseOnUncaughtExceptions=Pause on uncaught exceptions. Click to pause on all ex
598598
# when the debugger will pause on all exceptions.
599599
pauseOnExceptions=Pause on all exceptions. Click to ignore exceptions
600600

601+
# LOCALIZATION NOTE (replayPrevious): The replay previous button tooltip
602+
# when the debugger will go back in stepping history.
603+
replayPrevious=Go back one step in history
604+
605+
# LOCALIZATION NOTE (replayNext): The replay next button tooltip
606+
# when the debugger will go forward in stepping history.
607+
replayNext=Go forward one step in history
608+
601609
# LOCALIZATION NOTE (loadingText): The text that is displayed in the script
602610
# editor when the loading process has started but there is no file to display
603611
# yet.

assets/panel/prefs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,4 @@ pref("devtools.debugger.features.code-coverage", false);
5656
pref("devtools.debugger.features.event-listeners", false);
5757
pref("devtools.debugger.features.code-folding", false);
5858
pref("devtools.debugger.features.outline", true);
59+
pref("devtools.debugger.features.replay", false);

src/actions/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as fileSearch from "./file-search";
1414
import * as ast from "./ast";
1515
import * as coverage from "./coverage";
1616
import * as projectTextSearch from "./project-text-search";
17+
import * as replay from "./replay";
1718
import * as quickOpen from "./quick-open";
1819
import * as sourceTree from "./source-tree";
1920
import * as sources from "./sources";
@@ -33,6 +34,7 @@ export default {
3334
...ast,
3435
...coverage,
3536
...projectTextSearch,
37+
...replay,
3638
...quickOpen,
3739
...sourceTree,
3840
...debuggee,

src/actions/replay.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
4+
5+
// @flow
6+
7+
/**
8+
* Redux actions for replay
9+
* @module actions/replay
10+
*/
11+
12+
import { getHistoryFrame } from "../selectors";
13+
import { selectLocation } from "./sources";
14+
15+
export function timeTravelTo(position: Number) {
16+
return ({ dispatch, getState }: any) => {
17+
const data = getHistoryFrame(getState(), position);
18+
dispatch({
19+
type: "TRAVEL_TO",
20+
data,
21+
position
22+
});
23+
dispatch(selectLocation(data.paused.frames[0].location));
24+
};
25+
}
26+
27+
export function clearHistory() {
28+
return ({ dispatch, getState }: any) => {
29+
dispatch({
30+
type: "CLEAR_HISTORY"
31+
});
32+
};
33+
}

src/actions/types.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,25 @@ type UIAction =
222222
type: "CLOSE_PROJECT_SEARCH"
223223
};
224224

225+
type ReplayAction =
226+
| {
227+
type: "TRAVEL_TO",
228+
data: {
229+
paused: {
230+
why: Why,
231+
scopes: Scope[],
232+
frames: Frame[],
233+
selectedFrameId: string,
234+
loadedObjects: Object
235+
},
236+
expressions?: Object[]
237+
},
238+
position: number
239+
}
240+
| {
241+
type: "CLEAR_HISTORY"
242+
};
243+
225244
type PauseAction =
226245
| { type: "BREAK_ON_NEXT", value: boolean }
227246
| { type: "RESUME", value: void }
@@ -397,4 +416,5 @@ export type Action =
397416
| FileTextSearchAction
398417
| ProjectTextSearchAction
399418
| CoverageAction
400-
| DebugeeAction;
419+
| DebugeeAction
420+
| ReplayAction;

src/components/PrimaryPanes/index.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ class PrimaryPanes extends Component<Props> {
109109
}
110110
}
111111

112+
renderOutline() {
113+
const { selectLocation } = this.props;
114+
115+
const outlineComp = features.outline ? (
116+
<Outline selectLocation={selectLocation} />
117+
) : null;
118+
119+
return outlineComp;
120+
}
121+
122+
renderSources() {
123+
const { sources, selectLocation } = this.props;
124+
return <SourcesTree sources={sources} selectLocation={selectLocation} />;
125+
}
126+
112127
render() {
113128
const { selectedTab } = this.props;
114129

src/components/SecondaryPanes/CommandBar.css

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
overflow: hidden;
1010
z-index: 1;
1111
background-color: var(--theme-toolbar-background);
12+
align-items: center;
1213
}
1314

1415
html[dir="rtl"] .command-bar {
@@ -27,7 +28,10 @@ img.resume,
2728
img.rewind,
2829
img.reverseStepOver,
2930
img.reverseStepIn,
30-
img.reverseStepOut {
31+
img.reverseStepOut,
32+
img.replay-previous,
33+
img.replay-next,
34+
img.resume {
3135
background-color: var(--theme-body-color);
3236
}
3337

@@ -88,6 +92,35 @@ img.reverseStepOut {
8892
margin-inline-start: 0.2em;
8993
}
9094

95+
.command-bar .filler {
96+
flex-grow: 1;
97+
}
98+
99+
.command-bar img.replay-previous {
100+
mask: url(/images/back.svg) no-repeat;
101+
mask-size: 75%;
102+
margin-top: 5px;
103+
}
104+
105+
.command-bar img.replay-next {
106+
mask: url(/images/forward.svg) no-repeat;
107+
mask-size: 75%;
108+
margin-top: 5px;
109+
}
110+
111+
.command-bar .replay-inactive {
112+
opacity: 0.5;
113+
}
114+
115+
.command-bar .step-position {
116+
color: var(--theme-comment-alt);
117+
margin-inline-end: 1em;
118+
}
119+
120+
.command-bar .replay-active {
121+
color: var(--theme-highlight-blue);
122+
}
123+
91124
.command-bar .subSettings {
92125
float: right;
93126
}

src/components/SecondaryPanes/CommandBar.js

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { features } from "../../utils/prefs";
1313
import {
1414
isPaused as getIsPaused,
1515
getIsWaitingOnBreak,
16+
getHistory,
17+
getHistoryPosition,
1618
getShouldPauseOnExceptions,
1719
getShouldIgnoreCaughtExceptions,
1820
getCanRewind
@@ -113,6 +115,10 @@ type Props = {
113115
isPaused: boolean,
114116
pauseOnExceptions: (boolean, boolean) => void,
115117
shouldPauseOnExceptions: boolean,
118+
historyPosition: number,
119+
history: any,
120+
timeTravelTo: number => void,
121+
clearHistory: () => void,
116122
shouldIgnoreCaughtExceptions: boolean,
117123
isWaitingOnBreak: boolean,
118124
horizontal: boolean,
@@ -153,6 +159,10 @@ class CommandBar extends Component<Props> {
153159
this.props[action]();
154160
}
155161

162+
setHistory(offset) {
163+
this.props.timeTravelTo(this.props.historyPosition + offset);
164+
}
165+
156166
renderStepButtons() {
157167
const { isPaused } = this.props;
158168
const className = isPaused ? "active" : "disabled";
@@ -187,12 +197,17 @@ class CommandBar extends Component<Props> {
187197
];
188198
}
189199

200+
resume() {
201+
this.props.resume();
202+
this.props.clearHistory();
203+
}
204+
190205
renderPauseButton() {
191206
const { isPaused, breakOnNext, isWaitingOnBreak } = this.props;
192207

193208
if (isPaused) {
194209
return debugBtn(
195-
this.props.resume,
210+
() => this.resume(),
196211
"resume",
197212
"active",
198213
L10N.getFormatStr("resumeButtonTooltip", formatKey("resume"))
@@ -305,6 +320,55 @@ class CommandBar extends Component<Props> {
305320
];
306321
}
307322

323+
replayPreviousButton() {
324+
const historyLength = this.props.history.length;
325+
if (!historyLength || !features.replay) {
326+
return null;
327+
}
328+
const enabled = this.props.historyPosition === 0;
329+
const activeClass = enabled ? "replay-inactive" : "";
330+
return debugBtn(
331+
() => this.setHistory(-1),
332+
`replay-previous ${activeClass}`,
333+
"active",
334+
L10N.getStr("replayPrevious"),
335+
enabled
336+
);
337+
}
338+
339+
replayNextButton() {
340+
const historyLength = this.props.history.length;
341+
if (!historyLength || !features.replay) {
342+
return null;
343+
}
344+
const enabled = this.props.historyPosition + 1 === historyLength;
345+
const activeClass = enabled ? "replay-inactive" : "";
346+
return debugBtn(
347+
() => this.setHistory(1),
348+
`replay-next ${activeClass}`,
349+
"active",
350+
L10N.getStr("replayNext"),
351+
enabled
352+
);
353+
}
354+
355+
renderStepPosition() {
356+
if (!this.props.history.length || !features.replay) {
357+
return null;
358+
}
359+
const position = this.props.historyPosition + 1;
360+
const total = this.props.history.length;
361+
const activePrev = position > 1 ? "replay-active" : "replay-inactive";
362+
const activeNext = position < total ? "replay-active" : "replay-inactive";
363+
return (
364+
<div className="step-position">
365+
<span className={activePrev}>{position}</span>
366+
<span> | </span>
367+
<span className={activeNext}>{total}</span>
368+
</div>
369+
);
370+
}
371+
308372
render() {
309373
return (
310374
<div
@@ -317,6 +381,10 @@ class CommandBar extends Component<Props> {
317381
{this.renderStepButtons()}
318382
{this.renderPauseOnExceptions()}
319383
{this.renderReverseStepButtons()}
384+
<div className="filler" />
385+
{this.replayPreviousButton()}
386+
{this.renderStepPosition()}
387+
{this.replayNextButton()}
320388
</div>
321389
);
322390
}
@@ -330,6 +398,8 @@ export default connect(
330398
state => {
331399
return {
332400
isPaused: getIsPaused(state),
401+
history: getHistory(state),
402+
historyPosition: getHistoryPosition(state),
333403
isWaitingOnBreak: getIsWaitingOnBreak(state),
334404
shouldPauseOnExceptions: getShouldPauseOnExceptions(state),
335405
shouldIgnoreCaughtExceptions: getShouldIgnoreCaughtExceptions(state),

0 commit comments

Comments
 (0)