Bug 1465491 Part 1 - Web Replay tests.
☠☠ backed out by 3113161a3af1 ☠ ☠
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 09 Aug 2018 15:39:35 +0000
changeset 430870 1ac3e522f55fe1d893089d31e0f650a26e7ce332
parent 430869 dfdd922e68028b57b288fe2ecfac1a0b50ae6c2b
child 430871 8616f91338375095850ced71af2668a7b9ee9c1c
push id34415
push userebalazs@mozilla.com
push dateFri, 10 Aug 2018 09:17:55 +0000
treeherdermozilla-central@d999fb858fb2 [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-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_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
@@ -624,16 +624,18 @@ support-files =
   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]
@@ -714,8 +716,34 @@ skip-if = os == 'linux' && !asan # bug 1
 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_breakpoints-01.js]
 skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_breakpoints-02.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_breakpoints-03.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_breakpoints-04.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_breakpoints-05.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_record.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_stepping-01.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_stepping-02.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_stepping-03.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_stepping-04.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_recovery-01.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_replay-01.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_replay-02.js]
+skip-if = os != "mac" || debug || !nightly_build
+[browser_dbg_rr_replay-03.js]
+skip-if = os != "mac" || debug || !nightly_build
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,31 @@
+/* -*- 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();
+
+  // Rewind to the beginning of the recording.
+  await rewindToLine(client, undefined);
+
+  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_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>