Bug 1609815 - Remove Web Replay UI. r=loganfsmyth

Differential Revision: https://phabricator.services.mozilla.com/D60679

--HG--
extra : source : cfe2388a674e024d1920245e867f9982087c8164
This commit is contained in:
Jason Laster 2020-02-10 21:03:28 +00:00
Родитель e4b1423210
Коммит 897b14485f
140 изменённых файлов: 125 добавлений и 11551 удалений

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

@ -7,7 +7,6 @@ module.exports = {
"globals": {
"exports": true,
"isWorker": true,
"isReplaying": true,
"loader": true,
"module": true,
"reportError": true,

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

@ -39,10 +39,7 @@ DevToolsModules(
'prettyPrint.svg',
'regex-match.svg',
'reload.svg',
'replay-pause.svg',
'replay-resume.svg',
'resume.svg',
'rewind.svg',
'search.svg',
'stepIn.svg',
'stepOut.svg',

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

@ -1,7 +0,0 @@
<!-- 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/. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M11.5 2.5V13.5" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
<path d="M4.5 2.5V13.5" stroke="black" stroke-width="1.5" stroke-linecap="round"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 482 B

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

@ -1,6 +0,0 @@
<!-- 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/. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 13.5329V2.46713C4 2.26746 4.22254 2.14836 4.38868 2.25912L12.688 7.79199C12.8364 7.89094 12.8364 8.10906 12.688 8.20801L4.38868 13.7409C4.22254 13.8516 4 13.7325 4 13.5329Z" stroke="black" stroke-width="1.5" stroke-linejoin="round"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 561 B

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

@ -1,6 +0,0 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
<path d="M12 13V3L5 8l7 5zm1 0c0 .81-.92 1.31-1.58.84l-7-5.03a1 1 0 0 1 0-1.62l7-5.03c.66-.47 1.58.03 1.58.84v10z"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 422 B

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

@ -134,31 +134,3 @@ export function resume(cx: ThreadContext) {
}
};
}
/**
* rewind
* @memberof actions/pause
* @static
* @returns {Function} {@link command}
*/
export function rewind(cx: ThreadContext) {
return ({ dispatch, getState }: ThunkArgs) => {
if (cx.isPaused) {
return dispatch(command(cx, "rewind"));
}
};
}
/**
* reverseStepOver
* @memberof actions/pause
* @static
* @returns {Function} {@link command}
*/
export function reverseStepOver(cx: ThreadContext) {
return ({ dispatch, getState }: ThunkArgs) => {
if (cx.isPaused) {
return dispatch(command(cx, "reverseStepOver"));
}
};
}

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

@ -7,14 +7,13 @@
import {
getSelectedSource,
getSelectedFrame,
getCanRewind,
getClosestBreakpointPosition,
getBreakpoint,
} from "../../selectors";
import { addHiddenBreakpoint } from "../breakpoints";
import { setBreakpointPositions } from "../breakpoints/breakpointPositions";
import { resume, rewind } from "./commands";
import { resume } from "./commands";
import type { ThunkArgs } from "../types";
import type { ThreadContext, SourceLocation } from "../../types";
@ -47,12 +46,6 @@ export function continueToHere(cx: ThreadContext, location: SourceLocation) {
const pauseLocation = column && position ? position.location : location;
// If we're replaying and the user selects a line above the currently
// paused line, lets rewind to it. NOTE: this ignores a couple important
// cases like loops, and wanting to play forward to the next function call.
const action =
getCanRewind(getState()) && line < debugLine ? rewind : resume;
// Set a hidden breakpoint if we do not already have a breakpoint
// at the closest position
if (!getBreakpoint(getState(), pauseLocation)) {
@ -65,6 +58,6 @@ export function continueToHere(cx: ThreadContext, location: SourceLocation) {
);
}
dispatch(action(cx));
dispatch(resume(cx));
};
}

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

@ -15,8 +15,6 @@ export {
stepOver,
stepOut,
resume,
rewind,
reverseStepOver,
seekToPosition,
} from "./commands";
export { fetchFrames } from "./fetchFrames";

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

@ -8,7 +8,6 @@ import { selectLocation } from "../sources";
import { evaluateExpressions } from "../expressions";
import { fetchScopes } from "./fetchScopes";
import assert from "../../utils/assert";
import { getCanRewind } from "../../reducers/threads";
import type { Frame, ThreadContext } from "../../types";
import type { ThunkArgs } from "../types";
@ -33,10 +32,6 @@ export function selectFrame(cx: ThreadContext, frame: Frame) {
frame,
});
if (getCanRewind(getState())) {
client.fetchAncestorFramePositions(frame.index);
}
dispatch(selectLocation(cx, frame.location));
dispatch(evaluateExpressions(cx));

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

