Bug 1609815 - Remove Web Replay UI. r=loganfsmyth
☠☠ backed out by 1f891f357702 ☠ ☠
authorJason Laster <jlaster@mozilla.com>
Thu, 23 Jan 2020 20:07:37 +0000
changeset 511551 c41e5432b3c509f98e79af9e0e234e43ce02d02d
parent 511550 93b81b2495cdccac2c8e42b8ba5a839cf09d479c
child 511552 02d7f5d2d0b392bacde5cbf53d66078958091497
push id105869
push userjlaster@mozilla.com
push dateThu, 23 Jan 2020 20:13:07 +0000
treeherderautoland@c41e5432b3c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersloganfsmyth
bugs1609815
milestone74.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1609815 - Remove Web Replay UI. r=loganfsmyth Differential Revision: https://phabricator.services.mozilla.com/D60679
devtools/.eslintrc.js
devtools/client/debugger/images/moz.build
devtools/client/debugger/images/replay-pause.svg
devtools/client/debugger/images/replay-resume.svg
devtools/client/debugger/images/rewind.svg
devtools/client/debugger/src/actions/pause/commands.js
devtools/client/debugger/src/actions/pause/continueToHere.js
devtools/client/debugger/src/actions/pause/index.js
devtools/client/debugger/src/actions/pause/selectFrame.js
devtools/client/debugger/src/client/firefox/commands.js
devtools/client/debugger/src/client/firefox/events.js
devtools/client/debugger/src/client/firefox/types.js
devtools/client/debugger/src/components/App.js
devtools/client/debugger/src/components/SecondaryPanes/CommandBar.css
devtools/client/debugger/src/components/SecondaryPanes/CommandBar.js
devtools/client/debugger/src/components/SecondaryPanes/FrameTimeline.css
devtools/client/debugger/src/components/SecondaryPanes/FrameTimeline.js
devtools/client/debugger/src/components/SecondaryPanes/index.js
devtools/client/debugger/src/components/SecondaryPanes/moz.build
devtools/client/debugger/src/components/shared/AccessibleImage.css
devtools/client/debugger/src/debugger.css
devtools/client/debugger/src/reducers/pause.js
devtools/client/debugger/src/reducers/threads.js
devtools/client/debugger/src/selectors/index.js
devtools/client/debugger/src/selectors/pause.js
devtools/client/debugger/src/utils/pause/why.js
devtools/client/debugger/test/mochitest/helpers.js
devtools/client/definitions.js
devtools/client/framework/browser-menus.js
devtools/client/framework/components/ToolboxToolbar.js
devtools/client/framework/devtools-browser.js
devtools/client/framework/toolbox-options.js
devtools/client/framework/toolbox.js
devtools/client/inspector/inspector.js
devtools/client/jar.mn
devtools/client/locales/en-US/debugger.properties
devtools/client/locales/en-US/menus.properties
devtools/client/locales/en-US/startup.properties
devtools/client/locales/en-US/toolbox.properties
devtools/client/locales/en-US/webconsole.properties
devtools/client/moz.build
devtools/client/shared/test/test-actor.js
devtools/client/themes/images/command-replay.svg
devtools/client/themes/toolbox.css
devtools/client/themes/webconsole.css
devtools/client/webconsole/actions/input.js
devtools/client/webconsole/reducers/messages.js
devtools/client/webconsole/test/node/package.json
devtools/client/webconsole/test/node/store/messages.test.js
devtools/client/webreplay/components/WebReplayPlayer.js
devtools/client/webreplay/components/moz.build
devtools/client/webreplay/menu.js
devtools/client/webreplay/mochitest/browser.ini
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-06.js
devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-07.js
devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js
devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js
devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-01.js
devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-02.js
devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-03.js
devtools/client/webreplay/mochitest/browser_dbg_rr_record.js
devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js
devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js
devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-05.js
devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-06.js
devtools/client/webreplay/mochitest/browser_rr_inspector-01.js
devtools/client/webreplay/mochitest/browser_rr_inspector-02.js
devtools/client/webreplay/mochitest/browser_rr_inspector-03.js
devtools/client/webreplay/mochitest/browser_rr_object_preview-01.js
devtools/client/webreplay/mochitest/browser_rr_object_preview-02.js
devtools/client/webreplay/mochitest/examples/blackbox.js
devtools/client/webreplay/mochitest/examples/doc_control_flow.html
devtools/client/webreplay/mochitest/examples/doc_debugger_statements.html
devtools/client/webreplay/mochitest/examples/doc_events.html
devtools/client/webreplay/mochitest/examples/doc_inspector_basic.html
devtools/client/webreplay/mochitest/examples/doc_inspector_styles.html
devtools/client/webreplay/mochitest/examples/doc_minified.html
devtools/client/webreplay/mochitest/examples/doc_rr_basic.html
devtools/client/webreplay/mochitest/examples/doc_rr_blackbox.html
devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html
devtools/client/webreplay/mochitest/examples/doc_rr_error.html
devtools/client/webreplay/mochitest/examples/doc_rr_logs.html
devtools/client/webreplay/mochitest/examples/doc_rr_objects.html
devtools/client/webreplay/mochitest/examples/minified.js
devtools/client/webreplay/mochitest/examples/styles.css
devtools/client/webreplay/mochitest/head.js
devtools/client/webreplay/moz.build
devtools/server/actors/highlighters.js
devtools/server/actors/highlighters/auto-refresh.js
devtools/server/actors/inspector/event-collector.js
devtools/server/actors/inspector/inspector.js
devtools/server/actors/inspector/walker.js
devtools/server/actors/moz.build
devtools/server/actors/object.js
devtools/server/actors/object/previewers.js
devtools/server/actors/object/property-iterator.js
devtools/server/actors/object/utils.js
devtools/server/actors/replay/connection-worker.js
devtools/server/actors/replay/connection.js
devtools/server/actors/replay/control.js
devtools/server/actors/replay/debugger.js
devtools/server/actors/replay/graphics.js
devtools/server/actors/replay/inspector.js
devtools/server/actors/replay/moz.build
devtools/server/actors/replay/replay.js
devtools/server/actors/replay/rrIConnection.idl
devtools/server/actors/replay/rrIControl.idl
devtools/server/actors/replay/rrIGraphics.idl
devtools/server/actors/replay/rrIReplay.idl
devtools/server/actors/replay/utils/findStepOffsets.js
devtools/server/actors/replay/utils/moz.build
devtools/server/actors/stylesheets.js
devtools/server/actors/targets/browsing-context.js
devtools/server/actors/thread.js
devtools/server/actors/utils/make-debugger.js
devtools/shared/DevToolsUtils.js
devtools/shared/Loader.jsm
devtools/shared/builtin-modules.js
devtools/shared/execution-point-utils.js
devtools/shared/fronts/targets/target-mixin.js
devtools/shared/fronts/thread.js
devtools/shared/layout/utils.js
devtools/shared/moz.build
devtools/shared/specs/thread.js
devtools/shared/worker/loader.js
--- a/devtools/.eslintrc.js
+++ b/devtools/.eslintrc.js
@@ -2,17 +2,16 @@
 
 module.exports = {
   "plugins": [
     "react"
   ],
   "globals": {
     "exports": true,
     "isWorker": true,
-    "isReplaying": true,
     "loader": true,
     "module": true,
     "reportError": true,
     "require": true,
   },
   "overrides": [{
     "files": [
       "client/framework/**",
--- a/devtools/client/debugger/images/moz.build
+++ b/devtools/client/debugger/images/moz.build
@@ -34,20 +34,17 @@ DevToolsModules(
     'next.svg',
     'pane-collapse.svg',
     'pane-expand.svg',
     'pause.svg',
     'plus.svg',
     'prettyPrint.svg',
     'regex-match.svg',
     'reload.svg',
-    'replay-pause.svg',
-    'replay-resume.svg',
     'resume.svg',
-    'rewind.svg',
     'search.svg',
     'stepIn.svg',
     'stepOut.svg',
     'stepOver.svg',
     'tab.svg',
     'webconsole-logpoint.svg',
     'whole-word-match.svg',
     'window.svg',
deleted file mode 100644
--- a/devtools/client/debugger/images/replay-pause.svg
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/debugger/images/replay-resume.svg
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/debugger/images/rewind.svg
+++ /dev/null
@@ -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>
--- a/devtools/client/debugger/src/actions/pause/commands.js
+++ b/devtools/client/debugger/src/actions/pause/commands.js
@@ -129,36 +129,8 @@ export function stepOut(cx: ThreadContex
 export function resume(cx: ThreadContext) {
   return ({ dispatch, getState }: ThunkArgs) => {
     if (cx.isPaused) {
       recordEvent("continue");
       return dispatch(command(cx, "resume"));
     }
   };
 }
-
-/**
- * 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"));
-    }
-  };
-}
--- a/devtools/client/debugger/src/actions/pause/continueToHere.js
+++ b/devtools/client/debugger/src/actions/pause/continueToHere.js
@@ -2,24 +2,23 @@
  * 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 {
   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";
 
 export function continueToHere(cx: ThreadContext, location: SourceLocation) {
   return async function({ dispatch, getState }: ThunkArgs) {
     const { line, column, sourceId } = location;
     const selectedSource = getSelectedSource(getState());
@@ -42,29 +41,23 @@ export function continueToHere(cx: Threa
     // If the user selects a location in the editor,
     // there must be a place we can pause on that line.
     if (column && !position) {
       return;
     }
 
     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)) {
       await dispatch(
         addHiddenBreakpoint(cx, {
           sourceId: selectedSource.id,
           line: pauseLocation.line,
           column: pauseLocation.column,
         })
       );
     }
 
-    dispatch(action(cx));
+    dispatch(resume(cx));
   };
 }
--- a/devtools/client/debugger/src/actions/pause/index.js
+++ b/devtools/client/debugger/src/actions/pause/index.js
@@ -10,18 +10,16 @@
  */
 
 export {
   selectThread,
   stepIn,
   stepOver,
   stepOut,
   resume,
-  rewind,
-  reverseStepOver,
   seekToPosition,
 } from "./commands";
 export { fetchFrames } from "./fetchFrames";
 export { fetchScopes } from "./fetchScopes";
 export { paused } from "./paused";
 export { resumed } from "./resumed";
 export { continueToHere } from "./continueToHere";
 export { breakOnNext } from "./breakOnNext";
--- a/devtools/client/debugger/src/actions/pause/selectFrame.js
+++ b/devtools/client/debugger/src/actions/pause/selectFrame.js
@@ -3,17 +3,16 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 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";
 
 /**
  * @memberof actions/pause
  * @static
  */
@@ -28,18 +27,14 @@ export function selectFrame(cx: ThreadCo
 
     dispatch({
       type: "SELECT_FRAME",
       cx,
       thread: cx.thread,
       frame,
     });
 
-    if (getCanRewind(getState())) {
-      client.fetchAncestorFramePositions(frame.index);
-    }
-
     dispatch(selectLocation(cx, frame.location));
 
     dispatch(evaluateExpressions(cx));
     dispatch(fetchScopes(cx));
   };
 }
--- a/devtools/client/debugger/src/client/firefox/commands.js
+++ b/devtools/client/debugger/src/client/firefox/commands.js
@@ -157,24 +157,16 @@ function stepIn(thread: string): Promise
 function stepOver(thread: string): Promise<*> {
   return lookupThreadFront(thread).stepOver();
 }
 
 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();
 }
 
 async function sourceContents({
   actor,
   thread,
 }: SourceActor): Promise<{| source: any, contentType: ?string |}> {
@@ -215,52 +207,30 @@ async function removeWatchpoint(object: 
 // See also duplicate code in breakpoint-actor-map.js :(
 function locationKey(location: BreakpointLocation) {
   const { sourceUrl, line, column } = location;
   const sourceId = location.sourceId || "";
   // $FlowIgnore
   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)];
 }
 
 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));
 }
 
 function evaluateInFrame(
   script: Script,
   options: EvaluateParam
@@ -554,18 +524,16 @@ const clientCommands = {
   loadObjectProperties,
   releaseActor,
   interrupt,
   pauseGrip,
   resume,
   stepIn,
   stepOut,
   stepOver,
-  rewind,
-  reverseStepOver,
   breakOnNext,
   sourceContents,
   getSourceForActor,
   getSourceActorBreakpointPositions,
   getSourceActorBreakableLines,
   hasBreakpoint,
   setBreakpoint,
   setXHRBreakpoint,
--- a/devtools/client/debugger/src/client/firefox/events.js
+++ b/devtools/client/debugger/src/client/firefox/events.js
@@ -135,28 +135,20 @@ function newSource(threadFront: ThreadFr
     data: prepareSourcePayload(threadFront, source),
   });
 }
 
 function threadListChanged() {
   actions.updateThreads();
 }
 
-function replayFramePositions(
-  threadFront: ThreadFront,
-  { positions, frame, thread }: Object
-) {
-  actions.setFramePositions(positions, frame, thread);
-}
-
 const clientEvents = {
   paused,
   resumed,
   newSource,
-  replayFramePositions,
 };
 
 export {
   removeEventsTopTarget,
   setupEvents,
   setupEventsTopTarget,
   clientEvents,
   addThreadEventListeners,
--- a/devtools/client/debugger/src/client/firefox/types.js
+++ b/devtools/client/debugger/src/client/firefox/types.js
@@ -368,18 +368,16 @@ export type ExpressionResult =
 export type ThreadFront = {
   actorID: string,
   parentFront: Target,
   getFrames: (number, number) => Promise<{| frames: FrameFront[] |}>,
   resume: Function => Promise<*>,
   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,
   pauseGrip: (Grip | Function) => ObjectFront,
   pauseOnExceptions: (boolean, boolean) => Promise<*>,
   setBreakpoint: (BreakpointLocation, BreakpointOptions) => Promise<*>,
   removeBreakpoint: PendingLocation => Promise<*>,
   setXHRBreakpoint: (path: string, method: string) => Promise<boolean>,
--- a/devtools/client/debugger/src/components/App.js
+++ b/devtools/client/debugger/src/components/App.js
@@ -14,17 +14,16 @@ import A11yIntention from "./A11yIntenti
 import { ShortcutsModal } from "./ShortcutsModal";
 
 import {
   getSelectedSource,
   getPaneCollapse,
   getActiveSearch,
   getQuickOpenEnabled,
   getOrientation,
-  getCanRewind,
 } from "../selectors";
 
 import type { OrientationType } from "../reducers/types";
 import type { Source } from "../types";
 
 import { KeyShortcuts } from "devtools-modules";
 import Services from "devtools-services";
 const shortcuts = new KeyShortcuts({ window });
@@ -62,17 +61,16 @@ import WhyPaused from "./SecondaryPanes/
 type OwnProps = {||};
 type Props = {
   selectedSource: ?Source,
   orientation: OrientationType,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
   activeSearch: ?ActiveSearchType,
   quickOpenEnabled: boolean,
-  canRewind: boolean,
   setActiveSearch: typeof actions.setActiveSearch,
   closeActiveSearch: typeof actions.closeActiveSearch,
   closeProjectSearch: typeof actions.closeProjectSearch,
   openQuickOpen: typeof actions.openQuickOpen,
   closeQuickOpen: typeof actions.closeQuickOpen,
   setOrientation: typeof actions.setOrientation,
 };
 
@@ -314,19 +312,19 @@ class App extends Component<Props, State
         additionalClass={additionalClass}
         enabled={this.state.shortcutsModalEnabled}
         handleClose={() => this.toggleShortcutsModal()}
       />
     );
   }
 
   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 && (
             <QuickOpenModal
               shortcutsModalEnabled={this.state.shortcutsModalEnabled}
               toggleShortcutsModal={() => this.toggleShortcutsModal()}
             />
           )}
@@ -338,17 +336,16 @@ class App extends Component<Props, State
 }
 
 App.childContextTypes = {
   shortcuts: PropTypes.object,
   l10n: PropTypes.object,
 };
 
 const mapStateToProps = state => ({
-  canRewind: getCanRewind(state),
   selectedSource: getSelectedSource(state),
   startPanelCollapsed: getPaneCollapse(state, "start"),
   endPanelCollapsed: getPaneCollapse(state, "end"),
   activeSearch: getActiveSearch(state),
   quickOpenEnabled: getQuickOpenEnabled(state),
   orientation: getOrientation(state),
 });
 
--- a/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.css
+++ b/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.css
@@ -14,30 +14,22 @@
 html[dir="rtl"] .command-bar {
   border-right: 1px solid var(--theme-splitter-color);
 }
 
 .command-bar .filler {
   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);
 }
 
 .command-bar-bottom {
   flex: none;
   justify-content: flex-end;
   border-bottom: none;
--- a/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/CommandBar.js
@@ -7,17 +7,16 @@
 import PropTypes from "prop-types";
 import React, { Component } from "react";
 
 import { connect } from "../../utils/connect";
 import classnames from "classnames";
 import { features } from "../../utils/prefs";
 import {
   getIsWaitingOnBreak,
-  getCanRewind,
   getSkipPausing,
   getCurrentThread,
   getThreadContext,
 } from "../../selectors";
 import { formatKeyShortcut } from "../../utils/text";
 import actions from "../../actions";
 import { debugBtn } from "../shared/Button/CommandBarButton";
 import AccessibleImage from "../shared/AccessibleImage";
@@ -77,25 +76,22 @@ function formatKey(action) {
 
 type OwnProps = {|
   horizontal: boolean,
 |};
 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,
 };
 
 class CommandBar extends Component<Props> {
   componentWillUnmount() {
     const shortcuts = this.context.shortcuts;
     COMMANDS.forEach(action => shortcuts.off(getKey(action)));
@@ -195,78 +191,16 @@ class CommandBar extends Component<Props
     return debugBtn(
       () => breakOnNext(cx),
       "pause",
       "active",
       L10N.getFormatStr("pauseButtonTooltip", formatKey("resume"))
     );
   }
 
-  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;
 
     if (!features.skipPausing) {
       return null;
     }
 
     return (
@@ -292,43 +226,38 @@ class CommandBar extends Component<Props
 
   render() {
     return (
       <div
         className={classnames("command-bar", {
           vertical: !this.props.horizontal,
         })}
       >
-        {this.props.canRewind
-          ? this.renderReplayButtons()
-          : this.renderStepButtons()}
+        {this.renderStepButtons()}
         <div className="filler" />
         {this.renderSkipPausingButton()}
       </div>
     );
   }
 }
 
 CommandBar.contextTypes = {
   shortcuts: PropTypes.object,
 };
 
 const mapStateToProps = state => ({
   cx: getThreadContext(state),
   isWaitingOnBreak: getIsWaitingOnBreak(state, getCurrentThread(state)),
-  canRewind: getCanRewind(state),
   skipPausing: getSkipPausing(state),
 });
 
 export default connect<Props, OwnProps, _, _, _, _>(
   mapStateToProps,
   {
     resume: actions.resume,
     stepIn: actions.stepIn,
     stepOut: actions.stepOut,
     stepOver: actions.stepOver,
     breakOnNext: actions.breakOnNext,
-    rewind: actions.rewind,
-    reverseStepOver: actions.reverseStepOver,
     pauseOnExceptions: actions.pauseOnExceptions,
     toggleSkipPausing: actions.toggleSkipPausing,
   }
 )(CommandBar);
deleted file mode 100644
--- a/devtools/client/debugger/src/components/SecondaryPanes/FrameTimeline.css
+++ /dev/null
@@ -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;
-}
deleted file mode 100644
--- a/devtools/client/debugger/src/components/SecondaryPanes/FrameTimeline.js
+++ /dev/null
@@ -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);
--- a/devtools/client/debugger/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/index.js
@@ -40,17 +40,16 @@ import Frames from "./Frames";
 import Threads from "./Threads";
 import Accordion from "../shared/Accordion";
 import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
 import XHRBreakpoints from "./XHRBreakpoints";
 import EventListeners from "./EventListeners";
 import DOMMutationBreakpoints from "./DOMMutationBreakpoints";
 import WhyPaused from "./WhyPaused";
