Bug 1465491 Part 1 - Web Replay tests.
☠☠ backed out by 6436144d9173 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 30 Jul 2018 15:49:43 +0000
changeset 429280 2073834a70d6bd2ff07812679430dd150d59e356
parent 429279 55378521296aebfbcaf88003533bac492448b79c
child 429281 32c5b19c77c93c9941214f533ae41c556b6ec8e4
push id34359
push usershindli@mozilla.com
push dateTue, 31 Jul 2018 09:53:19 +0000
treeherdermozilla-central@5a49a2ff0ee0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1465491
milestone63.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 1465491 Part 1 - Web Replay tests.
devtools/client/debugger/new/test/mochitest/browser.ini
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js
devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html
devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html
devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -295,16 +295,19 @@ support-files =
   examples/simple2.js
   examples/simple3.js
   examples/frames.js
   examples/pause-points.js
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
+  examples/doc_rr_basic.html
+  examples/doc_rr_continuous.html
+  examples/doc_rr_recovery.html
 
 [browser_dbg-asm.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
 skip-if = (os == "win" && ccov) # Bug 1453549
 [browser_dbg-sourcemapped-scopes.js]
 skip-if = ccov || (verify && debug && (os == 'linux')) # Bug 1441545
 [browser_dbg-sourcemapped-stepping.js]
@@ -383,8 +386,36 @@ skip-if = os == 'linux' && !asan # bug 1
 [browser_dbg-sources-named-eval.js]
 [browser_dbg-stepping.js]
 skip-if = debug || (verify && (os == 'win')) || (os == "win" && os_version == "6.1")
 [browser_dbg-tabs.js]
 [browser_dbg-tabs-pretty-print.js]
 [browser_dbg-toggling-tools.js]
 [browser_dbg-wasm-sourcemaps.js]
 skip-if = true
+[browser_dbg_rr_record.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_breakpoints-01.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_breakpoints-02.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_breakpoints-03.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_breakpoints-04.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_breakpoints-05.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_stepping-01.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_stepping-02.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_stepping-03.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_stepping-04.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_recovery-01.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_replay-01.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_replay-02.js]
+skip-if = os != "mac" || debug
+[browser_dbg_rr_replay-03.js]
+skip-if = os != "mac" || debug
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
@@ -0,0 +1,43 @@
+/* -*- 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/ */
+
+// Test basic breakpoint functionality in web replay.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+
+  // Visit a lot of breakpoints so that we are sure we have crossed major
+  // checkpoint boundaries.
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 9);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 8);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 7);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 6);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 7);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 8);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 9);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js
@@ -0,0 +1,29 @@
+/* -*- 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/ */
+
+// Test unhandled divergence while evaluating at a breakpoint with Web Replay.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await checkEvaluateInTopFrame(client, "testStepping2()", undefined);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js
@@ -0,0 +1,29 @@
+/* -*- 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/ */
+
+// Test some issues when stepping around after hitting a breakpoint while recording.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_continuous.html", 19);
+  await resumeToLine(client, 19);
+  await reverseStepOverToLine(client, 18);
+  await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined);
+  await stepInToLine(client, 22);
+  await setBreakpoint(client, "doc_rr_continuous.html", 24);
+  await resumeToLine(client, 24);
+  await setBreakpoint(client, "doc_rr_continuous.html", 22);
+  await rewindToLine(client, 22);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js
@@ -0,0 +1,36 @@
+/* -*- 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/ */
+
+// Test navigating back to earlier breakpoints while recording, then resuming
+// recording.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_continuous.html", 14);
+  await resumeToLine(client, 14);
+  let value = await evaluateInTopFrame(client, "number");
+  await resumeToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value + 1);
+  await rewindToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value);
+  await resumeToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value + 1);
+  await resumeToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value + 2);
+  await resumeToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value + 3);
+  await rewindToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", value + 2);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js
@@ -0,0 +1,30 @@
+/* -*- 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/ */
+
+// Test hitting breakpoints when rewinding past the point where the breakpoint
+// script was created.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  client.rewind();
+  await once(Services.ppmm, "HitRecordingBeginning");
+
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 1);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 2);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js
@@ -0,0 +1,19 @@
+/* -*- 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/ */
+
+// Test basic recording of a tab without any debugging.
+async function test() {
+  waitForExplicitFinish();
+
+  var recordingTab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  await gBrowser.removeTab(recordingTab);
+
+  ok(true, "Finished");
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js
@@ -0,0 +1,28 @@
+/* -*- 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/ */
+
+// Test basic recovery of crashed child processes in web replay.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_recovery.html", 21);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1)", undefined);
+  await stepOverToLine(client, 22);
+  await stepOverToLine(client, 23);
+  await checkEvaluateInTopFrame(client, "SpecialPowers.Cu.recordReplayDirective(/* CrashSoon */ 1); " +
+                                        "SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2)", undefined);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js
@@ -0,0 +1,39 @@
+/* -*- 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/ */
+
+// Basic test for saving a recording and then replaying it in a new tab.
+async function test() {
+  waitForExplicitFinish();
+
+  let recordingFile = newRecordingFile();
+  let recordingTab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
+  ok(tabParent, "Found recording tab parent");
+  ok(tabParent.saveRecording(recordingFile), "Saved recording");
+  await once(Services.ppmm, "SaveRecordingFinished");
+
+  let replayingTab = gBrowser.addTab(null, { replayExecution: recordingFile });
+  gBrowser.selectedTab = replayingTab;
+  await once(Services.ppmm, "HitRecordingEndpoint");
+
+  let toolbox = await attachDebugger(replayingTab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 9);
+  await resumeToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(recordingTab);
+  await gBrowser.removeTab(replayingTab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js
@@ -0,0 +1,49 @@
+/* -*- 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/ */
+
+// Test ending a recording at a breakpoint and then separately replaying to the end.
+async function test() {
+  waitForExplicitFinish();
+
+  let recordingFile = newRecordingFile();
+  let recordingTab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+
+  let toolbox = await attachDebugger(recordingTab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_continuous.html", 14);
+  await resumeToLine(client, 14);
+  await resumeToLine(client, 14);
+  await reverseStepOverToLine(client, 13);
+  let lastNumberValue = await evaluateInTopFrame(client, "number");
+
+  let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
+  ok(tabParent, "Found recording tab parent");
+  ok(tabParent.saveRecording(recordingFile), "Saved recording");
+  await once(Services.ppmm, "SaveRecordingFinished");
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(recordingTab);
+
+  let replayingTab = gBrowser.addTab(null, { replayExecution: recordingFile });
+  gBrowser.selectedTab = replayingTab;
+  await once(Services.ppmm, "HitRecordingEndpoint");
+
+  toolbox = await attachDebugger(replayingTab);
+  client = toolbox.threadClient;
+  await client.interrupt();
+  await checkEvaluateInTopFrame(client, "number", lastNumberValue);
+  await reverseStepOverToLine(client, 13);
+  await setBreakpoint(client, "doc_rr_continuous.html", 14);
+  await rewindToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", lastNumberValue - 1);
+  await resumeToLine(client, 14);
+  await checkEvaluateInTopFrame(client, "number", lastNumberValue);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(replayingTab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js
@@ -0,0 +1,32 @@
+/* -*- 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/ */
+
+// Test for saving a recording and then replaying it in a new tab, with rewinding disabled.
+async function test() {
+  waitForExplicitFinish();
+
+  await pushPref("devtools.recordreplay.enableRewinding", false);
+
+  let recordingFile = newRecordingFile();
+  let recordingTab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
+  ok(tabParent, "Found recording tab parent");
+  ok(tabParent.saveRecording(recordingFile), "Saved recording");
+  await once(Services.ppmm, "SaveRecordingFinished");
+
+  let replayingTab = gBrowser.addTab(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);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js
@@ -0,0 +1,29 @@
+/* -*- 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/ */
+
+// Test basic step-over/back functionality in web replay.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await reverseStepOverToLine(client, 20);
+  await checkEvaluateInTopFrame(client, "number", 9);
+  await checkEvaluateInTopFrameThrows(client, "window.alert(3)");
+  await stepOverToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js
@@ -0,0 +1,30 @@
+/* -*- 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/ */
+
+// Test fixes for some simple stepping bugs.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 22);
+  await rewindToLine(client, 22);
+  await stepInToLine(client, 25);
+  await stepOverToLine(client, 26);
+  await stepOverToLine(client, 27);
+  await reverseStepInToLine(client, 33);
+  await reverseStepOverToLine(client, 32);
+  await reverseStepOutToLine(client, 26);
+  await reverseStepOverToLine(client, 25);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js
@@ -0,0 +1,28 @@
+/* -*- 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/ */
+
+// Test stepping back while recording, then resuming recording.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_continuous.html", 13);
+  await resumeToLine(client, 13);
+  let value = await evaluateInTopFrame(client, "number");
+  await reverseStepOverToLine(client, 12);
+  await checkEvaluateInTopFrame(client, "number", value - 1);
+  await resumeToLine(client, 13);
+  await resumeToLine(client, 13);
+  await checkEvaluateInTopFrame(client, "number", value + 1);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js
@@ -0,0 +1,45 @@
+/* -*- 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/ */
+
+// Stepping past the beginning or end of a frame should act like a step-out.
+async function test() {
+  waitForExplicitFinish();
+
+  let tab = gBrowser.addTab(null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  let toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_basic.html", 21);
+  await rewindToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+  await reverseStepOverToLine(client, 20);
+  await reverseStepOverToLine(client, 12);
+
+  // After reverse-stepping out of the topmost frame we should rewind to the
+  // last breakpoint hit.
+  await reverseStepOverToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 9);
+
+  await stepOverToLine(client, 22);
+  await stepOverToLine(client, 23);
+  // Line 13 seems like it should be the next stepping point, but the column
+  // numbers reported by the JS engine and required by the pause points do not
+  // match, and we don't stop here.
+  //await stepOverToLine(client, 13);
+  await stepOverToLine(client, 17);
+  await stepOverToLine(client, 18);
+
+  // After forward-stepping out of the topmost frame we should run forward to
+  // the next breakpoint hit.
+  await stepOverToLine(client, 21);
+  await checkEvaluateInTopFrame(client, "number", 10);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html
@@ -0,0 +1,38 @@
+<html lang="en" dir="ltr">
+<body>
+<div id="maindiv">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, 1);
+}
+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, 1);
+// Simulate a longer recording by marking major checkpoints whenever possible.
+SpecialPowers.Cu.recordReplayDirective(/* AlwaysMarkMajorCheckpoints */ 4);
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_continuous.html
@@ -0,0 +1,28 @@
+<html lang="en" dir="ltr">
+<body>
+<div id="maindiv">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>
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html
@@ -0,0 +1,26 @@
+<html lang="en" dir="ltr">
+<body>
+<div id="maindiv">Hello World!</div>
+</body>
+<script>
+// this line intentionally left blank
+// this line intentionally left blank
+// this line intentionally left blank
+const cpmm = SpecialPowers.Services.cpmm;
+var number = 0;
+function f() {
+  updateNumber();
+  if (number >= 10) {
+    cpmm.sendAsyncMessage("RecordingFinished");
+    return;
+  }
+  window.setTimeout(f, 1);
+}
+function updateNumber() {
+  number++;
+  document.getElementById("maindiv").innerHTML = "Number: " + number;
+  SpecialPowers.Cu.recordReplayDirective(/* MaybeCrash */ 2);
+}
+window.setTimeout(f, 1);
+</script>
+</html>