@ -162,14 +162,6 @@ function stepOut(thread: string): Promise<*> {
return lookupThreadFront(thread).stepOut();
}
function rewind(thread: string): Promise<*> {
return lookupThreadFront(thread).rewind();
}
function reverseStepOver(thread: string): Promise<*> {
return lookupThreadFront(thread).reverseStepOver();
}
function breakOnNext(thread: string): Promise<*> {
return lookupThreadFront(thread).breakOnNext();
}
@ -220,25 +212,6 @@ function locationKey(location: BreakpointLocation) {
return `${sourceUrl}:${sourceId}:${line}:${column}`;
}
function maybeGenerateLogGroupId(options) {
if (
options.logValue &&
currentTarget.traits &&
currentTarget.traits.canRewind
) {
return { ...options, logGroupId: `logGroup-${Math.random()}` };
}
return options;
}
async function maybeClearLogpoint(location: BreakpointLocation) {
const bp = breakpoints[locationKey(location)];
if (bp && bp.options.logGroupId && currentTarget) {
const consoleFront = await currentTarget.getFront("console");
consoleFront.emit("clearLogpointMessages", bp.options.logGroupId);
}
}
function hasBreakpoint(location: BreakpointLocation) {
return !!breakpoints[locationKey(location)];
}
@ -247,15 +220,12 @@ function setBreakpoint(
location: BreakpointLocation,
options: BreakpointOptions
) {
maybeClearLogpoint(location);
options = maybeGenerateLogGroupId(options);
breakpoints[locationKey(location)] = { location, options };
return forEachThread(thread => thread.setBreakpoint(location, options));
}
function removeBreakpoint(location: PendingLocation) {
maybeClearLogpoint((location: any));
delete breakpoints[locationKey((location: any))];
return forEachThread(thread => thread.removeBreakpoint(location));
@ -559,8 +529,6 @@ const clientCommands = {
stepIn,
stepOut,
stepOver,
rewind,
reverseStepOver,
breakOnNext,
sourceContents,
getSourceForActor,

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

@ -140,18 +140,10 @@ function threadListChanged() {
actions.updateThreads();
}
function replayFramePositions(
threadFront: ThreadFront,
{ positions, frame, thread }: Object
) {
actions.setFramePositions(positions, frame, thread);
}
const clientEvents = {
paused,
resumed,
newSource,
replayFramePositions,
};
export {

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

@ -373,8 +373,6 @@ export type ThreadFront = {
stepIn: Function => Promise<*>,
stepOver: Function => Promise<*>,
stepOut: Function => Promise<*>,
rewind: Function => Promise<*>,
reverseStepOver: Function => Promise<*>,
breakOnNext: () => Promise<*>,
// FIXME: unclear if SourceId or ActorId here
source: ({ actor: SourceId }) => SourceClient,

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

@ -19,7 +19,6 @@ import {
getActiveSearch,
getQuickOpenEnabled,
getOrientation,
getCanRewind,
} from "../selectors";
import type { OrientationType } from "../reducers/types";
@ -67,7 +66,6 @@ type Props = {
endPanelCollapsed: boolean,
activeSearch: ?ActiveSearchType,
quickOpenEnabled: boolean,
canRewind: boolean,
setActiveSearch: typeof actions.setActiveSearch,
closeActiveSearch: typeof actions.closeActiveSearch,
closeProjectSearch: typeof actions.closeProjectSearch,
@ -319,9 +317,9 @@ class App extends Component<Props, State> {
}
render() {
const { quickOpenEnabled, canRewind } = this.props;
const { quickOpenEnabled } = this.props;
return (
<div className={classnames("debugger", { "can-rewind": canRewind })}>
<div className={classnames("debugger")}>
<A11yIntention>
{this.renderLayout()}
{quickOpenEnabled === true && (
@ -343,7 +341,6 @@ App.childContextTypes = {
};
const mapStateToProps = state => ({
canRewind: getCanRewind(state),
selectedSource: getSelectedSource(state),
startPanelCollapsed: getPaneCollapse(state, "start"),
endPanelCollapsed: getPaneCollapse(state, "end"),

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

@ -19,20 +19,12 @@ html[dir="rtl"] .command-bar {
flex-grow: 1;
}
.command-bar .replay-inactive {
opacity: 0.5;
}
.command-bar .step-position {
color: var(--theme-text-color-inactive);
padding-top: 8px;
margin-inline-end: 4px;
}
.command-bar .replay-active {
color: var(--theme-highlight-blue);
}
.command-bar .active .disable-pausing {
background-color: var(--theme-icon-checked-color);
}

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

@ -12,7 +12,6 @@ import classnames from "classnames";
import { features } from "../../utils/prefs";
import {
getIsWaitingOnBreak,
getCanRewind,
getSkipPausing,
getCurrentThread,
getThreadContext,
@ -82,15 +81,12 @@ type Props = {
cx: ThreadContext,
isWaitingOnBreak: boolean,
horizontal: boolean,
canRewind: boolean,
skipPausing: boolean,
resume: typeof actions.resume,
stepIn: typeof actions.stepIn,
stepOut: typeof actions.stepOut,
stepOver: typeof actions.stepOver,
breakOnNext: typeof actions.breakOnNext,
rewind: typeof actions.rewind,
reverseStepOver: typeof actions.reverseStepOver,
pauseOnExceptions: typeof actions.pauseOnExceptions,
toggleSkipPausing: typeof actions.toggleSkipPausing,
};
@ -200,68 +196,6 @@ class CommandBar extends Component<Props> {
);
}
renderReplayButtons() {
const { cx } = this.props;
const className = cx.isPaused ? "active" : "disabled";
return [
debugBtn(
() => this.props.breakOnNext(cx),
"pause",
!cx.isPaused ? "active" : "disabled",
L10N.getFormatStr("pauseButtonTooltip", formatKey("resume")),
cx.isPaused
),
<div key="divider-1" className="divider" />,
debugBtn(
() => this.props.rewind(cx),
"rewind",
className,
"Rewind Execution",
!cx.isPaused
),
debugBtn(
() => this.props.resume(cx),
"resume",
className,
L10N.getFormatStr("resumeButtonTooltip", formatKey("resume")),
!cx.isPaused
),
<div key="divider-2" className="divider" />,
debugBtn(
() => this.props.reverseStepOver(cx),
"reverseStepOver",
className,
"Reverse step over",
!cx.isPaused
),
debugBtn(
() => this.props.stepOver(cx),
"stepOver",
className,
L10N.getFormatStr("stepOverTooltip", formatKey("stepOver")),
!cx.isPaused
),
<div key="divider-3" className="divider" />,
debugBtn(
() => this.props.stepOut(cx),
"stepOut",
className,
L10N.getFormatStr("stepOutTooltip", formatKey("stepOut")),
!cx.isPaused
),
debugBtn(
() => this.props.stepIn(cx),
"stepIn",
className,
L10N.getFormatStr("stepInTooltip", formatKey("stepIn")),
!cx.isPaused
),
];
}
renderSkipPausingButton() {
const { skipPausing, toggleSkipPausing } = this.props;
@ -297,9 +231,7 @@ class CommandBar extends Component<Props> {
vertical: !this.props.horizontal,
})}
>
{this.props.canRewind
? this.renderReplayButtons()
: this.renderStepButtons()}
{this.renderStepButtons()}
<div className="filler" />
{this.renderSkipPausingButton()}
</div>
@ -314,7 +246,6 @@ CommandBar.contextTypes = {
const mapStateToProps = state => ({
cx: getThreadContext(state),
isWaitingOnBreak: getIsWaitingOnBreak(state, getCurrentThread(state)),
canRewind: getCanRewind(state),
skipPausing: getSkipPausing(state),
});
@ -326,8 +257,6 @@ export default connect<Props, OwnProps, _, _, _, _>(
stepOut: actions.stepOut,
stepOver: actions.stepOver,
breakOnNext: actions.breakOnNext,
rewind: actions.rewind,
reverseStepOver: actions.reverseStepOver,
pauseOnExceptions: actions.pauseOnExceptions,
toggleSkipPausing: actions.toggleSkipPausing,
}

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

@ -1,56 +0,0 @@
/* 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/. */
.theme-light {
--progress-playing-background: hsl(207, 100%, 97%);
--progressbar-background: #ffffff;
--replay-head-background: var(--purple-50);
}
.theme-dark {
--progress-playing-background: #071a2b;
--progressbar-background: #0c0c0d;
--replay-head-background: var(--theme-highlight-purple);
}
body.scrubbing {
user-select: none;
}
.frame-timeline-container {
border-bottom: 1px solid var(--theme-splitter-color);
background-color: var(--accordion-header-background);
padding: 8px;
}
.frame-timeline-bar {
background-color: var(--progressbar-background);
border: 1px solid var(--theme-splitter-color);
width: 100%;
height: 20px;
position: relative;
}
.frame-timeline-marker {
background-color: var(--replay-head-background);
display: inline-block;
opacity: 0.4;
width: 2px;
height: 100%;
}
.scrubbing .frame-timeline-marker,
.scrubbing .frame-timeline-bar,
.scrubbing .frame-timeline-container,
.frame-timeline-marker:hover {
cursor: ew-resize;
}
.frame-timeline-progress {
background-color: var(--progress-playing-background);
width: 50%;
height: 100%;
position: relative;
display: inline-block;
}

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

@ -1,293 +0,0 @@
/* 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 React, { Component } from "react";
import { isEqual } from "lodash";
import { connect } from "../../utils/connect";
import {
getFramePositions,
getSelectedFrame,
getThreadContext,
} from "../../selectors";
import type { SourceLocation, Frame } from "../../types";
import { getSelectedLocation } from "../../reducers/sources";
import actions from "../../actions";
import classnames from "classnames";
import "./FrameTimeline.css";
type Props = {
framePositions: any,
selectedLocation: ?SourceLocation,
previewLocation: typeof actions.previewPausedLocation,
seekToPosition: typeof actions.seekToPosition,
selectedFrame: Frame,
};
type OwnProps = {};
type State = {
scrubbing: boolean,
percentage: number,
displayedLocation: any,
displayedFrame: Frame,
};
function isSameLocation(
frameLocation: SourceLocation,
selectedLocation: ?SourceLocation
) {
if (!frameLocation.sourceUrl || !selectedLocation) {
return;
}
return (
frameLocation.line === selectedLocation.line &&
frameLocation.column === selectedLocation.column &&
selectedLocation.sourceId.includes(frameLocation.sourceUrl)
);
}
function getBoundingClientRect(element: ?HTMLElement) {
if (!element) {
// $FlowIgnore
return;
}
return element.getBoundingClientRect();
}
class FrameTimeline extends Component<Props, State> {
_timeline: ?HTMLElement;
_marker: ?HTMLElement;
constructor(props: Props) {
super(props);
}
state = {
scrubbing: false,
percentage: 0,
displayedLocation: null,
displayedFrame: {},
};
componentDidUpdate(prevProps: Props, prevState: State) {
if (!document.body) {
return;
}
// To please Flow.
const bodyClassList = document.body.classList;
if (this.state.scrubbing && !prevState.scrubbing) {
document.addEventListener("mousemove", this.onMouseMove);
document.addEventListener("mouseup", this.onMouseUp);
bodyClassList.add("scrubbing");
}
if (!this.state.scrubbing && prevState.scrubbing) {
document.removeEventListener("mousemove", this.onMouseMove);
document.removeEventListener("mouseup", this.onMouseUp);
bodyClassList.remove("scrubbing");
}
}
getProgress(clientX: number) {
const { width, left } = getBoundingClientRect(this._timeline);
const progress = ((clientX - left) / width) * 100;
if (progress < 0) {
return 0;
} else if (progress > 99) {
return 99;
}
return progress;
}
getPosition(percentage: ?number) {
const { framePositions } = this.props;
if (!framePositions) {
return;
}
if (!percentage) {
percentage = this.state.percentage;
}
const displayedPositions = framePositions.filter(
point => point.position.kind === "OnStep"
);
const displayIndex = Math.floor(
(percentage / 100) * displayedPositions.length
);
return displayedPositions[displayIndex];
}
displayPreview(percentage: number) {
const { previewLocation } = this.props;
const position = this.getPosition(percentage);
if (position) {
previewLocation(position.location);
}
}
onMouseDown = (event: SyntheticMouseEvent<>) => {
const progress = this.getProgress(event.clientX);
this.setState({ scrubbing: true, percentage: progress });
};
onMouseUp = (event: MouseEvent) => {
const { seekToPosition, selectedLocation } = this.props;
const progress = this.getProgress(event.clientX);
const position = this.getPosition(progress);
this.setState({
scrubbing: false,
percentage: progress,
displayedLocation: selectedLocation,
});
if (position) {
seekToPosition(position);
}
};
onMouseMove = (event: MouseEvent) => {
const percentage = this.getProgress(event.clientX);
this.displayPreview(percentage);
this.setState({ percentage });
};
getProgressForNewFrame() {
const { framePositions, selectedLocation, selectedFrame } = this.props;
this.setState({
displayedLocation: selectedLocation,
displayedFrame: selectedFrame,
});
let progress = 0;
if (!framePositions) {
return progress;
}
const displayedPositions = framePositions.filter(
point => point.position.kind === "OnStep"
);
const index = displayedPositions.findIndex(pos =>
isSameLocation(pos.location, selectedLocation)
);
if (index != -1) {
progress = Math.floor((index / displayedPositions.length) * 100);
this.setState({ percentage: progress });
}
return progress;
}
getVisibleProgress() {
const {
percentage,
displayedLocation,
displayedFrame,
scrubbing,
} = this.state;
const { selectedLocation, selectedFrame } = this.props;
let progress = percentage;
if (
!isEqual(displayedLocation, selectedLocation) &&
displayedFrame.index !== selectedFrame.index &&
!scrubbing
) {
progress = this.getProgressForNewFrame();
}
return progress;
}
renderMarker() {
return (
<div className="frame-timeline-marker" ref={r => (this._marker = r)} />
);
}
renderProgress() {
const progress = this.getVisibleProgress();
let maxWidth = "100%";
if (this._timeline && this._marker) {
const timelineWidth = getBoundingClientRect(this._timeline).width;
const markerWidth = getBoundingClientRect(this._timeline).width;
maxWidth = timelineWidth - markerWidth - 2;
}
return (
<div
className="frame-timeline-progress"
style={{
width: `${progress}%`,
"max-width": maxWidth,
}}
/>
);
}
renderTimeline() {
return (
<div
className="frame-timeline-bar"
onMouseDown={this.onMouseDown}
ref={r => (this._timeline = r)}
>
{this.renderProgress()}
{this.renderMarker()}
</div>
);
}
render() {
const { scrubbing } = this.state;
const { framePositions } = this.props;
if (!framePositions) {
return null;
}
return (
<div className={classnames("frame-timeline-container", { scrubbing })}>
{this.renderTimeline()}
</div>
);
}
}
const mapStateToProps = state => {
const selectedFrame: Frame = (getSelectedFrame(
state,
getThreadContext(state).thread
): any);
return {
framePositions: getFramePositions(state),
selectedLocation: getSelectedLocation(state),
selectedFrame,
};
};
export default connect<Props, OwnProps, _, _, _, _>(
mapStateToProps,
{
seekToPosition: actions.seekToPosition,
previewLocation: actions.previewPausedLocation,
}
)(FrameTimeline);

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

@ -45,7 +45,6 @@ import XHRBreakpoints from "./XHRBreakpoints";
import EventListeners from "./EventListeners";
import DOMMutationBreakpoints from "./DOMMutationBreakpoints";
import WhyPaused from "./WhyPaused";
import FrameTimeline from "./FrameTimeline";
import Scopes from "./Scopes";
@ -523,7 +522,6 @@ class SecondaryPanes extends Component<Props, State> {
return (
<div className="secondary-panes-wrapper">
<CommandBar horizontal={this.props.horizontal} />
<FrameTimeline />
<div
className={classnames(
"secondary-panes",

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

@ -13,7 +13,6 @@ CompiledModules(
'DOMMutationBreakpoints.js',
'EventListeners.js',
'Expressions.js',
'FrameTimeline.js',
'index.js',
'Scopes.js',
'Thread.js',
@ -28,7 +27,6 @@ DevToolsModules(
'DOMMutationBreakpoints.css',
'EventListeners.css',
'Expressions.css',
'FrameTimeline.css',
'Scopes.css',
'SecondaryPanes.css',
'Threads.css',

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

@ -153,15 +153,6 @@ html[dir="rtl"] .img.more-tabs {
mask-image: url(resource://devtools/client/debugger/images/resume.svg);
}
.img.reverseStepOver {
mask-image: url(resource://devtools/client/debugger/images/stepOver.svg);
transform: scaleX(-1);
}
.img.rewind {
mask-image: url(resource://devtools/client/debugger/images/rewind.svg);
}
.img.search {
mask-image: url(resource://devtools/client/debugger/images/search.svg);
}

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

@ -45,7 +45,6 @@
@import url("./components/SecondaryPanes/EventListeners.css");
@import url("./components/SecondaryPanes/DOMMutationBreakpoints.css");
@import url("./components/SecondaryPanes/Expressions.css");
@import url("./components/SecondaryPanes/FrameTimeline.css");
@import url("./components/SecondaryPanes/Frames/Frames.css");
@import url("./components/SecondaryPanes/Frames/Group.css");
@import url("./components/SecondaryPanes/Scopes.css");

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

@ -30,18 +30,10 @@ import type {
ThreadContext,
Previews,
SourceLocation,
ExecutionPoint,
HighlightedCalls,
} from "../types";
export type Command =
| null
| "stepOver"
| "stepIn"
| "stepOut"
| "resume"
| "rewind"
| "reverseStepOver";
export type Command = null | "stepOver" | "stepIn" | "stepOut" | "resume";
// Pause state associated with an individual thread.
type ThreadPauseState = {
@ -49,9 +41,6 @@ type ThreadPauseState = {
isWaitingOnBreak: boolean,
frames: ?(any[]),
framesLoading: boolean,
replayFramePositions: {
[FrameId]: Array<ExecutionPoint>,
},
frameScopes: {
generated: {
[FrameId]: {
@ -283,14 +272,6 @@ function update(
});
}
case "SET_FRAME_POSITIONS":
return updateThreadState({
replayFramePositions: {
...threadState().replayFramePositions,
[action.frame]: action.positions,
},
});
case "BREAK_ON_NEXT":
return updateThreadState({ isWaitingOnBreak: true });

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

@ -108,10 +108,6 @@ export const getAllThreads: Selector<Thread[]> = createSelector(
]
);
export function getCanRewind(state: State) {
return state.threads.traits.canRewind;
}
export function supportsWasm(state: State) {
return features.wasm && state.threads.traits.wasmBinarySource;
}

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

@ -53,7 +53,6 @@ export {
getSelectedFrame,
getSelectedFrames,
getVisibleSelectedFrame,
getFramePositions,
} from "./pause";
// eslint-disable-next-line import/named

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

@ -57,19 +57,3 @@ export const getVisibleSelectedFrame: Selector<?{
};
}
);
export function getFramePositions(state: State) {
const threadId = getCurrentThread(state);
const currentThread = state.pause.threads[threadId];
if (
!currentThread ||
!currentThread.selectedFrameId ||
!currentThread.replayFramePositions
) {
return null;
}
const currentFrameId = currentThread.selectedFrameId;
return currentThread.replayFramePositions[currentFrameId];
}

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

@ -21,7 +21,6 @@ const reasons = {
setWatchpoint: "whyPaused.setWatchpoint",
mutationBreakpoint: "whyPaused.mutationBreakpoint",
interrupted: "whyPaused.interrupted",
replayForcedPause: "whyPaused.replayForcedPause",
// V8
DOM: "whyPaused.breakpoint",

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

@ -569,7 +569,6 @@ function isSelectedFrameSelected(dbg, state) {
async function clearDebuggerPreferences(prefs = []) {
resetSchemaVersion();
asyncStorage.clear();
Services.prefs.clearUserPref("devtools.recordreplay.enabled");
Services.prefs.clearUserPref("devtools.debugger.alphabetize-outline");
Services.prefs.clearUserPref("devtools.debugger.pause-on-exceptions");
Services.prefs.clearUserPref("devtools.debugger.pause-on-caught-exceptions");

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

@ -78,16 +78,6 @@ loader.lazyGetter(
"WhatsNewPanel",
() => require("devtools/client/whats-new/panel").WhatsNewPanel
);
loader.lazyGetter(
this,
"reloadAndRecordTab",
() => require("devtools/client/webreplay/menu.js").reloadAndRecordTab
);
loader.lazyGetter(
this,
"reloadAndStopRecordingTab",
() => require("devtools/client/webreplay/menu.js").reloadAndStopRecordingTab
);
// Other dependencies
loader.lazyRequireGetter(
@ -566,28 +556,6 @@ exports.ToolboxButtons = [
return toolbox.isPaintFlashing;
},
},
{
id: "command-button-replay",
description: l10n("toolbox.buttons.replay"),
isTargetSupported: target =>
Services.prefs.getBoolPref("devtools.recordreplay.enabled") &&
!target.canRewind &&
target.isLocalTab,
onClick: () => reloadAndRecordTab(),
isChecked: () => false,
experimentalURL:
"https://developer.mozilla.org/en-US/docs/Mozilla/Projects/WebReplay",
},
{
id: "command-button-stop-replay",
description: l10n("toolbox.buttons.stopReplay"),
isTargetSupported: target =>
Services.prefs.getBoolPref("devtools.recordreplay.enabled") &&
target.canRewind &&
target.isLocalTab,
onClick: () => reloadAndStopRecordingTab(),
isChecked: () => true,
},
{
id: "command-button-fission-prefs",
description: "DevTools Fission preferences",

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

@ -301,8 +301,6 @@ exports.addMenus = function(doc) {
addTopLevelItems(doc);
addAllToolsToMenu(doc);
require("devtools/client/webreplay/menu").addWebReplayMenu(doc);
};
/**

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

@ -36,11 +36,6 @@ loader.lazyGetter(this, "MenuList", function() {
require("devtools/client/shared/components/menu/MenuList")
);
});
loader.lazyGetter(this, "WebReplayPlayer", function() {
return createFactory(
require("devtools/client/webreplay/components/WebReplayPlayer")
);
});
loader.lazyRequireGetter(
this,
@ -467,17 +462,6 @@ class ToolboxToolbar extends Component {
? DebugTargetInfo({ debugTargetData, L10N, toolbox })
: null;
if (toolbox.target.canRewind) {
return div(
{},
WebReplayPlayer({
toolbox: toolbox,
}),
debugTargetInfo,
toolbar
);
}
return div({}, debugTargetInfo, toolbar);
}
}

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

@ -145,12 +145,6 @@ var gDevToolsBrowser = (exports.gDevToolsBrowser = {
remoteEnabled && win.gMultiProcessBrowser
);
// Enable record/replay menu items?
const recordReplayEnabled = Services.prefs.getBoolPref(
"devtools.recordreplay.enabled"
);
toggleMenuItem("menu_webreplay", recordReplayEnabled);
// The profiler's popup is experimental. The plan is to eventually turn it on
// everywhere, but while it's under active development we don't want everyone
// having it enabled. For now the default pref is to turn it on with Nightly,

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

@ -202,12 +202,8 @@ OptionsPanel.prototype = {
const checkboxInput = this.panelDoc.createElement("input");
checkboxInput.setAttribute("type", "checkbox");
checkboxInput.setAttribute("id", button.id);
const defaultValue =
button.id !== "command-button-replay"
? true
: Services.prefs.getBoolPref("devtools.recordreplay.mvp.enabled");
if (Services.prefs.getBoolPref(button.visibilityswitch, defaultValue)) {
if (Services.prefs.getBoolPref(button.visibilityswitch, true)) {
checkboxInput.setAttribute("checked", true);
}
checkboxInput.addEventListener(
@ -218,24 +214,6 @@ OptionsPanel.prototype = {
checkboxLabel.appendChild(checkboxInput);
checkboxLabel.appendChild(checkboxSpanLabel);
if (button.id === "command-button-replay") {
const experimentalLink = this.panelDoc.createElement("a");
experimentalLink.title = experimentalLink.href = button.experimentalURL;
experimentalLink.textContent = L10N.getStr(
"options.experimentalNotice"
);
// Cannot use a real link when we are in the Browser Toolbox.
experimentalLink.addEventListener("click", e => {
e.preventDefault();
openDocLink(button.experimentalURL, { relatedToCurrent: true });
});
const checkbox = this.panelDoc.createElement("span");
checkbox.className = "experimental-notice";
checkboxLabel.appendChild(checkbox);
checkbox.appendChild(experimentalLink);
}
return checkboxLabel;
};

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

@ -157,17 +157,6 @@ loader.lazyGetter(this, "registerHarOverlay", () => {
return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
});
loader.lazyGetter(
this,
"reloadAndRecordTab",
() => require("devtools/client/webreplay/menu.js").reloadAndRecordTab
);
loader.lazyGetter(
this,
"reloadAndStopRecordingTab",
() => require("devtools/client/webreplay/menu.js").reloadAndStopRecordingTab
);
loader.lazyRequireGetter(
this,
"defaultThreadOptions",
@ -769,12 +758,6 @@ Toolbox.prototype = {
useOnlyShared: true,
}).require;
// The web console is immediately loaded when replaying, so that the
// timeline will always be populated with generated messages.
if (this.target.isReplayEnabled()) {
await this.loadTool("webconsole");
}
this.isReady = true;
const framesPromise = this._listFrames();
@ -2143,12 +2126,7 @@ Toolbox.prototype = {
_commandIsVisible: function(button) {
const { isTargetSupported, isCurrentlyVisible, visibilityswitch } = button;
const defaultValue =
button.id !== "command-button-replay"
? true
: Services.prefs.getBoolPref("devtools.recordreplay.mvp.enabled");
if (!Services.prefs.getBoolPref(visibilityswitch, defaultValue)) {
if (!Services.prefs.getBoolPref(visibilityswitch, true)) {
return false;
}
@ -2869,12 +2847,7 @@ Toolbox.prototype = {
* Tells the target tab to reload.
*/
reloadTarget: function(force) {
if (this.target.canRewind) {
// Recording tabs need to be reloaded in a new content process.
reloadAndRecordTab();
} else {
this.target.reload({ force: force });
}
this.target.reload({ force: force });
},
/**
@ -3540,11 +3513,7 @@ Toolbox.prototype = {
},
closeToolbox: async function() {
const shouldStopRecording = this.target.isReplayEnabled();
await this.destroy();
if (shouldStopRecording) {
reloadAndStopRecordingTab();
}
},
/**

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

@ -182,8 +182,6 @@ function Inspector(toolbox) {
this.onSidebarSelect = this.onSidebarSelect.bind(this);
this.onSidebarShown = this.onSidebarShown.bind(this);
this.onSidebarToggle = this.onSidebarToggle.bind(this);
this.handleThreadPaused = this.handleThreadPaused.bind(this);
this.handleThreadResumed = this.handleThreadResumed.bind(this);
this.onReflowInSelection = this.onReflowInSelection.bind(this);
}
@ -197,18 +195,6 @@ Inspector.prototype = {
// Localize all the nodes containing a data-localization attribute.
localizeMarkup(this.panelDoc);
// When replaying, we need to listen to changes in the target's pause state.
if (this.currentTarget.isReplayEnabled()) {
let dbg = this._toolbox.getPanel("jsdebugger");
if (!dbg) {
dbg = await this._toolbox.loadTool("jsdebugger");
}
this._replayResumed = !dbg.isPaused();
this.currentTarget.threadFront.on("paused", this.handleThreadPaused);
this.currentTarget.threadFront.on("resumed", this.handleThreadResumed);
}
await this.toolbox.targetList.watchTargets(
[this.toolbox.targetList.TYPES.FRAME],
this._onTargetAvailable,
@ -467,10 +453,8 @@ Inspector.prototype = {
// A helper to tell if the target has or is about to navigate.
// this._pendingSelection changes on "will-navigate" and "new-root" events.
// When replaying, if the target is unpaused then we consider it to be
// navigating so that its tree will not be constructed.
const hasNavigated = () => {
return pendingSelection !== this._pendingSelection || this._replayResumed;
return pendingSelection !== this._pendingSelection;
};
// If available, set either the previously selected node or the body
@ -1367,22 +1351,6 @@ Inspector.prototype = {
);
},
/**
* When replaying, reset the inspector whenever the target pauses.
*/
handleThreadPaused() {
this._replayResumed = false;
this.onNewRoot();
},
/**
* When replaying, reset the inspector whenever the target resumes.
*/
handleThreadResumed() {
this._replayResumed = true;
this.onNewRoot();
},
/**
* Handler for "markuploaded" event fired on a new root mutation and after the markup
* view is initialized. Expands the current selected node and restores the saved

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

@ -107,7 +107,6 @@ devtools.jar:
skin/images/command-paintflashing.svg (themes/images/command-paintflashing.svg)
skin/images/command-screenshot.svg (themes/images/command-screenshot.svg)
skin/images/command-responsivemode.svg (themes/images/command-responsivemode.svg)
skin/images/command-replay.svg (themes/images/command-replay.svg)
skin/images/command-pick.svg (themes/images/command-pick.svg)
skin/images/command-pick-accessibility.svg (themes/images/command-pick-accessibility.svg)
skin/images/command-frames.svg (themes/images/command-frames.svg)
@ -170,7 +169,6 @@ devtools.jar:
skin/images/datastore.svg (themes/images/datastore.svg)
skin/images/globe.svg (themes/images/globe.svg)
skin/images/next.svg (themes/images/next.svg)
skin/images/next-circle.svg (themes/images/next-circle.svg)
skin/images/folder.svg (themes/images/folder.svg)
skin/images/sad-face.svg (themes/images/sad-face.svg)
skin/images/shape-swatch.svg (themes/images/shape-swatch.svg)

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

@ -777,11 +777,6 @@ whyPaused.mutationBreakpointRemoved=Removed:
# a JS execution
whyPaused.interrupted=Paused at Execution
# LOCALIZATION NOTE (whyPaused.replayForcedPause): The text that is displayed
# in a info block explaining how the debugger is currently paused in a
# recording.
whyPaused.replayForcedPause=Paused in Recording
# LOCALIZATION NOTE (whyPaused.resumeLimit): The text that is displayed
# in a info block explaining how the debugger is currently paused while stepping
# in or out of the stack

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

@ -33,12 +33,6 @@ browserContentToolboxMenu.accesskey = x
toggleProfilerButtonMenu.label = Enable Profiler Toolbar Icon
toggleProfilerButtonMenu.accesskey = P
devtoolsWebReplay.label = Web Replay
devtoolsRecordNewTab.label = Open New Recording Tab
devtoolsReloadAndRecordTab.label = Reload and Record Tab
devtoolsSaveRecording.label = Save Recording
devtoolsReplayNewTab.label = Load Recording in New Tab
devToolboxMenuItem.label = Toggle Tools
devToolboxMenuItem.accesskey = T

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

@ -231,16 +231,6 @@ application.tooltip=Application Panel
# Keyboard shortcut will be shown inside brackets.
toolbox.buttons.responsive = Responsive Design Mode (%S)
# LOCALIZATION NOTE (toolbox.buttons.replay):
# This is the tooltip of the button in the toolbox toolbar that enables
# the web replay record feature.
toolbox.buttons.replay = Enable WebReplay
# LOCALIZATION NOTE (toolbox.buttons.stopReplay):
# This is the tooltip of the button in the toolbox toolbar that dissables
# the web replay feature.
toolbox.buttons.stopReplay = Disable WebReplay
# LOCALIZATION NOTE (toolbox.buttons.paintflashing):
# This is the tooltip of the paintflashing button in the toolbox toolbar
# that toggles paintflashing.

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

@ -245,22 +245,6 @@ toolbox.debugTargetInfo.targetType.worker=Worker
# appears to be taking a while to do so.
browserToolbox.statusMessage=Browser Toolbox connection status:
# LOCALIZATION NOTE (toolbox.replay.jumpMessage2): This is the label
# shown in the web replay timeline marker
toolbox.replay.jumpMessage2=Jump to %1$S
# LOCALIZATION NOTE (toolbox.replay.resume): This is the text that appears in the
# Replay command bar to prompt the user to resume the program.
toolbox.replay.resume=Resume
# LOCALIZATION NOTE (toolbox.replay.rewind): This is the text that appears in the
# Replay command bar to prompt the user to rewind the program.
toolbox.replay.rewind=Rewind
# LOCALIZATION NOTE (toolbox.replay.pause): This is the text that appears in the
# Replay command bar to prompt the user to pause the program.
toolbox.replay.pause=Pause
# LOCALIZATION NOTE (toolbox.debugTargetErrorPage.title): This is the title
# for the Error view shown by the toolbox when a connection to a debug target
# could not be made
@ -276,11 +260,6 @@ toolbox.debugTargetErrorPage.description = Cannot connect to the debug target. S
# This entire text is treated as a link to an MDN page.
options.deprecationNotice=Deprecated. Learn More…
# LOCALIZATION NOTE (options.experimentalNotice): This is the text that appears in the
# settings panel for the checkbox that enables Replay.
# This entire text is treated as a link to an MDN page.
options.experimentalNotice=Experimental. Learn More…
# LOCALIZATION NOTE (options.enableMultiProcessToolbox): This is the text that appears in the
# settings panel for the checkbox that enables the Multiprocess Browser Toolbox.
options.enableMultiProcessToolbox=Enable the Multiprocess Browser Toolbox (requires restarting the Browser Toolbox)

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

@ -222,19 +222,6 @@ webconsole.menu.exportSubmenu.exportCliboard.label=Clipboard
# the output of the console.
webconsole.menu.exportSubmenu.exportFile.label=File
# LOCALIZATION NOTE (webconsole.menu.timeWarp.label)
# Label used for a context-menu item displayed for any log. Clicking on it will
# jump to the execution point where the log item was generated.
webconsole.menu.timeWarp.label=Jump here
# LOCALIZATION NOTE (webconsole.jumpButton.tooltip)
# Label used for the tooltip on the "jump" button in the console. It's displayed when
# the user recorded execution with WebReplay, is now paused in the debugger, and hover a
# message in the console output. Clicking on it will jump to the execution point where the
# log item was generated.
# Parameters: %S is the level of the message.
webconsole.jumpButton.tooltip=%S - Jump here
# LOCALIZATION NOTE (webconsole.clearButton.tooltip)
# Label used for the tooltip on the clear logs button in the console top toolbar bar.
# Clicking on it will clear the content of the console.

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

@ -27,7 +27,6 @@ DIRS += [
'styleeditor',
'themes',
'webconsole',
'webreplay',
'whats-new',
]

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

@ -21,7 +21,6 @@ const {
} = require("devtools/shared/inspector/css-logic");
const InspectorUtils = require("InspectorUtils");
const Debugger = require("Debugger");
const ReplayInspector = require("devtools/server/actors/replay/inspector");
// Set up a dummy environment so that EventUtils works. We need to be careful to
// pass a window object into each EventUtils method we call rather than having
@ -305,11 +304,6 @@ var TestActor = (exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
},
get content() {
// When replaying, the content window is in the replaying process. We can't
// use isReplaying here because this actor is loaded into its own sandbox.
if (Debugger.recordReplayProcessKind() == "Middleman") {
return ReplayInspector.window;
}
return this.targetActor.window;
},

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

@ -1,7 +0,0 @@
<!-- 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/. -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="context-fill #0c0c0d">
<circle cx="8" cy="8" r="7" fill="none" stroke="context-fill" stroke-width="2"/>
<circle cx="8" cy="8" r="4"/>
</svg>

До

Ширина:  |  Высота:  |  Размер: 446 B

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

@ -1,9 +0,0 @@
<!-- 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/. -->
<svg viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" fill="#0274E8">
<path d="M8,0 C3.581722,-2.705415e-16 5.41083001e-16,3.581722 0,8 C-5.41083001e-16,12.418278 3.581722,16 8,16 C12.418278,16 16,12.418278 16,8 C16,5.87826808 15.1571453,3.84343678 13.6568542,2.34314575 C12.1565632,0.842854723 10.1217319,1.2991861e-16 8,0 Z M8,15 C4.13400675,15 1,11.8659932 1,8 C1,4.13400675 4.13400675,1 8,1 C11.8659932,1 15,4.13400675 15,8 C15,9.85651543 14.2625021,11.6369928 12.9497475,12.9497475 C11.6369928,14.2625021 9.85651543,15 8,15 Z" id="Shape"></path>
<path d="M11.5,4 C11.2238576,4 11,4.22385763 11,4.5 L11,7.5 C10.9257751,7.36047643 10.8195383,7.24053164 10.69,7.15 L5.57,3.63 C5.2714387,3.432438 4.89004177,3.40955419 4.57,3.57 C4.22912746,3.732229 4.00866545,4.0725914 4,4.45 L4,11.55 C4.00294215,11.9207587 4.21077995,12.2594573 4.54,12.43 C4.68034771,12.5091766 4.83885991,12.5505276 5,12.55 C5.20390805,12.5495172 5.4027955,12.4867107 5.57,12.37 L10.69,8.82 C10.8195383,8.72946836 10.9257751,8.60952357 11,8.47 L11,11.47 C11,11.7461424 11.2238576,11.97 11.5,11.97 C11.7761424,11.97 12,11.7461424 12,11.47 L12,4.47 C11.9841101,4.20563806 11.7648386,3.99952289 11.5,4 Z M5,11.55 L5,4.45 L10.12,8 L5,11.55 Z" id="Shape"></path>
</svg>

До

Ширина:  |  Высота:  |  Размер: 1.4 KiB

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

@ -307,11 +307,6 @@
fill-opacity: 0.25;
}
#command-button-stop-replay::before,
#command-button-replay::before {
background-image: url("chrome://devtools/skin/images/command-replay.svg");
}
#command-button-fission-prefs::before {
content: "FIS";
font-weight: bold;
@ -328,16 +323,6 @@
height: 12px;
}
#command-button-replay,
#command-button-stop-replay {
background-color: transparent;
}
#command-button-replay:hover,
#command-button-stop-replay:hover {
background: var(--toolbarbutton-background);
}
#command-button-rulers::before {
background-image: url("chrome://devtools/skin/images/command-rulers.svg");
}
@ -429,289 +414,3 @@
background-color: var(--theme-toolbar-hover);
z-index: 1;
}
/*. webreplay */
.webreplay-player {
-moz-appearance: none;
background: var(--theme-tab-toolbar-background);
border-bottom: 1px solid var(--theme-splitter-color);
box-sizing: border-box;
min-height: 29px;
--progressbar-transition: 200ms;
}
.theme-light .webreplay-player {
--commandbar-button-hover-background: #efefef;
--progress-recording-background: hsl(0, 100%, 97%);
--progress-playing-background: hsl(207, 100%, 97%);
--recording-marker-background: hsl(14.9, 100%, 67%);
--replaying-marker-background: var(--blue-40);
--replaying-marker-highlighted-background: var(--blue-60);
--replaying-marker-location-background: var(--blue-50);
--recording-marker-background-hover: hsl(14.9, 100%, 47%);
--replaying-marker-background-hover: var(--blue-60);
--progress-recording-line: #d0021b;
--progressbar-background: #fff;
--progressbar-line-color: var(--blue-40);
--proggressbar-border-color: var(--theme-splitter-color);
--tick-future-background: #bfc9d2;
--tick-background: var(--blue-50);
--tick-recording-background: #d0021b;
--replay-head-background: var(--purple-50);
}
.theme-dark .webreplay-player {
--commandbar-button-hover-background: #1a1a1a;
--progress-recording-background: #310707;
--progress-playing-background: #071a2b;
--progress-recording-line: #ff2038;
--recording-marker-background: #9b3131;
--recording-marker-background-hover: #a82323;
--replaying-marker-background: #266fb1;
--replaying-marker-highlighted-background: #3084d0;
--replaying-marker-location-background: #3084d0;
--replaying-marker-background-hover: #3a8edb;
--progressbar-background: #0c0c0d;
--proggressbar-border-color: var(--theme-splitter-color);
--progressbar-line-color: #0a4786;
--tick-future-background: #bfc9d2;
--tick-background: var(--blue-50);
--tick-recording-background: #e77884;
--replay-head-background: var(--theme-highlight-purple);
}
.webreplay-player .overlay-container {
display: flex;
}
.webreplay-player .progressBar {
position: relative;
width: 100%;
height: 20px;
background: var(--progressbar-background);
margin: 4px 10px 4px 0;
border: 1px solid var(--proggressbar-border-color);
overflow: hidden;
}
.webreplay-player .progress {
position: absolute;
width: 100%;
height: 100%;
background: var(--progress-playing-background);
transition-duration: var(--progressbar-transition);
}
.webreplay-player #overlay:not(.recording) .progress::after {
background: var(--replay-head-background);
width: 1px;
height: 100%;
right: -0.5px;
opacity: 0.4;
display: block;
content: "";
position: absolute;
}
.webreplay-player .recording .progress {
background: var(--progress-recording-background);
transition-duration: var(--progressbar-transition);
}
.webreplay-player .message {
position: absolute;
height: 100%;
width: 7px;
height: 7px;
border-radius: 4.5px;
top: calc(50% - 3.5px);
background: var(--replaying-marker-background);
}
.webreplay-player .animate .message {
transition-duration: 100ms;
}
.webreplay-player .message.overlayed {
border: 1px solid var(--progress-playing-background);
top: 5.5px;
}
.webreplay-player .message.overlayed.future {
border-color: var(--progressbar-background);
}
.webreplay-player .message.highlighted {
background-color: var(--replaying-marker-highlighted-background);
transform: scale(1.25);
transition-duration: 100ms;
}
.webreplay-player .message.uncached {
opacity: 0.5;
}
.webreplay-player .message.location {
background: var(--replaying-marker-location-background);
}
.webreplay-player .recording .message.highlighted {
background-color: var(--recording-marker-background-hover);
}
.webreplay-player .recording .message.overlayed {
border-color: var(--progress-recording-background);
}
.webreplay-player .recording .message {
background: var(--recording-marker-background);
}
.webreplay-player .recording .message:hover {
background: var(--recording-marker-background-hover);
}
.webreplay-player .message:hover {
background: var(--replaying-marker-background-hover);
cursor: pointer;
}
.webreplay-player .message:hover::before {
transform: scale(0.1);
}
.webreplay-player .commands {
display: flex;
margin: 0 4px;
}
.webreplay-player .command-button {
display: flex;
min-width: 20px;
}
.webreplay-player .command-button.primary {
min-width: 22px;
}
.webreplay-player .btn {
width: 14px;
height: 14px;
mask-size: 14px;
background: var(--theme-icon-color);
align-self: center;
margin: 0 auto;
}
.webreplay-player .primary .btn {
width: 18px;
height: 18px;
mask-size: 18px;
}
.webreplay-player .command-button.active:hover {
background: var(--commandbar-button-hover-background);
cursor: pointer;
}
.webreplay-player .command-button.active {
opacity: 1;
}
.webreplay-player div.command-button .rewind {
transform: scaleX(-1);
}
.webreplay-player div.command-button .previous {
transform: scaleX(-1);
margin-left: 8px;
}
.webreplay-player div.command-button .next {
margin-right: 8px;
}
.webreplay-player .progress-line {
width: 0%;
height: 1px;
background: var(--progressbar-line-color);
position: absolute;
left: 0;
top: 50%;
transition-duration: var(--progressbar-transition);
}
.webreplay-player .progress-line.end {
opacity: 0.3;
}
.webreplay-player .recording .progress-line {
background: var(--progress-recording-line);
opacity: 0.3;
}
.webreplay-player .tick {
position: absolute;
height: 100%;
}
.webreplay-player .tick::before,
.webreplay-player .tick::after {
height: 1.5px;
width: 1px;
right: 0;
position: absolute;
content: "";
display: block;
}
.webreplay-player .recording .tick::before,
.webreplay-player .recording .tick::after {
background: var(--tick-recording-background);
}
.webreplay-player .tick.future::before,
.webreplay-player .tick.future::after {
background: var(--tick-future-background);
}
.webreplay-player .tick::before,
.webreplay-player .tick::after {
background: var(--tick-background);
}
.webreplay-player .tick::after {
bottom: 0;
}
.webreplay-player .tick::before {
top: 0;
}
.webreplay-player #overlay:hover .tick {
opacity: 1;
}
.webreplay-player #overlay .tick {
opacity: 0.5;
}
.webreplay-player #overlay .tick:hover ~ .tick,
.webreplay-player #overlay .tick.highlight ~ .tick:not(.highlight) {
opacity: 0.5;
}
.webreplay-player .untraversed {
position: absolute;
height: 100%;
background: #000000;
opacity: 0.2;
}
.webreplay-player .unscanned {
position: absolute;
height: 100%;
background: #000000;
opacity: 0.1;
}

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

@ -130,15 +130,6 @@ a {
border-bottom-width: 0;
}
.can-rewind .webconsole-output .message:last-of-type {
border-bottom: 1px solid var(--purple-50);
}
.can-rewind .webconsole-output .paused ~ .message:last-of-type,
.can-rewind .webconsole-output .paused:last-of-type {
border-bottom-width: 0;
}
/*
* By default, prevent any element in message to overflow.
* We exclude network messages as it may cause issues in the network detail panel.
@ -294,26 +285,11 @@ a {
background-image: url(chrome://devtools/skin/images/webconsole/navigation.svg);
}
.message:hover > .icon.rewindable {
background-image: url(chrome://devtools/skin/images/next-circle.svg);
cursor: pointer;
transform: rotate(180deg);
}
.message > .icon.logpoint {
background-image: url(resource://devtools/client/debugger/images/webconsole-logpoint.svg);
color: var(--theme-graphs-purple);
}
/*
* we flip the next.svg icon by default because when we're
* not paused, we would jump back. We remove the transform here
* because we want to jump forward.
*/
.message.paused ~ .message:hover .icon.rewindable {
transform: none;
}
.message > .message-body-wrapper {
flex: auto;
min-width: 0px;

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

@ -214,11 +214,6 @@ function terminalInputChanged(expression) {
return;
}
// The server does not support eager evaluation when replaying.
if (hud.currentTarget.isReplayEnabled()) {
return;
}
const { terminalInput = "" } = getState().history;
// Only re-evaluate if the expression did change.
if (

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

@ -59,16 +59,6 @@ loader.lazyRequireGetter(
"devtools/client/webconsole/utils/messages",
true
);
ChromeUtils.defineModuleGetter(
this,
"pointPrecedes",
"resource://devtools/shared/execution-point-utils.js"
);
ChromeUtils.defineModuleGetter(
this,
"pointEquals",
"resource://devtools/shared/execution-point-utils.js"
);
const { UPDATE_REQUEST } = require("devtools/client/netmonitor/src/constants");
@ -76,8 +66,6 @@ const {
processNetworkUpdates,
} = require("devtools/client/netmonitor/src/utils/request-utils");
const maxNumber = 100000;
const MessageState = overrides =>
Object.freeze(
Object.assign(
@ -109,12 +97,6 @@ const MessageState = overrides =>
// Map of the form {messageId : networkInformation}
// `networkInformation` holds request, response, totalTime, ...
networkMessagesUpdateById: {},
// Set of logpoint IDs that have been removed
removedLogpointIds: new Set(),
// Any execution point we are currently paused at, when replaying.
pausedExecutionPoint: null,
// Whether any messages with execution points have been seen.
hasExecutionPoints: false,
},
overrides
)
@ -132,9 +114,6 @@ function cloneState(state) {
frontsToRelease: [...state.frontsToRelease],
repeatById: { ...state.repeatById },
networkMessagesUpdateById: { ...state.networkMessagesUpdateById },
removedLogpointIds: new Set(state.removedLogpointIds),
pausedExecutionPoint: state.pausedExecutionPoint,
hasExecutionPoints: state.hasExecutionPoints,
warningGroupsById: new Map(state.warningGroupsById),
};
}
@ -158,16 +137,6 @@ function addMessage(newMessage, state, filtersState, prefsState, uiState) {
return state;
}
// After messages with a given logpoint ID have been removed, ignore all
// future messages with that ID.
if (
newMessage.logpointId &&
state.removedLogpointIds &&
state.removedLogpointIds.has(newMessage.logpointId)
) {
return state;
}
if (newMessage.type === constants.MESSAGE_TYPE.END_GROUP) {
// Compute the new current group.
state.currentGroup = getNewCurrentGroup(currentGroup, groupsById);
@ -193,27 +162,7 @@ function addMessage(newMessage, state, filtersState, prefsState, uiState) {
newMessage.indent = parentGroups.length;
}
ensureExecutionPoint(state, newMessage);
if (newMessage.executionPoint) {
state.hasExecutionPoints = true;
}
// When replaying, we might get two messages with the same execution point and
// logpoint ID. In this case the first message is provisional and should be
// removed.
const removedIds = [];
if (newMessage.logpointId) {
const existingMessage = [...state.messagesById.values()].find(existing => {
return (
existing.logpointId == newMessage.logpointId &&
pointEquals(existing.executionPoint, newMessage.executionPoint)
);
});
if (existingMessage) {
removedIds.push(existingMessage.id);
}
}
// Check if the current message could be placed in a Warning Group.
// This needs to be done before setting the new message in messagesById so we have a
@ -381,15 +330,6 @@ function messages(
let newState;
switch (action.type) {
case constants.PAUSED_EXECUTION_POINT:
if (
state.pausedExecutionPoint &&
action.executionPoint &&
pointEquals(state.pausedExecutionPoint, action.executionPoint)
) {
return state;
}
return { ...state, pausedExecutionPoint: action.executionPoint };
case constants.MESSAGES_ADD:
// Preemptively remove messages that will never be rendered
const list = [];
@ -465,26 +405,6 @@ function messages(
);
}
case constants.MESSAGES_CLEAR_LOGPOINT: {
const removedIds = [];
for (const [id, message] of messagesById) {
if (message.logpointId == action.logpointId) {
removedIds.push(id);
}
}
return removeMessagesFromState(
{
...state,
removedLogpointIds: new Set([
...state.removedLogpointIds,
action.logpointId,
]),
},
removedIds
);
}
case constants.MESSAGE_OPEN:
const openState = { ...state };
openState.messagesUiById = [...messagesUiById, action.id];
@ -1486,70 +1406,6 @@ function getDefaultFiltersCounter() {
return count;
}
// Make sure that message has an execution point which can be used for sorting
// if other messages with real execution points appear later.
function ensureExecutionPoint(state, newMessage) {
if (newMessage.executionPoint) {
return;
}
// Add a lastExecutionPoint property which will group messages evaluated during
// the same replay pause point. When applicable, it will place the message immediately
// after the last visible message in the group without an execution point when sorting.
let point = { checkpoint: 0, progress: 0 },
messageCount = 1;
if (state.pausedExecutionPoint) {
point = state.pausedExecutionPoint;
const lastMessage = getLastMessageWithPoint(state, point);
if (lastMessage.lastExecutionPoint) {
messageCount = lastMessage.lastExecutionPoint.messageCount + 1;
}
} else if (state.visibleMessages.length) {
const lastId = state.visibleMessages[state.visibleMessages.length - 1];
const lastMessage = state.messagesById.get(lastId);
if (lastMessage.executionPoint) {
// If the message is evaluated while we are not paused, we want
// to make sure that those messages are placed immediately after the execution
// point's message.
point = lastMessage.executionPoint;
messageCount = maxNumber + 1;
} else {
point = lastMessage.lastExecutionPoint.point;
messageCount = lastMessage.lastExecutionPoint.messageCount + 1;
}
}
newMessage.lastExecutionPoint = { point, messageCount };
}
function getLastMessageWithPoint(state, point) {
// Find all of the messageIds with no real execution point and the same progress
// value as the given point.
const filteredMessageId = state.visibleMessages.filter(function(p) {
const currentMessage = state.messagesById.get(p);
if (currentMessage.executionPoint) {
return false;
}
return point.progress === currentMessage.lastExecutionPoint.point.progress;
});
const lastMessageId = filteredMessageId[filteredMessageId.length - 1];
return state.messagesById.get(lastMessageId) || {};
}
function messageExecutionPoint(state, id) {
const message = state.messagesById.get(id);
return message.executionPoint || message.lastExecutionPoint.point;
}
function messageCountSinceLastExecutionPoint(state, id) {
const message = state.messagesById.get(id);
return message.lastExecutionPoint
? message.lastExecutionPoint.messageCount
: 0;
}
/**
* Sort state.visibleMessages if needed.
*
@ -1565,44 +1421,6 @@ function maybeSortVisibleMessages(
sortWarningGroupMessage = false,
timeStampSort = false
) {
// When using log points while replaying, messages can be added out of order
// with respect to how they originally executed. Use the execution point
// information in the messages to sort visible messages according to how
// they originally executed. This isn't necessary if we haven't seen any
// messages with execution points, as either we aren't replaying or haven't
// seen any messages yet.
if (state.hasExecutionPoints) {
state.visibleMessages.sort((a, b) => {
const pointA = messageExecutionPoint(state, a);
const pointB = messageExecutionPoint(state, b);
if (pointPrecedes(pointB, pointA)) {
return true;
} else if (pointPrecedes(pointA, pointB)) {
return false;
}
// When messages have the same execution point, they can still be
// distinguished by the number of messages since the last one which did
// have an execution point.
let countA = messageCountSinceLastExecutionPoint(state, a);
let countB = messageCountSinceLastExecutionPoint(state, b);
// Messages with real execution points will not have a message count.
// We overwrite that with maxNumber so that we can differentiate A) messages
// from evaluations while replaying a paused point and B) messages from evaluations
// when not replaying a paused point.
if (pointA.progress === pointB.progress) {
if (!countA) {
countA = maxNumber;
} else if (!countB) {
countB = maxNumber;
}
}
return countA > countB;
});
}
if (state.warningGroupsById.size > 0 && sortWarningGroupMessage) {
function getNaturalOrder(messageA, messageB) {
const aFirst = -1;
@ -1716,6 +1534,3 @@ function shouldGroupWarningMessages(
}
exports.messages = messages;
// Export for testing purpose.
exports.ensureExecutionPoint = ensureExecutionPoint;

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

@ -1,9 +1,6 @@
{
"name": "webconsole-tests",
"version": "0.0.1",
"engines": {
"node": ">=8.9.4"
},
"scripts": {
"//": [
"Here's the script to run tests with `npm test`. Here's what it does: ",

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

@ -12,9 +12,6 @@ const {
getAllMessagesById,
getVisibleMessages,
} = require("devtools/client/webconsole/selectors/messages");
const {
ensureExecutionPoint,
} = require("devtools/client/webconsole/reducers/messages");
const {
clonePacket,
@ -43,15 +40,10 @@ describe("Message reducer:", () => {
it("adds a message to an empty store", () => {
const { dispatch, getState } = setupStore();
// Retrieve the store before adding the message to not pollute the
// ensureExecutionPoint results.
const state = getState();
const packet = stubPackets.get("console.log('foobar', 'test')");
dispatch(actions.messagesAdd([packet]));
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
ensureExecutionPoint(state.messages, message);
expect(getFirstMessage(getState())).toEqual(message);
});

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

@ -1,770 +0,0 @@
/* 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/. */
"use strict";
const { Component } = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { sortBy, range } = require("devtools/client/shared/vendor/lodash");
ChromeUtils.defineModuleGetter(
this,
"pointEquals",
"resource://devtools/shared/execution-point-utils.js"
);
const { LocalizationHelper } = require("devtools/shared/l10n");
const L10N = new LocalizationHelper(
"devtools/client/locales/toolbox.properties"
);
const getFormatStr = (key, a) => L10N.getFormatStr(`toolbox.replay.${key}`, a);
const { div } = dom;
const markerWidth = 7;
const imgResource = "resource://devtools/client/debugger/images";
const imgChrome = "chrome://devtools/skin/images";
const shouldLog = false;
function classname(name, bools) {
for (const key in bools) {
if (bools[key]) {
name += ` ${key}`;
}
}
return name;
}
function log(message) {
if (shouldLog) {
console.log(message);
}
}
function isError(message) {
return message.source === "javascript" && message.level === "error";
}
function CommandButton({ img, className, onClick, active }) {
const images = {
rewind: "replay-resume",
resume: "replay-resume",
next: "next",
previous: "next",
pause: "replay-pause",
play: "replay-resume",
};
const filename = images[img];
const path = filename == "next" ? imgChrome : imgResource;
const attrs = {
className: classname(`command-button ${className}`, { active }),
onClick,
};
if (active) {
attrs.title = L10N.getStr(`toolbox.replay.${img}`);
}
return dom.div(
attrs,
dom.img({
className: `btn ${img} ${className}`,
style: {
maskImage: `url("${path}/${filename}.svg")`,
},
})
);
}
function getMessageProgress(message) {
return getProgress(message.executionPoint);
}
function getProgress(executionPoint) {
return executionPoint && executionPoint.progress;
}
function getClosestMessage(messages, executionPoint) {
const progress = getProgress(executionPoint);
return sortBy(messages, message =>
Math.abs(progress - getMessageProgress(message))
)[0];
}
function sameLocation(m1, m2) {
const f1 = m1.frame;
const f2 = m2.frame;
return (
f1.source === f2.source && f1.line === f2.line && f1.column === f2.column
);
}
function getMessageLocation(message) {
const {
frame: { source, line, column },
} = message;
return { sourceUrl: source, line, column };
}
/*
*
* The player has 4 valid states
* - Paused: (paused, !recording, !seeking)
* - Playing: (!paused, !recording, !seeking)
* - Seeking: (!paused, !recording, seeking)
* - Recording: (!paused, recording, !seeking)
*
*/
class WebReplayPlayer extends Component {
static get propTypes() {
return {
toolbox: PropTypes.object,
};
}
constructor(props) {
super(props);
this.state = {
executionPoint: null,
recordingEndpoint: null,
seeking: false,
recording: true,
paused: false,
messages: [],
highlightedMessage: null,
hoveredMessageOffset: null,
unscannedRegions: [],
cachedPoints: [],
shouldAnimate: true,
start: 0,
end: 1,
};
this.lastPaint = null;
this.hoveredMessage = null;
this.overlayWidth = 1;
this.onProgressBarClick = this.onProgressBarClick.bind(this);
this.onProgressBarMouseOver = this.onProgressBarMouseOver.bind(this);
this.onPlayerMouseLeave = this.onPlayerMouseLeave.bind(this);
}
componentDidMount() {
this.overlayWidth = this.updateOverlayWidth();
this.threadFront.on("paused", this.onPaused.bind(this));
this.threadFront.on("resumed", this.onResumed.bind(this));
this.threadFront.on("replayStatusUpdate", this.onStatusUpdate.bind(this));
this.toolbox.getPanelWhenReady("webconsole").then(panel => {
const consoleFrame = panel.hud.ui;
consoleFrame.on("message-hover", this.onConsoleMessageHover.bind(this));
consoleFrame.wrapper.subscribeToStore(this.onConsoleUpdate.bind(this));
});
}
componentDidUpdate(prevProps, prevState) {
this.overlayWidth = this.updateOverlayWidth();
if (prevState.closestMessage != this.state.closestMessage) {
this.scrollToMessage(this.state.closestMessage);
}
}
get toolbox() {
return this.props.toolbox;
}
get console() {
return this.toolbox.getPanel("webconsole");
}
get threadFront() {
return this.toolbox.threadFront;
}
isCached(message) {
if (!message.executionPoint) {
return false;
}
return this.state.cachedPoints.includes(message.executionPoint.progress);
}
isRecording() {
return !this.isPaused() && this.state.recording;
}
isReplaying() {
return !this.isPaused() && !this.state.recording;
}
isPaused() {
return this.state.paused;
}
isSeeking() {
return this.state.seeking;
}
getTickSize() {
const { start, end } = this.state;
const minSize = 10;
if (!start && !end) {
return minSize;
}
const maxSize = this.overlayWidth / 10;
const ratio = end - start;
return (1 - ratio) * maxSize + minSize;
}
getClosestMessage(point) {
return getClosestMessage(this.state.messages, point);
}
getMousePosition(e) {
const { start, end } = this.state;
const { left, width } = e.currentTarget.getBoundingClientRect();
const clickLeft = e.clientX;
const clickPosition = (clickLeft - left) / width;
return (end - start) * clickPosition + start;
}
paint(point) {
if (point && this.lastPaint !== point) {
this.lastPaint = point;
this.threadFront.paint(point);
}
}
onPaused(packet) {
if (packet && packet.recordingEndpoint) {
const { executionPoint, recordingEndpoint } = packet;
const closestMessage = this.getClosestMessage(executionPoint);
const pausedMessage = this.state.messages
.filter(message => message.executionPoint)
.find(message => pointEquals(message.executionPoint, executionPoint));
this.setState({
executionPoint,
recordingEndpoint,
paused: true,
seeking: false,
recording: false,
closestMessage,
pausedMessage,
});
}
}
onResumed(packet) {
this.setState({ paused: false, closestMessage: null, pausedMessage: null });
}
onStatusUpdate({ status }) {
const {
recording,
executionPoint,
unscannedRegions,
cachedPoints,
} = status;
log(`progress: ${recording ? "rec" : "play"} ${executionPoint.progress}`);
if (this.state.seeking) {
return;
}
// We want to prevent responding to interrupts
if (this.isRecording() && !recording) {
return;
}
const newState = {
recording,
executionPoint,
unscannedRegions,
cachedPoints,
};
if (recording) {
newState.recordingEndpoint = executionPoint;
newState.shouldAnimate = true;
}
this.setState(newState);
}
onConsoleUpdate(consoleState) {
const {
messages: { visibleMessages, messagesById },
} = consoleState;
if (visibleMessages != this.state.visibleMessages) {
let messages = visibleMessages
.map(id => messagesById.get(id))
.filter(message => message.source == "console-api" || isError(message));
messages = sortBy(messages, message => getMessageProgress(message));
this.setState({ messages, visibleMessages, shouldAnimate: false });
}
}
onConsoleMessageHover(type, message) {
if (type == "mouseleave") {
return this.setState({ highlightedMessage: null });
}
if (type == "mouseenter") {
return this.setState({ highlightedMessage: message.id });
}
return null;
}
setTimelinePosition({ position, direction }) {
this.setState({ [direction]: position });
}
findMessage(message) {
const consoleOutput = this.console.hud.ui.outputNode;
return consoleOutput.querySelector(
`.message[data-message-id="${message.id}"]`
);
}
scrollToMessage(message) {
if (!message) {
return;
}
const element = this.findMessage(message);
const consoleOutput = this.console.hud.ui.outputNode;
if (element) {
const consoleHeight = consoleOutput.getBoundingClientRect().height;
const elementTop = element.getBoundingClientRect().top;
if (elementTop < 30 || elementTop + 50 > consoleHeight) {
element.scrollIntoView({ block: "center", behavior: "smooth" });
}
}
}
unhighlightConsoleMessage() {
if (this.hoveredMessage) {
this.hoveredMessage.classList.remove("highlight");
}
}
highlightConsoleMessage(message) {
if (!message) {
return;
}
const element = this.findMessage(message);
if (!element) {
return;
}
this.unhighlightConsoleMessage();
element.classList.add("highlight");
this.hoveredMessage = element;
}
showMessage(message) {
this.highlightConsoleMessage(message);
this.scrollToMessage(message);
this.paint(message.executionPoint);
}
onMessageMouseEnter(message, offset) {
this.setState({ hoveredMessageOffset: offset });
this.previewLocation(message);
this.showMessage(message);
}
onMessageMouseLeave() {
this.setState({ hoveredMessageOffset: null });
}
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;
}
const direction = e.shiftKey ? "end" : "start";
const position = this.getMousePosition(e);
this.setTimelinePosition({ position, direction });
}
onProgressBarMouseOver(e) {
const mousePosition = this.getMousePosition(e) * 100;
const closestMessage = sortBy(this.state.messages, message =>
Math.abs(this.getVisiblePercent(message.executionPoint) - mousePosition)
).filter(message => this.isCached(message))[0];
if (!closestMessage) {
return;
}
this.showMessage(closestMessage);
}
onPlayerMouseLeave() {
this.unhighlightConsoleMessage();
this.clearPreviewLocation();
return this.threadFront.paintCurrentPoint();
}
seek(executionPoint) {
if (!executionPoint) {
return null;
}
// set seeking to the current execution point to avoid a progress bar jump
this.setState({ seeking: true });
return this.threadFront.timeWarp(executionPoint);
}
next() {
if (!this.isPaused()) {
return null;
}
const { messages, executionPoint, recordingEndpoint } = this.state;
let seekPoint = messages
.map(m => m.executionPoint)
.filter(point => point.progress > executionPoint.progress)
.slice(0)[0];
if (!seekPoint) {
seekPoint = recordingEndpoint;
}
return this.seek(seekPoint);
}
async previous() {
if (this.isRecording()) {
await this.threadFront.interrupt();
}
if (!this.isPaused()) {
return null;
}
const { messages, executionPoint } = this.state;
const seekPoint = messages
.map(m => m.executionPoint)
.filter(point => point.progress < executionPoint.progress)
.slice(-1)[0];
return this.seek(seekPoint);
}
resume() {
if (!this.isPaused()) {
return null;
}
return this.threadFront.resume();
}
async rewind() {
return this.threadFront.rewind();
}
pause() {
if (this.isPaused()) {
return null;
}
return this.threadFront.interrupt();
}
renderCommands() {
const paused = this.isPaused();
const recording = this.isRecording();
const seeking = this.isSeeking();
return [
CommandButton({
className: "",
active: paused || recording,
img: "rewind",
onClick: () => this.rewind(),
}),
CommandButton({
className: "primary",
active: !paused || seeking,
img: "pause",
onClick: () => this.pause(),
}),
CommandButton({
className: "",
active: paused,
img: "resume",
onClick: () => this.resume(),
}),
];
}
updateOverlayWidth() {
const el = ReactDOM.findDOMNode(this).querySelector(".progressBar");
return el ? el.clientWidth : 1;
}
// calculate pixel distance from two points
getDistanceFrom(to, from) {
const toPercent = this.getPercent(to);
const fromPercent = this.getPercent(from);
return ((toPercent - fromPercent) * this.overlayWidth) / 100;
}
getOffset(point) {
const percent = this.getPercent(point);
return (percent * this.overlayWidth) / 100;
}
getPercent(executionPoint) {
const { recordingEndpoint } = this.state;
if (!recordingEndpoint) {
return 100;
}
if (!executionPoint) {
return 0;
}
const ratio = executionPoint.progress / recordingEndpoint.progress;
return ratio * 100;
}
getVisiblePercent(executionPoint) {
const { start, end } = this.state;
const position = this.getPercent(executionPoint) / 100;
if (position < start || position > end) {
return -1;
}
return ((position - start) / (end - start)) * 100;
}
getVisibleOffset(point) {
const percent = this.getVisiblePercent(point);
return (percent * this.overlayWidth) / 100;
}
renderMessage(message, index) {
const {
messages,
executionPoint,
pausedMessage,
highlightedMessage,
} = this.state;
const offset = this.getVisibleOffset(message.executionPoint);
const previousMessage = messages[index - 1];
if (offset < 0) {
return null;
}
// Check to see if two messages overlay each other on the timeline
const isOverlayed =
previousMessage &&
this.getDistanceFrom(
message.executionPoint,
previousMessage.executionPoint
) < markerWidth;
// Check to see if a message appears after the current execution point
const isFuture =
this.getDistanceFrom(message.executionPoint, executionPoint) >
markerWidth / 2;
const isHighlighted = highlightedMessage == message.id;
const uncached = message.executionPoint && !this.isCached(message);
const atPausedLocation =
pausedMessage && sameLocation(pausedMessage, message);
let frameLocation = "";
if (message.frame) {
const { source, line, column } = message.frame;
const filename = source.split("/").pop();
frameLocation = `${filename}:${line}`;
if (column > 100) {
frameLocation += `:${column}`;
}
}
return dom.a({
className: classname("message", {
overlayed: isOverlayed,
future: isFuture,
highlighted: isHighlighted,
uncached,
location: atPausedLocation,
}),
style: {
left: `${Math.max(offset - markerWidth / 2, 0)}px`,
zIndex: `${index + 100}`,
},
title: uncached
? "Loading..."
: getFormatStr("jumpMessage2", frameLocation),
onClick: e => {
e.preventDefault();
e.stopPropagation();
this.seek(message.executionPoint);
},
onMouseEnter: () => this.onMessageMouseEnter(message, offset),
onMouseLeave: () => this.onMessageMouseLeave(),
});
}
renderMessages() {
const messages = this.state.messages;
return messages.map((message, index) => this.renderMessage(message, index));
}
renderTicks() {
const tickSize = this.getTickSize();
const ticks = Math.round(this.overlayWidth / tickSize);
return range(ticks).map((value, index) => this.renderTick(index));
}
renderTick(index) {
const { executionPoint, hoveredMessageOffset } = this.state;
const tickSize = this.getTickSize();
const offset = Math.round(this.getOffset(executionPoint));
const position = index * tickSize;
const isFuture = position > offset;
const shouldHighlight = hoveredMessageOffset > position;
return dom.span({
className: classname("tick", {
future: isFuture,
highlight: shouldHighlight,
}),
style: {
left: `${position}px`,
width: `${tickSize}px`,
},
});
}
renderUnscannedRegions() {
return this.state.unscannedRegions.map(
this.renderUnscannedRegion.bind(this)
);
}
renderUnscannedRegion({ start, end, traversed }) {
let startOffset = this.getVisibleOffset({ progress: start });
let endOffset = this.getVisibleOffset({ progress: end });
if (startOffset > this.overlayWidth || endOffset < 0) {
return null;
}
if (startOffset < 0) {
startOffset = 0;
}
if (endOffset > this.overlayWidth) {
endOffset = this.overlayWidth;
}
return dom.span({
className: traversed ? classname("unscanned") : classname("untraversed"),
style: {
left: `${startOffset}px`,
width: `${endOffset - startOffset}px`,
},
});
}
render() {
const percent = this.getVisiblePercent(this.state.executionPoint);
const recording = this.isRecording();
const { shouldAnimate } = this.state;
return div(
{
className: "webreplay-player",
},
div(
{
id: "overlay",
className: classname("", {
recording: recording,
paused: !recording,
}),
onMouseLeave: this.onPlayerMouseLeave,
},
div(
{
className: classname("overlay-container", {
animate: shouldAnimate,
}),
},
div({ className: "commands" }, ...this.renderCommands()),
div(
{
className: "progressBar",
onClick: this.onProgressBarClick,
onDoubleClick: () => this.setState({ start: 0, end: 1 }),
onMouseOver: this.onProgressBarMouseOver,
},
div({
className: "progress",
style: { width: `${percent}%` },
}),
div({
className: "progress-line",
style: { width: `${percent}%` },
}),
div({
className: "progress-line end",
style: { left: `${percent}%`, width: `${100 - percent}%` },
}),
...this.renderMessages(),
...this.renderTicks(),
...this.renderUnscannedRegions()
)
)
)
);
}
}
module.exports = WebReplayPlayer;

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

@ -1,3 +0,0 @@
DevToolsModules(
'WebReplayPlayer.js',
)

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

@ -1,139 +0,0 @@
/* 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/. */
"use strict";
const { Cc, Ci } = require("chrome");
const { LocalizationHelper } = require("devtools/shared/l10n");
const MENUS_L10N = new LocalizationHelper(
"devtools/client/locales/menus.properties"
);
function l10n(key) {
return MENUS_L10N.getStr(key);
}
const ChromeUtils = require("ChromeUtils");
ChromeUtils.defineModuleGetter(
this,
"Services",
"resource://gre/modules/Services.jsm"
);
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyModuleGetters(this, {
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
});
function RecordNewTab() {
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
gBrowser.selectedTab = gBrowser.addWebTab("about:blank", {
recordExecution: "*",
});
Services.telemetry.scalarAdd("devtools.webreplay.new_recording", 1);
}
function ReloadAndRecordTab() {
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
const url = gBrowser.currentURI.spec;
gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, {
recordExecution: "*",
newFrameloader: true,
remoteType: E10SUtils.DEFAULT_REMOTE_TYPE,
});
Services.ppmm.addMessageListener("RecordingInitialized", function listener() {
Services.ppmm.removeMessageListener("RecordingInitialized", listener);
gBrowser.loadURI(url, {
triggeringPrincipal: gBrowser.selectedBrowser.contentPrincipal,
});
});
Services.telemetry.scalarAdd("devtools.webreplay.reload_recording", 1);
}
function ReloadAndStopRecordingTab() {
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
const url = gBrowser.currentURI.spec;
gBrowser.updateBrowserRemoteness(gBrowser.selectedBrowser, {
newFrameloader: true,
remoteType: E10SUtils.DEFAULT_REMOTE_TYPE,
});
gBrowser.loadURI(url, {
triggeringPrincipal: gBrowser.selectedBrowser.contentPrincipal,
});
Services.telemetry.scalarAdd("devtools.webreplay.stop_recording", 1);
}
function SaveRecording() {
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
const fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
const window = gBrowser.ownerGlobal;
fp.init(window, null, Ci.nsIFilePicker.modeSave);
fp.open(rv => {
if (
rv == Ci.nsIFilePicker.returnOK ||
rv == Ci.nsIFilePicker.returnReplace
) {
const remoteTab =
gBrowser.selectedTab.linkedBrowser.frameLoader.remoteTab;
if (!remoteTab || !remoteTab.saveRecording(fp.file.path)) {
window.alert("Current tab is not recording");
}
}
});
Services.telemetry.scalarAdd("devtools.webreplay.save_recording", 1);
}
function ReplayNewTab() {
const { gBrowser } = Services.wm.getMostRecentWindow("navigator:browser");
const fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
const window = gBrowser.ownerGlobal;
fp.init(window, null, Ci.nsIFilePicker.modeOpen);
fp.open(rv => {
if (
rv == Ci.nsIFilePicker.returnOK ||
rv == Ci.nsIFilePicker.returnReplace
) {
gBrowser.selectedTab = gBrowser.addWebTab(null, {
replayExecution: fp.file.path,
});
}
});
Services.telemetry.scalarAdd("devtools.webreplay.load_recording", 1);
}
const menuItems = [
{ id: "devtoolsRecordNewTab", command: RecordNewTab },
{ id: "devtoolsReloadAndRecordTab", command: ReloadAndRecordTab },
{ id: "devtoolsSaveRecording", command: SaveRecording },
{ id: "devtoolsReplayNewTab", command: ReplayNewTab },
];
exports.addWebReplayMenu = function(doc) {
const menu = doc.createXULElement("menu");
menu.id = "menu_webreplay";
menu.setAttribute("label", l10n("devtoolsWebReplay.label"));
menu.setAttribute("hidden", "true");
const popup = doc.createXULElement("menupopup");
popup.id = "menupopup_webreplay";
menu.appendChild(popup);
for (const { id, command } of menuItems) {
const menuitem = doc.createXULElement("menuitem");
menuitem.id = id;
menuitem.setAttribute("label", l10n(id + ".label"));
menuitem.addEventListener("command", command);
popup.appendChild(menuitem);
}
const mds = doc.getElementById("menu_devtools_separator");
if (mds) {
mds.parentNode.insertBefore(menu, mds);
}
};
exports.reloadAndRecordTab = ReloadAndRecordTab;
exports.reloadAndStopRecordingTab = ReloadAndStopRecordingTab;

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

@ -1,45 +0,0 @@
[DEFAULT]
tags = devtools-webreplay
subsuite = devtools-webreplay
skip-if = os != "mac" || debug || !nightly_build
support-files =
head.js
!/devtools/client/shared/test/shared-head.js
!/devtools/client/shared/test/telemetry-test-helpers.js
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
!/devtools/client/debugger/test/mochitest/helpers.js
!/devtools/client/debugger/test/mochitest/helpers/context.js
!/devtools/client/inspector/test/shared-head.js
!/devtools/client/webconsole/test/browser/head.js
examples/*
[browser_dbg_rr_breakpoints-01.js]
[browser_dbg_rr_breakpoints-02.js]
[browser_dbg_rr_breakpoints-03.js]
[browser_dbg_rr_breakpoints-04.js]
[browser_dbg_rr_breakpoints-05.js]
[browser_dbg_rr_breakpoints-06.js]
[browser_dbg_rr_breakpoints-07.js]
[browser_dbg_rr_record.js]
[browser_dbg_rr_stepping-01.js]
[browser_dbg_rr_stepping-02.js]
[browser_dbg_rr_stepping-03.js]
[browser_dbg_rr_stepping-04.js]
[browser_dbg_rr_stepping-05.js]
[browser_dbg_rr_stepping-06.js]
[browser_dbg_rr_replay-01.js]
[browser_dbg_rr_replay-02.js]
[browser_dbg_rr_replay-03.js]
[browser_dbg_rr_console_warp-01.js]
[browser_dbg_rr_console_warp-02.js]
[browser_dbg_rr_logpoint-01.js]
[browser_dbg_rr_logpoint-02.js]
[browser_dbg_rr_logpoint-03.js]
[browser_rr_inspector-01.js]
[browser_rr_inspector-02.js]
[browser_rr_inspector-03.js]
[browser_rr_object_preview-01.js]
[browser_rr_object_preview-02.js]

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

@ -1,37 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test basic breakpoint functionality in web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
// Visit a lot of breakpoints so that we are sure we have crossed major
// checkpoint boundaries.
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 9);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 8);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 7);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 6);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 7);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 8);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 9);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await shutdownDebugger(dbg);
});

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

@ -1,23 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test unhandled divergence while evaluating at a breakpoint with Web Replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await checkEvaluateInTopFrameThrows(dbg, "window.alert(3)");
await checkEvaluateInTopFrame(dbg, "number", 10);
await checkEvaluateInTopFrameThrows(dbg, "window.alert(3)");
await checkEvaluateInTopFrame(dbg, "number", 10);
await checkEvaluateInTopFrame(dbg, "testStepping2()", undefined);
await shutdownDebugger(dbg);
});

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

@ -1,21 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test some issues when stepping around after hitting a breakpoint while recording.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
await addBreakpoint(dbg, "doc_rr_continuous.html", 19);
await resumeToLine(dbg, 19);
await reverseStepOverToLine(dbg, 18);
await stepInToLine(dbg, 22);
await addBreakpoint(dbg, "doc_rr_continuous.html", 24);
await resumeToLine(dbg, 24);
await addBreakpoint(dbg, "doc_rr_continuous.html", 22);
await rewindToLine(dbg, 22);
await shutdownDebugger(dbg);
});

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

@ -1,29 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test navigating back to earlier breakpoints while recording, then resuming
// recording.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
await addBreakpoint(dbg, "doc_rr_continuous.html", 14);
await resumeToLine(dbg, 14);
const value = await evaluateInTopFrame(dbg, "number");
await resumeToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value + 1);
await rewindToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value);
await resumeToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value + 1);
await resumeToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value + 2);
await resumeToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value + 3);
await rewindToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", value + 2);
await shutdownDebugger(dbg);
});

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

@ -1,24 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test hitting breakpoints when rewinding past the point where the breakpoint
// script was created.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
// Rewind to the beginning of the recording.
await rewindToLine(dbg, undefined);
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 1);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 2);
await shutdownDebugger(dbg);
});

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

@ -1,38 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test hitting breakpoints when using tricky control flow constructs:
// catch, finally, generators, and async/await.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_control_flow.html", {
waitForRecording: true,
});
await rewindToBreakpoint(10);
await resumeToBreakpoint(12);
await resumeToBreakpoint(18);
await resumeToBreakpoint(20);
await resumeToBreakpoint(32);
await resumeToBreakpoint(27);
await resumeToLine(dbg, 32);
await resumeToLine(dbg, 27);
await resumeToBreakpoint(42);
await resumeToBreakpoint(44);
await resumeToBreakpoint(50);
await resumeToBreakpoint(54);
await shutdownDebugger(dbg);
async function rewindToBreakpoint(line) {
await addBreakpoint(dbg, "doc_control_flow.html", line);
await rewindToLine(dbg, line);
}
async function resumeToBreakpoint(line) {
await addBreakpoint(dbg, "doc_control_flow.html", line);
await resumeToLine(dbg, line);
}
});

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

@ -1,31 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Pausing at a debugger statement on startup confuses the debugger.
PromiseTestUtils.whitelistRejectionsGlobally(/Unknown source actor/);
// Test interaction of breakpoints with debugger statements.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_debugger_statements.html");
await resume(dbg);
invokeInTab("foo");
await waitForPaused(dbg);
const pauseLine = getVisibleSelectedFrameLine(dbg);
ok(pauseLine == 7, "Paused at first debugger statement");
await addBreakpoint(dbg, "doc_debugger_statements.html", 8);
await resumeToLine(dbg, 8);
await resumeToLine(dbg, 9);
await dbg.actions.removeAllBreakpoints(getContext(dbg));
await rewindToLine(dbg, 7);
await resumeToLine(dbg, 9);
await shutdownDebugger(dbg);
});

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

@ -1,31 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test basic console time warping functionality in web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_error.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, dbg, "Number 5");
await checkEvaluateInTopFrame(dbg, "number", 5);
// Initially we are paused inside the 'new Error()' call on line 19. The
// first reverse step takes us to the start of that line.
await reverseStepOverToLine(dbg, 19);
await reverseStepOverToLine(dbg, 18);
await addBreakpoint(dbg, "doc_rr_error.html", 12);
await rewindToLine(dbg, 12);
await checkEvaluateInTopFrame(dbg, "number", 4);
await resumeToLine(dbg, 12);
await checkEvaluateInTopFrame(dbg, "number", 5);
await shutdownDebugger(dbg);
});

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

@ -1,26 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef, no-unused-vars */
"use strict";
// Test basic console time warping functionality in web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_logs.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
let message = await warpToMessage(hud, dbg, "number: 1");
// ok(message.classList.contains("paused-before"), "paused before message is shown");
await stepOverToLine(dbg, 18);
await reverseStepOverToLine(dbg, 17);
message = findMessage(hud, "number: 1");
// ok(message.classList.contains("paused-before"), "paused before message is shown");
await shutdownDebugger(dbg);
});

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

@ -1,51 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test basic logpoint functionality in web replay. When logpoints are added,
// new messages should appear in the correct order and allow time warping.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await selectSource(dbg, "doc_rr_basic.html");
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await setBreakpointOptions(dbg, "doc_rr_basic.html", 21, undefined, {
logValue: `"Logpoint Number " + number`,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 6, undefined, {
logValue: `"Logpoint Beginning"`,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 8, undefined, {
logValue: `"Logpoint Ending"`,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
const messages = await waitForMessageCount(hud, "Logpoint", 12);
ok(
!findMessages(hud, "Loading"),
"Loading messages should have been removed"
);
ok(messages[0].textContent.includes("Beginning"));
for (let i = 1; i <= 10; i++) {
ok(messages[i].textContent.includes("Number " + i));
}
ok(messages[11].textContent.includes("Ending"));
await warpToMessage(hud, dbg, "Number 5");
await checkEvaluateInTopFrame(dbg, "number", 5);
await reverseStepOverToLine(dbg, 20);
await addBreakpoint(dbg, "doc_rr_basic.html", 22);
await resumeToLine(dbg, 22);
await shutdownDebugger(dbg);
});

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

@ -1,40 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test that logpoints appear and disappear as expected as breakpoints are
// modified. Also test that conditional logpoints work.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await selectSource(dbg, "doc_rr_basic.html");
await addBreakpoint(dbg, "doc_rr_basic.html", 21, undefined, {
logValue: `displayName, "Logpoint Number", number`,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 6, undefined, {
logValue: `displayName, "Logpoint Beginning"`,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 8, undefined, {
logValue: `displayName, " Logpoint Ending"`,
});
await waitForMessageCount(hud, "Logpoint", 12);
await disableBreakpoint(dbg, findSource(dbg, "doc_rr_basic.html"), 6);
await waitForMessageCount(hud, "Logpoint", 11);
await waitForMessageCount(hud, "updateNumber Logpoint", 10);
await setBreakpointOptions(dbg, "doc_rr_basic.html", 21, undefined, {
logValue: `"Logpoint Number " + number`,
condition: `number % 2 == 0`,
});
await waitForMessageCount(hud, "Logpoint", 6);
await shutdownDebugger(dbg);
});

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

@ -1,37 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test event logpoints when replaying.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_events.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await dbg.actions.addEventListenerBreakpoints(["event.mouse.mousedown"]);
await dbg.actions.toggleEventLogging();
const msg = await waitForMessage(hud, "mousedown");
// The message's inline preview should contain useful properties.
const regexps = [
/target: HTMLDivElement/,
/clientX: \d+/,
/clientY: \d+/,
/layerX: \d+/,
/layerY: \d+/,
];
for (const regexp of regexps) {
ok(regexp.test(msg.textContent), `Message text includes ${regexp}`);
}
// When expanded, other properties should be visible.
await checkMessageObjectContents(msg, ["altKey: false", "bubbles: true"]);
await shutdownDebugger(dbg);
});

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

@ -1,15 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test basic recording of a tab without any debugging.
add_task(async function() {
const recordingTab = await openRecordingTab("doc_rr_basic.html");
await once(Services.ppmm, "RecordingFinished");
await gBrowser.removeTab(recordingTab);
ok(true, "Finished");
});

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

@ -1,36 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Basic test for saving a recording and then replaying it in a new tab.
add_task(async function() {
const recordingFile = newRecordingFile();
const recordingTab = await openRecordingTab("doc_rr_basic.html");
await once(Services.ppmm, "RecordingFinished");
const remoteTab = recordingTab.linkedBrowser.frameLoader.remoteTab;
ok(remoteTab, "Found recording remote tab");
ok(remoteTab.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
const replayingTab = BrowserTestUtils.addTab(gBrowser, null, {
replayExecution: recordingFile,
});
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
const dbg = await attachDebugger(replayingTab);
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 9);
await resumeToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await gBrowser.removeTab(recordingTab);
await shutdownDebugger(dbg);
});

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

@ -1,50 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test ending a recording at a breakpoint and then separately replaying to the end.
add_task(async function() {
waitForExplicitFinish();
const recordingFile = newRecordingFile();
const recordingTab = await openRecordingTab("doc_rr_continuous.html");
let dbg = await attachDebugger(recordingTab);
await addBreakpoint(dbg, "doc_rr_continuous.html", 14);
await resumeToLine(dbg, 14);
await resumeToLine(dbg, 14);
await reverseStepOverToLine(dbg, 13);
const lastNumberValue = await evaluateInTopFrame(dbg, "number");
const remoteTab = recordingTab.linkedBrowser.frameLoader.remoteTab;
ok(remoteTab, "Found recording remote tab");
ok(remoteTab.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
await shutdownDebugger(dbg);
const replayingTab = BrowserTestUtils.addTab(gBrowser, null, {
replayExecution: recordingFile,
});
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
dbg = await attachDebugger(replayingTab);
// The recording does not actually end at the point where we saved it, but
// will do at the next checkpoint. Rewind to the point we are interested in.
await addBreakpoint(dbg, "doc_rr_continuous.html", 14);
await rewindToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", lastNumberValue);
await reverseStepOverToLine(dbg, 13);
await rewindToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", lastNumberValue - 1);
await resumeToLine(dbg, 14);
await checkEvaluateInTopFrame(dbg, "number", lastNumberValue);
await shutdownDebugger(dbg);
});

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

@ -1,31 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test for saving a recording and then replaying it in a new tab,
// with rewinding disabled.
add_task(async function() {
await pushPref("devtools.recordreplay.enableRewinding", false);
const recordingFile = newRecordingFile();
const recordingTab = await openRecordingTab("doc_rr_basic.html");
await once(Services.ppmm, "RecordingFinished");
const remoteTab = recordingTab.linkedBrowser.frameLoader.remoteTab;
ok(remoteTab, "Found recording remote tab");
ok(remoteTab.saveRecording(recordingFile), "Saved recording");
await once(Services.ppmm, "SaveRecordingFinished");
const replayingTab = BrowserTestUtils.addTab(gBrowser, null, {
replayExecution: recordingFile,
});
gBrowser.selectedTab = replayingTab;
await once(Services.ppmm, "HitRecordingEndpoint");
ok(true, "Replayed to end of recording");
await gBrowser.removeTab(recordingTab);
await gBrowser.removeTab(replayingTab);
});

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

@ -1,23 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test basic step-over/back functionality in web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await reverseStepOverToLine(dbg, 20);
await checkEvaluateInTopFrame(dbg, "number", 9);
await checkEvaluateInTopFrameThrows(dbg, "window.alert(3)");
await stepOverToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await shutdownDebugger(dbg);
});

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

@ -1,38 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test fixes for some simple stepping bugs.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 22);
await rewindToLine(dbg, 22);
await stepInToLine(dbg, 25);
await stepOverToLine(dbg, 26);
await stepOverToLine(dbg, 27);
await reverseStepOverToLine(dbg, 26);
await stepInToLine(dbg, 30);
await stepOverToLine(dbg, 31);
await stepOverToLine(dbg, 32);
// Check that the scopes pane shows the value of the local variable.
for (let i = 1; ; i++) {
if (getScopeLabel(dbg, i) == "c") {
is("NaN", getScopeValue(dbg, i));
break;
}
}
await stepOverToLine(dbg, 33);
await reverseStepOverToLine(dbg, 32);
await stepOutToLine(dbg, 27);
await reverseStepOverToLine(dbg, 26);
await reverseStepOverToLine(dbg, 25);
await shutdownDebugger(dbg);
});

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

@ -1,21 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test stepping back while recording, then resuming recording.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
await addBreakpoint(dbg, "doc_rr_continuous.html", 13);
await resumeToLine(dbg, 13);
const value = await evaluateInTopFrame(dbg, "number");
await reverseStepOverToLine(dbg, 12);
await checkEvaluateInTopFrame(dbg, "number", value - 1);
await resumeToLine(dbg, 13);
await resumeToLine(dbg, 13);
await checkEvaluateInTopFrame(dbg, "number", value + 1);
await shutdownDebugger(dbg);
});

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