-import FrameTimeline from "./FrameTimeline";
 
 import Scopes from "./Scopes";
 
 import "./SecondaryPanes.css";
 
 import type {
   Expression,
   Frame,
@@ -517,17 +516,16 @@ class SecondaryPanes extends Component<P
     );
   }
 
   render() {
     const { skipPausing } = this.props;
     return (
       <div className="secondary-panes-wrapper">
         <CommandBar horizontal={this.props.horizontal} />
-        <FrameTimeline />
         <div
           className={classnames(
             "secondary-panes",
             skipPausing && "skip-pausing"
           )}
         >
           {this.props.horizontal
             ? this.renderHorizontalLayout()
--- a/devtools/client/debugger/src/components/SecondaryPanes/moz.build
+++ b/devtools/client/debugger/src/components/SecondaryPanes/moz.build
@@ -8,30 +8,28 @@ DIRS += [
     'Frames',
 ]
 
 CompiledModules(
     'CommandBar.js',
     'DOMMutationBreakpoints.js',
     'EventListeners.js',
     'Expressions.js',
-    'FrameTimeline.js',
     'index.js',
     'Scopes.js',
     'Thread.js',
     'Threads.js',
     'UtilsBar.js',
     'WhyPaused.js',
     'XHRBreakpoints.js',
 )
 
 DevToolsModules(
     'CommandBar.css',
     'DOMMutationBreakpoints.css',
     'EventListeners.css',
     'Expressions.css',
-    'FrameTimeline.css',
     'Scopes.css',
     'SecondaryPanes.css',
     'Threads.css',
     'WhyPaused.css',
     'XHRBreakpoints.css',
 )
--- a/devtools/client/debugger/src/components/shared/AccessibleImage.css
+++ b/devtools/client/debugger/src/components/shared/AccessibleImage.css
@@ -148,25 +148,16 @@ html[dir="rtl"] .img.more-tabs {
 .img.regex-match {
   mask-image: url(resource://devtools/client/debugger/images/regex-match.svg);
 }
 
 .img.resume {
   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);
 }
 
 .img.shortcuts {
   mask-image: url(resource://devtools/client/debugger/images/help.svg);
 }
 
--- a/devtools/client/debugger/src/debugger.css
+++ b/devtools/client/debugger/src/debugger.css
@@ -39,17 +39,16 @@
 @import url("./components/PrimaryPanes/Sources.css");
 @import url("./components/ProjectSearch.css");
 @import url("./components/QuickOpenModal.css");
 @import url("./components/SecondaryPanes/Breakpoints/Breakpoints.css");
 @import url("./components/SecondaryPanes/CommandBar.css");
 @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");
 @import url("./components/SecondaryPanes/SecondaryPanes.css");
 @import url("./components/SecondaryPanes/WhyPaused.css");
 @import url("./components/SecondaryPanes/Threads.css");
 @import url("./components/SecondaryPanes/XHRBreakpoints.css");
 @import url("./components/ShortcutsModal.css");
--- a/devtools/client/debugger/src/reducers/pause.js
+++ b/devtools/client/debugger/src/reducers/pause.js
@@ -25,37 +25,26 @@ import type {
   ChromeFrame,
   FrameId,
   MappedLocation,
   ThreadId,
   Context,
   ThreadContext,
   Previews,
   SourceLocation,
-  ExecutionPoint,
 } 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 = {
   why: ?Why,
   isWaitingOnBreak: boolean,
   frames: ?(any[]),
   framesLoading: boolean,
-  replayFramePositions: {
-    [FrameId]: Array<ExecutionPoint>,
-  },
   frameScopes: {
     generated: {
       [FrameId]: {
         pending: boolean,
         scope: Scope,
       },
     },
     original: {
@@ -274,24 +263,16 @@ function update(
         frameScopes: {
           ...threadState().frameScopes,
           original,
           mappings,
         },
       });
     }
 
-    case "SET_FRAME_POSITIONS":
-      return updateThreadState({
-        replayFramePositions: {
-          ...threadState().replayFramePositions,
-          [action.frame]: action.positions,
-        },
-      });
-
     case "BREAK_ON_NEXT":
       return updateThreadState({ isWaitingOnBreak: true });
 
     case "SELECT_FRAME":
       return updateThreadState({ selectedFrameId: action.frame.id });
 
     case "CONNECT":
       return {
--- a/devtools/client/debugger/src/reducers/threads.js
+++ b/devtools/client/debugger/src/reducers/threads.js
@@ -103,20 +103,16 @@ export const getAllThreads: Selector<Thr
   getMainThread,
   getThreads,
   (mainThread, threads) => [
     mainThread,
     ...sortBy(threads, thread => thread.name),
   ]
 );
 
-export function getCanRewind(state: State) {
-  return state.threads.traits.canRewind;
-}
-
 export function supportsWasm(state: State) {
   return features.wasm && state.threads.traits.wasmBinarySource;
 }
 
 // checks if a path begins with a thread actor
 // e.g "server1.conn0.child1/workerTarget22/context1/dbg-workers.glitch.me"
 export function startsWithThreadActor(state: State, path: string) {
   const threadActors = getAllThreads(state).map(t => t.actor);
--- a/devtools/client/debugger/src/selectors/index.js
+++ b/devtools/client/debugger/src/selectors/index.js
@@ -48,17 +48,16 @@ export { getCallStackFrames } from "./ge
 export { getBreakpointSources } from "./breakpointSources";
 export { isLineInScope } from "./isLineInScope";
 export { getXHRBreakpoints, shouldPauseOnAnyXHR } from "./breakpoints";
 export * from "./visibleColumnBreakpoints";
 export {
   getSelectedFrame,
   getSelectedFrames,
   getVisibleSelectedFrame,
-  getFramePositions,
 } from "./pause";
 
 // eslint-disable-next-line import/named
 import { objectInspector } from "devtools-reps";
 
 const { reducer } = objectInspector;
 
 Object.keys(reducer).forEach(function(key) {
--- a/devtools/client/debugger/src/selectors/pause.js
+++ b/devtools/client/debugger/src/selectors/pause.js
@@ -52,24 +52,8 @@ export const getVisibleSelectedFrame: Se
 
     return {
       id,
       displayName,
       location: _getSelectedLocation(selectedFrame, selectedLocation),
     };
   }
 );
-
-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];
-}
--- a/devtools/client/debugger/src/utils/pause/why.js
+++ b/devtools/client/debugger/src/utils/pause/why.js
@@ -16,17 +16,16 @@ const reasons = {
   exception: "whyPaused.exception",
   resumeLimit: "whyPaused.resumeLimit",
   breakpointConditionThrown: "whyPaused.breakpointConditionThrown",
   eventBreakpoint: "whyPaused.eventBreakpoint",
   getWatchpoint: "whyPaused.getWatchpoint",
   setWatchpoint: "whyPaused.setWatchpoint",
   mutationBreakpoint: "whyPaused.mutationBreakpoint",
   interrupted: "whyPaused.interrupted",
-  replayForcedPause: "whyPaused.replayForcedPause",
 
   // V8
   DOM: "whyPaused.breakpoint",
   EventListener: "whyPaused.pauseOnDOMEvents",
   XHR: "whyPaused.XHR",
   promiseRejection: "whyPaused.promiseRejection",
   assert: "whyPaused.assert",
   debugCommand: "whyPaused.debugCommand",
--- a/devtools/client/debugger/test/mochitest/helpers.js
+++ b/devtools/client/debugger/test/mochitest/helpers.js
@@ -564,17 +564,16 @@ function isSelectedFrameSelected(dbg, st
 }
 
 /**
  * Clear all the debugger related preferences.
  */
 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");
   Services.prefs.clearUserPref("devtools.debugger.ignore-caught-exceptions");
   Services.prefs.clearUserPref("devtools.debugger.pending-selected-location");
   Services.prefs.clearUserPref("devtools.debugger.expressions");
   Services.prefs.clearUserPref("devtools.debugger.call-stack-visible");
   Services.prefs.clearUserPref("devtools.debugger.scopes-visible");
--- a/devtools/client/definitions.js
+++ b/devtools/client/definitions.js
@@ -73,26 +73,16 @@ loader.lazyGetter(
   "ApplicationPanel",
   () => require("devtools/client/application/panel").ApplicationPanel
 );
 loader.lazyGetter(
   this,
   "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(
   this,
   "AccessibilityStartup",
   "devtools/client/accessibility/accessibility-startup",
   true
 );
@@ -562,38 +552,16 @@ exports.ToolboxButtons = [
     onClick(event, toolbox) {
       toolbox.togglePaintFlashing();
     },
     isChecked(toolbox) {
       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",
     isTargetSupported: target => !AppConstants.MOZILLA_OFFICIAL,
     onClick: (event, toolbox) => DevToolsFissionPrefs.showTooltip(toolbox),
     isChecked: () => DevToolsFissionPrefs.isAnyPreferenceEnabled(),
   },
   {
     id: "command-button-responsive",
--- a/devtools/client/framework/browser-menus.js
+++ b/devtools/client/framework/browser-menus.js
@@ -296,18 +296,16 @@ function removeTopLevelItems(doc) {
  *
  * @param {HTMLDocument} doc
  *        The document to which menus are to be added.
  */
 exports.addMenus = function(doc) {
   addTopLevelItems(doc);
 
   addAllToolsToMenu(doc);
-
-  require("devtools/client/webreplay/menu").addWebReplayMenu(doc);
 };
 
 /**
  * Remove menus from a browser document
  *
  * @param {HTMLDocument} doc
  *        The document to which menus are to be removed.
  */
--- a/devtools/client/framework/components/ToolboxToolbar.js
+++ b/devtools/client/framework/components/ToolboxToolbar.js
@@ -31,21 +31,16 @@ loader.lazyGetter(this, "MenuItem", func
     require("devtools/client/shared/components/menu/MenuItem")
   );
 });
 loader.lazyGetter(this, "MenuList", function() {
   return createFactory(
     require("devtools/client/shared/components/menu/MenuList")
   );
 });
-loader.lazyGetter(this, "WebReplayPlayer", function() {
-  return createFactory(
-    require("devtools/client/webreplay/components/WebReplayPlayer")
-  );
-});
 
 loader.lazyRequireGetter(
   this,
   "getUnicodeUrl",
   "devtools/client/shared/unicode-url",
   true
 );
 
@@ -462,24 +457,13 @@ class ToolboxToolbar extends Component {
           this.renderToolboxControls()
         )
       : div({ className: classnames.join(" ") });
 
     const debugTargetInfo = debugTargetData
       ? DebugTargetInfo({ debugTargetData, L10N, toolbox })
       : null;
 
-    if (toolbox.target.canRewind) {
-      return div(
-        {},
-        WebReplayPlayer({
-          toolbox: toolbox,
-        }),
-        debugTargetInfo,
-        toolbar
-      );
-    }
-
     return div({}, debugTargetInfo, toolbar);
   }
 }
 
 module.exports = ToolboxToolbar;
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -140,22 +140,16 @@ var gDevToolsBrowser = (exports.gDevTool
     );
     const remoteEnabled = chromeEnabled && devtoolsRemoteEnabled;
     toggleMenuItem("menu_browserToolbox", remoteEnabled);
     toggleMenuItem(
       "menu_browserContentToolbox",
       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,
     // with the option to flip the pref in other releases. This feature flag will
     // go away once it is fully shipped.
     const isPopupFeatureFlagEnabled = Services.prefs.getBoolPref(
       "devtools.performance.popup.feature-flag",
       AppConstants.NIGHTLY_BUILD
--- a/devtools/client/framework/toolbox-options.js
+++ b/devtools/client/framework/toolbox-options.js
@@ -197,50 +197,28 @@ OptionsPanel.prototype = {
 
     const createCommandCheckbox = button => {
       const checkboxLabel = this.panelDoc.createElement("label");
       const checkboxSpanLabel = this.panelDoc.createElement("span");
       checkboxSpanLabel.textContent = button.description;
       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(
         "change",
         onCheckboxClick.bind(this, checkboxInput)
       );
 
       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;
     };
 
     for (const button of toolbarButtons) {
       if (!button.isTargetSupported(this.toolbox.target)) {
         continue;
       }
 
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -152,27 +152,16 @@ loader.lazyGetter(this, "DEBUG_TARGET_TY
   return require("devtools/client/shared/remote-debugging/constants")
     .DEBUG_TARGET_TYPES;
 });
 
 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",
   "devtools/client/shared/thread-utils",
   true
 );
 
 loader.lazyRequireGetter(
@@ -764,22 +753,16 @@ Toolbox.prototype = {
 
       await domReady;
 
       this.browserRequire = BrowserLoader({
         window: this.win,
         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();
 
       Services.prefs.addObserver(
         "devtools.cache.disabled",
         this._applyCacheSettings
       );
@@ -2138,22 +2121,17 @@ Toolbox.prototype = {
   },
 
   /**
    * Ensure the visibility of each toolbox button matches the preference value.
    */
   _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;
     }
 
     if (isTargetSupported && !isTargetSupported(this.target)) {
       return false;
     }
 
     if (isCurrentlyVisible && !isCurrentlyVisible()) {
@@ -2864,22 +2842,17 @@ Toolbox.prototype = {
     // See Bug 1519087.
     event.preventDefault();
   },
 
   /**
    * 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 });
   },
 
   /**
    * Loads the tool next to the currently selected tool.
    */
   selectNextTool: function() {
     const definitions = this.component.panelDefinitions;
     const index = definitions.findIndex(({ id }) => id === this.currentToolId);
@@ -3530,21 +3503,17 @@ Toolbox.prototype = {
    *
    * @return The notification box component.
    */
   getNotificationBox: function() {
     return this.notificationBox;
   },
 
   closeToolbox: async function() {
-    const shouldStopRecording = this.target.isReplayEnabled();
     await this.destroy();
-    if (shouldStopRecording) {
-      reloadAndStopRecordingTab();
-    }
   },
 
   /**
    * Public API to check is the current toolbox is currently being destroyed.
    */
   isDestroying: function() {
     return this._destroyer;
   },
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -177,43 +177,29 @@ function Inspector(toolbox) {
   this.onShowBoxModelHighlighterForNode = this.onShowBoxModelHighlighterForNode.bind(
     this
   );
   this.onSidebarHidden = this.onSidebarHidden.bind(this);
   this.onSidebarResized = this.onSidebarResized.bind(this);
   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);
 }
 
 Inspector.prototype = {
   /**
    * InspectorPanel.open() is effectively an asynchronous constructor.
    * Set any attributes or listeners that rely on the document being loaded or fronts
    * from the InspectorFront and Target here.
    */
   async init() {
     // 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,
       this._onTargetDestroyed
     );
 
     // Store the URL of the target page prior to navigation in order to ensure
     // telemetry counts in the Grid Inspector are not double counted on reload.
@@ -462,20 +448,18 @@ Inspector.prototype = {
       return this._defaultNode;
     }
     const walker = this.walker;
     let rootNode = null;
     const pendingSelection = this._pendingSelection;
 
     // 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
     // as default selected, else set documentElement
     return walker
       .getRootNode()
       .then(node => {
         if (hasNavigated()) {
@@ -1363,32 +1347,16 @@ Inspector.prototype = {
     this._pendingSelection = onNodeSelected;
     this._getDefaultNodeForSelection().then(
       onNodeSelected,
       this._handleRejectionIfNotDestroyed
     );
   },
 
   /**
-   * 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
    * highlighter state.
    */
   async onMarkupLoaded() {
     if (!this.markup) {
       return;
     }
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -102,17 +102,16 @@ devtools.jar:
     skin/images/performance-details-flamegraph.svg (themes/images/performance-details-flamegraph.svg)
     skin/breadcrumbs.css (themes/breadcrumbs.css)
     skin/chart.css (themes/chart.css)
     skin/widgets.css (themes/widgets.css)
     skin/rules.css (themes/rules.css)
     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)
     skin/images/command-console.svg (themes/images/command-console.svg)
     skin/images/command-eyedropper.svg (themes/images/command-eyedropper.svg)
     skin/images/command-rulers.svg (themes/images/command-rulers.svg)
     skin/images/command-measure.svg (themes/images/command-measure.svg)
     skin/images/command-noautohide.svg (themes/images/command-noautohide.svg)
--- a/devtools/client/locales/en-US/debugger.properties
+++ b/devtools/client/locales/en-US/debugger.properties
@@ -772,21 +772,16 @@ whyPaused.mutationBreakpointAdded=Added:
 # is displayed to describe a removed node which triggers a subtree modification
 whyPaused.mutationBreakpointRemoved=Removed:
 
 # LOCALIZATION NOTE (whyPaused.interrupted): The text that is displayed
 # in a info block explaining how the debugger is currently paused at
 # 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
 whyPaused.resumeLimit=Paused while stepping
 
 # LOCALIZATION NOTE (whyPaused.pauseOnDOMEvents): The text that is displayed
 # in a info block explaining how the debugger is currently paused on a
 # dom event
--- a/devtools/client/locales/en-US/menus.properties
+++ b/devtools/client/locales/en-US/menus.properties
@@ -28,19 +28,13 @@ browserToolboxMenu.accesskey = e
 browserContentToolboxMenu.label = Browser Content Toolbox
 browserContentToolboxMenu.accesskey = x
 
 # LOCALIZATION NOTE (toggleProfilerButtonMenu.label): This is the label for the
 # application menu item that toggles the profiler button to record performance profiles.
 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
 
 getMoreDevtoolsCmd.label = Get More Tools
 getMoreDevtoolsCmd.accesskey = M
--- a/devtools/client/locales/en-US/startup.properties
+++ b/devtools/client/locales/en-US/startup.properties
@@ -226,26 +226,16 @@ application.panelLabel=Application Panel
 application.tooltip=Application Panel
 
 # LOCALIZATION NOTE (toolbox.buttons.responsive):
 # This is the tooltip of the button in the toolbox toolbar that toggles
 # the Responsive mode.
 # 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.
 toolbox.buttons.paintflashing = Toggle paint flashing
 
 # LOCALIZATION NOTE (toolbox.buttons.screenshot):
 # This is the tooltip of the button in the toolbox toolbar that allows you to
 # take a screenshot of the entire page
--- a/devtools/client/locales/en-US/toolbox.properties
+++ b/devtools/client/locales/en-US/toolbox.properties
@@ -240,47 +240,26 @@ toolbox.debugTargetInfo.targetType.proce
 toolbox.debugTargetInfo.targetType.tab=Tab
 toolbox.debugTargetInfo.targetType.worker=Worker
 
 # LOCALIZATION NOTE (browserToolbox.statusMessage): This is the label
 # shown next to status details when the Browser Toolbox fails to connect or
 # 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
 toolbox.debugTargetErrorPage.title = Error
 
 # LOCALIZATION NOTE (toolbox.debugTargetErrorPage.description): This is the
 # text that appears in the Error view and explains to the user that an error
 # has happened while trying to connect to a debug target
 toolbox.debugTargetErrorPage.description = Cannot connect to the debug target. See error details below:
 
 # LOCALIZATION NOTE (options.deprecationNotice): This is the text that appears in the
 # settings panel for panel that will be removed in future releases.
 # 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)
--- a/devtools/client/locales/en-US/webconsole.properties
+++ b/devtools/client/locales/en-US/webconsole.properties
@@ -218,29 +218,16 @@ webconsole.menu.exportSubmenu.label=Expo
 webconsole.menu.exportSubmenu.exportCliboard.label=Clipboard
 
 # LOCALIZATION NOTE (webconsole.menu.exportFile.label)
 # Label used for a context-menu item displayed on the output. Clicking on it
 # opens a file picker to allow the user save a file containing
 # 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.
 webconsole.clearButton.tooltip=Clear the Web Console output
 
 # LOCALIZATION NOTE (webconsole.toggleFilterButton.tooltip)
 # Label used for the tooltip on the toggle filter bar button in the console top
 # toolbar bar. Clicking on it will toggle the visibility of an additional bar which
--- a/devtools/client/moz.build
+++ b/devtools/client/moz.build
@@ -22,17 +22,16 @@ DIRS += [
     'performance-new',
     'preferences',
     'responsive',
     'shared',
     'storage',
     'styleeditor',
     'themes',
     'webconsole',
-    'webreplay',
     'whats-new',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 DevToolsModules(
     'definitions.js',
     'menus.js',
--- a/devtools/client/shared/test/test-actor.js
+++ b/devtools/client/shared/test/test-actor.js
@@ -16,17 +16,16 @@ const {
 } = require("devtools/shared/layout/utils");
 const defer = require("devtools/shared/defer");
 const {
   isAuthorStylesheet,
   getCSSStyleRules,
 } = 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
 // it rely on the |window| global.
 const EventUtils = {};
 EventUtils.window = {};
 EventUtils.parent = {};
 /* eslint-disable camelcase */
@@ -300,21 +299,16 @@ var testSpec = protocol.generateActorSpe
 
 var TestActor = (exports.TestActor = protocol.ActorClassWithSpec(testSpec, {
   initialize: function(conn, targetActor, options) {
     this.conn = conn;
     this.targetActor = targetActor;
   },
 
   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;
   },
 
   /**
    * Helper to retrieve a DOM element.
    * @param {string | array} selector Either a regular selector string
    *   or a selector array. If an array, each item, except the last one
    *   are considered matching an iframe, so that we can query element
deleted file mode 100644
--- a/devtools/client/themes/images/command-replay.svg
+++ /dev/null
@@ -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>
--- a/devtools/client/themes/toolbox.css
+++ b/devtools/client/themes/toolbox.css
@@ -302,47 +302,32 @@
 #command-button-responsive.checked::before {
   fill-opacity: 0.15;
 }
 
 .theme-dark #command-button-responsive.checked::before {
   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;
 }
 
 .fission-pref-icon::before {
   content: "";
   background-image: url("chrome://devtools/skin/images/help.svg");
   -moz-context-properties: fill;
   fill: var(--theme-body-color);
   display: block;
   background-size: 12px;
   width: 12px;
   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");
 }
 
 #command-button-measure::before {
   background-image: url("chrome://devtools/skin/images/command-measure.svg");
 }
 
@@ -424,294 +409,8 @@
   opacity: 0;
   transition: none;
 }
 
 #toolbox-container.tabs-reordering .devtools-tab.selected {
   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;
-}
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -125,25 +125,16 @@ a {
 }
 
 /* We already paint a top border on jsterm-input-container (and we need to keep
  * it when scrolling console content), so remove the last item's border. */
 .webconsole-app:not(.jsterm-editor) .message:last-child {
   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.
  * This makes console reflows faster (See Bug 1487457).
  */
 .message:not(.network) * {
   overflow: hidden;
 }
@@ -294,36 +285,21 @@ a {
   background-image: url(chrome://devtools/skin/images/alert-small.svg);
 }
 
 .message.navigationMarker > .icon {
   color: var(--console-navigation-color);
   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;
   margin: var(--console-output-vertical-padding) 0;
 }
 
 .message-body-wrapper .table-widget-body {
   overflow: visible;
--- a/devtools/client/webconsole/actions/input.js
+++ b/devtools/client/webconsole/actions/input.js
@@ -209,21 +209,16 @@ function setInputValue(value) {
 
 function terminalInputChanged(expression) {
   return async ({ dispatch, webConsoleUI, hud, client, getState }) => {
     const prefs = getAllPrefs(getState());
     if (!prefs.eagerEvaluation) {
       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 (
       (!terminalInput && !expression) ||
       (typeof terminalInput === "string" &&
         typeof expression === "string" &&
         expression.trim() === terminalInput.trim())
     ) {
--- a/devtools/client/webconsole/reducers/messages.js
+++ b/devtools/client/webconsole/reducers/messages.js
@@ -54,35 +54,23 @@ loader.lazyRequireGetter(
   true
 );
 loader.lazyRequireGetter(
   this,
   "getParentWarningGroupMessageId",
   "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");
 
 const {
   processNetworkUpdates,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 
-const maxNumber = 100000;
-
 const MessageState = overrides =>
   Object.freeze(
     Object.assign(
       {
         // List of all the messages added to the console.
         messagesById: new Map(),
         // List of additional data associated with messages (populated async or on-demand at a
         // later time after the message is received).
@@ -104,22 +92,16 @@ const MessageState = overrides =>
         // Array of fronts to release (i.e. fronts logged in removed messages).
         // This array *should not* be consumed by any UI component.
         frontsToRelease: [],
         // Map of the form {messageId : numberOfRepeat}
         repeatById: {},
         // 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
     )
   );
 
 function cloneState(state) {
   return {
     messagesById: new Map(state.messagesById),
@@ -127,19 +109,16 @@ function cloneState(state) {
     filteredMessagesCount: { ...state.filteredMessagesCount },
     messagesUiById: [...state.messagesUiById],
     messagesPayloadById: new Map(state.messagesPayloadById),
     groupsById: new Map(state.groupsById),
     currentGroup: state.currentGroup,
     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),
   };
 }
 
 /**
  * Add a console message to the state.
  *
  * @param {ConsoleMessage} newMessage: The message to add to the state.
@@ -153,26 +132,16 @@ function cloneState(state) {
 function addMessage(newMessage, state, filtersState, prefsState, uiState) {
   const { messagesById, groupsById, currentGroup, repeatById } = state;
 
   if (newMessage.type === constants.MESSAGE_TYPE.NULL_MESSAGE) {
     // When the message has a NULL type, we don't add it.
     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);
     return state;
   }
 
   if (newMessage.allowRepeating && messagesById.size > 0) {
     const lastMessage = messagesById.get(getLastMessageId(state));
@@ -188,37 +157,17 @@ function addMessage(newMessage, state, f
 
   // Add the new message with a reference to the parent group.
   const parentGroups = getParentGroups(currentGroup, groupsById);
   if (!isWarningGroup(newMessage)) {
     newMessage.groupId = currentGroup;
     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
   // proper message.
   const warningGroupType = getWarningGroupType(newMessage);
 
   // If the preference for warning grouping is true, and the new message could be in a
   // warning group.
@@ -376,25 +325,16 @@ function messages(
     groupsById,
     visibleMessages,
   } = state;
 
   const { logLimit } = prefsState;
 
   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 = [];
       let prunableCount = 0;
       let lastMessageRepeatId = -1;
       for (let i = action.messages.length - 1; i >= 0; i--) {
         const message = action.messages[i];
         if (
@@ -460,36 +400,16 @@ function messages(
       return removeMessagesFromState(
         {
           ...state,
         },
         removedIds
       );
     }
 
-    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];
       const currMessage = messagesById.get(action.id);
 
       // If the message is a console.group/groupCollapsed or a warning group.
       if (isGroupType(currMessage.type) || isWarningGroup(currMessage)) {
         // We want to make its children visible
@@ -1481,133 +1401,31 @@ function getDefaultFiltersCounter() {
   const count = DEFAULT_FILTERS.reduce((res, filter) => {
     res[filter] = 0;
     return res;
   }, {});
   count.global = 0;
   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.
  *
  * @param {MessageState} state
  * @param {Boolean} sortWarningGroupMessage: set to true to sort warningGroup
  *                                           messages. Default to false, as in some
  *                                           situations we already take care of putting
  *                                           the ids at the right position.
  * @param {Boolean} timeStampSort: set to true to sort messages by their timestamps.
  */
 function maybeSortVisibleMessages(
   state,
   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;
       const bFirst = 1;
 
       // It can happen that messages are emitted in the same microsecond, making their
       // timestamp similar. In such case, we rely on which message came first through
       // the console API service, checking their id.
@@ -1711,11 +1529,8 @@ function shouldGroupWarningMessages(
   if (!warningGroup || !Array.isArray(warningGroup)) {
     return false;
   }
 
   return warningGroup.length > 1;
 }
 
 exports.messages = messages;
-
-// Export for testing purpose.
-exports.ensureExecutionPoint = ensureExecutionPoint;
--- a/devtools/client/webconsole/test/node/package.json
+++ b/devtools/client/webconsole/test/node/package.json
@@ -1,14 +1,11 @@
 {
   "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: ",
       " * Run mocha on components, middleware, store and utils folders, on .test.js files.",
       "   We need to specify them so we don't run unwanted tests (e.g. in node_modules).",
       " * We require jsdom-global to inject `document` and `window` objects which are",
       "   not in nodejs natively.",
       " * Finally we require mocha-test-setup where we configure Enzyme and",
--- a/devtools/client/webconsole/test/node/store/messages.test.js
+++ b/devtools/client/webconsole/test/node/store/messages.test.js
@@ -7,19 +7,16 @@ const {
   getAllMessagesPayloadById,
   getAllNetworkMessagesUpdateById,
   getAllRepeatById,
   getCurrentGroup,
   getGroupsById,
   getAllMessagesById,
   getVisibleMessages,
 } = require("devtools/client/webconsole/selectors/messages");
-const {
-  ensureExecutionPoint,
-} = require("devtools/client/webconsole/reducers/messages");
 
 const {
   clonePacket,
   getFirstMessage,
   getLastMessage,
   getMessageAt,
   setupActions,
   setupStore,
@@ -38,25 +35,20 @@ describe("Message reducer:", () => {
   before(() => {
     actions = setupActions();
   });
 
   describe("messagesById", () => {
     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);
     });
 
     it("increments repeat on a repeating log message", () => {
       const key1 = "console.log('foobar', 'test')";
       const { dispatch, getState } = setupStore([key1, key1], { actions });
 
deleted file mode 100644
--- a/devtools/client/webreplay/components/WebReplayPlayer.js
+++ /dev/null
@@ -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;
deleted file mode 100644
--- a/devtools/client/webreplay/components/moz.build
+++ /dev/null
@@ -1,3 +0,0 @@
-DevToolsModules(
-    'WebReplayPlayer.js',
-)
\ No newline at end of file
deleted file mode 100644
--- a/devtools/client/webreplay/menu.js
+++ /dev/null
@@ -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;
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser.ini
+++ /dev/null
@@ -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]
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-06.js
+++ /dev/null
@@ -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);
-  }
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-07.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_logpoint-03.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js
+++ /dev/null
@@ -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");
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-05.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-06.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-02.js
+++ /dev/null
@@ -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");
-  }
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_rr_inspector-03.js
+++ /dev/null
@@ -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);
-  }
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_rr_object_preview-01.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/browser_rr_object_preview-02.js
+++ /dev/null
@@ -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);
-});
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/blackbox.js
+++ /dev/null
@@ -1,5 +0,0 @@
-function blackboxed(...args) {
-  for (const arg of args) {
-    arg();
-  }
-}
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_control_flow.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_debugger_statements.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_events.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_inspector_basic.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_inspector_styles.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_minified.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_basic.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_blackbox.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_continuous.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_error.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_logs.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/doc_rr_objects.html
+++ /dev/null
@@ -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>
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/minified.js
+++ /dev/null
@@ -1,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);
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/examples/styles.css
+++ /dev/null
@@ -1,7 +0,0 @@
-body {
-    background-color: green;
-}
-
-div {
-    background-color: red;
-}
deleted file mode 100644
--- a/devtools/client/webreplay/mochitest/head.js
+++ /dev/null
@@ -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);
deleted file mode 100644
--- a/devtools/client/webreplay/moz.build
+++ /dev/null
@@ -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' ]
--- a/devtools/server/actors/highlighters.js
+++ b/devtools/server/actors/highlighters.js
@@ -5,17 +5,16 @@
 "use strict";
 
 const { Ci, Cu } = require("chrome");
 
 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,
 } = require("devtools/shared/specs/highlighters");
 
 loader.lazyRequireGetter(
   this,
   "isWindowIncluded",
@@ -448,19 +447,17 @@ exports.HighlighterActor = protocol.Acto
     this._highlighterEnv.window.focus();
     return pickResults;
   },
 
   _findAndAttachElement: function(event) {
     // 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);
   },
 
   _onSuppressedEvent(event) {
     if (event.type == "mousemove") {
       this._onHovered(event);
     } else if (event.type == "mouseup") {
       // Suppressed mousedown/mouseup events will be sent to us before they have
--- a/devtools/server/actors/highlighters/auto-refresh.js
+++ b/devtools/server/actors/highlighters/auto-refresh.js
@@ -1,17 +1,16 @@
 /* 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 { 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");
 const {
   getAdjustedQuads,
   getWindowDimensions,
 } = require("devtools/shared/layout/utils");
 
@@ -98,17 +97,17 @@ AutoRefreshHighlighter.prototype = {
     if (!this.highlighterEnv) {
       return null;
     }
     return this.highlighterEnv.window;
   },
 
   /* Window containing the target content. */
   get contentWindow() {
-    return isReplaying ? ReplayInspector.window : this.win;
+    return this.win;
   },
 
   /**
    * Show the highlighter on a given node
    * @param {DOMNode} node
    * @param {Object} options
    *        Object used for passing options
    */
@@ -293,17 +292,17 @@ AutoRefreshHighlighter.prototype = {
 
   _hide: function() {
     // To be implemented by sub classes
     // When called, sub classes should actually hide the highlighter
     throw new Error("Custom highlighter class had to implement _hide method");
   },
 
   _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();
   },
 
   _stopRefreshLoop: function() {
     if (this.rafID && !Cu.isDeadWrapper(this.rafWin)) {
       this.rafWin.cancelAnimationFrame(this.rafID);
--- a/devtools/server/actors/inspector/event-collector.js
+++ b/devtools/server/actors/inspector/event-collector.js
@@ -11,17 +11,16 @@ const { Cu } = require("chrome");
 const Services = require("Services");
 const {
   isAfterPseudoElement,
   isBeforePseudoElement,
   isMarkerPseudoElement,
   isNativeAnonymous,
 } = require("devtools/shared/layout/utils");
 const Debugger = require("Debugger");
-const ReplayInspector = require("devtools/server/actors/replay/inspector");
 const {
   EXCLUDED_LISTENER,
 } = require("devtools/server/actors/inspector/constants");
 
 // eslint-disable-next-line
 const JQUERY_LIVE_REGEX = /return typeof \w+.*.event\.triggered[\s\S]*\.event\.(dispatch|handle).*arguments/;
 
 const REACT_EVENT_NAMES = [
@@ -903,28 +902,23 @@ class EventCollector {
    */
   // eslint-disable-next-line complexity
   processHandlerForEvent(listenerArray, listener, dbg) {
     let globalDO;
 
     try {
       const { capturing, handler } = listener;
 
-      let listenerDO;
-      if (isReplaying) {
-        listenerDO = ReplayInspector.getDebuggerObject(handler);
-      } else {
-        const global = Cu.getGlobalForObject(handler);
+      const global = Cu.getGlobalForObject(handler);
 
-        // It is important that we recreate the globalDO for each handler because
-        // their global object can vary e.g. resource:// URLs on a video control. If
-        // we don't do this then all chrome listeners simply display "native code."
-        globalDO = dbg.addDebuggee(global);
-        listenerDO = globalDO.makeDebuggeeValue(handler);
-      }
+      // It is important that we recreate the globalDO for each handler because
+      // their global object can vary e.g. resource:// URLs on a video control. If
+      // we don't do this then all chrome listeners simply display "native code."
+      globalDO = dbg.addDebuggee(global);
+      let listenerDO = globalDO.makeDebuggeeValue(handler);
 
       const { normalizeListener } = listener;
 
       if (normalizeListener) {
         listenerDO = normalizeListener(listenerDO, listener);
       }
 
       const hide = listener.hide || {};
--- a/devtools/server/actors/inspector/inspector.js
+++ b/devtools/server/actors/inspector/inspector.js
@@ -49,17 +49,16 @@
  * seen on the client side", we guarantee that every time we've seen a node,
  * we connect it up through its parents.
  */
 
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const { LongStringActor } = require("devtools/server/actors/string");
 const defer = require("devtools/shared/defer");
-const ReplayInspector = require("devtools/server/actors/replay/inspector");
 
 const { inspectorSpec } = require("devtools/shared/specs/inspector");
 
 loader.lazyRequireGetter(
   this,
   "InspectorActorUtils",
   "devtools/server/actors/inspector/utils"
 );
@@ -130,17 +129,17 @@ exports.InspectorActor = protocol.ActorC
     this._highlighterPromise = null;
     this._pageStylePromise = null;
     this._walkerPromise = null;
     this.walker = null;
     this.targetActor = null;
   },
 
   get window() {
-    return isReplaying ? ReplayInspector.window : this.targetActor.window;
+    return this.targetActor.window;
   },
 
   getWalker: function(options = {}) {
     if (this._walkerPromise) {
       return this._walkerPromise;
     }
 
     const deferred = defer();
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -6,17 +6,16 @@
 
 const { Cc, Ci, Cu } = require("chrome");
 
 const Services = require("Services");
 const protocol = require("devtools/shared/protocol");
 const { walkerSpec } = require("devtools/shared/specs/walker");
 const { LongStringActor } = require("devtools/server/actors/string");
 const InspectorUtils = require("InspectorUtils");
-const ReplayInspector = require("devtools/server/actors/replay/inspector");
 const {
   EXCLUDED_LISTENER,
 } = require("devtools/server/actors/inspector/constants");
 
 loader.lazyRequireGetter(
   this,
   "getFrameElement",
   "devtools/shared/layout/utils",
@@ -277,17 +276,17 @@ var WalkerActor = protocol.ActorClassWit
    *        The top-level Actor for this tab.
    * @param {Object} options
    *        - {Boolean} showAllAnonymousContent: Show all native anonymous content
    *        - {Boolean} showUserAgentShadowRoots: Show shadow roots for user-agent widgets
    */
   initialize: function(conn, targetActor, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.targetActor = targetActor;
-    this.rootWin = isReplaying ? ReplayInspector.window : targetActor.window;
+    this.rootWin = targetActor.window;
     this.rootDoc = this.rootWin.document;
     this._refMap = new Map();
     this._pendingMutations = [];
     this._activePseudoClassLocks = new Set();
     this._mutationBreakpoints = new WeakMap();
     this.customElementWatcher = new CustomElementWatcher(
       targetActor.chromeEventHandler
     );
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -8,17 +8,16 @@ DIRS += [
     'accessibility',
     'addon',
     'descriptors',
     'emulation',
     'highlighters',
     'inspector',
     'network-monitor',
     'object',
-    'replay',
     'targets',
     'utils',
     'webconsole',
     'worker',
 ]
 
 DevToolsModules(
     'actor-registry.js',
--- a/devtools/server/actors/object.js
+++ b/devtools/server/actors/object.js
@@ -210,22 +210,16 @@ const proto = {
       // Bug 1348761: getOwnPropertyNames is unnecessary slow on TypedArrays
       return getArrayLength(this.obj);
     }
 
     if (isStorage(this.obj)) {
       return getStorageLength(this.obj);
     }
 
-    if (isReplaying) {
-      // When replaying we can get the number of properties directly, to avoid
-      // needing to enumerate all of them.
-      return this.obj.getOwnPropertyNamesCount();
-    }
-
     try {
       return this.obj.getOwnPropertyNames().length;
     } catch (err) {
       // The above can throw when the debuggee does not subsume the object's
       // compartment, or for some WrappedNatives like Cu.Sandbox.
     }
 
     return null;
@@ -469,22 +463,17 @@ const proto = {
         } catch (ex) {
           // The above can throw if the cache becomes stale.
         }
         if (!getter) {
           obj._safeGetters = null;
           continue;
         }
 
-        let result;
-        if (isReplaying && this.obj.replayHasPropertyValue(name)) {
-          result = this.obj.replayPropertyValue(name);
-        } else {
-          result = getter.call(this.obj);
-        }
+        const result = getter.call(this.obj);
         if (!result || "throw" in result) {
           continue;
         }
 
         let getterValue = undefined;
         if ("return" in result) {
           getterValue = result.return;
         } else if ("yield" in result) {
--- a/devtools/server/actors/object/previewers.js
+++ b/devtools/server/actors/object/previewers.js
@@ -192,19 +192,17 @@ const previewers = {
           } else if (!desc) {
             items.push(null);
           } else {
             items.push(hooks.createValueGrip(undefined));
           }
         } else if (raw && !Object.getOwnPropertyDescriptor(raw, i)) {
           items.push(null);
         } else {
-          // Workers do not have access to Cu, and when recording/replaying we
-          // don't have a raw object. In either case we do not need to deal with
-          // xray wrappers.
+          // Workers do not have access to Cu.
           const value = DevToolsUtils.getProperty(obj, i);
           items.push(hooks.createValueGrip(value));
         }
 
         if (items.length == OBJECT_PREVIEW_MAX_ITEMS) {
           break;
         }
       }
@@ -510,20 +508,17 @@ function GenericObject(
       )
     );
 
     if (++i == OBJECT_PREVIEW_MAX_ITEMS) {
       break;
     }
   }
 
-  // Do not search for safe getters when generating previews while replaying.
-  // This involves a lot of back-and-forth communication which we don't want to
-  // incur while previewing objects.
-  if (i < OBJECT_PREVIEW_MAX_ITEMS && !isReplaying) {
+  if (i < OBJECT_PREVIEW_MAX_ITEMS) {
     preview.safeGetterValues = objectActor._findSafeGetterValues(
       Object.keys(preview.ownProperties),
       OBJECT_PREVIEW_MAX_ITEMS - i
     );
   }
 
   return true;
 }
@@ -857,22 +852,16 @@ previewers.Object = [
 
   function PseudoArray({ obj, hooks }, grip, rawObj) {
     // An object is considered a pseudo-array if all the following apply:
     // - All its properties are array indices except, optionally, a "length" property.
     // - At least it has the "0" array index.
     // - The array indices are consecutive.
     // - The value of "length", if present, is the number of array indices.
 
-    // Don't generate pseudo array previews when replaying. We don't want to
-    // have to enumerate all the properties in order to determine this.
-    if (isReplaying) {
-      return false;
-    }
-
     let keys;
     try {
       keys = obj.getOwnPropertyNames();
     } catch (err) {
       // The above can throw when the debuggee does not subsume the object's
       // compartment, or for some WrappedNatives like Cu.Sandbox.
       return false;
     }
--- a/devtools/server/actors/object/property-iterator.js
+++ b/devtools/server/actors/object/property-iterator.js
@@ -252,20 +252,16 @@ function enumObjectProperties(objectActo
         desc.getterPrototypeLevel = getterPrototypeLevel;
       }
       return desc;
     },
   };
 }
 
 function getMapEntries(obj, forPreview) {
-  if (isReplaying) {
-    return obj.containerContents(forPreview);
-  }
-
   // Iterating over a Map via .entries goes through various intermediate
   // objects - an Iterator object, then a 2-element Array object, then the
   // actual values we care about. We don't have Xrays to Iterator objects,
   // so we get Opaque wrappers for them. And even though we have Xrays to
   // Arrays, the semantics often deny access to the entires based on the
   // nature of the values. So we need waive Xrays for the iterator object
   // and the tupes, and then re-apply them on the underlying values until
   // we fix bug 1023984.
@@ -347,20 +343,16 @@ function enumStorageEntries(objectActor)
           },
         },
       };
     },
   };
 }
 
 function getWeakMapEntries(obj, forPreview) {
-  if (isReplaying) {
-    return obj.containerContents(forPreview);
-  }
-
   // We currently lack XrayWrappers for WeakMap, so when we iterate over
   // the values, the temporary iterator objects get created in the target
   // compartment. However, we _do_ have Xrays to Object now, so we end up
   // Xraying those temporary objects, and filtering access to |it.value|
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
@@ -397,20 +389,16 @@ function enumWeakMapEntries(objectActor,
           },
         },
       };
     },
   };
 }
 
 function getSetValues(obj, forPreview) {
-  if (isReplaying) {
-    return obj.containerContents(forPreview);
-  }
-
   // We currently lack XrayWrappers for Set, so when we iterate over
   // the values, the temporary iterator objects get created in the target
   // compartment. However, we _do_ have Xrays to Object now, so we end up
   // Xraying those temporary objects, and filtering access to |it.value|
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
@@ -444,20 +432,16 @@ function enumSetEntries(objectActor, for
         enumerable: true,
         value: gripFromEntry(objectActor, val),
       };
     },
   };
 }
 
 function getWeakSetEntries(obj, forPreview) {
-  if (isReplaying) {
-    return obj.containerContents(forPreview);
-  }
-
   // We currently lack XrayWrappers for WeakSet, so when we iterate over
   // the values, the temporary iterator objects get created in the target
   // compartment. However, we _do_ have Xrays to Object now, so we end up
   // Xraying those temporary objects, and filtering access to |it.value|
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
--- a/devtools/server/actors/object/utils.js
+++ b/devtools/server/actors/object/utils.js
@@ -215,20 +215,18 @@ function isArray(object) {
  * @return Number
  * @throws if the object is not an array.
  */
 function getArrayLength(object) {
   if (!isArray(object)) {
     throw new Error("Expected an array, got a " + object.class);
   }
 
-  // Real arrays have a reliable `length` own property. When replaying, always
-  // get the length property, as we can't invoke getters on the proxy returned
-  // by unsafeDereference().
-  if (object.class === "Array" || isReplaying) {
+  // Real arrays have a reliable `length` own property.
+  if (object.class === "Array") {
     return DevToolsUtils.getProperty(object, "length");
   }
 
   // For typed arrays, `DevToolsUtils.getProperty` is not reliable because the `length`
   // getter could be shadowed by an own property, and `getOwnPropertyNames` is
   // unnecessarily slow. Obtain the `length` getter safely and call it manually.
   const typedProto = Object.getPrototypeOf(Uint8Array.prototype);
   const getter = Object.getOwnPropertyDescriptor(typedProto, "length").get;
@@ -319,20 +317,16 @@ function getPropNamesFromObject(obj, raw
     if (isStorage(obj)) {
       // local and session storage cannot be iterated over using
       // Object.getOwnPropertyNames() because it skips keys that are duplicated
       // on the prototype e.g. "key", "getKeys" so we need to gather the real
       // keys using the storage.key() function.
       for (let j = 0; j < rawObj.length; j++) {
         names.push(rawObj.key(j));
       }
-    } else if (isReplaying) {
-      // When replaying we can access a batch of properties for use in generating
-      // the preview. This avoids needing to enumerate all properties.
-      names = obj.getEnumerableOwnPropertyNamesForPreview();
     } else {
       names = obj.getOwnPropertyNames();
     }
   } catch (ex) {
     // Calling getOwnPropertyNames() on some wrapped native prototypes is not
     // allowed: "cannot modify properties of a WrappedNative". See bug 952093.
   }
 
deleted file mode 100644
--- a/devtools/server/actors/replay/connection-worker.js
+++ /dev/null
@@ -1,129 +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/. */
-/* eslint-disable spaced-comment, brace-style, indent-legacy, no-shadow */
-
-"use strict";
-
-// This worker is spawned in the parent process the first time a middleman
-// connects to a cloud based replaying process, and manages the web sockets
-// which are used to connect to those remote processes. This could be done on
-// the main thread, but is handled here because main thread web sockets must
-// be associated with particular windows, which is not the case for the
-// scope which connection.js is created in.
-
-self.addEventListener("message", function({ data }) {
-  const { type, id } = data;
-  switch (type) {
-    case "connect":
-      try {
-        doConnect(id, data.channelId, data.address);
-      } catch (e) {
-        dump(`doConnect error: ${e}\n`);
-      }
-      break;
-    case "send":
-      try {
-        doSend(id, data.buf);
-      } catch (e) {
-        dump(`doSend error: ${e}\n`);
-      }
-      break;
-    default:
-      ThrowError(`Unknown event type ${type}`);
-  }
-});
-
-const gConnections = [];
-
-async function doConnect(id, channelId, address) {
-  if (gConnections[id]) {
-    ThrowError(`Duplicate connection ID ${id}`);
-  }
-  const connection = { outgoing: [] };
-  gConnections[id] = connection;
-
-  // The cloud server address we are given provides us with the address of a
-  // websocket server we should connect to.
-  const response = await fetch(address);
-  const text = await response.text();
-
-  if (!/^wss?:\/\//.test(text)) {
-    ThrowError(`Invalid websocket address ${text}`);
-  }
-
-  const socket = new WebSocket(text);
-  socket.onopen = evt => onOpen(id, evt);
-  socket.onclose = evt => onClose(id, evt);
-  socket.onmessage = evt => onMessage(id, evt);
-  socket.onerror = evt => onError(id, evt);
-
-  await new Promise(resolve => (connection.openWaiter = resolve));
-
-  while (gConnections[id]) {
-    if (connection.outgoing.length) {
-      const buf = connection.outgoing.shift();
-      try {
-        socket.send(buf);
-      } catch (e) {
-        ThrowError(`Send error ${e}`);
-      }
-    } else {
-      await new Promise(resolve => (connection.sendWaiter = resolve));
-    }
-  }
-}
-
-function doSend(id, buf) {
-  const connection = gConnections[id];
-  connection.outgoing.push(buf);
-  if (connection.sendWaiter) {
-    connection.sendWaiter();
-    connection.sendWaiter = null;
-  }
-}
-
-function onOpen(id) {
-  // Messages can now be sent to the socket.
-  gConnections[id].openWaiter();
-}
-
-function onClose(id, evt) {
-  gConnections[id] = null;
-}
-
-// Message data must be sent to the main thread in the order it was received.
-// This is a bit tricky with web socket messages as they return data blobs,
-// and blob.arrayBuffer() returns a promise such that multiple promises could
-// be resolved out of order. Make sure this doesn't happen by using a single
-// async frame to resolve the promises and forward them in order.
-const gMessages = [];
-let gMessageWaiter = null;
-(async function processMessages() {
-  while (true) {
-    if (gMessages.length) {
-      const { id, promise } = gMessages.shift();
-      const buf = await promise;
-      postMessage({ id, buf });
-    } else {
-      await new Promise(resolve => (gMessageWaiter = resolve));
-    }
-  }
-})();
-
-function onMessage(id, evt) {
-  gMessages.push({ id, promise: evt.data.arrayBuffer() });
-  if (gMessageWaiter) {
-    gMessageWaiter();
-    gMessageWaiter = null;
-  }
-}
-
-function onError(id, evt) {
-  ThrowError(`Socket error ${evt}`);
-}
-
-function ThrowError(msg) {
-  dump(`Connection Worker Error: ${msg}\n`);
-  throw new Error(msg);
-}
deleted file mode 100644
--- a/devtools/server/actors/replay/connection.js
+++ /dev/null
@@ -1,39 +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/. */
-/* eslint-disable spaced-comment, brace-style, indent-legacy, no-shadow */
-
-"use strict";
-
-// This file provides an interface for connecting middleman processes with
-// replaying processes living remotely in the cloud.
-
-let gWorker;
-let gCallback;
-let gNextConnectionId = 1;
-
-// eslint-disable-next-line no-unused-vars
-function Initialize(callback) {
-  gWorker = new Worker("connection-worker.js");
-  gWorker.addEventListener("message", onMessage);
-  gCallback = callback;
-}
-
-function onMessage(evt) {
-  gCallback(evt.data.id, evt.data.buf);
-}
-
-// eslint-disable-next-line no-unused-vars
-function Connect(channelId, address, callback) {
-  const id = gNextConnectionId++;
-  gWorker.postMessage({ type: "connect", id, channelId, address });
-  return id;
-}
-
-// eslint-disable-next-line no-unused-vars
-function SendMessage(id, buf) {
-  gWorker.postMessage({ type: "send", id, buf });
-}
-
-// eslint-disable-next-line no-unused-vars
-var EXPORTED_SYMBOLS = ["Initialize", "Connect", "SendMessage"];
deleted file mode 100644
--- a/devtools/server/actors/replay/control.js
+++ /dev/null
@@ -1,2135 +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/. */
-/* eslint-disable spaced-comment, brace-style, indent-legacy, no-shadow */
-
-"use strict";
-
-// This file provides an interface which the ReplayDebugger uses to interact
-// with a middleman's child recording and replaying processes. There can be
-// several child processes in existence at once; this is largely hidden from the
-// ReplayDebugger, and the position of each child is managed to provide a fast
-// and stable experience when rewinding or running forward.
-
-const CC = Components.Constructor;
-
-// Create a sandbox with the resources we need. require() doesn't work here.
-const sandbox = Cu.Sandbox(
-  CC("@mozilla.org/systemprincipal;1", "nsIPrincipal")()
-);
-Cu.evalInSandbox(
-  "Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
-    "Components.utils.import('resource://gre/modules/Services.jsm');" +
-    "Components.utils.import('resource://devtools/shared/execution-point-utils.js');" +
-    "Components.utils.import('resource://gre/modules/Timer.jsm');" +
-    "addDebuggerToGlobal(this);",
-  sandbox
-);
-const {
-  RecordReplayControl,
-  Services,
-  pointEquals,
-  pointToString,
-  positionToString,
-  findClosestPoint,
-  pointArrayIncludes,
-  pointPrecedes,
-  positionSubsumes,
-  setInterval,
-} = sandbox;
-
-const InvalidCheckpointId = 0;
-const FirstCheckpointId = 1;
-
-// Application State Control
-//
-// This section describes the strategy used for managing child processes so that
-// we can be responsive to user interactions. There is at most one recording
-// child process, and any number of replaying child processes.
-//
-// When the user is not viewing old parts of the recording, they will interact
-// with the recording process. The recording process only runs forward and adds
-// new data to the recording which the replaying processes can consume.
-//
-// Replaying processes can be created in two ways: we can spawn a replaying
-// process at the beginning of the recording, or we can ask an existing
-// replaying process to fork, which creates a new replaying process at the same
-// point in the recording.
-//
-// Replaying processes fit into one of the following categories:
-//
-// * Root children are spawned at the beginning of the recording. They stop at
-//   the first checkpoint and coordinate communication with other processes that
-//   have been forked from them.
-//
-// * Trunk children are forked from the root child. They run through the
-//   recording and periodically fork off new branches. There is only one trunk
-//   for each root child.
-//
-// * Branch children are forked from trunk or other branch children. They run
-//   forward to a specific point in the recording, and then sit idle while they
-//   periodically fork off new leaves.
-//
-// * Leaf children are forked from branches. They run through a part of the
-//   recording and can perform actions such as scanning the recording or
-//   performing operations at different points in the recording.
-//
-// During startup, we will spawn a single trunk child which runs through the
-// recording and forks a branch child at certain saved checkpoints. The saved
-// checkpoints are spaced so that they aren't far apart. Each branch child forks
-// a leaf child which scans the recording from that branch's saved checkpoint up
-// to the next saved checkpoint, and then sits idle and responds to queries
-// about the scanned region. Other leaf children can be forked from the
-// branches, allowing us to quickly perform tasks anywhere in the recording once
-// all the branches are in place.
-//
-// The number of leaves which can simultaneously be operating has a small
-// limit to avoid overloading the system. Tasks requiring new children are
-// placed in a queue and resolved in priority order.
-
-////////////////////////////////////////////////////////////////////////////////
-// Child Processes
-////////////////////////////////////////////////////////////////////////////////
-
-// Get a unique ID for a child process.
-function processId(rootId, forkId) {
-  return forkId ? `#${rootId}:${forkId}` : `#${rootId}`;
-}
-
-// Child which is always at the end of the recording. When there is a recording
-// child this is it, and when we are replaying an old execution this is a
-// replaying child that doesn't fork and is used like a recording child.
-let gMainChild;
-
-// All child processes, indexed by their ID.
-const gChildren = new Map();
-
-// All child processes that are currently unpaused.
-const gUnpausedChildren = new Set();
-
-// Information about a child recording or replaying process.
-function ChildProcess(rootId, forkId, recording, recordingLength, startPoint) {
-  // ID for the root recording/replaying process this is associated with.
-  this.rootId = rootId;
-
-  // ID for the particular fork of the root this process represents, or zero if
-  // not forked.
-  this.forkId = forkId;
-
-  // Unique ID for this process.
-  this.id = processId(rootId, forkId);
-  gChildren.set(this.id, this);
-
-  // Whether this process is recording.
-  this.recording = recording;
-
-  // How much of the recording is available to this process.
-  this.recordingLength = recordingLength;
-
-  // The execution point at which this process was created, or null.
-  this.startPoint = startPoint;
-
-  // Whether this process is paused.
-  this.paused = false;
-  gUnpausedChildren.add(this);
-
-  // The last point we paused at.
-  this.lastPausePoint = null;
-
-  // Whether this child has terminated and is unusable.
-  this.terminated = false;
-
-  // The last time a ping message was sent to the process.
-  this.lastPingTime = Date.now();
-
-  // All pings we have sent since they were reset for this process.
-  this.pings = [];
-
-  // Manifests queued up for this child. If the child is unpaused, the first one
-  // is currently being processed. Child processes initially have a manifest to
-  // run forward to the first checkpoint.
-  this.manifests = [
-    {
-      manifest: { kind: "primordial" },
-      onFinished: ({ point, maxRunningProcesses }) => {
-        if (this == gMainChild) {
-          getCheckpointInfo(FirstCheckpointId).point = point;
-          Services.tm.dispatchToMainThread(
-            recording ? maybeResumeRecording : setMainChild
-          );
-        }
-        if (maxRunningProcesses) {
-          gMaxRunningLeafChildren = maxRunningProcesses;
-        }
-      },
-    },
-  ];
-
-  // All manifests that have been sent to this child, used in crash recovery.
-  this.processedManifests = [];
-}
-
-const PingIntervalSeconds = 2;
-const MaxStalledPings = 10;
-let gNextPingId = 1;
-
-ChildProcess.prototype = {
-  // Stop this child.
-  terminate() {
-    gChildren.delete(this.id);
-    gUnpausedChildren.delete(this);
-    RecordReplayControl.terminate(this.rootId, this.forkId);
-    this.terminated = true;
-  },
-
-  // Take over the process used by another child that was just spawned.
-  transplantFrom(newChild) {
-    assert(this.terminated && !this.paused);
-    assert(!newChild.terminated && !newChild.paused);
-    this.terminated = false;
-    newChild.terminated = true;
-    gUnpausedChildren.delete(newChild);
-    gUnpausedChildren.add(this);
-
-    this.rootId = newChild.rootId;
-    this.forkId = newChild.forkId;
-    this.id = newChild.id;
-    gChildren.set(this.id, this);
-
-    this.recordingLength = newChild.recordingLength;
-    this.startPoint = newChild.startPoint;
-    this.lastPausePoint = newChild.lastPausePoint;
-    this.lastPingTime = Date.now();
-    this.pings = [];
-    this.manifests = newChild.manifests;
-    this.processedManifests = newChild.processedManifests;
-  },
-
-  // Get the execution point where this child is currently paused.
-  pausePoint() {
-    assert(this.paused);
-    return this.lastPausePoint;
-  },
-
-  // Get the checkpoint where this child is currently paused.
-  pauseCheckpoint() {
-    const point = this.pausePoint();
-    assert(!point.position);
-    return point.checkpoint;
-  },
-
-  // Send a manifest to paused child to execute. The child unpauses while
-  // executing the manifest, and pauses again when it finishes. This returns a
-  // promise that resolves with the manifest's result when it finishes.
-  // If specified, onFinishedCallback will be invoked with the manifest
-  // result as well.
-  sendManifest(manifest, onFinishedCallback) {
-    assert(!this.terminated);
-    return new Promise(resolve => {
-      this.manifests.push({
-        manifest,
-        onFinished(response) {
-          if (onFinishedCallback) {
-            onFinishedCallback(response);
-          }
-          resolve(response);
-        },
-      });
-
-      if (this.paused) {
-        this._startNextManifest();
-      }
-    });
-  },
-
-  // Called when the child's current manifest finishes.
-  manifestFinished(response) {
-    dumpv(`ManifestFinished ${this.id} ${stringify(response)}`);
-
-    assert(!this.paused);
-    this.paused = true;
-    gUnpausedChildren.delete(this);
-    const { manifest, onFinished } = this.manifests.shift();
-
-    if (response) {
-      if (response.point) {
-        this.lastPausePoint = response.point;
-      }
-      if (response.exception) {
-        ThrowError(response.exception);
-      }
-    }
-    onFinished(response);
-
-    this.processedManifests.push(manifest);
-    this._startNextManifest();
-  },
-
-  // Send this child the next manifest in its queue, if there is one.
-  _startNextManifest() {
-    assert(this.paused);
-
-    if (this.manifests.length) {
-      const { manifest } = this.manifests[0];
-      dumpv(`SendManifest ${this.id} ${stringify(manifest)}`);
-      RecordReplayControl.sendManifest(this.rootId, this.forkId, manifest);
-      this.paused = false;
-      gUnpausedChildren.add(this);
-
-      // Reset pings when sending a new manifest.
-      this.pings.length = 0;
-      this.lastPingTime = Date.now();
-    }
-  },
-
-  // Block until this child is paused. If maybeCreateCheckpoint is specified
-  // then a checkpoint is created if this child is recording, so that it pauses
-  // quickly (otherwise it could sit indefinitely if there is no page activity).
-  waitUntilPaused(maybeCreateCheckpoint) {
-    if (this.paused) {
-      return;
-    }
-    dumpv(`WaitUntilPaused ${this.id}`);
-    if (maybeCreateCheckpoint) {
-      assert(this.recording && !this.forkId);
-      RecordReplayControl.createCheckpointInRecording(this.rootId);
-    }
-    while (!this.paused) {
-      this.maybePing();
-      RecordReplayControl.maybeProcessNextMessage();
-    }
-    dumpv(`WaitUntilPaused Done`);
-    assert(this.paused || this.terminated);
-  },
-
-  // Hang Detection
-  //
-  // Replaying processes will be terminated if no execution progress has been
-  // made for some number of seconds. This generates a crash report for
-  // diagnosis and allows another replaying process to be spawned in its place.
-  // We detect that no progress is being made by periodically sending ping
-  // messages to the replaying process, and comparing the progress values
-  // returned by them. The ping messages are sent at least PingIntervalSeconds
-  // apart, and the process is considered hanged if at any point the last
-  // MaxStalledPings ping messages either did not respond or responded with the
-  // same progress value.
-  //
-  // Dividing our accounting between different ping messages avoids treating
-  // processes as hanged when the computer wakes up after sleeping: no pings
-  // will be sent while the computer is sleeping and processes are suspended,
-  // so the computer will need to be awake for some time before any processes
-  // are marked as hanged (before which they will hopefully be able to make
-  // progress).
-  isHanged() {
-    if (this.pings.length < MaxStalledPings) {
-      return false;
-    }
-
-    const firstIndex = this.pings.length - MaxStalledPings;
-    const firstValue = this.pings[firstIndex].progress;
-    if (!firstValue) {
-      // The child hasn't responded to any of the pings.
-      return true;
-    }
-
-    for (let i = firstIndex; i < this.pings.length; i++) {
-      if (this.pings[i].progress && this.pings[i].progress != firstValue) {
-        return false;
-      }
-    }
-
-    return true;
-  },
-
-  maybePing() {
-    assert(!this.paused);
-    if (this.recording) {
-      return;
-    }
-
-    const now = Date.now();
-    if (now < this.lastPingTime + PingIntervalSeconds * 1000) {
-      return;
-    }
-
-    if (this.isHanged()) {
-      // Try to get the child to crash, so that we can get a minidump.
-      RecordReplayControl.crashHangedChild(this.rootId, this.forkId);
-
-      // Treat the child as crashed even if we aren't able to get it to crash.
-      ChildCrashed(this.rootId, this.forkId);
-    } else {
-      const id = gNextPingId++;
-      RecordReplayControl.ping(this.rootId, this.forkId, id);
-      this.pings.push({ id });
-      this.lastPingTime = now;
-    }
-  },
-
-  pingResponse(id, progress) {
-    for (const entry of this.pings) {
-      if (entry.id == id) {
-        entry.progress = progress;
-        break;
-      }
-    }
-  },
-
-  updateRecording(length) {
-    RecordReplayControl.updateRecording(
-      this.rootId,
-      this.forkId,
-      this.recordingLength,
-      length
-    );
-    this.recordingLength = length;
-  },
-};
-
-// eslint-disable-next-line no-unused-vars
-function ManifestFinished(rootId, forkId, response) {
-  try {
-    const child = gChildren.get(processId(rootId, forkId));
-    if (child) {
-      child.manifestFinished(response);
-    }
-  } catch (e) {
-    dump(`ERROR: ManifestFinished threw exception: ${e} ${e.stack}\n`);
-  }
-}
-
-// eslint-disable-next-line no-unused-vars
-function PingResponse(rootId, forkId, pingId, progress) {
-  try {
-    const child = gChildren.get(processId(rootId, forkId));
-    if (child) {
-      child.pingResponse(pingId, progress);
-    }
-  } catch (e) {
-    dump(`ERROR: PingResponse threw exception: ${e} ${e.stack}\n`);
-  }
-}
-
-// The singleton ReplayDebugger, or undefined if it doesn't exist.
-let gDebugger;
-
-////////////////////////////////////////////////////////////////////////////////
-// Child Management
-////////////////////////////////////////////////////////////////////////////////
-
-// Priority levels for asynchronous manifests.
-const Priority = {
-  HIGH: 0,
-  MEDIUM: 1,
-  LOW: 2,
-};
-
-// The singleton root child, if any.
-let gRootChild;
-
-// The singleton trunk child, if any.
-let gTrunkChild;
-
-// All branch children available for creating new leaves, in order.
-const gBranchChildren = [];
-
-// ID for the next fork we create.
-let gNextForkId = 1;
-
-// Fork a replaying child, returning the new one.
-function forkChild(child, point) {
-  const forkId = gNextForkId++;
-  const newChild = new ChildProcess(
-    child.rootId,
-    forkId,
-    false,
-    child.recordingLength,
-    point
-  );
-
-  dumpv(`Forking ${child.id} -> ${newChild.id}`);
-
-  child.sendManifest({ kind: "fork", id: forkId });
-  return newChild;
-}
-
-// Immediately create a new leaf child and take it to endpoint, by forking the
-// closest branch child to endpoint. onFinished will be called when the child
-// reaches the endpoint.
-function newLeafChild(endpoint, onFinished = () => {}) {
-  assert(
-    !pointPrecedes(checkpointExecutionPoint(gLastSavedCheckpoint), endpoint)
-  );
-
-  let entry;
-  for (let i = gBranchChildren.length - 1; i >= 0; i--) {
-    entry = gBranchChildren[i];
-    if (!pointPrecedes(endpoint, entry.point)) {
-      break;
-    }
-  }
-
-  const child = forkChild(entry.child, entry.point);
-  if (pointEquals(endpoint, entry.point)) {
-    onFinished(child);
-  } else {
-    child.sendManifest({ kind: "runToPoint", endpoint }, () =>
-      onFinished(child)
-    );
-  }
-  return child;
-}
-
-// How many leaf children we can have running simultaneously.
-let gMaxRunningLeafChildren = 4;
-
-// How many leaf children are currently running.
-let gNumRunningLeafChildren = 0;
-
-// Resolve hooks for tasks waiting on a new leaf child, organized by priority.
-const gChildWaiters = [[], [], []];
-
-// Asynchronously create a new leaf child and take it to endpoint, respecting
-// the limits on the maximum number of running leaves and returning the new
-// child when it reaches the endpoint.
-async function ensureLeafChild(endpoint, priority = Priority.HIGH) {
-  if (gNumRunningLeafChildren < gMaxRunningLeafChildren) {
-    gNumRunningLeafChildren++;
-  } else {
-    await new Promise(resolve => gChildWaiters[priority].push(resolve));
-  }
-
-  return new Promise(resolve => {
-    newLeafChild(endpoint, child => resolve(child));
-  });
-}
-
-// After a leaf child becomes idle and/or is about to be terminated, mark it ass
-// no longer running and continue any task waiting on a new leaf.
-function stopRunningLeafChild() {
-  gNumRunningLeafChildren--;
-
-  if (gNumRunningLeafChildren < gMaxRunningLeafChildren) {
-    for (const waiters of gChildWaiters) {
-      if (waiters.length) {
-        const resolve = waiters.shift();
-        gNumRunningLeafChildren++;
-        resolve();
-      }
-    }
-  }
-}
-
-// Terminate a running leaf child that is no longer needed.
-function terminateRunningLeafChild(child) {
-  stopRunningLeafChild();
-
-  dumpv(`Terminate ${child.id}`);
-  child.terminate();
-}
-
-// The next ID to use for a root replaying process.
-let gNextRootId = 1;
-
-// Spawn the single trunk child.
-function spawnTrunkChild() {
-  if (!RecordReplayControl.canRewind()) {
-    return;
-  }
-
-  const id = gNextRootId++;
-  RecordReplayControl.spawnReplayingChild(id);
-  gRootChild = new ChildProcess(
-    id,
-    0,
-    false,
-    RecordReplayControl.recordingLength()
-  );
-
-  gTrunkChild = forkChild(
-    gRootChild,
-    checkpointExecutionPoint(FirstCheckpointId)
-  );
-
-  // We should be at the beginning of the recording, and don't have enough space
-  // to create any branches yet.
-  assert(gLastSavedCheckpoint == InvalidCheckpointId);
-}
-
-async function forkBranchChild(lastSavedCheckpoint, nextSavedCheckpoint) {
-  if (!RecordReplayControl.canRewind()) {
-    return;
-  }
-
-  // The recording is flushed and the trunk child updated every time we add
-  // a saved checkpoint, so the child we fork here will have the recording
-  // contents up to the next saved checkpoint. Branch children are only able to
-  // run up to the next saved checkpoint.
-  gTrunkChild.updateRecording(RecordReplayControl.recordingLength());
-
-  const point = checkpointExecutionPoint(lastSavedCheckpoint);
-  const child = forkChild(gTrunkChild, point);
-
-  dumpv(`AddBranchChild Checkpoint ${lastSavedCheckpoint} Child ${child.id}`);
-  gBranchChildren.push({ child, point });
-
-  const endpoint = checkpointExecutionPoint(nextSavedCheckpoint);
-  await gTrunkChild.sendManifest({
-    kind: "runToPoint",
-    endpoint,
-
-    // External calls are flushed in the trunk child so that external calls
-    // from throughout the recording will be cached in the root child.
-    flushExternalCalls: true,
-  });
-
-  updateStatus();
-}
-
-function respawnCrashedChild(child) {
-  const { startPoint, recordingLength, manifests, processedManifests } = child;
-
-  // Find another child to fork that didn't just crash.
-  let entry;
-  for (let i = gBranchChildren.length - 1; i >= 0; i--) {
-    const branch = gBranchChildren[i];
-    if (
-      !pointPrecedes(child.startPoint, branch.point) &&
-      !branch.child.terminated
-    ) {
-      entry = branch;
-      break;
-    }
-  }
-  if (!entry) {
-    entry = {
-      child: gRootChild,
-      point: checkpointExecutionPoint(FirstCheckpointId),
-    };
-  }
-
-  const newChild = forkChild(entry.child, entry.point);
-
-  dumpv(`Transplanting Child: ${child.id} from ${newChild.id}`);
-  child.transplantFrom(newChild);
-
-  if (recordingLength > child.recordingLength) {
-    child.updateRecording(recordingLength);
-  }
-
-  if (!pointEquals(startPoint, child.startPoint)) {
-    assert(pointPrecedes(child.startPoint, startPoint));
-    child.sendManifest({ kind: "runToPoint", endpoint: startPoint });
-  }
-
-  for (const manifest of processedManifests) {
-    if (manifest.kind != "primordial" && manifest.kind != "fork") {
-      child.sendManifest(manifest);
-    }
-  }
-
-  for (const { manifest, onFinished } of manifests) {
-    if (
-      manifest.kind != "primordial" &&
-      (manifest.kind != "fork" || manifest != manifests[0].manifest)
-    ) {
-      child.sendManifest(manifest, onFinished);
-    }
-  }
-}
-
-// eslint-disable-next-line no-unused-vars
-function Initialize(recordingChildId) {
-  try {
-    if (recordingChildId != undefined) {
-      assert(recordingChildId == 0);
-      gMainChild = new ChildProcess(recordingChildId, 0, true);
-    } else {
-      // If there is no recording child, spawn a replaying child in its place.
-      const id = gNextRootId++;
-      RecordReplayControl.spawnReplayingChild(id);
-      gMainChild = new ChildProcess(
-        id,
-        0,
-        false,
-        RecordReplayControl.recordingLength
-      );
-    }
-    gActiveChild = gMainChild;
-    return gControl;
-  } catch (e) {
-    dump(`ERROR: Initialize threw exception: ${e}\n`);
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// PromiseMap
-////////////////////////////////////////////////////////////////////////////////
-
-// Map from arbitrary keys to promises that resolve when any associated work is
-// complete. This is used to avoid doing redundant work in different async
-// tasks.
-function PromiseMap() {
-  this.map = new Map();
-}
-
-PromiseMap.prototype = {
-  // Returns either { promise } or { promise, resolve }. If resolve is included
-  // then the caller is responsible for invoking it later.
-  get(key) {
-    let promise = this.map.get(key);
-    if (promise) {
-      return { promise };
-    }
-
-    let resolve;
-    promise = new Promise(r => {
-      resolve = r;
-    });
-    this.map.set(key, promise);
-    return { promise, resolve };
-  },
-
-  set(key, value) {
-    this.map.set(key, value);
-  },
-};
-
-////////////////////////////////////////////////////////////////////////////////
-// Application State
-////////////////////////////////////////////////////////////////////////////////
-
-// Any child the user is interacting with, which may be paused or not.
-let gActiveChild = null;
-
-// Information about each checkpoint, indexed by the checkpoint's id.
-const gCheckpoints = [null];
-
-function CheckpointInfo() {
-  // The time taken to run from this checkpoint to the next one, excluding idle
-  // time.
-  this.duration = 0;
-
-  // Execution point at the checkpoint.
-  this.point = null;
-
-  // Whether the checkpoint is saved.
-  this.saved = false;
-
-  // If the checkpoint is saved, any debugger statement hits in its region.
-  this.debuggerStatements = [];
-
-  // If the checkpoint is saved, any events in its region.
-  this.events = [];
-}
-
-function getCheckpointInfo(id) {
-  while (id >= gCheckpoints.length) {
-    gCheckpoints.push(new CheckpointInfo());
-  }
-  return gCheckpoints[id];
-}
-
-// How much execution time elapses between two checkpoints.
-function checkpointRangeDuration(start, end) {
-  let time = 0;
-  for (let i = start; i < end; i++) {
-    time += gCheckpoints[i].duration;
-  }
-  return time;
-}
-
-// How much execution time has elapsed since a checkpoint.
-function timeSinceCheckpoint(id) {
-  return checkpointRangeDuration(id, gCheckpoints.length);
-}
-
-// How often we want to flush the recording.
-const FlushMs = 0.5 * 1000;
-
-// The most recent saved checkpoint. The recording is flushed at every saved
-// checkpoint.
-let gLastSavedCheckpoint = InvalidCheckpointId;
-const gSavedCheckpointWaiters = [];
-
-function addSavedCheckpoint(checkpoint) {
-  assert(checkpoint >= gLastSavedCheckpoint);
-  if (checkpoint == gLastSavedCheckpoint) {
-    return;
-  }
-
-  if (gLastSavedCheckpoint != InvalidCheckpointId) {
-    forkBranchChild(gLastSavedCheckpoint, checkpoint);
-  }
-
-  getCheckpointInfo(checkpoint).saved = true;
-  gLastSavedCheckpoint = checkpoint;
-
-  gSavedCheckpointWaiters.forEach(resolve => resolve());
-  gSavedCheckpointWaiters.length = 0;
-}
-
-function waitForSavedCheckpoint() {
-  return new Promise(resolve => gSavedCheckpointWaiters.push(resolve));
-}
-
-function addCheckpoint(checkpoint, duration) {
-  assert(!getCheckpointInfo(checkpoint).duration);
-  getCheckpointInfo(checkpoint).duration = duration;
-}
-
-function nextSavedCheckpoint(checkpoint) {
-  assert(checkpoint == InvalidCheckpointId || gCheckpoints[checkpoint].saved);
-  while (++checkpoint < gCheckpoints.length) {
-    if (gCheckpoints[checkpoint].saved) {
-      return checkpoint;
-    }
-  }
-  return undefined;
-}
-
-function forSavedCheckpointsInRange(start, end, callback) {
-  if (start == FirstCheckpointId && !gCheckpoints[start].saved) {
-    return;
-  }
-  assert(gCheckpoints[start].saved);
-  for (
-    let checkpoint = start;
-    checkpoint < end;
-    checkpoint = nextSavedCheckpoint(checkpoint)
-  ) {
-    callback(checkpoint);
-  }
-}
-
-function forAllSavedCheckpoints(callback) {
-  forSavedCheckpointsInRange(FirstCheckpointId, gLastSavedCheckpoint, callback);
-}
-
-function getSavedCheckpoint(checkpoint) {
-  while (!gCheckpoints[checkpoint].saved) {
-    checkpoint--;
-  }
-  return checkpoint;
-}
-
-// Get the execution point to use for a checkpoint.
-function checkpointExecutionPoint(checkpoint) {
-  return gCheckpoints[checkpoint].point;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Search State
-////////////////////////////////////////////////////////////////////////////////
-
-// All currently installed breakpoints.
-const gBreakpoints = [];
-
-// Recording Scanning
-//
-// Scanning a section of the recording between two neighboring saved checkpoints
-// allows the execution points for each script breakpoint position to be queried
-// by sending a manifest to the child which performed the scan. Scanning is done
-// using leaf children. When the child has finished scanning, it is marked as no
-// longer running and remains idle remains idle while it responds to queries on
-// the scanned data.
-
-// Map from saved checkpoints to the leaf child responsible for scanning that saved
-// checkpoint's region.
-const gSavedCheckpointChildren = new PromiseMap();
-
-// Set of saved checkpoints which have finished scanning.
-const gScannedSavedCheckpoints = new Set();
-
-// Ensure the region for a saved checkpoint has been scanned by some child, and
-// return that child.
-async function scanRecording(checkpoint) {
-  assert(gCheckpoints[checkpoint].saved);
-  if (checkpoint == gLastSavedCheckpoint) {
-    await waitForSavedCheckpoint();
-  }
-
-  const { promise, resolve } = gSavedCheckpointChildren.get(checkpoint);
-  if (!resolve) {
-    return promise;
-  }
-
-  const endpoint = checkpointExecutionPoint(nextSavedCheckpoint(checkpoint));
-  const child = await ensureLeafChild(checkpointExecutionPoint(checkpoint));
-  await child.sendManifest({ kind: "scanRecording", endpoint });
-
-  stopRunningLeafChild();
-
-  gScannedSavedCheckpoints.add(checkpoint);
-
-  // Update the unscanned regions in the UI.
-  updateStatus();
-
-  resolve(child);
-  return child;
-}
-
-function unscannedRegions() {
-  const result = [];
-
-  const traversedCheckpoint =
-    gTrunkChild && gTrunkChild.lastPausePoint
-      ? gTrunkChild.lastPausePoint.checkpoint
-      : FirstCheckpointId;
-
-  function addRegion(startCheckpoint, endCheckpoint) {
-    const start = checkpointExecutionPoint(startCheckpoint).progress;
-    const end = checkpointExecutionPoint(endCheckpoint).progress;
-    const traversed = endCheckpoint <= traversedCheckpoint;
-
-    if (
-      result.length &&
-      result[result.length - 1].end == start &&
-      result[result.length - 1].traversed == traversed
-    ) {
-      result[result.length - 1].end = end;
-    } else {
-      result.push({ start, end, traversed });
-    }
-  }
-
-  forAllSavedCheckpoints(checkpoint => {
-    if (!gScannedSavedCheckpoints.has(checkpoint)) {
-      addRegion(checkpoint, nextSavedCheckpoint(checkpoint));
-    }
-  });
-
-  const lastFlush = gLastSavedCheckpoint || FirstCheckpointId;
-  if (lastFlush != gRecordingEndpoint) {
-    addRegion(lastFlush, gMainChild.lastPausePoint.checkpoint);
-  }
-
-  return result;
-}
-
-// Map from saved checkpoints and positions to the breakpoint hits for that
-// position within the range of the checkpoint.
-const gHitSearches = new PromiseMap();
-
-// Only hits on script locations (Break and OnStep positions) can be found by
-// scanning the recording.
-function canFindHits(position) {
-  return position.kind == "Break" || position.kind == "OnStep";
-}
-
-// Find all hits on the specified position between a saved checkpoint and the
-// following saved checkpoint, using data from scanning the recording. This
-// returns a promise that resolves with the resulting hits.
-async function findHits(checkpoint, position) {
-  assert(canFindHits(position));
-  assert(gCheckpoints[checkpoint].saved);
-
-  const key = `${checkpoint}:${positionToString(position)}`;
-  const { promise, resolve } = gHitSearches.get(key);
-  if (!resolve) {
-    return promise;
-  }
-
-  const endpoint = nextSavedCheckpoint(checkpoint);
-  const child = await scanRecording(checkpoint);
-  const hits = await child.sendManifest({
-    kind: "findHits",
-    position,
-    startpoint: checkpoint,
-    endpoint,
-  });
-
-  resolve(hits);
-  return hits;
-}
-
-// Asynchronously find all hits on a breakpoint's position.
-async function findBreakpointHits(checkpoint, position) {
-  if (position.kind == "Break") {
-    findHits(checkpoint, position);
-  }
-}
-
-// Frame Steps
-//
-// When the recording scanning is not sufficient to figure out where to stop
-// when resuming, the steps for the currently paused frame can be fetched. This
-// mainly helps with finding the targets for EnterFrame breakpoints used when
-// stepping in, and will be used in the future to improve stepping performance.
-//
-// The steps for a frame are the list of execution points for breakpoint
-// positions traversed when executing a particular script frame, from the
-// initial EnterFrame to the final OnPop. The steps also include the EnterFrame
-// execution points for any direct callees of the frame.
-
-// Map from point strings to the steps which contain them.
-const gFrameSteps = new PromiseMap();
-
-// Map from frame entry point strings to the parent frame's entry point.
-const gParentFrames = new PromiseMap();
-
-// Find all the steps in the frame which point is part of. This returns a
-// promise that resolves with the steps that were found.
-async function findFrameSteps(point) {
-  if (!point.position) {
-    return null;
-  }
-
-  assert(
-    point.position.kind == "EnterFrame" ||
-      point.position.kind == "OnStep" ||
-      point.position.kind == "OnPop"
-  );
-
-  const { promise, resolve } = gFrameSteps.get(pointToString(point));
-  if (!resolve) {
-    return promise;
-  }
-
-  // Gather information which the child which did the scan can use to figure out
-  // the different frame steps.
-  const info = gControl.sendRequestMainChild({
-    type: "frameStepsInfo",
-    script: point.position.script,
-  });
-
-  const checkpoint = getSavedCheckpoint(point.checkpoint);
-  const child = await scanRecording(checkpoint);
-  const steps = await child.sendManifest({
-    kind: "findFrameSteps",
-    targetPoint: point,
-    ...info,
-  });
-
-  for (const p of steps) {
-    if (p.position.frameIndex == point.position.frameIndex) {
-      gFrameSteps.set(pointToString(p), steps);
-    } else {
-      assert(p.position.kind == "EnterFrame");
-      gParentFrames.set(pointToString(p), steps[0]);
-    }
-  }
-
-  resolve(steps);
-  return steps;
-}
-
-async function findParentFrameEntryPoint(point) {
-  assert(point.position.kind == "EnterFrame");
-  assert(point.position.frameIndex > 0);
-
-  const { promise, resolve } = gParentFrames.get(pointToString(point));
-  if (!resolve) {
-    return promise;
-  }
-
-  const checkpoint = getSavedCheckpoint(point.checkpoint);
-  const child = await scanRecording(checkpoint);
-  const { parentPoint } = await child.sendManifest({
-    kind: "findParentFrameEntryPoint",
-    point,
-  });
-
-  resolve(parentPoint);
-  return parentPoint;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Pause Data
-////////////////////////////////////////////////////////////////////////////////
-
-const gPauseData = new Map();
-const gQueuedPauseData = new Set();
-
-// Cached points indicate messages where we have gathered pause data. These are
-// shown differently in the UI.
-const gCachedPoints = new Map();
-
-async function queuePauseData({ point, trackCached }) {
-  if (gQueuedPauseData.has(pointToString(point))) {
-    return;
-  }
-  gQueuedPauseData.add(pointToString(point));
-
-  await waitForFlushed(point.checkpoint);
-
-  const child = await ensureLeafChild(point, Priority.LOW);
-  const data = await child.sendManifest({ kind: "getPauseData" });
-
-  addPauseData(point, data, trackCached);
-  terminateRunningLeafChild(child);
-}
-
-function addPauseData(point, data, trackCached) {
-  if (data.paintData) {
-    // Atomize paint data strings to ensure that we don't store redundant
-    // strings for execution points with the same paint data.
-    data.paintData = RecordReplayControl.atomize(data.paintData);
-  }
-  gPauseData.set(pointToString(point), data);
-
-  if (trackCached) {
-    gCachedPoints.set(pointToString(point), point);
-    updateStatus();
-  }
-}
-
-function maybeGetPauseData(point) {
-  return gPauseData.get(pointToString(point));
-}
-
-function cachedPoints() {
-  return [...gCachedPoints.values()].map(point => point.progress);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Pause State
-////////////////////////////////////////////////////////////////////////////////
-
-// The pause mode classifies the current state of the debugger.
-const PauseModes = {
-  // The main child is the active child, and is either paused or actively
-  // recording. gPausePoint is the last point the main child reached.
-  RUNNING: "RUNNING",
-
-  // gActiveChild is a replaying child paused or being taken to gPausePoint.
-  PAUSED: "PAUSED",
-
-  // gActiveChild is null, and we are looking for the last breakpoint hit prior
-  // to or following gPausePoint, at which we will pause.
-  RESUMING_BACKWARD: "RESUMING_BACKWARD",
-  RESUMING_FORWARD: "RESUMING_FORWARD",
-};
-
-// Current pause mode.
-let gPauseMode = PauseModes.RUNNING;
-
-// In PAUSED mode, the point we are paused at or sending the active child to.
-let gPausePoint = null;
-
-// In PAUSED mode, any debugger requests that have been sent to the child.
-const gDebuggerRequests = [];
-
-function addDebuggerRequest(request) {
-  gDebuggerRequests.push({
-    request,
-    stack: Error().stack,
-  });
-}
-
-function setPauseState(mode, point, child) {
-  assert(mode);
-  const idString = child ? ` ${child.id}` : "";
-  dumpv(`SetPauseState ${mode} ${JSON.stringify(point)}${idString}`);
-
-  if (gActiveChild && gActiveChild != gMainChild && gActiveChild != child) {
-    terminateRunningLeafChild(gActiveChild);
-  }
-
-  gPauseMode = mode;
-  gPausePoint = point;
-  gActiveChild = child;
-
-  if (mode == PauseModes.PAUSED) {
-    simulateNearbyNavigation();
-  }
-}
-
-// Mark the debugger as paused, and asynchronously send a child to the pause
-// point.
-function setReplayingPauseTarget(point) {
-  assert(!gDebuggerRequests.length);
-
-  const child = newLeafChild(point);
-  setPauseState(PauseModes.PAUSED, point, child);
-  updateStatus();
-
-  gDebugger._onPause();
-  findFrameSteps(point);
-}
-
-// Synchronously bring a new leaf child to the current pause point.
-function bringNewReplayingChildToPausePoint() {
-  const child = newLeafChild(gPausePoint);
-  setPauseState(PauseModes.PAUSED, gPausePoint, child);
-
-  child.sendManifest({
-    kind: "batchDebuggerRequest",
-    requests: gDebuggerRequests.map(r => r.request),
-  });
-}
-
-// Find the point where the debugger should pause when running forward or
-// backward from a point and using a given set of breakpoints. Returns null if
-// there is no point to pause at before hitting the beginning or end of the
-// recording.
-async function resumeTarget(point, forward, breakpoints) {
-  let startCheckpoint = point.checkpoint;
-  if (!forward && !point.position) {
-    startCheckpoint--;
-    if (startCheckpoint == InvalidCheckpointId) {
-      return null;
-    }
-  }
-  startCheckpoint = getSavedCheckpoint(startCheckpoint);
-
-  let checkpoint = startCheckpoint;
-  for (; ; forward ? checkpoint++ : checkpoint--) {
-    if ([InvalidCheckpointId, gLastSavedCheckpoint].includes(checkpoint)) {
-      return null;
-    }
-
-    if (!gCheckpoints[checkpoint].saved) {
-      continue;
-    }
-
-    const hits = [];
-
-    // Find any breakpoint hits in this region of the recording.
-    for (const bp of breakpoints) {
-      if (canFindHits(bp)) {
-        const bphits = await findHits(checkpoint, bp);
-        hits.push(...bphits);
-      }
-    }
-
-    // When there are stepping breakpoints, look for breakpoint hits in the
-    // steps for the current frame.
-    if (
-      checkpoint == startCheckpoint &&
-      gBreakpoints.some(bp => bp.kind == "EnterFrame" || bp.kind == "OnPop")
-    ) {
-      const steps = await findFrameSteps(point);
-      hits.push(
-        ...steps.filter(p => {
-          return breakpoints.some(bp => positionSubsumes(bp, p.position));
-        })
-      );
-    }
-
-    // Always pause at debugger statements, as if they are breakpoint hits.
-    hits.push(...getCheckpointInfo(checkpoint).debuggerStatements);
-
-    const hit = findClosestPoint(
-      hits,
-      gPausePoint,
-      /* before */ !forward,
-      /* inclusive */ false
-    );
-    if (hit) {
-      return hit;
-    }
-  }
-}
-
-async function finishResume() {
-  assert(
-    gPauseMode == PauseModes.RESUMING_FORWARD ||
-      gPauseMode == PauseModes.RESUMING_BACKWARD
-  );
-  const forward = gPauseMode == PauseModes.RESUMING_FORWARD;
-
-  const point = await resumeTarget(gPausePoint, forward, gBreakpoints);
-  if (point) {
-    // We found a point to pause at.
-    setReplayingPauseTarget(point);
-  } else if (forward) {
-    // We searched the entire space forward to the end of the recording and
-    // didn't find any breakpoint hits, so resume recording.
-    assert(forward);
-    RecordReplayControl.restoreMainGraphics();
-    setPauseState(PauseModes.RUNNING, gMainChild.pausePoint(), gMainChild);
-    updateStatus();
-    maybeResumeRecording();
-  } else {
-    // We searched backward to the beginning of the recording, so restore the
-    // first checkpoint.
-    assert(!forward);
-    setReplayingPauseTarget(checkpointExecutionPoint(FirstCheckpointId));
-  }
-}
-
-// Unpause the active child and asynchronously pause at the next or previous
-// breakpoint hit.
-function resume(forward) {
-  gDebuggerRequests.length = 0;
-  if (gActiveChild.recording) {
-    if (forward) {
-      maybeResumeRecording();
-      return;
-    }
-    ensureFlushed();
-  }
-  if (
-    gPausePoint.checkpoint == FirstCheckpointId &&
-    !gPausePoint.position &&
-    !forward
-  ) {
-    gDebugger._hitRecordingBoundary();
-    return;
-  }
-  setPauseState(
-    forward ? PauseModes.RESUMING_FORWARD : PauseModes.RESUMING_BACKWARD,
-    gPausePoint,
-    null
-  );
-  finishResume();
-}
-
-// Synchronously bring the active child to the specified execution point.
-function timeWarp(point) {
-  gDebuggerRequests.length = 0;
-  setReplayingPauseTarget(point);
-  Services.cpmm.sendAsyncMessage("TimeWarpFinished");
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Crash Recovery
-////////////////////////////////////////////////////////////////////////////////
-
-// The maximum number of crashes which we can recover from.
-const MaxCrashes = 4;
-
-// How many child processes have crashed.
-let gNumCrashes = 0;
-
-// eslint-disable-next-line no-unused-vars
-function ChildCrashed(rootId, forkId) {
-  const id = processId(rootId, forkId);
-  dumpv(`Child Crashed: ${id}`);
-
-  const child = gChildren.get(id);
-  if (!child) {
-    return;
-  }
-
-  // Crashes in recording children can't be recovered.
-  if (child.recording) {
-    ThrowError("Child is recording");
-  }
-
-  // Crashes in root processes can't be recovered, as all communication to forks
-  // happens through the root.
-  if (!forkId) {
-    ThrowError("Child is replaying root");
-  }
-
-  // Children shouldn't be crashing if they aren't doing anything.
-  if (child.paused) {
-    ThrowError("Child is paused");
-  }
-
-  if (++gNumCrashes > MaxCrashes) {
-    ThrowError("Too many crashes");
-  }
-
-  child.terminate();
-
-  // If the child crashed while forking, the forked child may or may not have
-  // been created. Mark the fork as crashed so that a new process will be
-  // generated in its place (and any successfully forked process ignored).
-  const { manifest } = child.manifests[0];
-  if (manifest.kind == "fork") {
-    ChildCrashed(rootId, manifest.id);
-  }
-
-  respawnCrashedChild(child);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Simulated Navigation
-////////////////////////////////////////////////////////////////////////////////
-
-// When the user is paused somewhere in the recording, we want to obtain pause
-// data for points which they can get to via the UI. This includes all messages
-// on the timeline (including those for logpoints), breakpoints that can be
-// reached by rewinding and resuming, and points that can be reached by
-// stepping. In the latter two cases, we only want to queue up the pause data
-// for points that are close to the current pause point, so that we don't waste
-// time and resources getting pause data that isn't immediately needed.
-// We handle the latter by simulating the results of user interactions to
-// visit nearby breakpoints and steps, and queueing up the pause data for those
-// points. The simulation is approximate, as stepping behavior in particular
-// depends on information known to the thread actor which isn't available here.
-//
-// When the user navigates through the recording, these simulations are repeated
-// to queue up pause data at new points. Ideally, we will capture the pause data
-// quickly enough that it will be immediately available when the user pauses at
-// a new location, so that they don't experience loading hiccups.
-
-// Get the pause data for nearby breakpoint hits, ignoring steps.
-async function simulateBreakpointNavigation(point, forward, count) {
-  if (!count) {
-    return;
-  }
-
-  const breakpoints = gBreakpoints.filter(bp => bp.kind == "Break");
-  const next = await resumeTarget(point, forward, breakpoints);
-  if (next) {
-    queuePauseData({ point: next });
-    simulateBreakpointNavigation(next, forward, count - 1);
-  }
-}
-
-async function findFrameEntryPoint(point) {
-  // We never want the debugger to stop at EnterFrame points corresponding to
-  // when a frame was pushed on the stack. Instead, find the first point in the
-  // frame which is a breakpoint site.
-  assert(point.position.kind == "EnterFrame");
-  const steps = await findFrameSteps(point);
-  assert(pointEquals(steps[0], point));
-  return steps[1];
-}
-
-// eslint-disable-next-line complexity
-async function simulateSteppingNavigation(point, count, frameCount, last) {
-  if (!count || !point.position) {
-    return;
-  }
-  const { script } = point.position;
-  const dbgScript = gDebugger._getScript(script);
-
-  const steps = await findFrameSteps(point);
-  const pointIndex = steps.findIndex(p => pointEquals(p, point));
-
-  if (last != "reverseStepOver") {
-    for (let i = pointIndex + 1; i < steps.length; i++) {
-      const p = steps[i];
-      if (isStepOverTarget(p)) {
-        queuePauseData({ point: p, snapshot: steps[0] });
-        simulateSteppingNavigation(p, count - 1, frameCount, "stepOver");
-        break;
-      }
-    }
-  }
-
-  if (last != "stepOver") {
-    for (let i = pointIndex - 1; i >= 1; i--) {
-      const p = steps[i];
-      if (isStepOverTarget(p)) {
-        queuePauseData({ point: p, snapshot: steps[0] });
-        simulateSteppingNavigation(p, count - 1, frameCount, "reverseStepOver");
-        break;
-      }
-    }
-  }
-
-  if (frameCount) {
-    for (let i = pointIndex + 1; i < steps.length; i++) {
-      const p = steps[i];
-      if (isStepOverTarget(p)) {
-        break;
-      }
-      if (p.position.script != script) {
-        // Currently, the debugger will stop at the EnterFrame site and then run
-        // forward to the first breakpoint site before pausing. We need pause
-        // data from both points, unfortunately.
-        queuePauseData({ point: p, snapshot: steps[0] });
-
-        const np = await findFrameEntryPoint(p);
-        queuePauseData({ point: np, snapshot: steps[0] });
-        if (canFindHits(np.position)) {
-          findHits(getSavedCheckpoint(point.checkpoint), np.position);
-        }
-        simulateSteppingNavigation(np, count - 1, frameCount - 1, "stepIn");
-        break;
-      }
-    }
-  }
-
-  if (
-    frameCount &&
-    last != "stepOver" &&
-    last != "reverseStepOver" &&
-    point.position.frameIndex
-  ) {
-    // The debugger will stop at the OnPop for the frame before finding a place
-    // in the parent frame to pause at.
-    queuePauseData({ point: steps[steps.length - 1], snapshot: steps[0] });
-
-    const parentEntryPoint = await findParentFrameEntryPoint(steps[0]);
-    const parentSteps = await findFrameSteps(parentEntryPoint);
-    for (let i = 0; i < parentSteps.length; i++) {
-      const p = parentSteps[i];
-      if (pointPrecedes(point, p)) {
-        // When stepping out we will stop at the next breakpoint site,
-        // and do not need a point that is a stepping target.
-        queuePauseData({ point: p, snapshot: parentSteps[0] });
-        if (canFindHits(p.position)) {
-          findHits(getSavedCheckpoint(point.checkpoint), p.position);
-        }
-        simulateSteppingNavigation(p, count - 1, frameCount - 1, "stepOut");
-        break;
-      }
-    }
-  }
-
-  function isStepOverTarget(p) {
-    const { kind, offset } = p.position;
-    return (
-      kind == "OnPop" ||
-      (kind == "OnStep" && dbgScript.getOffsetMetadata(offset).isStepStart)
-    );
-  }
-}
-
-function simulateNearbyNavigation() {
-  // How many breakpoint hit navigations are simulated on either side of the
-  // pause point.
-  const numBreakpointHits = 2;
-
-  // Maximum number of steps to take when simulating nearby steps.
-  const numSteps = 4;
-
-  // Maximum number of times to change frames when simulating nearby steps.
-  const numChangeFrames = 2;
-
-  simulateBreakpointNavigation(gPausePoint, true, numBreakpointHits);
-  simulateBreakpointNavigation(gPausePoint, false, numBreakpointHits);
-  simulateSteppingNavigation(gPausePoint, numSteps, numChangeFrames);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Logpoints
-////////////////////////////////////////////////////////////////////////////////
-
-// All installed logpoints. Logpoints are given to us by the debugger, after
-// which we need to asynchronously send a child to every point where the
-// logpoint's position is reached, evaluate code there and invoke the callback
-// associated with the logpoint.
-const gLogpoints = [];
-
-async function evaluateLogpoint({
-  point,
-  text,
-  condition,
-  callback,
-  snapshot,
-  fast,
-}) {
-  assert(point);
-
-  const child = await ensureLeafChild(point, Priority.MEDIUM);
-  const { result, resultData } = await child.sendManifest({
-    kind: "hitLogpoint",
-    text,
-    condition,
-    fast,
-  });
-  terminateRunningLeafChild(child);
-
-  if (result) {
-    callback(point, result, resultData);
-  }
-}
-
-// Asynchronously invoke a logpoint's callback with all results from hitting
-// the logpoint in the range of the recording covered by checkpoint.
-async function findLogpointHits(
-  checkpoint,
-  { position, text, condition, messageCallback, validCallback }
-) {
-  if (!validCallback()) {
-    return;
-  }
-
-  const hits = await findHits(checkpoint, position);
-  hits.sort((a, b) => pointPrecedes(b, a));
-
-  // Show an initial message while we evaluate the logpoint at each point.
-  if (!condition) {
-    for (const point of hits) {
-      messageCallback(point, ["Loading..."]);
-    }
-  }
-
-  for (const point of hits) {
-    // When the fast logpoints mode is set, replaying children do not diverge
-    // from the recording when evaluating logpoints. If the evaluation has side
-    // effects then the page can behave differently later, including crashes if
-    // the recording is perturbed. Caveat emptor!
-    const fast = getPreference("fastLogpoints");
-
-    // Create a snapshot at the most recent checkpoint in case there are other
-    // nearby logpoints, except in fast mode where there is no need to rewind.
-    const snapshot = !fast && checkpointExecutionPoint(point.checkpoint);
-
-    // We wait on each logpoint in the list before starting the next one, so
-    // that hopefully all the logpoints associated with the same save checkpoint
-    // will be handled by the same process, with no duplicated work due to
-    // different processes handling logpoints that are very close to each other.
-    await evaluateLogpoint({
-      point,
-      text,
-      condition,
-      callback: messageCallback,
-      snapshot,
-      fast,
-    });
-
-    if (!validCallback()) {
-      return;
-    }
-  }
-
-  // Now that we've done the evaluation, gather pause data for each point we
-  // logged. We do this in a second pass because we want to do the evaluations
-  // as quickly as possible.
-  for (const point of hits) {
-    await queuePauseData({ point, trackCached: true });
-
-    if (!validCallback()) {
-      return;
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Event Breakpoints
-////////////////////////////////////////////////////////////////////////////////
-
-// Event kinds which will be logged. For now this set can only grow, as we don't
-// have a way to remove old event logpoints from the client.
-const gLoggedEvents = [];
-
-const gEventFrameEntryPoints = new PromiseMap();
-
-async function findEventFrameEntry(checkpoint, progress) {
-  const { promise, resolve } = gEventFrameEntryPoints.get(progress);
-  if (!resolve) {
-    return promise;
-  }
-
-  const savedCheckpoint = getSavedCheckpoint(checkpoint);
-  const child = await scanRecording(savedCheckpoint);
-  const { rv } = await child.sendManifest({
-    kind: "findEventFrameEntry",
-    checkpoint,
-    progress,
-  });
-
-  const point = await findFrameEntryPoint(rv);
-  resolve(point);
-  return point;
-}
-
-async function findEventLogpointHits(checkpoint, event, callback) {
-  for (const info of getCheckpointInfo(checkpoint).events) {
-    if (info.event == event) {
-      const point = await findEventFrameEntry(info.checkpoint, info.progress);
-      if (point) {
-        callback(point, ["Loading..."]);
-        evaluateLogpoint({ point, text: "arguments[0]", callback });
-        queuePauseData({ point, trackCached: true });
-      }
-    }
-  }
-}
-
-function setActiveEventBreakpoints(events, callback) {
-  dumpv(`SetActiveEventBreakpoints ${JSON.stringify(events)}`);
-
-  for (const event of events) {
-    if (gLoggedEvents.some(info => info.event == event)) {
-      continue;
-    }
-    gLoggedEvents.push({ event, callback });
-    forAllSavedCheckpoints(checkpoint =>
-      findEventLogpointHits(checkpoint, event, callback)
-    );
-  }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Saving Recordings
-////////////////////////////////////////////////////////////////////////////////
-
-// Resume manifests are sent when the main child is sent forward through the
-// recording. Update state according to new data produced by the resume.
-function handleResumeManifestResponse({
-  point,
-  duration,
-  consoleMessages,
-  scripts,
-  debuggerStatements,
-  events,
-}) {
-  if (!point.position) {
-    addCheckpoint(point.checkpoint - 1, duration);
-    getCheckpointInfo(point.checkpoint).point = point;
-  }
-
-  if (gDebugger) {
-    consoleMessages.forEach(msg => {
-      gDebugger._newConsoleMessage(msg);
-    });
-  }
-
-  if (gDebugger) {
-    scripts.forEach(script => gDebugger._onNewScript(script));
-  }
-
-  consoleMessages.forEach(msg => {
-    if (msg.executionPoint) {
-      queuePauseData({ point: msg.executionPoint, trackCached: true });
-    }
-  });
-
-  const savedCheckpoint = getSavedCheckpoint(
-    point.position ? point.checkpoint : point.checkpoint - 1
-  );
-
-  for (const point of debuggerStatements) {
-    getCheckpointInfo(savedCheckpoint).debuggerStatements.push(point);
-  }
-
-  for (const event of events) {
-    getCheckpointInfo(savedCheckpoint).events.push(event);
-  }
-
-  // In repaint stress mode, the child process creates a checkpoint before every
-  // paint. By gathering the pause data at these checkpoints, we will perform a
-  // repaint at all of these checkpoints, ensuring that all the normal paints
-  // can be repainted.
-  if (RecordReplayControl.inRepaintStressMode()) {
-    queuePauseData({ point });
-  }
-}
-
-// If necessary, continue executing in the main child.
-function maybeResumeRecording() {
-  if (gActiveChild != gMainChild) {
-    return;
-  }
-
-  if (
-    !gLastSavedCheckpoint ||
-    timeSinceCheckpoint(gLastSavedCheckpoint) >= FlushMs
-  ) {
-    ensureFlushed();
-  }
-
-  const checkpoint = gMainChild.pausePoint().checkpoint;
-  if (!gMainChild.recording && checkpoint == gRecordingEndpoint) {
-    ensureFlushed();
-    Services.cpmm.sendAsyncMessage("HitRecordingEndpoint");
-    if (gDebugger) {
-      gDebugger._hitRecordingBoundary();
-    }
-    return;
-  }
-  gMainChild.sendManifest(
-    {
-      kind: "resume",
-      breakpoints: gBreakpoints,
-      pauseOnDebuggerStatement: !!gDebugger,
-    },
-    response => {
-      handleResumeManifestResponse(response);
-
-      gPausePoint = gMainChild.pausePoint();
-      if (gDebugger) {
-        updateStatus();
-        gDebugger._onPause();
-      } else {
-        Services.tm.dispatchToMainThread(maybeResumeRecording);
-      }
-    }
-  );
-}
-
-// Resolve callbacks for any promises waiting on the recording to be flushed.
-const gFlushWaiters = [];
-
-function waitForFlushed(checkpoint) {
-  if (checkpoint < gLastSavedCheckpoint) {
-    return undefined;
-  }
-  return new Promise(resolve => {
-    gFlushWaiters.push(resolve);
-  });
-}
-
-let gLastFlushTime = Date.now();
-
-// If necessary, synchronously flush the recording to disk.
-function ensureFlushed() {
-  gMainChild.waitUntilPaused(true);
-
-  gLastFlushTime = Date.now();
-
-  if (gLastSavedCheckpoint == gMainChild.pauseCheckpoint()) {
-    return;
-  }
-
-  if (gMainChild.recording) {
-    gMainChild.sendManifest({ kind: "flushRecording" });
-    gMainChild.waitUntilPaused();
-  }
-
-  // We now have a usable recording for replaying children, so spawn them if
-  // necessary.
-  if (!gTrunkChild) {
-    spawnTrunkChild();
-  }
-
-  const oldSavedCheckpoint = gLastSavedCheckpoint || FirstCheckpointId;
-  addSavedCheckpoint(gMainChild.pauseCheckpoint());
-
-  // Checkpoints where the recording was flushed to disk are saved. This allows
-  // the recording to be scanned as soon as it has been flushed.
-
-  // Flushing creates a new region of the recording for replaying children
-  // to scan.
-  forSavedCheckpointsInRange(
-    oldSavedCheckpoint,
-    gLastSavedCheckpoint,
-    checkpoint => {
-      scanRecording(checkpoint);
-
-      // Scan for breakpoint and logpoint hits in this new region.
-      gBreakpoints.forEach(position =>
-        findBreakpointHits(checkpoint, position)
-      );
-      gLogpoints.forEach(logpoint => findLogpointHits(checkpoint, logpoint));
-      for (const { event, callback } of gLoggedEvents) {
-        findEventLogpointHits(checkpoint, event, callback);
-      }
-    }
-  );
-
-  for (const waiter of gFlushWaiters) {
-    waiter();
-  }
-  gFlushWaiters.length = 0;
-}
-
-const CheckFlushMs = 1000;
-
-setInterval(() => {
-  // Periodically make sure the recording is flushed. If the tab is sitting
-  // idle we still want to keep the recording up to date.
-  const elapsed = Date.now() - gLastFlushTime;
-  if (
-    elapsed > CheckFlushMs &&
-    gMainChild.lastPausePoint &&
-    gMainChild.lastPausePoint.checkpoint != gLastSavedCheckpoint
-  ) {
-    ensureFlushed();
-  }
-
-  // Ping children that are executing manifests to ensure they haven't hanged.
-  for (const child of gUnpausedChildren) {
-    if (!child.recording) {
-      child.maybePing();
-    }
-  }
-}, 1000);
-
-// eslint-disable-next-line no-unused-vars
-function BeforeSaveRecording() {
-  if (gActiveChild == gMainChild) {
-    // The recording might not be up to date, ensure it flushes after pausing.
-    ensureFlushed();
-  }
-}
-
-// eslint-disable-next-line no-unused-vars
-function AfterSaveRecording() {
-  Services.cpmm.sendAsyncMessage("SaveRecordingFinished");
-}
-
-let gRecordingEndpoint;
-
-async function setMainChild() {
-  assert(!gMainChild.recording);
-
-  const { endpoint } = await gMainChild.sendManifest({ kind: "setMainChild" });
-  gRecordingEndpoint = endpoint;
-  Services.tm.dispatchToMainThread(maybeResumeRecording);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Debugger Operations
-////////////////////////////////////////////////////////////////////////////////
-
-// From the debugger's perspective, there is a single target to interact with,
-// represented by gActiveChild. The details of the various children the control
-// system is managing are hidden away. This object describes the interface which
-// the debugger uses to access the control system.
-const gControl = {
-  // Get the current point where the active child is paused, or null.
-  pausePoint() {
-    if (gActiveChild && gActiveChild == gMainChild) {
-      return gActiveChild.paused ? gActiveChild.pausePoint() : null;
-    }
-    if (gPauseMode == PauseModes.PAUSED) {
-      return gPausePoint;
-    }
-    return null;
-  },
-
-  lastPausePoint() {
-    return gPausePoint;
-  },
-
-  findFrameSteps(point) {
-    return findFrameSteps(point);
-  },
-
-  async findAncestorFrameEntryPoint(point, index) {
-    const steps = await findFrameSteps(point);
-    point = steps[0];
-    while (index--) {
-      point = await findParentFrameEntryPoint(point);
-    }
-    return point;
-  },
-
-  // Ensure the active child is paused.
-  waitUntilPaused() {
-    // The debugger should not use this method while we are actively resuming.
-    assert(gActiveChild);
-
-    if (gActiveChild == gMainChild) {
-      gActiveChild.waitUntilPaused(true);
-    }
-  },
-
-  // Add a breakpoint where the active child should pause while resuming.
-  addBreakpoint(position) {
-    dumpv(`AddBreakpoint ${JSON.stringify(position)}`);
-    gBreakpoints.push(position);
-
-    // Start searching for breakpoint hits in the recording immediately.
-    if (canFindHits(position)) {
-      forAllSavedCheckpoints(checkpoint =>
-        findBreakpointHits(checkpoint, position)
-      );
-    }
-
-    if (gActiveChild == gMainChild) {
-      // The recording child will update its breakpoints when it reaches the
-      // next checkpoint, so force it to create a checkpoint now.
-      gActiveChild.waitUntilPaused(true);
-    }
-
-    simulateNearbyNavigation();
-  },
-
-  // Clear all installed breakpoints.
-  clearBreakpoints() {
-    dumpv(`ClearBreakpoints`);
-    gBreakpoints.length = 0;
-
-    if (gActiveChild == gMainChild) {
-      // As for addBreakpoint(), update the active breakpoints in the recording
-      // child immediately.
-      gActiveChild.waitUntilPaused(true);
-    }
-  },
-
-  // Get the last known point in the recording.
-  recordingEndpoint() {
-    return gMainChild.lastPausePoint;
-  },
-
-  // If the active child is currently recording, switch to a replaying one if
-  // possible.
-  maybeSwitchToReplayingChild() {
-    assert(gControl.pausePoint());
-    if (gActiveChild == gMainChild && RecordReplayControl.canRewind()) {
-      const point = gActiveChild.pausePoint();
-
-      if (point.position) {
-        // We can only flush the recording at checkpoints, so we need to send the
-        // main child forward and pause/flush ASAP.
-        gMainChild.sendManifest(
-          {
-            kind: "resume",
-            breakpoints: [],
-            pauseOnDebuggerStatement: false,
-          },
-          handleResumeManifestResponse
-        );
-        gMainChild.waitUntilPaused(true);
-      }
-
-      ensureFlushed();
-      bringNewReplayingChildToPausePoint();
-    }
-  },
-
-  // Synchronously send a debugger request to a paused active child, returning
-  // the response.
-  sendRequest(request) {
-    let data;
-    gActiveChild.sendManifest(
-      { kind: "debuggerRequest", request },
-      finishData => {
-        data = finishData;
-      }
-    );
-    while (!data) {
-      gActiveChild.waitUntilPaused();
-    }
-
-    if (data.unhandledDivergence) {
-      bringNewReplayingChildToPausePoint();
-    } else {
-      addDebuggerRequest(request);
-    }
-    return data.response;
-  },
-
-  // Synchronously send a debugger request to the main child, which will always
-  // be at the end of the recording and can receive requests even when the
-  // active child is not currently paused.
-  sendRequestMainChild(request) {
-    gMainChild.waitUntilPaused(true);
-    let data;
-    gMainChild.sendManifest(
-      { kind: "debuggerRequest", request },
-      finishData => {
-        data = finishData;
-      }
-    );
-    gMainChild.waitUntilPaused();
-    assert(!data.divergedFromRecording);
-    return data.response;
-  },
-
-  resume,
-  timeWarp,
-
-  // Add a new logpoint.
-  addLogpoint(logpoint) {
-    gLogpoints.push(logpoint);
-    forAllSavedCheckpoints(checkpoint =>
-      findLogpointHits(checkpoint, logpoint)
-    );
-  },
-
-  setActiveEventBreakpoints,
-
-  debuggerRequests() {
-    return gDebuggerRequests;
-  },
-
-  getPauseDataAndRepaint() {
-    // Use cached pause data if possible, which we can immediately return
-    // without waiting for the child to arrive at the pause point.
-    if (!gDebuggerRequests.length) {
-      const data = maybeGetPauseData(gPausePoint);
-      if (data) {
-        // After the child pauses, it will need to generate the pause data so
-        // that any referenced objects will be instantiated.
-        gActiveChild.sendManifest({
-          kind: "debuggerRequest",
-          request: { type: "pauseData" },
-        });
-        addDebuggerRequest({ type: "pauseData" });
-        RecordReplayControl.hadRepaint(data.paintData);
-        return data;
-      }
-    }
-    gControl.maybeSwitchToReplayingChild();
-    const data = gControl.sendRequest({ type: "pauseData" });
-    if (!data) {
-      RecordReplayControl.clearGraphics();
-    } else {
-      addPauseData(gPausePoint, data, /* trackCached */ true);
-      RecordReplayControl.hadRepaint(data.paintData);
-    }
-    return data;
-  },
-
-  paint(point) {
-    const data = maybeGetPauseData(point);
-    if (data) {
-      RecordReplayControl.hadRepaint(data.paintData);
-    }
-  },
-
-  isPausedAtDebuggerStatement() {
-    const point = gControl.pausePoint();
-    if (point) {
-      const checkpoint = getSavedCheckpoint(point.checkpoint);
-      const { debuggerStatements } = getCheckpointInfo(checkpoint);
-      return pointArrayIncludes(debuggerStatements, point);
-    }
-    return false;
-  },
-};
-
-function updateStatus() {
-  if (gDebugger && gDebugger.replayingOnStatusUpdate) {
-    gDebugger.replayingOnStatusUpdate({
-      recording: gActiveChild && gActiveChild.recording,
-      executionPoint: gPausePoint,
-      cachedPoints: cachedPoints(),
-      unscannedRegions: unscannedRegions(),
-    });
-  }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Utilities
-///////////////////////////////////////////////////////////////////////////////
-
-function getPreference(name) {
-  return Services.prefs.getBoolPref(`devtools.recordreplay.${name}`);
-}
-
-const loggingFullEnabled = getPreference("loggingFull");
-const loggingEnabled = loggingFullEnabled || getPreference("logging");
-
-// eslint-disable-next-line no-unused-vars
-function ConnectDebugger(dbg) {
-  gDebugger = dbg;
-  dbg._control = gControl;
-}
-
-const startTime = Date.now();
-
-// eslint-disable-next-line no-unused-vars
-function currentTime() {
-  return (((Date.now() - startTime) / 10) | 0) / 100;
-}
-
-function dumpv(str) {
-  if (loggingEnabled) {
-    dump(`[ReplayControl ${currentTime()}] ${str}\n`);
-  }
-}
-
-function assert(v) {
-  if (!v) {
-    ThrowError("Assertion Failed!");
-  }
-}
-
-function ThrowError(msg) {
-  const error = new Error(msg);
-  dump(`ReplayControl Server Error: ${msg} Stack: ${error.stack}\n`);
-  throw error;
-}
-
-function stringify(object) {
-  const str = JSON.stringify(object);
-  if (str && str.length >= 4096 && !loggingFullEnabled) {
-    return `${str.substr(0, 4096)} TRIMMED ${str.length}`;
-  }
-  return str;
-}
-
-// eslint-disable-next-line no-unused-vars
-var EXPORTED_SYMBOLS = [
-  "Initialize",
-  "ConnectDebugger",
-  "ManifestFinished",
-  "BeforeSaveRecording",
-  "AfterSaveRecording",
-  "ChildCrashed",
-  "PingResponse",
-];
deleted file mode 100644
--- a/devtools/server/actors/replay/debugger.js
+++ /dev/null
@@ -1,1570 +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/. */
-/* eslint-disable spaced-comment, brace-style, indent-legacy */
-
-// When recording/replaying an execution with Web Replay, Devtools server code
-// runs in the middleman process instead of the recording/replaying process the
-// code is interested in.
-//
-// This file defines replay objects analogous to those constructed by the
-// C++ Debugger (Debugger, Debugger.Object, etc.), which implement similar
-// methods and properties to those C++ objects. These replay objects are
-// created in the middleman process, and describe things that exist in the
-// recording/replaying process, inspecting them via the interface provided by
-// control.js.
-
-"use strict";
-
-const RecordReplayControl = !isWorker && require("RecordReplayControl");
-const Services = require("Services");
-const ChromeUtils = require("ChromeUtils");
-
-ChromeUtils.defineModuleGetter(
-  this,
-  "positionSubsumes",
-  "resource://devtools/shared/execution-point-utils.js"
-);
-
-loader.lazyRequireGetter(
-  this,
-  "ReplayInspector",
-  "devtools/server/actors/replay/inspector"
-);
-
-///////////////////////////////////////////////////////////////////////////////
-// ReplayDebugger
-///////////////////////////////////////////////////////////////////////////////
-
-// Possible preferred directions of travel.
-const Direction = {
-  FORWARD: "FORWARD",
-  BACKWARD: "BACKWARD",
-  NONE: "NONE",
-};
-
-// Pool of ReplayDebugger things that are grouped together and can refer to each
-// other. Many things --- frames, objects, environments --- are specific to
-// a pool and cannot be used in any other context. Normally a pool is associated
-// with some point at which the debugger paused, but they may also be associated
-// with the values in a console or logpoint message.
-function ReplayPool(dbg, pauseData) {
-  this.dbg = dbg;
-
-  // All ReplayDebuggerFramees that have been created for this pool, indexed by
-  // their index (zero is the oldest frame, with the index increasing for newer
-  // frames).
-  this.frames = [];
-
-  // All ReplayDebuggerObjects and ReplayDebuggerEnvironments that are
-  // associated with this pool, indexed by their id.
-  this.objects = [];
-
-  if (pauseData) {
-    this.addPauseData(pauseData);
-  }
-}
-
-ReplayPool.prototype = {
-  getObject(id) {
-    if (id && !this.objects[id]) {
-      if (this != this.dbg._pool) {
-        return null;
-      }
-      const data = this.dbg._sendRequest({ type: "getObject", id });
-      this.addObject(data);
-    }
-    return this.objects[id];
-  },
-
-  addObject(data) {
-    switch (data.kind) {
-      case "Object":
-        this.objects[data.id] = new ReplayDebuggerObject(this, data);
-        break;
-      case "Environment":
-        this.objects[data.id] = new ReplayDebuggerEnvironment(this, data);
-        break;
-      default:
-        ThrowError("Unknown object kind");
-    }
-  },
-
-  getFrame(index) {
-    if (index == NewestFrameIndex) {
-      if (this.frames.length) {
-        return this.frames[this.frames.length - 1];
-      }
-    } else {
-      assert(index < this.frames.length);
-      if (this.frames[index]) {
-        return this.frames[index];
-      }
-    }
-
-    assert(this == this.dbg._pool);
-    const data = this.dbg._sendRequest({ type: "getFrame", index });
-
-    if (index == NewestFrameIndex) {
-      if ("index" in data) {
-        index = data.index;
-      } else {
-        // There are no frames on the stack.
-        return null;
-      }
-    }
-
-    this.frames[index] = new ReplayDebuggerFrame(this, data);
-    return this.frames[index];
-  },
-
-  addPauseData(pauseData) {
-    if (!pauseData) {
-      return;
-    }
-
-    for (const { data, preview } of Object.values(pauseData.objects)) {
-      if (!this.objects[data.id]) {
-        this.addObject(data);
-      }
-      this.getObject(data.id)._preview = {
-        ...preview,
-        properties: mapify(preview.properties),
-        callResults: mapify(preview.callResults),
-      };
-    }
-
-    for (const { data, names } of Object.values(pauseData.environments)) {
-      if (!this.objects[data.id]) {
-        this.addObject(data);
-      }
-      this.getObject(data.id)._setNames(names);
-    }
-
-    if (pauseData.frames) {
-      for (const frame of pauseData.frames) {
-        this.frames[frame.index] = new ReplayDebuggerFrame(this, frame);
-      }
-    }
-
-    if (pauseData.popFrameResult) {
-      this.popFrameResult = this.convertCompletionValue(
-        pauseData.popFrameResult
-      );
-    }
-  },
-
-  convertValue(value) {
-    if (isNonNullObject(value)) {
-      if (value.object) {
-        return this.getObject(value.object);
-      }
-      switch (value.special) {
-        case "undefined":
-          return undefined;
-        case "Infinity":
-          return Infinity;
-        case "-Infinity":
-          return -Infinity;
-        case "NaN":
-          return NaN;
-        case "0":
-          return -0;
-      }
-    }
-    return value;
-  },
-
-  convertCompletionValue(value) {
-    if ("return" in value) {
-      return { return: this.convertValue(value.return) };
-    }
-    if ("throw" in value) {
-      return {
-        throw: this.convertValue(value.throw),
-        stack: value.stack,
-      };
-    }
-    ThrowError("Unexpected completion value");
-    return null; // For eslint
-  },
-};
-
-function ReplayDebugger() {
-  const existing = RecordReplayControl.registerReplayDebugger(this);
-  if (existing) {
-    // There is already a ReplayDebugger in existence, use that. There can only
-    // be one ReplayDebugger in the process.
-    return existing;
-  }
-
-  // We should have been connected to control.js by the call above.
-  assert(this._control);
-
-  // Preferred direction of travel when not explicitly resumed.
-  this._direction = Direction.NONE;
-
-  // All breakpoint positions and handlers installed by this debugger.
-  this._breakpoints = [];
-
-  // The current pool of pause-local state.
-  this._pool = new ReplayPool(this);
-
-  // All ReplayDebuggerScripts and ReplayDebuggerScriptSources that have been
-  // created, indexed by their id. These stay valid even after unpausing.
-  this._scripts = [];
-  this._scriptSources = [];
-
-  // How many nested thread-wide paused have been entered.
-  this._threadPauseCount = 0;
-
-  // Flag set if the dispatched _performPause() call can be ignored because the
-  // server entered a thread-wide pause first.
-  this._cancelPerformPause = false;
-
-  // After we are done pausing, callback describing how to resume.
-  this._resumeCallback = null;
-
-  // Handler called when hitting the beginning/end of the recording, or when
-  // a time warp target has been reached.
-  this.replayingOnForcedPause = null;
-
-  // Handler called when the status changes and the UI needs to be updated.
-  this.replayingOnStatusUpdate = null;
-}
-
-// Frame index used to refer to the newest frame in the child process.
-const NewestFrameIndex = -1;
-
-ReplayDebugger.prototype = {
-  /////////////////////////////////////////////////////////
-  // General methods
-  /////////////////////////////////////////////////////////
-
-  replaying: true,
-
-  canRewind: RecordReplayControl.canRewind,
-
-  replayCurrentExecutionPoint() {
-    return this._control.lastPausePoint();
-  },
-
-  replayFramePositions(point) {
-    return this._control.findFrameSteps(point);
-  },
-
-  async replayAncestorFramePositions(point, index) {
-    const ancestor = await this._control.findAncestorFrameEntryPoint(
-      point,
-      index
-    );
-    return this._control.findFrameSteps(ancestor);
-  },
-
-  replayRecordingEndpoint() {
-    return this._control.recordingEndpoint();
-  },
-
-  replayDebuggerRequests() {
-    return this._control.debuggerRequests();
-  },
-
-  addDebuggee() {},
-  removeAllDebuggees() {},
-
-  replayingContent(url) {
-    return this._sendRequestMainChild({ type: "getContent", url });
-  },
-
-  _processResponse(request, response, divergeResponse) {
-    dumpv(`SendRequest: ${stringify(request)} -> ${stringify(response)}`);
-    if (!response) {
-      if (divergeResponse) {
-        return divergeResponse;
-      }
-      ThrowError(`Unhandled recording divergence in ${request.type}`);
-    }
-    return response;
-  },
-
-  // Send a request object to the child process, and synchronously wait for it
-  // to respond. divergeResponse must be specified for requests that can diverge
-  // from the recording and which we want to recover gracefully.
-  _sendRequest(request, divergeResponse) {
-    const response = this._control.sendRequest(request);
-    return this._processResponse(request, response, divergeResponse);
-  },
-
-  // Send a request that requires the child process to perform actions that
-  // diverge from the recording. In such cases we want to be interacting with a
-  // replaying process (if there is one), as recording child processes won't
-  // provide useful responses to such requests.
-  _sendRequestAllowDiverge(request, divergeResponse) {
-    this._control.maybeSwitchToReplayingChild();
-    return this._sendRequest(request, divergeResponse);
-  },
-
-  _sendRequestMainChild(request) {
-    const response = this._control.sendRequestMainChild(request);
-    return this._processResponse(request, response);
-  },
-
-  getDebuggees() {
-    return [];
-  },
-
-  replayGetExecutionPointPosition({ position }) {
-    const script = this._getScript(position.script);
-    if (position.kind == "EnterFrame") {
-      return { script, offset: script.mainOffset };
-    }
-    return { script, offset: position.offset };
-  },
-
-  /////////////////////////////////////////////////////////
-  // Paused/running state
-  /////////////////////////////////////////////////////////
-
-  // Paused State Management
-  //
-  // The single ReplayDebugger is exclusively responsible for controlling the
-  // position of the child process by keeping track of when it pauses and
-  // sending it commands to resume.
-  //
-  // The general goal of controlling this position is to make the child process
-  // execute at predictable times, similar to how it would execute if the
-  // debuggee was in the same process as this one (as is the case when not
-  // replaying), as described below:
-  //
-  // - After the child pauses, the it will only resume executing when an event
-  //   loop is running that is *not* associated with the thread actor's nested
-  //   pauses. As long as the thread actor has pushed a pause, the child will
-  //   remain paused.
-  //
-  // - After the child resumes, installed breakpoint handlers will only execute
-  //   when an event loop is running (which, because of the above point, cannot
-  //   be associated with a thread actor's nested pause).
-
-  get _paused() {
-    return !!this._control.pausePoint();
-  },
-
-  replayResumeBackward() {
-    this._resume(/* forward = */ false);
-  },
-  replayResumeForward() {
-    this._resume(/* forward = */ true);
-  },
-
-  _resume(forward) {
-    this._ensurePaused();
-    this._setResume(() => {
-      this._direction = forward ? Direction.FORWARD : Direction.BACKWARD;
-      dumpv("Resuming " + this._direction);
-      this._control.resume(forward);
-    });
-  },
-
-  // Called when replaying and hitting the beginning or end of recording.
-  _hitRecordingBoundary() {
-    this._capturePauseData();
-    this.replayingOnForcedPause(this.getNewestFrame());
-  },
-
-  replayTimeWarp(target) {
-    this._ensurePaused();
-    this._setResume(() => {
-      this._direction = Direction.NONE;
-      dumpv("Warping " + JSON.stringify(target));
-      this._control.timeWarp(target);
-
-      // timeWarp() doesn't return until the child has reached the target of
-      // the warp, after which we force the thread to pause.
-      assert(this._paused);
-      this._capturePauseData();
-      this.replayingOnForcedPause(this.getNewestFrame());
-    });
-  },
-
-  replayPause() {
-    this._ensurePaused();
-
-    // Cancel any pending resume.
-    this._resumeCallback = null;
-  },
-
-  _ensurePaused() {
-    if (!this._paused) {
-      this._control.waitUntilPaused();
-      assert(this._paused);
-    }
-  },
-
-  // This hook is called whenever the child has paused, which can happen
-  // within a control method (resume, timeWarp, waitUntilPaused) or be
-  // delivered via the event loop.
-  _onPause() {
-    // Call _performPause() soon via the event loop to check for breakpoint
-    // handlers at this point.
-    this._cancelPerformPause = false;
-    Services.tm.dispatchToMainThread(this._performPause.bind(this));
-  },
-
-  _performPause() {
-    // The child paused at some time in the past and any breakpoint handlers
-    // may still need to be called. If we've entered a thread-wide pause or
-    // have already told the child to resume, don't call handlers.
-    if (!this._paused || this._cancelPerformPause || this._resumeCallback) {
-      return;
-    }
-
-    const point = this.replayCurrentExecutionPoint();
-    dumpv("PerformPause " + JSON.stringify(point));
-
-    if (!point.position) {
-      // We paused at a checkpoint, and there are no handlers to call.
-    } else {
-      // Call any handlers for this point, unless one resumes execution.
-      for (const { handler, position } of this._breakpoints) {
-        if (positionSubsumes(position, point.position)) {
-          handler();
-          assert(!this._threadPauseCount);
-          if (this._resumeCallback) {
-            break;
-          }
-        }
-      }
-
-      if (
-        this._control.isPausedAtDebuggerStatement() &&
-        this.onDebuggerStatement
-      ) {
-        this._capturePauseData();
-        this.onDebuggerStatement(this.getNewestFrame());
-      }
-    }
-
-    // If no handlers entered a thread-wide pause (resetting this._direction)
-    // or gave an explicit resume, continue traveling in the same direction
-    // we were going when we paused.
-    assert(!this._threadPauseCount);
-    if (!this._resumeCallback) {
-      switch (this._direction) {
-        case Direction.FORWARD:
-          this.replayResumeForward();
-          break;
-        case Direction.BACKWARD:
-          this.replayResumeBackward();
-          break;
-      }
-    }
-  },
-
-  replayPushThreadPause() {
-    // The thread has paused so that the user can interact with it. The child
-    // will stay paused until this thread-wide pause has been popped.
-    this._ensurePaused();
-    assert(!this._resumeCallback);
-    if (++this._threadPauseCount == 1) {
-      // There is no preferred direction of travel after an explicit pause.
-      this._direction = Direction.NONE;
-
-      // If breakpoint handlers for the pause haven't been called yet, don't
-      // call them at all.
-      this._cancelPerformPause = true;
-    }
-    const point = this.replayCurrentExecutionPoint();
-    dumpv("PushPause " + JSON.stringify(point));
-  },
-
-  replayPopThreadPause() {
-    dumpv("PopPause");
-
-    // After popping the last thread-wide pause, the child can resume.
-    if (--this._threadPauseCount == 0 && this._resumeCallback) {
-      Services.tm.dispatchToMainThread(this._performResume.bind(this));
-    }
-  },
-
-  _setResume(callback) {
-    assert(this._paused);
-
-    // Overwrite any existing resume direction.
-    this._resumeCallback = callback;
-
-    // The child can resume immediately if there is no thread-wide pause.
-    if (!this._threadPauseCount) {
-      Services.tm.dispatchToMainThread(this._performResume.bind(this));
-    }
-  },
-
-  _performResume() {
-    this._ensurePaused();
-    if (this._resumeCallback && !this._threadPauseCount) {
-      const callback = this._resumeCallback;
-      this._invalidateAfterUnpause();
-      this._resumeCallback = null;
-      callback();
-    }
-  },
-
-  replayPaint(data) {
-    this._control.paint(data);
-  },
-
-  replayPaintCurrentPoint() {
-    if (this._control.childIsRecording()) {
-      return RecordReplayControl.restoreMainGraphics();
-    }
-
-    const point = this._control.lastPausePoint();
-    return this._control.paint(point);
-  },
-
-  // Clear out all data that becomes invalid when the child unpauses.
-  _invalidateAfterUnpause() {
-    this._pool = new ReplayPool(this);
-  },
-
-  // Fill in the debugger with (hopefully) all data the client/server need to
-  // pause at the current location. This also updates graphics to match the
-  // current location.
-  _capturePauseData() {
-    if (this._pool.frames.length) {
-      return;
-    }
-
-    const pauseData = this._control.getPauseDataAndRepaint();
-    if (!pauseData || !pauseData.frames) {
-      return;
-    }
-
-    for (const data of Object.values(pauseData.scripts)) {
-      this._addScript(data);
-    }
-
-    for (const { scriptId, offset, metadata } of pauseData.offsetMetadata) {
-      if (this._scripts[scriptId]) {
-        const script = this._getScript(scriptId);
-        script._addOffsetMetadata(offset, metadata);
-      }
-    }
-
-    this._pool.addPauseData(pauseData);
-  },
-
-  _virtualConsoleLog(logpoint) {
-    const { position, text, condition } = logpoint;
-    dumpv(`AddLogpoint ${JSON.stringify(position)} ${text} ${condition}`);
-    this._control.addLogpoint(logpoint);
-  },
-
-  /////////////////////////////////////////////////////////
-  // Breakpoint management
-  /////////////////////////////////////////////////////////
-
-  _setBreakpoint(handler, position, data) {
-    dumpv(`AddBreakpoint ${JSON.stringify(position)}`);
-    this._control.addBreakpoint(position);
-    this._breakpoints.push({ handler, position, data });
-  },
-
-  _clearMatchingBreakpoints(callback) {
-    const newBreakpoints = this._breakpoints.filter(bp => !callback(bp));
-    if (newBreakpoints.length != this._breakpoints.length) {
-      dumpv("ClearBreakpoints");
-      this._control.clearBreakpoints();
-      for (const { position } of newBreakpoints) {
-        dumpv("AddBreakpoint " + JSON.stringify(position));
-        this._control.addBreakpoint(position);
-      }
-    }
-    this._breakpoints = newBreakpoints;
-  },
-
-  _searchBreakpoints(callback) {
-    for (const breakpoint of this._breakpoints) {
-      const v = callback(breakpoint);
-      if (v) {
-        return v;
-      }
-    }
-    return undefined;
-  },
-
-  // Getter for a breakpoint kind that has no script/offset/frameIndex.
-  _breakpointKindGetter(kind) {
-    return this._searchBreakpoints(({ position, data }) => {
-      return position.kind == kind ? data : null;
-    });
-  },
-
-  // Setter for a breakpoint kind that has no script/offset/frameIndex.
-  _breakpointKindSetter(kind, handler, callback) {
-    if (handler) {
-      this._setBreakpoint(callback, { kind }, handler);
-    } else {
-      this._clearMatchingBreakpoints(({ position }) => position.kind == kind);
-    }
-  },
-
-  // Clear OnStep and OnPop hooks for all frames.
-  replayClearSteppingHooks() {
-    this._clearMatchingBreakpoints(
-      ({ position }) => position.kind == "OnStep" || position.kind == "OnPop"
-    );
-  },
-
-  /////////////////////////////////////////////////////////
-  // Script methods
-  /////////////////////////////////////////////////////////
-
-  _getScript(id) {
-    if (!id) {
-      return undefined;
-    }
-    const rv = this._scripts[id];
-    if (rv) {
-      return rv;
-    }
-    return this._addScript(
-      this._sendRequestMainChild({ type: "getScript", id })
-    );
-  },
-
-  _addScript(data) {
-    if (!this._scripts[data.id]) {
-      this._scripts[data.id] = new ReplayDebuggerScript(this, data);
-    }
-    return this._scripts[data.id];
-  },
-
-  _convertScriptQuery(query) {
-    // Make a copy of the query, converting properties referring to debugger
-    // things into their associated ids.
-    const rv = Object.assign({}, query);
-    if ("global" in query) {
-      // Script queries might be sent to a different process from the one which
-      // is paused at the current point and which we are interacting with.
-      NYI();
-    }
-    if ("source" in query) {
-      rv.source = query.source._data.id;
-    }
-    return rv;
-  },
-
-  findScripts(query) {
-    const data = this._sendRequestMainChild({
-      type: "findScripts",
-      query: this._convertScriptQuery(query),
-    });
-    return data.map(script => this._addScript(script));
-  },
-
-  _onNewScript(data) {
-    if (this.onNewScript) {
-      const script = this._addScript(data);
-      this.onNewScript(script);
-    }
-  },
-
-  /////////////////////////////////////////////////////////
-  // ScriptSource methods
-  /////////////////////////////////////////////////////////
-
-  _getSource(id) {
-    const source = this._scriptSources[id];
-    if (source) {
-      return source;
-    }
-    return this._addSource(
-      this._sendRequestMainChild({ type: "getSource", id })
-    );
-  },
-
-  _addSource(data) {
-    if (!this._scriptSources[data.id]) {
-      this._scriptSources[data.id] = new ReplayDebuggerScriptSource(this, data);
-    }
-    return this._scriptSources[data.id];
-  },
-
-  findSources() {
-    const data = this._sendRequestMainChild({ type: "findSources" });
-    return data.map(source => this._addSource(source));
-  },
-
-  findSourceURLs() {
-    return this.findSources().map(source => source.url);
-  },
-
-  adoptSource(source) {
-    assert(source._dbg == this);
-    return source;
-  },
-
-  /////////////////////////////////////////////////////////
-  // Object methods
-  /////////////////////////////////////////////////////////
-
-  // Convert a value for sending to the child.
-  _convertValueForChild(value) {
-    if (isNonNullObject(value)) {
-      assert(value instanceof ReplayDebuggerObject);
-      assert(value._pool == this._pool);
-      return { object: value._data.id };
-    } else if (
-      value === undefined ||
-      value == Infinity ||
-      value == -Infinity ||
-      Object.is(value, NaN) ||
-      Object.is(value, -0)
-    ) {
-      return { special: "" + value };
-    }
-    return value;
-  },