@ -1,37 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Stepping past the beginning or end of a frame should act like a step-out.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_basic.html", {
waitForRecording: true,
});
await addBreakpoint(dbg, "doc_rr_basic.html", 21);
await rewindToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await waitForSelectedLocation(dbg, 21);
await reverseStepOverToLine(dbg, 20);
await reverseStepOverToLine(dbg, 12);
// After reverse-stepping out of the topmost frame we should rewind to the
// last breakpoint hit.
await reverseStepOverToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 9);
await stepOverToLine(dbg, 22);
await stepOverToLine(dbg, 23);
await stepOverToLine(dbg, 13);
await stepOverToLine(dbg, 17);
await stepOverToLine(dbg, 18);
// After forward-stepping out of the topmost frame we should run forward to
// the next breakpoint hit.
await stepOverToLine(dbg, 21);
await checkEvaluateInTopFrame(dbg, "number", 10);
await shutdownDebugger(dbg);
});

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

@ -1,31 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Test stepping in pretty-printed code.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_minified.html", {
waitForRecording: true,
});
await selectSource(dbg, "minified.js");
await prettyPrint(dbg);
await dbg.actions.addEventListenerBreakpoints(["event.mouse.click"]);
await dbg.actions.toggleEventLogging();
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, dbg, "click", 12);
await stepInToLine(dbg, 2);
await stepOutToLine(dbg, 12);
await stepInToLine(dbg, 9);
await stepOutToLine(dbg, 13);
await stepInToLine(dbg, 5);
await stepOutToLine(dbg, 14);
await shutdownDebugger(dbg);
});

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

@ -1,50 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Blackboxing, then stepping past the beginning or end of a frame should act
// like a step-out.
add_task(async function() {
info("Start debugger");
const dbg = await attachRecordingDebugger("doc_rr_blackbox.html", {
waitForRecording: true,
});
info("Step forward from in blackboxed source");
await addBreakpoint(dbg, "blackbox.js", 3);
await rewindToLine(dbg, 3, "blackbox.js");
await clickElement(dbg, "blackbox");
await waitForDispatch(dbg, "BLACKBOX");
await stepOverToLine(dbg, 20, "doc_rr_blackbox.html");
info("Unblackbox");
await selectSource(dbg, "blackbox.js");
await clickElement(dbg, "blackbox");
await waitForDispatch(dbg, "BLACKBOX");
info("Step backward from in blackboxed source");
await rewindToLine(dbg, 3, "blackbox.js");
await clickElement(dbg, "blackbox");
await waitForDispatch(dbg, "BLACKBOX");
await reverseStepOverToLine(dbg, 15, "doc_rr_blackbox.html");
info("Step forward when called from blackboxed source");
await addBreakpoint(dbg, "doc_rr_blackbox.html", 17);
await resumeToLine(dbg, 17, "doc_rr_blackbox.html");
await stepOverToLine(dbg, 17, "doc_rr_blackbox.html");
await stepOverToLine(dbg, 20, "doc_rr_blackbox.html");
info("Step backward when called from blackboxed source");
await rewindToLine(dbg, 17, "doc_rr_blackbox.html");
await reverseStepOverToLine(dbg, 15, "doc_rr_blackbox.html");
info("Unblackbox 2");
await selectSource(dbg, "blackbox.js");
await clickElement(dbg, "blackbox");
await waitForDispatch(dbg, "BLACKBOX");
info("Finish");
await shutdownDebugger(dbg);
});

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

@ -1,57 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
this
);
function getContainerForNodeFront(nodeFront, { markup }) {
return markup.getContainer(nodeFront);
}
// Test basic inspector functionality in web replay: the inspector is able to
// show contents when paused according to the child's current position.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_inspector_basic.html", {
waitForRecording: true,
disableLogging: true,
skipInterrupt: true,
});
const { inspector } = await openInspector();
let nodeFront = await getNodeFront("#maindiv", inspector);
let container = getContainerForNodeFront(nodeFront, inspector);
ok(!container, "No node container while unpaused");
await interrupt(dbg);
nodeFront = await getNodeFront("#maindiv", inspector);
await waitFor(
() => inspector.markup && getContainerForNodeFront(nodeFront, inspector)
);
container = getContainerForNodeFront(nodeFront, inspector);
ok(
container.editor.textEditor.textNode.state.value == "GOODBYE",
"Correct late element text"
);
await addBreakpoint(dbg, "doc_inspector_basic.html", 9);
await rewindToLine(dbg, 9);
nodeFront = await getNodeFront("#maindiv", inspector);
await waitFor(
() => inspector.markup && getContainerForNodeFront(nodeFront, inspector)
);
container = getContainerForNodeFront(nodeFront, inspector);
ok(
container.editor.textEditor.textNode.state.value == "HELLO",
"Correct early element text"
);
await shutdownDebugger(dbg);
});

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

@ -1,49 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
this
);
// Test that the element highlighter works when paused and replaying.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_inspector_basic.html", {
waitForRecording: true,
disableLogging: true,
});
const { toolbox } = dbg;
await addBreakpoint(dbg, "doc_inspector_basic.html", 9);
await rewindToLine(dbg, 9);
const { testActor } = await openInspector();
info("Waiting for element picker to become active.");
toolbox.win.focus();
await toolbox.nodePicker.start();
info("Moving mouse over div.");
await moveMouseOver("#maindiv", 1, 1);
// Checks in isNodeCorrectlyHighlighted are off for an unknown reason, even
// though the highlighting appears correctly in the UI.
info("Performing checks");
await testActor.isNodeCorrectlyHighlighted("#maindiv", is);
await shutdownDebugger(dbg);
function moveMouseOver(selector, x, y) {
info("Waiting for element " + selector + " to be highlighted");
testActor.synthesizeMouse({
selector,
x,
y,
options: { type: "mousemove" },
});
return toolbox.nodePicker.once("picker-node-hovered");
}
});

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

@ -1,54 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/inspector/test/shared-head.js",
this
);
function getComputedViewProperty(view, name) {
let prop;
for (const property of view.styleDocument.querySelectorAll(
"#computed-container .computed-property-view"
)) {
const nameSpan = property.querySelector(".computed-property-name");
const valueSpan = property.querySelector(".computed-property-value");
if (nameSpan.firstChild.textContent === name) {
prop = { nameSpan: nameSpan, valueSpan: valueSpan };
break;
}
}
return prop;
}
// Test that styles for elements can be viewed when using web replay.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_inspector_styles.html", {
waitForRecording: true,
disableLogging: true,
});
const { inspector, view } = await openComputedView();
await checkBackgroundColor("body", "rgb(0, 128, 0)");
await checkBackgroundColor("#maindiv", "rgb(0, 0, 255)");
await addBreakpoint(dbg, "doc_inspector_styles.html", 11);
await rewindToLine(dbg, 11);
await dbg.toolbox.selectTool("inspector");
await checkBackgroundColor("#maindiv", "rgb(255, 0, 0)");
await shutdownDebugger(dbg);
async function checkBackgroundColor(node, color) {
await selectNode(node, inspector);
const value = getComputedViewProperty(view, "background-color").valueSpan;
const nodeInfo = view.getNodeInfo(value);
is(nodeInfo.value.property, "background-color");
is(nodeInfo.value.value, color);
}
});

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

@ -1,131 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
function checkJumpIcon(msg) {
const jumpIcon = msg.querySelector(".jump-definition");
ok(jumpIcon, "Found a jump icon");
}
// Test the objects produced by console.log() calls and by evaluating various
// expressions in the console after time warping.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_objects.html", {
waitForRecording: true,
});
const { threadFront, toolbox } = dbg;
const console = await toolbox.selectTool("webconsole");
const hud = console.hud;
let msg;
await waitForMessage(hud, "Array(20) [ 0, 1, 2, 3, 4, 5,");
await waitForMessage(hud, "Uint8Array(20) [ 0, 1, 2, 3, 4, 5,");
await waitForMessage(hud, "Set(22) [ {…}, {…}, 0, 1, 2, 3, 4, 5,");
await waitForMessage(
hud,
"Map(21) { {…} → {…}, 0 → 1, 1 → 2, 2 → 3, 3 → 4, 4 → 5,"
);
await waitForMessage(hud, "WeakSet(20) [ {…}, {…}, {…},");
await waitForMessage(hud, "WeakMap(20) { {…} → {…}, {…} → {…},");
await waitForMessage(
hud,
"Object { a: 0, a0: 0, a1: 1, a2: 2, a3: 3, a4: 4,"
);
await waitForMessage(hud, "/abc/gi");
await waitForMessage(hud, "Date");
// Note: this message has an associated stack but we don't have an easy way to
// check its contents as BrowserTest.checkMessageStack requires the stack to
// be collapsed.
await waitForMessage(hud, 'RangeError: "foo"');
msg = await waitForMessage(hud, "function bar()");
checkJumpIcon(msg);
await warpToMessage(hud, dbg, "Done");
const requests = await threadFront.debuggerRequests();
requests.forEach(({ request, stack }) => {
if (request.type != "pauseData" && request.type != "repaint") {
dump(`Unexpected debugger request stack:\n${stack}\n`);
ok(false, `Unexpected debugger request while paused: ${request.type}`);
}
});
BrowserTest.execute(hud, "Error('helo')");
await waitForMessage(hud, "helo");
BrowserTest.execute(
hud,
`
function f() {
throw Error("there");
}
f();
`
);
await BrowserTest.checkMessageStack(hud, "Error: there", [3, 5]);
BrowserTest.execute(hud, "Array(1, 2, 3)");
msg = await waitForMessage(hud, "Array(3) [ 1, 2, 3 ]");
await checkMessageObjectContents(msg, ["0: 1", "1: 2", "2: 3", "length: 3"]);
BrowserTest.execute(hud, "new Uint8Array([1, 2, 3, 4])");
msg = await waitForMessage(hud, "Uint8Array(4) [ 1, 2, 3, 4 ]");
await checkMessageObjectContents(msg, [
"0: 1",
"1: 2",
"2: 3",
"3: 4",
"byteLength: 4",
"byteOffset: 0",
]);
BrowserTest.execute(hud, `RegExp("abd", "g")`);
msg = await waitForMessage(hud, "/abd/g");
await checkMessageObjectContents(msg, ["global: true", `source: "abd"`]);
BrowserTest.execute(hud, "new Set([1, 2, 3])");
msg = await waitForMessage(hud, "Set(3) [ 1, 2, 3 ]");
await checkMessageObjectContents(
msg,
["0: 1", "1: 2", "2: 3", "size: 3"],
["<entries>"]
);
BrowserTest.execute(hud, "new Map([[1, {a:1}], [2, {b:2}]])");
msg = await waitForMessage(hud, "Map { 1 → {…}, 2 → {…} }");
await checkMessageObjectContents(
msg,
["0: 1 → Object { … }", "1: 2 → Object { … }", "size: 2"],
["<entries>"]
);
BrowserTest.execute(hud, "new WeakSet([{a:1}, {b:2}])");
msg = await waitForMessage(hud, "WeakSet [ {…}, {…} ]");
await checkMessageObjectContents(
msg,
["0: Object { … }", "1: Object { … }"],
["<entries>"]
);
BrowserTest.execute(hud, "new WeakMap([[{a:1},{b:1}], [{a:2},{b:2}]])");
msg = await waitForMessage(hud, "WeakMap { {…} → {…}, {…} → {…} }");
await checkMessageObjectContents(
msg,
["0: Object { … } → Object { … }", "1: Object { … } → Object { … }"],
["<entries>"]
);
BrowserTest.execute(hud, "baz");
msg = await waitForMessage(hud, "function baz()");
checkJumpIcon(msg);
await shutdownDebugger(dbg);
});

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

@ -1,44 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
// Inspecting objects can lead to uncaught rejections when shutting down.
PromiseTestUtils.whitelistRejectionsGlobally(
/can't be sent as the connection just closed/
);
function findNode(dbg, text) {
for (let index = 0; ; index++) {
const elem = findElement(dbg, "scopeNode", index);
if (elem && elem.innerText == text) {
return elem;
}
}
}
function toggleNode(dbg, text) {
return toggleObjectInspectorNode(findNode(dbg, text));
}
// Test that objects show up correctly in the scope pane.
add_task(async function() {
const dbg = await attachRecordingDebugger("doc_rr_objects.html", {
waitForRecording: true,
});
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await warpToMessage(hud, dbg, "Done");
// We should be able to expand the window and see its properties.
await toggleNode(dbg, "<this>");
findNode(dbg, "bar()");
findNode(dbg, "baz()");
await shutdownDebugger(dbg);
});

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

@ -1,5 +0,0 @@
function blackboxed(...args) {
for (const arg of args) {
arg();
}
}

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

@ -1,104 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
function trycatch() {
try {
throwError();
} catch (e) {
updateText("catch");
}
updateText("afterCatch");
startNextCallback();
}
function tryfinally() {
try {
throwError();
} finally {
updateText("finally");
startNextCallback();
}
}
function generator() {
for (const v of inner()) {
updateText(`generated ${v}`);
}
function *inner() {
for (const v of [1,2]) {
updateText(`yield ${v}`);
yield v;
}
}
startNextCallback();
}
async function asyncer() {
await timer();
updateText("after timer 1");
await timer();
updateText("after timer 2");
startNextCallback();
}
async function asyncerThrow() {
await timer();
updateText("after throw timer 1");
try {
await timerThrow();
} catch (e) {
updateText("after throw timer 2");
startNextCallback();
throw e;
}
}
const callbacks = [
trycatch,
tryfinally,
generator,
asyncer,
asyncerThrow,
];
startNextCallback();
// Helpers
function startNextCallback() {
if (callbacks.length) {
const callback = callbacks.shift();
window.setTimeout(() => {
try { callback() } catch (e) {}
});
} else {
const cpmm = SpecialPowers.Services.cpmm;
cpmm.sendAsyncMessage("RecordingFinished");
}
}
function updateText(text) {
document.getElementById("maindiv").innerHTML = text;
}
function throwError() {
throw new Error();
}
function timer() {
return new Promise(resolve => {
setTimeout(resolve);
});
}
function timerThrow() {
return new Promise((resolve, reject) => {
setTimeout(reject);
});
}
</script>
</html>

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

@ -1,12 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
function foo() {
debugger;
document.getElementById("maindiv").innerHTML = "Foobar!";
debugger;
}
</script>
</html>

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

@ -1,18 +0,0 @@
<script src="/tests/SimpleTest/EventUtils.js"></script>
<div id="divvy">Hello World!</div>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
const divvy = document.getElementById("divvy");
divvy.addEventListener("mousedown", e => {
divvy.innerText = "Goodbye World!";
window.setTimeout(recordingFinished);
});
window.setTimeout(() => {
synthesizeMouseAtCenter(divvy, {});
});
</script>

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

@ -1,14 +0,0 @@
<body>
<div style="color: red" onclick="console.log('LOG')" id="maindiv">HELLO</div>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
function foo() {
document.getElementById("maindiv").innerHTML = "GOODBYE";
window.setTimeout(recordingFinished);
}
window.setTimeout(foo);
</script>
</body>

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

@ -1,16 +0,0 @@
<body>
<link rel="stylesheet" href="styles.css">
<div id="maindiv">HELLO</div>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
function foo() {
document.getElementById("maindiv").innerHTML = "GOODBYE";
document.getElementById("maindiv").style.backgroundColor = "blue";
window.setTimeout(recordingFinished);
}
window.setTimeout(foo);
</script>
</body>

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

@ -1,13 +0,0 @@
<div id="divvy">Hello World!</div>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<script src="minified.js"></script>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
window.setTimeout(() => {
synthesizeMouseAtCenter(divvy, {});
window.setTimeout(recordingFinished);
});
</script>

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

@ -1,36 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
var number = 0;
function f() {
updateNumber();
if (number >= 10) {
window.setTimeout(recordingFinished);
return;
}
window.setTimeout(f, 100);
}
function updateNumber() {
number++;
document.getElementById("maindiv").innerHTML = "Number: " + number;
testStepping();
}
function testStepping() {
var a = 0;
testStepping2();
return a;
}
function testStepping2() {
var c = this; // Note: using 'this' causes the script to have a prologue.
c++;
c--;
}
window.setTimeout(f, 100);
</script>
</html>

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

@ -1,24 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script src="blackbox.js"></script>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
var number = 0;
function testStepping() {
number += 1;
blackboxed(
() => { number += 1; },
() => { number += 1; },
() => { number += 1; },
);
window.setTimeout(recordingFinished);
}
window.setTimeout(testStepping);
</script>
</html>

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

@ -1,28 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
var number = 0;
function f() {
updateNumber();
window.setTimeout(f, 1);
}
function updateNumber() {
number++;
document.getElementById("maindiv").innerHTML = "Number: " + number;
testStepping();
}
function testStepping() {
var a = 0;
testStepping2();
return a;
}
function testStepping2() {
var c = 0;
c++;
c--;
}
window.setTimeout(f, 100);
</script>
</html>

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

@ -1,23 +0,0 @@
<html lang="en" dir="ltr">
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
var number = 0;
function f() {
number++;
document.getElementById("maindiv").innerHTML = "Number: " + number;
if (number >= 10) {
window.setTimeout(recordingFinished);
return;
}
window.setTimeout(f, 1);
throw new Error("Number " + number);
}
window.setTimeout(f, 1);
</script>
</html>

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

@ -1,26 +0,0 @@
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8"/>
</head>
<body>
<div id="maindiv" style="padding-top:50px">Hello World!</div>
</body>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
var number = 0;
function f() {
number++;
console.log({ number });
number++;
console.log({ number });
number++;
console.log({ number });
window.setTimeout(recordingFinished);
}
window.setTimeout(f, 1);
</script>
</html>

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

@ -1,58 +0,0 @@
<div id="foo">BAR</div>
<script>
const cpmm = SpecialPowers.Services.cpmm;
function recordingFinished() {
cpmm.sendAsyncMessage("RecordingFinished");
}
function foo() {
// Create various objects which the debugger must be able to show in the scopes
// pane using only the pause data, i.e. without additional debugger requests.
// Not performing debugger requests allows the debugger to finish updating the
// UI using cached pause data, and without any replaying process actually being
// at the point where we are pausing.
var a = Array();
var b = new Uint8Array(20);
var c = new Set([{a:0},{b:1}]);
var d = new Map([[{a:0},{b:1}]]);
var e = new WeakSet();
var f = new WeakMap();
var g = { a:0 };
for (let i = 0; i < 20; i++) {
a.push(i);
b[i] = i;
c.add(i);
d.set(i, i + 1);
e.add({ i });
f.set({ i }, { j: i + 1 });
g[`a${i}`] = i;
}
var h = /abc/gi;
var i = new Date();
var j = RangeError("foo");
var k = document.getElementById("foo");
var l = bar;
console.log(a);
console.log(b);
console.log(c);
console.log(d);
console.log(e);
console.log(f);
console.log(g);
console.log(h);
console.log(i);
console.log(j);
console.log(l);
console.log("Done");
window.setTimeout(recordingFinished);
}
foo();
function bar() {
console.log("bar");
}
function baz() {
console.log("baz");
}
</script>

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

@ -1 +0,0 @@
const s={getWindow:()=>window};function f(){this.getElementById("divvy").innerHTML="Done!"}const nf=f.bind(document);function DOMEvent(n,e){console.log("DOMEvent",e)}function h(n){n=new DOMEvent(n,s.getWindow()),!1===nf.call(s,n)}document.getElementById("divvy").addEventListener("click",h);

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

@ -1,7 +0,0 @@
body {
background-color: green;
}
div {
background-color: red;
}

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

@ -1,249 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint-disable no-undef */
"use strict";
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
this
);
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/debugger/test/mochitest/helpers.js",
this
);
const EXAMPLE_URL =
"http://example.com/browser/devtools/client/webreplay/mochitest/examples/";
// Attach a debugger to a tab, returning a promise that resolves with the
// debugger's toolbox.
async function attachDebugger(tab) {
const target = await TargetFactory.forTab(tab);
const toolbox = await gDevTools.showToolbox(target, "jsdebugger");
const dbg = createDebuggerContext(toolbox);
const threadFront = dbg.toolbox.threadFront;
return { ...dbg, tab, threadFront };
}
async function openRecordingTab(url) {
const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
gBrowser.selectedTab = tab;
await once(Services.ppmm, "RecordingInitialized");
openTrustedLinkIn(EXAMPLE_URL + url, "current");
return tab;
}
async function attachRecordingDebugger(
url,
{ waitForRecording, disableLogging, skipInterrupt } = {}
) {
if (!disableLogging) {
await pushPref("devtools.recordreplay.logging", true);
}
const tab = await openRecordingTab(url);
if (waitForRecording) {
await once(Services.ppmm, "RecordingFinished");
}
const dbg = await attachDebugger(tab);
if (!skipInterrupt) {
await interrupt(dbg);
}
return dbg;
}
async function waitForPausedNoSource(dbg) {
await waitForState(dbg, state => isPaused(dbg), "paused");
}
async function shutdownDebugger(dbg) {
await dbg.actions.removeAllBreakpoints(getContext(dbg));
await waitForRequestsToSettle(dbg);
await dbg.toolbox.destroy();
await gBrowser.removeTab(dbg.tab);
}
async function interrupt(dbg) {
await dbg.actions.breakOnNext(getThreadContext(dbg));
await waitForPausedNoSource(dbg);
}
function resumeThenPauseAtLineFunctionFactory(method) {
return async function(dbg, lineno) {
await dbg.actions[method](getThreadContext(dbg));
if (lineno !== undefined) {
await waitForPaused(dbg);
} else {
await waitForPausedNoSource(dbg);
}
const pauseLine = getVisibleSelectedFrameLine(dbg);
ok(pauseLine == lineno, `Paused at line ${pauseLine} expected ${lineno}`);
};
}
// Define various methods that resume a thread in a specific way and ensure it
// pauses at a specified line.
var rewindToLine = resumeThenPauseAtLineFunctionFactory("rewind");
var resumeToLine = resumeThenPauseAtLineFunctionFactory("resume");
var reverseStepOverToLine = resumeThenPauseAtLineFunctionFactory(
"reverseStepOver"
);
var stepOverToLine = resumeThenPauseAtLineFunctionFactory("stepOver");
var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut");
// Return a promise that resolves when a thread evaluates a string in the
// topmost frame, with the result throwing an exception.
async function checkEvaluateInTopFrameThrows(dbg, text) {
const threadFront = dbg.toolbox.target.threadFront;
const consoleFront = await dbg.toolbox.target.getFront("console");
const { frames } = await threadFront.getFrames(0, 1);
ok(frames.length == 1, "Got one frame");
const options = { thread: threadFront.actor, frameActor: frames[0].actorID };
const response = await consoleFront.evaluateJSAsync(text, options);
ok(response.exception, "Eval threw an exception");
}
// Return a pathname that can be used for a new recording file.
function newRecordingFile() {
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
return OS.Path.join(
OS.Constants.Path.tmpDir,
"MochitestRecording" + Math.round(Math.random() * 1000000000)
);
}
function findMessage(hud, text, selector = ".message") {
const messages = findMessages(hud, text, selector);
return messages ? messages[0] : null;
}
function findMessages(hud, text, selector = ".message") {
const messages = hud.ui.outputNode.querySelectorAll(selector);
const elements = Array.prototype.filter.call(messages, el =>
el.textContent.includes(text)
);
if (elements.length == 0) {
return null;
}
return elements;
}
function waitForMessage(hud, text, selector = ".message") {
return waitUntilPredicate(() => findMessage(hud, text, selector));
}
function waitForMessages(hud, text, selector = ".message") {
return waitUntilPredicate(() => findMessages(hud, text, selector));
}
async function waitForMessageCount(hud, text, length, selector = ".message") {
let messages;
await waitUntil(() => {
messages = findMessages(hud, text, selector);
return messages && messages.length == length;
});
ok(messages.length == length, "Found expected message count");
return messages;
}
async function warpToMessage(hud, dbg, text, maybeLine) {
let messages = await waitForMessages(hud, text);
ok(messages.length == 1, "Found one message");
const message = messages.pop();
const menuPopup = await openConsoleContextMenu(message);
console.log(`.>> menu`, menuPopup);
const timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
ok(timeWarpItem, "Time warp menu item is available");
timeWarpItem.click();
await hideConsoleContextMenu();
await once(Services.ppmm, "TimeWarpFinished");
await waitForPaused(dbg);
messages = findMessages(hud, "", ".paused");
ok(messages.length == 1, "Found one paused message");
if (maybeLine) {
const pauseLine = getVisibleSelectedFrameLine(dbg);
ok(pauseLine == maybeLine, `Paused at line ${maybeLine} after warp`);
}
return message;
async function openConsoleContextMenu(element) {
const onConsoleMenuOpened = hud.ui.wrapper.once("menu-open");
synthesizeContextMenuEvent(element);
await onConsoleMenuOpened;
return dbg.toolbox.topDoc.getElementById("webconsole-menu");
}
function hideConsoleContextMenu() {
const popup = dbg.toolbox.topDoc.getElementById("webconsole-menu");
if (!popup) {
return Promise.resolve();
}
const onPopupHidden = once(popup, "popuphidden");
popup.hidePopup();
return onPopupHidden;
}
}
// For tests that need webconsole test features.
const BrowserTest = {
gTestPath,
ok,
is,
registerCleanupFunction,
waitForExplicitFinish,
BrowserTestUtils,
};
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/webconsole/test/browser/head.js",
BrowserTest
);
async function checkMessageObjectContents(msg, expected, expandList = []) {
const oi = msg.querySelector(".tree");
const node = oi.querySelector(".tree-node");
BrowserTest.expandObjectInspectorNode(node);
for (const label of expandList) {
const labelNode = await waitFor(() =>
BrowserTest.findObjectInspectorNode(oi, label)
);
BrowserTest.expandObjectInspectorNode(labelNode);
}
const properties = await waitFor(() => {
const nodes = BrowserTest.getObjectInspectorNodes(oi);
if (nodes && nodes.length > 1) {
return [...nodes].map(n => n.textContent);
}
return null;
});
expected.forEach(s => {
ok(properties.find(v => v.includes(s)), `Object contents include "${s}"`);
});
}
PromiseTestUtils.whitelistRejectionsGlobally(/NS_ERROR_NOT_INITIALIZED/);
PromiseTestUtils.whitelistRejectionsGlobally(/Error in asyncStorage/);
// When running the full test suite, long delays can occur early on in tests,
// before child processes have even been spawned. Allow a longer timeout to
// avoid failures from this.
requestLongerTimeout(4);

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

@ -1,18 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DIRS += [
'components',
]
DevToolsModules(
'menu.js',
)
with Files('**'):
BUG_COMPONENT = ('Core', 'Web Replay')
BROWSER_CHROME_MANIFESTS += [ 'mochitest/browser.ini' ]

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

@ -10,7 +10,6 @@ const ChromeUtils = require("ChromeUtils");
const EventEmitter = require("devtools/shared/event-emitter");
const protocol = require("devtools/shared/protocol");
const Services = require("Services");
const ReplayInspector = require("devtools/server/actors/replay/inspector");
const {
highlighterSpec,
customHighlighterSpec,
@ -453,9 +452,7 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
// originalTarget allows access to the "real" element before any retargeting
// is applied, such as in the case of XBL anonymous elements. See also
// https://developer.mozilla.org/docs/XBL/XBL_1.0_Reference/Anonymous_Content#Event_Flow_and_Targeting
const node = isReplaying
? ReplayInspector.findEventTarget(event)
: event.originalTarget || event.target;
const node = event.originalTarget || event.target;
return this._walker.attachElement(node);
},

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

@ -6,7 +6,6 @@
const { Cu } = require("chrome");
const EventEmitter = require("devtools/shared/event-emitter");
const ReplayInspector = require("devtools/server/actors/replay/inspector");
const {
isNodeValid,
} = require("devtools/server/actors/highlighters/utils/markup");
@ -103,7 +102,7 @@ AutoRefreshHighlighter.prototype = {
/* Window containing the target content. */
get contentWindow() {
return isReplaying ? ReplayInspector.window : this.win;
return this.win;
},
/**
@ -298,7 +297,7 @@ AutoRefreshHighlighter.prototype = {
},
_startRefreshLoop: function() {
const win = isReplaying ? this.win : this.currentNode.ownerGlobal;
const win = this.currentNode.ownerGlobal;
this.rafID = win.requestAnimationFrame(this._startRefreshLoop.bind(this));
this.rafWin = win;
this.update();

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше