Merge mozilla-central to autoland
authorarthur.iakab <aiakab@mozilla.com>
Thu, 17 Jan 2019 06:22:18 +0200
changeset 511339 370daad5d829ca6017a98368eb70f1d6fd5a376b
parent 511338 5035c0df097405358d3233832538679c058766e4 (current diff)
parent 511326 347b8151960cc293c35ec28aeec85fb74a174c05 (diff)
child 511340 521176ad7ae8c205307c4447c70a4de546c6cac8
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone66.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
Merge mozilla-central to autoland
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_console_warp-01.js
devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.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_error.html
devtools/client/debugger/new/test/mochitest/examples/doc_rr_logs.html
devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html
--- a/.eslintignore
+++ b/.eslintignore
@@ -101,16 +101,17 @@ devtools/client/storage/test/*.html
 !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
 devtools/server/tests/browser/storage-*.html
 !devtools/server/tests/browser/storage-unsecured-iframe.html
 devtools/server/tests/browser/stylesheets-nested-iframes.html
 devtools/client/shared/webpack/shims/test/test_clipboard.html
 devtools/shared/qrcode/tests/mochitest/test_decode.html
 devtools/shared/tests/mochitest/*.html
 devtools/shared/webconsole/test/test_*.html
+devtools/client/webreplay/mochitest/examples/*.html
 
 # Soon to be removed, the new/ directory is explicitly excluded below due to
 # also being an imported repository.
 devtools/client/debugger/**
 
 # Ignore devtools imported repositories
 devtools/client/debugger/new/**
 devtools/client/shared/components/reps/**
--- a/browser/components/aboutconfig/content/aboutconfig.css
+++ b/browser/components/aboutconfig/content/aboutconfig.css
@@ -1,12 +1,28 @@
 /* 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/. */
 
+html {
+  height: 100%;
+}
+
+body.config-background {
+  height: 100%;
+  background-image: url("chrome://browser/content/aboutconfig/background.svg");
+  background-repeat: no-repeat;
+  background-position: center center;
+  background-size: 450px;
+}
+
+body.config-warning {
+  background-image: url("chrome://browser/content/aboutconfig/background.svg#warning");
+}
+
 .title {
   background-image: url("chrome://global/skin/icons/warning.svg");
   -moz-context-properties: fill;
   fill: #fcd100;
 }
 
 #search {
   position: sticky;
--- a/browser/components/aboutconfig/content/aboutconfig.js
+++ b/browser/components/aboutconfig/content/aboutconfig.js
@@ -238,16 +238,17 @@ class PrefRow {
 
   endEdit() {
     this.editing = false;
     this.refreshElement();
     gPrefInEdit = null;
   }
 }
 
+let gPrefObserverRegistered = false;
 let gPrefObserver = {
   observe(subject, topic, data) {
     let pref = gExistingPrefs.get(data) || gDeletedPrefs.get(data);
     if (pref) {
       pref.refreshValue();
       if (!pref.editing) {
         pref.refreshElement();
       }
@@ -261,35 +262,41 @@ let gPrefObserver = {
   },
 };
 
 if (!Preferences.get("browser.aboutConfig.showWarning")) {
   // When showing the filtered preferences directly, remove the warning elements
   // immediately to prevent flickering, but wait to filter the preferences until
   // the value of the textbox has been restored from previous sessions.
   document.addEventListener("DOMContentLoaded", loadPrefs, { once: true });
-  window.addEventListener("load", filterPrefs, { once: true });
+  window.addEventListener("load", () => {
+    if (document.getElementById("search").value) {
+      filterPrefs();
+    }
+  }, { once: true });
 }
 
 function onWarningButtonClick() {
   Services.prefs.setBoolPref("browser.aboutConfig.showWarning",
     document.getElementById("showWarningNextTime").checked);
   loadPrefs();
-  filterPrefs();
 }
 
 function loadPrefs() {
+  document.body.className = "config-background";
   [...document.styleSheets].find(s => s.title == "infop").disabled = true;
 
   document.body.textContent = "";
   let search = document.createElement("input");
   search.type = "text";
   search.id = "search";
   document.l10n.setAttributes(search, "about-config-search");
   document.body.appendChild(search);
+  search.focus();
+
   let prefs = document.createElement("table");
   prefs.id = "prefs";
   document.body.appendChild(prefs);
 
   for (let name of Services.prefs.getChildList("")) {
     new PrefRow(name);
   }
 
@@ -329,21 +336,16 @@ function loadPrefs() {
       pref.save();
     } else {
       // This is "button-reset" or "button-delete".
       pref.editing = false;
       Services.prefs.clearUserPref(pref.name);
       pref.editButton.focus();
     }
   });
-
-  Services.prefs.addObserver("", gPrefObserver);
-  window.addEventListener("unload", () => {
-    Services.prefs.removeObserver("", gPrefObserver);
-  }, { once: true });
 }
 
 function filterPrefs() {
   if (gPrefInEdit) {
     gPrefInEdit.endEdit();
   }
   gDeletedPrefs.clear();
 
@@ -360,16 +362,29 @@ function filterPrefs() {
 
   let prefsElement = document.getElementById("prefs");
   prefsElement.textContent = "";
   let fragment = document.createDocumentFragment();
   for (let pref of prefArray) {
     fragment.appendChild(pref.element);
   }
   prefsElement.appendChild(fragment);
+
+  // We only start observing preference changes after the first search is done,
+  // so that newly added preferences won't appear while the page is still empty.
+  if (!gPrefObserverRegistered) {
+    gPrefObserverRegistered = true;
+    Services.prefs.addObserver("", gPrefObserver);
+    window.addEventListener("unload", () => {
+      Services.prefs.removeObserver("", gPrefObserver);
+    }, { once: true });
+  }
+
+  document.body.classList.toggle("config-warning",
+    location.href.split(":").every(l => gFilterString.includes(l)));
 }
 
 function prefHasDefaultValue(name) {
   try {
     switch (Services.prefs.getPrefType(name)) {
       case Ci.nsIPrefBranch.PREF_STRING:
         gDefaultBranch.getStringPref(name);
         return true;
--- a/browser/components/aboutconfig/content/aboutconfig.notftl
+++ b/browser/components/aboutconfig/content/aboutconfig.notftl
@@ -6,16 +6,16 @@
 about-config-warning-title = Here be dragons!
 about-config-warning-text = Changing these advanced settings can be harmful to the stability, security, and performance of this application. You should only continue if you are sure of what you are doing.
 about-config-warning-checkbox = Annoy me again, please!
 about-config-warning-button = I accept the risk
 
 about-config-title = about:config
 
 about-config-search =
-    .placeholder = Search
+    .placeholder = Search or press ESC to show all
 
 about-config-pref-add = Add
 about-config-pref-toggle = Toggle
 about-config-pref-edit = Edit
 about-config-pref-save = Save
 about-config-pref-reset = Reset
 about-config-pref-delete = Delete
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutconfig/content/background.svg
@@ -0,0 +1,65 @@
+<!-- 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="300" height="300" viewBox="0 0 300 300">
+  <defs>
+    <linearGradient id="a" x1="-300.021" y1="-272.736" x2="547.138" y2="574.423" gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#ccfbff"/>
+      <stop offset="1" stop-color="#c9e4ff"/>
+    </linearGradient>
+    <linearGradient id="b" x1="-18.672" y1="23.78" x2="279.805" y2="322.256" gradientUnits="userSpaceOnUse">
+      <stop offset="0" stop-color="#00c8d7"/>
+      <stop offset="1" stop-color="#0a84ff"/>
+    </linearGradient>
+  </defs>
+  <ellipse cx="143.566" cy="245.472" rx="55.042" ry="8.362" fill="#eaeaee"/>
+  <path d="M226.699 132.246a.5.5 0 0 0-.5-.5h-20.26c.373.357.727.688 1.065 1h19.2a.5.5 0 0 0 .496-.5zm10.246 5.161h-25.78c.344.64.632 1.31.862 2h24.918c1.333 0 1.333-2 0-2zM111.457 174.8h-78.3c-1.333 0-1.333 2 0 2h78.3c1.333 0 1.333-2 0-2zM102.31 121.507H60.818a1 1 0 0 0 0 2h41.492a1 1 0 1 0 0-2zM70.336 117.6H82.1a.5.5 0 0 0 0-1H70.336a.5.5 0 0 0 0 1z" fill="#eaeaee"/>
+  <path d="M218.021 73.275c-13.46 6.187-25.033 17.553-33.015 29.334-.68-4.923-2.033-13.595-8.695-12.824-5.526 1.2-3.27 9.415-5.625 11.703-4.842-3.222-11.04-3.006-16.137-.425-2.732.092-7.255 3.603-6.785-1.047.455-4.266-3.784-9.708-8.059-6.328-4.3 3.769-3.422 10.22-4.266 15.054-3.838 1.986-6.739 5.71-11.27 5.348-1.964 1.394-2.775 1.087-3.837-1.324-9.531-13.608-24.785-23.196-41.014-26.225-5.232.666-3.723 8.624 1.073 8.23 7.746 1.9 10.969 10.638 14.91 16.823 3.016 6.266 7.47 16.605.178 21.482-4.169 2.17-.923 7.235 2.712 7.729 5.337 2.916 8.288 10.281 3.952 15.263-.747 4.28 6.719 5.59 3.394 10.577-2.266 2.292-6.149 4.981-4.713 9.013 1.985 2.602 1.082 6.83-2.762 6.905-4.03 1.212-5.327 6.28-3.417 9.771-2.465 1.512-7.492.626-8.856 4.639-.38 2.898-1.618 4.742-3.941 6.572-4.48 4.84-1.47 12.293 4.148 14.601 9.956 5.455 21.33 8.291 32.678 8.225 2.471 7.876 4.71 17.566 12.271 22.092 4.658 2.01 8.789-2.98 8.069-7.469-.125-2.368-.248-4.525 2.76-3.004 9.505 1.6 19.28 1.35 28.747-.394-.64 4.452.22 11.636 6.188 11.267 7.318-1.639 9.578-10.346 12.232-16.377 3.496-9.657 5.749-20.444 5.971-30.332.943 3.384 3.844 7.672 7.613 6.53 2.691 5.338 10.62 2.783 14.166-.508 4.856-4.26 5.788-12.216 1.813-17.37.548-.777 4.62-10.155 4.62-10.155s1.54-.757 2.972-.82c9.754-2.5 12.767-17.223 4.248-22.84-2.191-.397-3.442.316-4.371-2.124-2.644-2.192-7.247-.494-9.47-.094 3.056-11.492-5.924-20.934-12.901-28.78-5.102-3.089-2.09-10.463 2.46-12.481 2.833-3.233-1.414-5.393-2.777-7.698-.522-8.351 6.457-15.021 10.904-21.205 3.198-2.27 11.054-4.663 7.518-9.896-.85-1.093-2.323-1.662-3.686-1.438zm-8.92 96.815c-.084 1.76 3.482 5.207-.064 3.676-2.368-.777-4.42 1-6.562 1.158 2.432-.94 5.144-3.992 6.627-4.834zm-13.708 16.81c1.247.486 7.26-.66 3.76 1.233a13.365 13.365 0 0 0-4.178 6.224c.424-2.175-1.038-6.573.418-7.457z" fill="#fff"/>
+  <path d="M265.193 79.359c-.096-2.982-5.21-2.556-5 .01 0 0 .027.723.041 13.895.007 6.586.011 13.187.006 18.166-.002 1.558-.006 2.593-.01 3.783-3.042.012-3.186.018-6.386.027-7.306.021-14.628.034-20.145.018a657.127 657.127 0 0 1-6.676-.045 94.266 94.266 0 0 1-1.795-.04c-2.06-.02-2.874.38-3.568.667-1.279.776-1.586 1.486-1.816 1.912-.461.851-.451 1.112-.506 1.357-.11.49-.119.693-.143.938-.048.49-.074.975-.1 1.592a195.374 195.374 0 0 0-.117 4.914c-.058 4.02-.084 9.365-.091 14.72-.014 10.71.043 21.452.043 21.452a5 5 0 0 0 5 4.974h40.237v41.179c1.434 5.083 10.044 3.985 10 0v-46.179a5 5 0 0 0-5-5H228.92c-.015-3.379-.044-8.226-.033-16.412.007-5.333.032-10.656.09-14.588.008-.623.024-.925.035-1.472 1.42.012 2.753.025 4.66.03 5.554.017 12.886.004 20.201-.017 14.63-.042 29.186-.125 29.186-.125 3.589.28 3.547-10.94-.057-10l-17.772.076c.004-1.186.008-2.213.01-3.757.005-4.986.001-11.588-.006-18.176.024-4.637-.04-9.297-.04-13.9" fill="#fff" stroke="#b1b1b3" stroke-width="2" stroke-linejoin="round"/>
+  <path d="M232.834 192.008a3.05 3.05 0 0 0-3.05 3.05v47.864c.116 2.623 6.083 2.556 6.099 0v-44.814h49.533c2.53-.217 2.743-5.921 0-6.1z" fill="#fff" stroke="#b1b1b3" stroke-width="2" stroke-linejoin="round"/>
+  <path d="M121.628 123.766c-.077-8.603-20.782-27.204-35.458-31.184 7.244 7.575 24.477 31.019 11.502 43.437 3.614 2.397 14.03 8.777 7.776 20.78 5.93 3.663 4.415 10.695 1.91 13.484 0 0 3.491-1.558 3.042-2.57 5.129 1.181-.927-26.044 11.228-43.947z" fill="#c069ff"/>
+  <path d="M79.732 89.336a1 1 0 0 0-.212 1.97 83.13 83.13 0 0 1 6.166 1.882c1.036.918 2.282 2.23 3.62 3.845 2.806 3.385 5.997 8.056 8.505 13.031 2.508 4.976 4.327 10.26 4.435 14.84.106 4.509-1.381 8.29-5.502 10.672a.5.5 0 0 0-.289.18c-.075.054-.101.103-.08.144a.5.5 0 0 0 .281.63c4.242 1.802 7.691 5.028 9.38 8.68 1.68 3.64 1.652 7.668-1.02 11.311a.5.5 0 0 0-.07.102.5.5 0 0 0 .347.748c2.292 1.164 3.416 3.117 3.64 5.4.23 2.325-.518 4.981-2.048 7.292a.5.5 0 1 0 .834.552c1.642-2.478 2.467-5.335 2.21-7.941-.219-2.231-1.274-4.277-3.25-5.637 1.18-.184 2.116.088 2.944.572 1.074.628 1.94 1.645 2.697 2.5l.75-.664c-.743-.84-1.669-1.955-2.941-2.699a5.44 5.44 0 0 0-2.186-.726 5.659 5.659 0 0 0-1.433.04c2.18-3.664 2.079-7.708.433-11.269-1.63-3.53-4.744-6.605-8.603-8.566 3.105-.343 6.038.59 8.545 1.98 2.813 1.56 5.07 3.686 6.344 5.11a.47.47 0 1 0 .699-.627c-1.331-1.487-3.644-3.671-6.586-5.303-2.207-1.224-4.777-2.137-7.531-2.176-.254-.004-.513.02-.77.031 2.591-1.597 5.177-6.109 5.148-10.16-.192-5.365-1.365-9.146-3.634-14.326-2.387-5.448-6.79-11.435-9.334-14.639-.34-.427-.703-.826-1.073-1.2 12.847 5.42 23.913 14.517 31.084 29.343a1 1 0 1 0 1.801-.871c-9.355-19.342-25.314-29.358-43.004-34.014a1 1 0 0 0-.297-.037z" fill="#8000d7"/>
+  <path d="M172.29 55.197a2.75 2.75 0 0 0-2.75 2.75v17.46h-23.155a2.75 2.75 0 1 0 0 5.5h25.904a2.75 2.75 0 0 0 2.75-2.75v-17.46h24.635V66.6a2.75 2.75 0 0 0 2.75 2.75h22.588v8.578a2.75 2.75 0 0 0 2.75 2.75h23.48a2.75 2.75 0 1 0 0-5.5H230.51V66.6a2.75 2.75 0 0 0-2.748-2.75h-22.588v-5.903a2.75 2.75 0 0 0-2.75-2.75zM71.313 48.953a1.5 1.5 0 0 0-1.5 1.5v9.524h-12.63a1.5 1.5 0 1 0 0 3h14.13a1.5 1.5 0 0 0 1.5-1.5v-9.524h13.439v3.219a1.5 1.5 0 0 0 1.5 1.5h12.32v4.68a1.5 1.5 0 0 0 1.5 1.5h12.809a1.5 1.5 0 1 0 0-3h-11.309v-4.68a1.5 1.5 0 0 0-1.5-1.5h-12.32v-3.219a1.5 1.5 0 0 0-1.5-1.5z" fill="#fff"/>
+  <path d="M206.885 62.973l.045-.1c-.058.027-.063.059-.045.1z" fill="#fff"/>
+  <path d="M77.937 214.941H39.95a1 1 0 1 1 0-2h37.987a1 1 0 1 1 0 2zM224.1 214.941h-26.982c-1.333 0-1.333-2 0-2H224.1c1.333 0 1.333 2 0 2z" fill="#eaeaee"/>
+  <path d="M211.476 145.947c-1.092-4.252-4.261-8.341-6.9-11.168a78.847 78.847 0 0 0-8.482-9.533.649.649 0 0 0-.907.018c-10.173-10.009-23.029-19.987-30.783-20.521-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.561a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.744a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842-.592 3.571-1.37 7.11-2.33 10.6l-6 3.638a1.452 1.452 0 0 0-.685 1.835l3.021 4.978a54.736 54.736 0 0 1-2.883 5.777l-5.979 1.836a1.305 1.305 0 0 0-.864 1.63l1.394 4.541a51.393 51.393 0 0 1-4.691 5.189l-3.93-.31a1.3 1.3 0 0 0-1.4 1.2l-.3 3.752c-3.258 2.357-5.892 3.822-5.892 6.703 0 4.144 21.362 13.922 38.462 11.896.685 3.3.66 5.941 1.716 9.143 2.852 8.125 8.097 15.38 9.523 14.219 2.714-5.141 1.497-6.442 1.348-12.618.446.174 6.79 2.38 20.703 2.38 6.707-.148 12.814-.78 19.314-2.437-.4 5.844.161 11.367.872 11.945 1.426 1.16 5.98-5.026 8.83-13.152.358-1.018.756-2.157 1.087-3.375 1.276-3.253 3.69-12.846 4.032-26.543.17-7.04.852-12.792.248-19.808 4.287 2.848-1.206.006 2.96 1.027 0 0 9.527-3.556 9.037-3.804-13.682-.677-12.814-4.106-12.944-5.062 1.528.06 2.89-.453 4.409-.639 6.147-.82 9.947-2.869 13.84-7.786 2.797-3.536 5.821-12.678 4.184-19.057z" fill="url(#a)"/>
+  <path d="M183.818 112.7l14.719 13.82c-2.947-6.93-.827-11.87 4.606-15.506-9.884-8.298 1.556-21.171 11.91-32.472-10.194 8.935-19.931 12.736-31.235 34.157z" fill="#c069ff"/>
+  <path d="M193.582 124.685c.033.013.067.03.1.045-9.986-9.641-22.2-18.942-29.683-19.457-4.534-.313-10.392.808-16.48 3.1l-6.645-1.817-1.521 5.56a63.823 63.823 0 0 0-10.4 7l-3.884-.09-.087 3.745a40.534 40.534 0 0 0-7.123 9.675l-1.083.338.36 1.153c-2.227 5.146-3.144 14.255-3.147 24.842a109.939 109.939 0 0 1-.921 4.824c.168-.72.327-1.43.475-2.127.06 3.039.177 6.023.332 8.836-1.014 1.2-4.408 5.548-2.546 8.155a10.876 10.876 0 0 0 3.445 2.858c.069.589.138 1.155.209 1.68 1.015 7.545 4.11 29 6.408 38.3.207-.02.417-.035.623-.06.685 3.3 1.558 6.56 2.615 9.76 2.852 8.126 6.074 14.145 7.5 12.984.976-2.145.594-15.41.62-12.742l-.224-8.867c8.633 3.978 19.523 5.633 28.87 3.9 15.65-3.042 21.467-14.704 22.172-22.606 1.194-13.372.822-17.312-1.415-19.893-5.48-6.322-2.51-3.27-7.61-8.044-3.428-3.21-10.52-6.355-11.427-6.7 7.99 2.938 16.156 4.758 24.659 5.177l.116-.951c-10.706-.61-24.216-5.348-24.216-5.348s23.783 4.316 33.476-.761c6.847-3.587 11.086-13.37 9.456-21.846-1.731-9.023-13.024-20.623-13.024-20.623z" fill="#f9f9fa"/>
+  <path d="M149.523 146.54l-5.537-1.957a1 1 0 0 0-1.219 1.406l1.99 3.81a1 1 0 0 0 1.349.423l3.547-1.853a1 1 0 0 0-.13-1.829z" fill="#fff"/>
+  <path d="M163.956 131.115c3.79 2.479 3.296 9.59.086 11.618M196.298 131.267c3.79 2.479 3.297 9.59.087 11.618" fill="none" stroke="#05a7eb" stroke-linecap="round"/>
+  <path d="M218.377 76.072a1.026 1.026 0 0 0-.457.133c-13.792 7.547-26.515 18.453-35.434 36.484a1.026 1.026 0 1 0 1.838.909c6.054-12.24 13.847-21.066 22.572-27.8-2.6 3.238-5.127 6.705-6.947 10.657-1.309 2.844-1.972 6.42-1.78 8.965.169 2.212 1.84 4.23 3.694 5.404-3.194.9-5.238 2.157-6.697 3.676-1.661 1.73-2.55 3.752-3.625 5.88a.5.5 0 1 0 .893.452c1.089-2.155 1.941-4.063 3.455-5.639 1.07-1.114 2.493-2.082 4.601-2.867-1.961 1.892-3.052 4.516-3.494 7-.303 1.705-.312 3.349-.084 4.692.228 1.343.646 2.42 1.477 2.927a.5.5 0 1 0 .521-.853c-.349-.213-.807-1.036-1.012-2.24-.204-1.205-.201-2.755.082-4.352.562-3.159 2.21-6.464 5.383-7.979a.472.472 0 0 0 .233-.117.5.5 0 0 0-.254-.947.5.5 0 0 0-.008 0c-2.222-1.478-3.205-3.353-3.38-5.566-.182-2.28.534-4.926 1.802-7.68 2.404-5.222 6.738-10.76 10.299-15.086a97.208 97.208 0 0 1 6.85-4.121 1.026 1.026 0 0 0-.528-1.932z" fill="#8000d7"/>
+  <path d="M173.593 106.182c2.029-2.682 2.088-10.651 2.922-12.134 1.89-3.366 6.265 9.2 3.707 17.078" fill="#c069ff"/>
+  <path d="M177.445 92.5a1.692 1.692 0 0 0-1.107.268c-.31.208-.525.486-.695.79-.393.7-.48 1.458-.64 2.481-.158 1.023-.305 2.24-.49 3.48-.367 2.48-.988 5.096-1.718 6.061l1.596 1.205c1.298-1.717 1.724-4.427 2.101-6.973.189-1.272.338-2.494.488-3.466.14-.899.326-1.531.375-1.672.06.048.01 0 .104.123.479.623 1.129 2.059 1.613 3.836.97 3.554 1.378 8.552.2 12.183l1.902.62c1.379-4.248.88-9.471-.172-13.329-.526-1.928-1.164-3.497-1.957-4.529-.397-.516-.85-.984-1.6-1.078z" fill="#8000d7"/>
+  <path d="M221.906 157.572a9.396 9.396 0 0 0-9.396 9.397 9.396 9.396 0 0 0 2.795 6.676l-2.451 5.197c-.43-.32-.912-.634-1.493-.928-1.603-.813-4.89-1.04-6.95.486-6.502-.322-8.898-.132-12.054-1.263-1.773.57-3.116 2.098-1.742 4.775 1.63.606 6.381 1.33 12.326 1.525 1.407 2.274 3.193 3.62 5.819 4.09l-.62 1.31a9.396 9.396 0 0 0-.615-.03 9.396 9.396 0 0 0-9.396 9.396 9.396 9.396 0 0 0 2.644 6.52l3.413-7.239 5.322 2.952-3.328 7.058a9.396 9.396 0 0 0 1.345.106 9.396 9.396 0 0 0 9.397-9.397 9.396 9.396 0 0 0-3.17-7.023l7.02-14.89a9.396 9.396 0 0 0 1.134.075 9.396 9.396 0 0 0 9.397-9.396 9.396 9.396 0 0 0-2.647-6.522l-3.056 6.485-5.323-2.952 2.971-6.302a9.396 9.396 0 0 0-1.342-.106z" fill="#f9f9fa"/>
+  <path d="M208.157 203.946c5.44.185 8.541-7.579 2.86-11.917l9.057-19.545c2.804.734 5.471.455 7.387-1.563 2.742-2.888 3.577-5.587 3.577-5.587-1.299 5.435-.962 12.2-10.328 10.964l-6.833 15.095c7.223 8.399-.664 17.012-6.991 15.73z" fill="#caeeff"/>
+  <rect width="5.45" height="5.635" x="270.462" y="42.458" ry=".115" transform="matrix(.8939 .44828 -.44287 .89659 0 0)" fill="#f0f0f0" stroke="#b1b1b3" stroke-linecap="round" stroke-linejoin="round"/>
+  <path d="M155.934 155.646a18.515 18.515 0 0 0-4.51-8.323c-5.144-5.145-12.507-4.867-12.817-4.857-1.333.06-1.244 2.06.089 2 1.06-.008 2.117.086 3.159.279a17.98 17.98 0 0 0 1.1 3.47c.213.49.755 1.526 1.41 2.021.393.297.904.493 1.397.483 1.596-.032 3.142-.8 4.458-1.746a16.947 16.947 0 0 1 3.75 7.1c.28 1.304 2.236.884 1.956-.42zm-10.821-6.082c-1.027-.642-1.8-3.029-2.178-4.592a15.045 15.045 0 0 1 6.547 3.285c-1.517.943-3.507 1.843-4.37 1.307zm48.127-15.875a1.41 1.41 0 0 0-1.37 1.41v3.163a1.412 1.412 0 0 0 1.41 1.408 1.412 1.412 0 0 0 1.41-1.41v-3.16a1.41 1.41 0 0 0-1.45-1.41zm-32.367.065a1.41 1.41 0 0 0-1.37 1.41v3.162a1.412 1.412 0 0 0 1.41 1.408 1.412 1.412 0 0 0 1.41-1.41v-3.16a1.41 1.41 0 0 0-1.45-1.41zm2.045-30.852a45.119 45.119 0 0 0-15.026 3.055l-6.025-1.646a2 2 0 0 0-2.457 1.4l-1.301 4.744a65.633 65.633 0 0 0-9.39 6.332l-3.141-.072h-.047a2 2 0 0 0-2 1.953l-.07 2.965a42.572 42.572 0 0 0-6.522 8.836l-.3.093a2 2 0 0 0-1.311 2.506l.144.465c-1.993 5.12-3.01 13.443-3.023 24.766a102.593 102.593 0 0 1-2.05 9.47L105.086 171a3.309 3.309 0 0 0-1.113 4.537l2.346 3.871a56.336 56.336 0 0 1-2.102 4.21l-5.215 1.599a3.309 3.309 0 0 0-2.189 4.129l1.082 3.523a49.794 49.794 0 0 1-3.217 3.555l-3.023-.238a3.318 3.318 0 0 0-3.553 3.033l-.225 2.83c-2.965 2.13-5.298 3.833-5.298 7.5 0 2.65 2.833 5.028 6.949 6.945.304.141.898.451.898.451s2.579 1.19 3.9 1.71a65.842 65.842 0 0 0 23.126 4.571c1.177 0 2.337-.043 3.463-.127a80.825 80.825 0 0 0 2.29 8.266c.517 1.469 5.172 14.354 9.76 14.354a2.627 2.627 0 0 0 1.678-.596c.831-.677 1.5-1.222 1.354-11.535a76.69 76.69 0 0 0 19.884 2.308 86.392 86.392 0 0 0 17.71-2.168c-.586 10.114.15 10.71.99 11.395a2.628 2.628 0 0 0 1.675.596c4.572 0 9.227-12.885 9.743-14.354.347-.989.7-2.137 1.046-3.412 1.345-3.458 4.547-14.007 4.747-26.611.098-6.234.25-8.756-.43-18.153.42.09.843.171 1.27.239 1.96.308 3.963.403 5.658.462 1.694.06 3.138.104 3.697.213.24.047.488.006.701-.113.686 1.204 1.458 2.01 2.34 2.559.81.505 1.717.819 2.685 1.14l-.09.19-.023-.002a.86.86 0 0 0-.017 0c-5.692 0-10.327 4.634-10.327 10.326v.002a10.325 10.325 0 0 0 2.907 7.164.93.93 0 0 0 1.508-.25l2.988-6.342 3.695 2.049-2.957 6.271a.93.93 0 0 0 .701 1.315 10.33 10.33 0 0 0 1.485.117c5.692 0 10.326-4.634 10.326-10.326v-.002c-.004-2.693-1.17-5.19-3.033-7.106l6.523-13.837c.186.01.372.035.559.037a.6.6 0 0 0 .006 0c5.691 0 10.326-4.635 10.326-10.327v-.002a10.324 10.324 0 0 0-2.908-7.166.93.93 0 0 0-1.508.25l-2.633 5.588-3.695-2.048 2.6-5.516a.93.93 0 0 0-.702-1.315c-.448-.067-.902-.099-1.355-.107a.891.891 0 0 0-.115-.01h-.004a.6.6 0 0 0-.006 0c-5.692 0-10.327 4.634-10.327 10.326v.002c.003 2.512 1.021 4.864 2.668 6.738l-1.97 4.184c-1.944-.918-3.488-1.4-4.827-1.398-1.276.002-2.334.522-3.242 1.382-3.722-.25-7.387-.197-10.033-.64-1.346-.226-2.42-.585-3.111-1.112-.533-.405-.863-.891-.983-1.584.057-.002 2.353-.075 3.012-.162 6.749-.9 10.822-3.211 15.031-8.527 3.282-4.146 7.092-13.147 5.287-20.18-1.2-4.662-4.607-9.055-7.29-11.947A80.77 80.77 0 0 0 197.52 124a2.62 2.62 0 0 0-1.652-.712 133.306 133.306 0 0 0-15.776-13.264c-6.315-4.414-11.527-6.79-15.492-7.065a24.82 24.82 0 0 0-1.683-.056zm-.002 2.006c.533 0 1.048.018 1.544.053 7.754.535 20.61 10.512 30.784 20.521a.655.655 0 0 1 .908-.017 78.847 78.847 0 0 1 8.482 9.535c2.636 2.827 5.806 6.916 6.9 11.168 1.638 6.379-2.116 14.9-4.917 18.441-3.893 4.918-7.582 6.966-13.729 7.785-.7.093-1.43.142-2.178.172a.99.99 0 0 0-.353-.076 73.652 73.652 0 0 1-26.102-5.54c-1.214-.551-2.04 1.271-.826 1.821a74.46 74.46 0 0 0 25.115 5.664c.17.045.358.055.553.06.137 1.036.638 1.87 1.399 2.45.91.694 2.137 1.068 3.566 1.307 2.858.479 6.578.4 10.309.662.151.01.3-.047.406-.156.811-.847 1.612-1.233 2.732-1.235 1.12-.002 2.584.43 4.541 1.37 1.849.886 2.323 2.29 2.4 3.562.06.979-.133 1.454-.234 1.797l-3.203-1.258c-.787-.319-1.049-.097-1.232.309l-1.617 3.43c-.992-.327-1.895-.622-2.594-1.057-.919-.573-1.679-1.39-2.412-2.994a.514.514 0 0 0-.145-.198s0-.003-.002-.004a1.03 1.03 0 0 0-.63-.398c-.984-.192-2.35-.192-4.022-.25-1.673-.059-3.597-.153-5.408-.438a19.489 19.489 0 0 1-1.764-.367c-.042-.085-.067-.085-.08-.021-1.086-.281-2.077-.646-2.844-1.127-1.185-.743-1.875-1.637-2.006-3.076a1.03 1.03 0 1 0-2.05.187c.188 2.083 1.391 3.648 2.962 4.633.683.428 1.441.754 2.233 1.025.264 3.306.576 9.202.447 15.512-.031 1.047-.027 2.078-.053 3.143-.342 13.7-3.373 22.782-4.648 26.035a48.73 48.73 0 0 1-1.031 3.377c-2.611 7.438-6.178 13.017-7.856 13.017a.637.637 0 0 1-.414-.146c-.669-.545-.51-5.867-.152-11.365a19.327 19.327 0 0 0 5.898-3.618c.468-.478-.25-1.179-.717-.7a18.885 18.885 0 0 1-5.593 3.4 87.37 87.37 0 0 1-19.4 2.607c-11.282 0-18.4-1.891-20.923-2.7l-.26-6.109c-.027-.667-1.03-.624-1 .043l.243 5.713c.149 6.176.112 12.182-.563 12.73a.64.64 0 0 1-.414.147c-1.68 0-5.262-5.58-7.873-13.017a88.88 88.88 0 0 1-2.613-9.762h-.006c-2.551-10.248-4.635-21.97-4.656-22.088-.112-.664-1.11-.486-.985.176.021.118 2.091 11.756 4.633 22a43.72 43.72 0 0 1-4.014.2c-9.93-.827-19.4-2.68-26.99-6.509-3.649-1.84-5.879-3.8-5.879-5.166 0-2.882 1.96-4.065 5.217-6.422l.3-3.752a1.306 1.306 0 0 1 1.294-1.199h.11l2.534.2a15.544 15.544 0 0 1-1.921 1.462c-.56.364-.015 1.203.544.84a15.693 15.693 0 0 0 2.715-2.201h.059a51.396 51.396 0 0 0 4.69-5.188l-1.393-4.54a1.305 1.305 0 0 1 .863-1.631l4.352-1.336-1.57 2.34c-.373.553.457 1.111.83.558l2.255-3.363.112-.033a59.576 59.576 0 0 0 3-6.034l-2.883-4.76a1.305 1.305 0 0 1 .437-1.79l4.594-2.786-.9 2.5c-.226.628.716.966.941.338l1.316-3.664.051-.03c.038-.138.069-.271.106-.407l.078-.219a.498.498 0 0 0 .027-.183 105.18 105.18 0 0 0 2.12-9.79c0-9.717.778-18.179 2.626-23.484l.684 1.71c.228.66 1.218.264.927-.372l-1.144-2.861-.307-.987 1.082-.338a39.766 39.766 0 0 1 6.223-8.724c.042.578.123 1.152.242 1.719a.5.5 0 0 0 .95-.317 24.71 24.71 0 0 1-.288-2.482l.084-3.614 3.883.09a63.833 63.833 0 0 1 10.4-7l1.522-5.56 5.139 1.404-2.528 1.117c-.642.259-.212 1.219.409.912l3.7-1.658a44.04 44.04 0 0 1 14.85-3.125zm58.964 53.61l-2.386 5.066a.93.93 0 0 0 .388 1.209l5.323 2.951a.93.93 0 0 0 1.29-.416l2.356-4.996c.933 1.385 1.575 2.946 1.578 4.639a8.452 8.452 0 0 1-8.465 8.465 8.364 8.364 0 0 1-1.017-.069.93.93 0 0 0-.96.526l-7.019 14.89a.93.93 0 0 0 .225 1.092 8.467 8.467 0 0 1 2.855 6.33c0 4.66-3.741 8.406-8.39 8.45l2.746-5.823a.93.93 0 0 0-.389-1.209l-5.322-2.951a.93.93 0 0 0-1.291.416l-2.711 5.75c-.932-1.384-1.574-2.946-1.576-4.637a8.452 8.452 0 0 1 8.463-8.465c.18.004.36.013.54.028a.93.93 0 0 0 .919-.53l2.351-4.986 3.055 1.197a.515.515 0 0 0 .672-.303s.455-1.237.363-2.75c-.068-1.122-.478-2.439-1.576-3.472l2.299-4.88a.93.93 0 0 0-.188-1.058 8.465 8.465 0 0 1-2.517-6.013v-.002c0-4.658 3.738-8.403 8.384-8.45z" fill="url(#b)"/>
+  <rect transform="rotate(-26.15)" ry=".112" y="220.851" x="180.031" height="4.719" width="4.607" fill="#f9f9fa" stroke="#b1b1b3" stroke-linejoin="round"/>
+  <rect width="20.351" height="26.033" x="233.574" y="129.019" ry=".11" fill="#f9f9fa" stroke="#b1b1b3" stroke-linejoin="round"/>
+  <rect width="2.066" height="2.066" x="235.964" y="140.625" ry=".112" fill="none" stroke="#b1b1b3" stroke-linejoin="round"/>
+  <path d="M191.077 187.385c-9.115-1.522-13.226-7.74-13.446-15.029 2.528.296 1.335.255 2.856.59.59 6.201 3.628 9.661 10.317 11.495 1.63.246 1.31 3.184.273 2.944zm-71.753-59.293c-1.878-1.372.928-3.98 1.573-3.595 7.914 4.718 11.287 34.094 2.717 53.785l-3.027-1.269c7.532-15.722 4.568-44.66-1.263-48.92zm-13.26 51.634c12.251 4.52 11.736 1.525 11.736 1.525l2.985 1.672c-2.402 3.843-8.005 2.87-16.064-.125-.9-.932.485-3.435 1.342-3.072zm49.223-75.062c-6.193 3.814-11.15 12.857-13.37 18.988-2.222 6.132 7.922 8.273 8.426 3.259.738-7.347 5.954-20.055 15.73-21.798 2.906-.87.193-2.13-1.285-2.498-2.393-.594-5.586-.362-9.501 2.049z" fill="#ebd0ff" stroke="#c069ff"/>
+  <path d="M144.492 118.43c-8.93-10.557-3.06-23.89-1.685-21.686 1.359 2.177.032 11.412 7.753 14.832 2.859 1.647-2.589 10.254-6.068 6.853z" fill="#c069ff"/>
+  <path d="M142.678 95.523c-.436-.078-.823.07-1.096.243-.547.347-.878.84-1.225 1.472-.692 1.266-1.29 3.147-1.564 5.446-.55 4.597.267 10.873 4.936 16.392l.03.035.034.034a3.678 3.678 0 0 0 3.865.818c1.225-.454 2.244-1.392 3.047-2.47.803-1.08 1.389-2.307 1.588-3.51.1-.602.104-1.206-.074-1.792a2.596 2.596 0 0 0-1.16-1.482l-.045-.025-.05-.022c-3.535-1.566-4.937-4.369-5.7-7.226-.382-1.43-.58-2.852-.77-4.088-.189-1.237-.291-2.258-.838-3.133-.188-.302-.542-.614-.978-.692zm-.518 2.608c.121.436.239.743.358 1.52.185 1.214.392 2.72.814 4.3.844 3.16 2.638 6.686 6.822 8.54l-.093-.05c.134.078.188.145.246.332.057.188.077.5.013.883-.127.768-.582 1.789-1.218 2.643-.636.854-1.453 1.534-2.139 1.789-.674.25-1.132.224-1.74-.356-4.224-5.025-4.94-10.669-4.446-14.812.249-2.078.818-3.779 1.334-4.723.024-.042.026-.027.05-.066z" fill="#8000d7"/>
+  <path d="M163.82 166.848c-.955-.013-1.453 1.442-.39 1.923a74.46 74.46 0 0 0 25.115 5.665c.169.044.358.054.553.058.136 1.037.637 1.872 1.398 2.451.694.529 1.572.87 2.582 1.11a20.895 10.408 0 0 0 .875-.875c-1.21-.228-2.192-.567-2.832-1.055-.532-.406-.863-.892-.982-1.584.057-.002 2.353-.075 3.011-.162a33.102 33.102 0 0 0 2.514-.432 20.895 10.408 0 0 0 .1-.986 20.895 10.408 0 0 0-.135-1.072c-.865.199-1.762.375-2.74.506-.7.093-1.431.141-2.178.171a.99.99 0 0 0-.354-.076 73.652 73.652 0 0 1-26.101-5.54c-.152-.07-.3-.1-.436-.102z" fill="url(#b)"/>
+  <path d="M254.555 132.044l-.08 4.767M254.555 147.537l-.08 4.767" fill="none" stroke="#b1b1b3" stroke-width="2" stroke-linecap="round"/>
+  <path d="M267.208 86.121c-5.415-2.471-11.865-.09-14.348 5.32-2.483 5.41-.088 11.863 5.321 14.346 5.41 2.483 12.192.23 14.347-5.318 2.453-6.314.089-11.879-5.32-14.348zm-.55 4.284a6.775 6.775 0 0 1 2.706 6.961l-4.844-2.224zm-2.766-1.162l-2.097 4.649-4.507-2.069a6.766 6.766 0 0 1 6.604-2.58zm-7.855 5.307l4.523 2.076-2.106 4.666a6.763 6.763 0 0 1-2.417-6.742zm7.249 3.327l4.828 2.216a6.776 6.776 0 0 1-6.96 2.509z" stroke="#b7b7c5" stroke-linejoin="round" fill="#f9f9fa"/>
+  <path d="M110.84 175.074c-.485.077-.92.473-.836 1.115.02.138.568 3.4 4.738 5.122 3.645 1.5 7.452 3.176 7.488 3.19l.35.157c-.024 1.68.275 3.343.664 4.967.078.325.17.654.332.947.654 1.183 1.374 2.405 2.528 3.172.82.557 1.782.87 2.773.9a4.77 4.77 0 0 0 3.578-1.982c.136-.16.053-.57.053-.57l-2.01-3.545s2.05-.3 2.887-.385a.5.5 0 0 0 .447-.484c.005-.2.1-4.932-2.951-6.69-1.677-.968-11.4-4.872-11.819-5.037-.62-.247-.99.682-.37.93.1.04 10.073 4.041 11.689 4.974 2.05 1.183 2.387 4.224 2.441 5.36l-3.236.334a.5.5 0 0 0-.383.744l2.272 3.988c-1.076 1.186-2.507 2.067-4.832.615-3.425-2.138-3.07-8.562-3.02-9.261 0-.022.005-.045 0-.067v-.008a.5.5 0 0 0-.05-.265.985.985 0 0 0-.55-.631c-.037-.016-3.857-1.698-7.52-3.203-3.061-1.261-3.5-3.464-3.525-3.584-.119-.637-.654-.88-1.138-.803z" fill="url(#b)"/>
+  <g id="warning" fill="#f9f9fa" stroke="#b1b1b3" stroke-linejoin="round">
+    <style>#warning:not(:target) { display: none; }</style>
+    <rect width="20.351" height="26.033" x="233.574" y="129.019" ry=".11"/>
+    <rect width="18.98" height="24.662" x="234.18" y="129.625" ry=".104"/>
+    <rect ry=".109" y="129.169" x="255.811" height="25.892" width="19.733"/>
+    <path d="M250.106 146.822a6.435 6.435 0 0 1-12.87 0c-.319-5.46 3.52-12.344 6.435-12.315 2.915.03 6.753 6.854 6.435 12.315z" opacity=".5" fill="#c5ecff" stroke="#079bf2"/>
+    <path d="M158.675 130.32c-3.79 2.48-3.297 9.59-.087 11.618M190.9 141.814c-3.455-2.293-3.006-8.87-.079-10.747" fill="none" stroke="#05a7eb"/>
+    <path d="M163.956 131.115c3.79 2.479 3.296 9.59.086 11.618M196.298 131.267c3.79 2.479 3.297 9.59.087 11.618" fill="none" stroke="#f9f9fa" stroke-width="1.5" stroke-linecap="round"/>
+    <g fill="#079cf1" stroke="none">
+      <path d="M239.45 142.567c-.61.057.015 1.231.316.559.28-.343.975.11.278.187-.603.077-.682 1.104-.19 1.6.269.29.626.216.85-.048.633.62.31-1.029.18-1.713-.154-.942-.988-.881-1.434-.585m1.171 1.645c-.263.734-1.017-.172-.335-.353.313-.191.265-.087.335.353M240.806 141.259c.36 1.227.575 2.453.644 3.68.397-.163.937.354 1.256-.45.41-.933.007-3.13-.902-2.604-.546.726-.244-1.653-1.04-.769l.042.143m1.302 1.303c.545.3.39 2.15-.249 1.64-.084-.548-.43-1.728.249-1.64"/>
+      <path d="M243.65 141.67c-1.125.306-.747 3.556.255 3.241 1.035-.166 1.03-3.71-.256-3.24m.267.714c.535.288.286 2.31-.302 1.704-.258-.453-.25-1.906.302-1.704"/>
+      <path d="M244.978 141.976c.003.933-.402 2.374.164 2.914.208.157.459.076.653-.108.526.782.5-.963.65-1.646.519-1.206-.542-1.394-.464-.056.018.653-.231 1.479-.661 1.085-.107-.708.1-1.506.17-2.256-.136.058-.524-.274-.512.067M247.248 141.755c-.391.731-.545.829-.465 1.571-.208.644-.28 1.779.304 1.752.429.268.69-.858.159-.585-.424-.09.078-1.013.106-1.448.643.569 1.141-.693.202-.584.388-.59.148-.994-.306-.706M240.344 146.673c-.968.095-1.728 2.437-.482 2.682.36.094.904.086.892-.594-.054-.422-.971.399-.956-.377-.062-.664.496-1.534.9-.941.286.69.528-.763.075-.69a.705.705 0 0 0-.429-.08"/>
+      <path d="M241.736 146.787c-.962.203-1.362 3.097-.101 3.068 1.279.124 1.208-3.419.1-3.068m.192.684c.478.3.053 2.269-.491 1.535-.202-.485.002-1.761.491-1.535"/>
+      <path d="M243.582 146.865c-.29.43-.771-.485-.716.663.1.752-.468 2.433.16 2.483.502-.104.064-1.701.336-2.234.241-.355.62-.122.53.514-.11.793.135 2.558.506 1.402-.079-.939.108-2.852-.686-2.853l-.084.016-.046.01M245.082 145.807c-.286.046-.478.577-.452 1.091-.454.35.289.988.088 1.946-.224 1.308.861 1.47.48.125-.416-1.292.208-1.259.307-1.898-.126-.19-.812-.446-.15-.568.401.221.236-1.012-.144-.714-.043-.001-.086.014-.129.018"/>
+      <path d="M245.539 146.096c-.16 1.346.92.146.277-.15-.065.078-.348-.155-.277.15m.05.894c.054.92.173 1.84.356 2.76.929.126.108-1.386.13-2.128.02-.718-.206-1.134-.487-.632"/>
+      <path d="M246.835 146.687c-.852.293-.29 3.08.768 2.537.345-.48.553.17.085.321-.366.102-1.04-.162-.556.623.576.33 1.717-.267 1.332-1.157-.328-.81-.666-1.583-.754-2.37-.317.118-.55-.047-.875.046m.42.57c.247.182.67 1.314.11 1.364-.587.18-.76-1.667-.11-1.364"/>
+    </g>
+  </g>
+</svg>
\ No newline at end of file
--- a/browser/components/aboutconfig/jar.mn
+++ b/browser/components/aboutconfig/jar.mn
@@ -2,11 +2,12 @@
 # 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/.
 
 browser.jar:
   content/browser/aboutconfig/aboutconfig.css     (content/aboutconfig.css)
   content/browser/aboutconfig/aboutconfig.html    (content/aboutconfig.html)
   content/browser/aboutconfig/aboutconfig.js      (content/aboutconfig.js)
+  content/browser/aboutconfig/background.svg      (content/background.svg)
 
 [localization] @AB_CD@.jar:
   browser/aboutConfig.ftl                         (content/aboutconfig.notftl)
--- a/browser/components/aboutconfig/test/browser/browser.ini
+++ b/browser/components/aboutconfig/test/browser/browser.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = asan # Bug 1520398
 support-files =
   head.js
 
 [browser_basic.js]
 [browser_clipboard.js]
 skip-if = debug # Bug 1507747
 subsuite = clipboard
 [browser_edit.js]
--- a/browser/components/aboutconfig/test/browser/browser_observe.js
+++ b/browser/components/aboutconfig/test/browser/browser_observe.js
@@ -12,16 +12,31 @@ add_task(async function setup() {
 
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref(PREF_BOOLEAN_DEFAULT_TRUE);
     Services.prefs.clearUserPref(PREF_NUMBER_DEFAULT_ZERO);
     Services.prefs.clearUserPref(PREF_STRING_DEFAULT_EMPTY);
   });
 });
 
+add_task(async function test_observe_add_user_pref_before_search() {
+  Assert.equal(Services.prefs.getPrefType(PREF_NEW),
+               Ci.nsIPrefBranch.PREF_INVALID);
+
+  await AboutConfigTest.withNewTab(async function() {
+    this.bypassWarningButton.click();
+
+    // No results are shown after the warning page is dismissed or bypassed,
+    // and newly added preferences should not be displayed.
+    Preferences.set(PREF_NEW, true);
+    Assert.ok(!this.prefsTable.firstElementChild);
+    Preferences.reset(PREF_NEW);
+  }, { dontBypassWarning: true });
+});
+
 add_task(async function test_observe_add_user_pref() {
   Assert.equal(Services.prefs.getPrefType(PREF_NEW),
                Ci.nsIPrefBranch.PREF_INVALID);
 
   await AboutConfigTest.withNewTab(async function() {
     for (let value of [false, true, "", "value", 0, -10]) {
       // A row should be added when a new preference is added.
       Assert.ok(!this.getRow(PREF_NEW));
--- a/browser/components/aboutconfig/test/browser/browser_search.js
+++ b/browser/components/aboutconfig/test/browser/browser_search.js
@@ -57,30 +57,28 @@ add_task(async function test_search() {
     // new preference with the same name but a different case.
     this.search("TEST.aboutconfig.a");
     Assert.equal(this.rows.length, 3);
   });
 });
 
 add_task(async function test_search_delayed() {
   await AboutConfigTest.withNewTab(async function() {
-    let prefs = this.document.getElementById("prefs");
-
     // Prepare the table and the search field for the test.
     this.search("test.aboutconfig.a");
     Assert.equal(this.rows.length, 2);
 
     // The table is updated in a single microtask, so we don't need to wait for
     // specific mutations, we can just continue when the children are updated.
     let prefsTableChanged = new Promise(resolve => {
       let observer = new MutationObserver(() => {
         observer.disconnect();
         resolve();
       });
-      observer.observe(prefs, { childList: true });
+      observer.observe(this.prefsTable, { childList: true });
     });
 
     // Add a character and test that the table is not updated immediately.
     EventUtils.synthesizeKey("b");
     Assert.equal(this.rows.length, 2);
 
     // The table will eventually be updated after a delay.
     await prefsTableChanged;
--- a/browser/components/aboutconfig/test/browser/browser_warning.js
+++ b/browser/components/aboutconfig/test/browser/browser_warning.js
@@ -4,25 +4,35 @@
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({
     set: [
       ["browser.aboutConfig.showWarning", true],
     ],
   });
 });
 
-add_task(async function test_load_warningpage() {
-  await AboutConfigTest.withNewTab(async function() {
-    // Test that the warning page is presented:
-    Assert.equal(this.document.getElementsByTagName("button").length, 1);
-    Assert.ok(!this.document.getElementById("search"));
-    Assert.ok(!this.document.getElementById("prefs"));
+add_task(async function test_showWarningNextTime() {
+  for (let test of [
+    { expectWarningPage: true, disableShowWarningNextTime: false },
+    { expectWarningPage: true, disableShowWarningNextTime: true },
+    { expectWarningPage: false },
+  ]) {
+    await AboutConfigTest.withNewTab(async function() {
+      if (test.expectWarningPage) {
+        this.assertWarningPage(true);
+        Assert.ok(this.document.getElementById("showWarningNextTime").checked);
+        if (test.disableShowWarningNextTime) {
+          this.document.getElementById("showWarningNextTime").click();
+        }
+        this.bypassWarningButton.click();
+      }
 
-    // Disable checkbox and reload.
-    this.document.getElementById("showWarningNextTime").click();
-    this.document.querySelector("button").click();
-  }, { dontBypassWarning: true });
+      // No results are shown after the warning page is dismissed or bypassed.
+      this.assertWarningPage(false);
+      Assert.ok(!this.prefsTable.firstElementChild);
+      Assert.equal(this.document.activeElement, this.searchInput);
 
-  await AboutConfigTest.withNewTab(async function() {
-    Assert.ok(this.document.getElementById("search"));
-    Assert.ok(this.document.getElementById("prefs"));
-  }, { dontBypassWarning: true });
+      // Pressing ESC shows all results immediately.
+      EventUtils.sendKey("escape");
+      Assert.ok(this.prefsTable.firstElementChild);
+    }, { dontBypassWarning: true });
+  }
 });
--- a/browser/components/aboutconfig/test/browser/head.js
+++ b/browser/components/aboutconfig/test/browser/head.js
@@ -88,40 +88,65 @@ class AboutConfigTest {
     this.browser = browser;
     this.document = browser.contentDocument;
     this.window = browser.contentWindow;
   }
 
   async setupNewTab(options) {
     await this.document.l10n.ready;
     if (!options.dontBypassWarning) {
-      this.document.querySelector("button").click();
+      this.bypassWarningButton.click();
+      this.search();
     }
   }
 
+  get showWarningNextTimeInput() {
+    return this.document.getElementById("showWarningNextTime");
+  }
+
+  get bypassWarningButton() {
+    return this.document.querySelector("button.primary");
+  }
+
+  get searchInput() {
+    return this.document.getElementById("search");
+  }
+
+  get prefsTable() {
+    return this.document.getElementById("prefs");
+  }
+
   /**
    * Array of AboutConfigRowTest objects, one for each row in the main table.
    */
   get rows() {
-    let elements = this.document.getElementById("prefs")
-                                .getElementsByTagName("tr");
+    let elements = this.prefsTable.getElementsByTagName("tr");
     return Array.map(elements, element => new AboutConfigRowTest(element));
   }
 
   /**
    * Returns the AboutConfigRowTest object for the row in the main table which
    * corresponds to the given preference name, or undefined if none is present.
    */
   getRow(name) {
     return this.rows.find(row => row.name == name);
   }
 
   /**
    * Performs a new search using the dedicated textbox. This also makes sure
    * that the list of preferences displayed is up to date.
    */
   search(value = "") {
-    let search = this.document.getElementById("search");
-    search.value = value;
-    search.focus();
+    this.searchInput.value = value;
+    this.searchInput.focus();
     EventUtils.sendKey("return");
   }
+
+  /**
+   * Checks whether or not the initial warning page is displayed.
+   */
+  assertWarningPage(expected) {
+    Assert.equal(!!this.showWarningNextTimeInput, expected);
+    Assert.equal(!!this.bypassWarningButton, expected);
+    Assert.equal(!this.searchInput, expected);
+    Assert.equal(!this.prefsTable, expected);
+  }
 }
--- a/browser/installer/windows/Makefile.in
+++ b/browser/installer/windows/Makefile.in
@@ -39,27 +39,16 @@ PPL_LOCALE_ARGS = \
   --l10n-dir=$(REAL_LOCALE_MERGEDIR)/browser/installer \
   --l10n-dir=$(call EXPAND_LOCALE_SRCDIR,browser/locales)/installer \
   --l10n-dir=$(topsrcdir)/browser/locales/en-US/installer \
   $(NULL)
 else
 PPL_LOCALE_ARGS=$(call EXPAND_LOCALE_SRCDIR,browser/locales)/installer
 endif
 
-# For building the maintenanceservice installer
-ifdef MOZ_MAINTENANCE_SERVICE
-maintenanceservice_installer::
-	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
-	$(call py_action,preprocessor,-Fsubstitution $(DEFINES) $(ACDEFINES) \
-	  $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
-	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
-	  --preprocess-locale $(topsrcdir) \
-	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
-endif
-
 $(CONFIG_DIR)/setup.exe::
 	$(RM) -r $(CONFIG_DIR)
 	$(MKDIR) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
 	$(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR)
 	$(call py_action,preprocessor,-Fsubstitution $(DEFINES) $(ACDEFINES) \
 	  $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
 	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
--- a/devtools/client/debugger/new/test/mochitest/browser.ini
+++ b/devtools/client/debugger/new/test/mochitest/browser.ini
@@ -651,21 +651,16 @@ support-files =
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
   examples/doc-windowless-workers.html
   examples/simple-worker.js
   examples/doc-event-handler.html
   examples/doc-eval-throw.html
-  examples/doc_rr_basic.html
-  examples/doc_rr_continuous.html
-  examples/doc_rr_logs.html
-  examples/doc_rr_recovery.html
-  examples/doc_rr_error.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-xhr-breakpoints.js]
 [browser_dbg-xhr-run-to-completion.js]
 [browser_dbg-scroll-run-to-completion.js]
@@ -770,41 +765,8 @@ skip-if = os == "win"
 [browser_dbg-toggling-tools.js]
 [browser_dbg-react-app.js]
 skip-if = os == "win"
 [browser_dbg-wasm-sourcemaps.js]
 skip-if = true
 [browser_dbg-windowless-workers.js]
 [browser_dbg-event-handler.js]
 [browser_dbg-eval-throw.js]
-[browser_dbg_rr_breakpoints-01.js]
-skip-if = true # bug 1513057 os != "mac" || debug || !nightly_build
-[browser_dbg_rr_breakpoints-02.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_breakpoints-03.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_breakpoints-04.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_breakpoints-05.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_record.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_stepping-01.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_stepping-02.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_stepping-03.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_stepping-04.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_recovery-01.js]
-skip-if = true # See bug 1481009
-[browser_dbg_rr_replay-01.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_replay-02.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_replay-03.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_console_warp-01.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-[browser_dbg_rr_console_warp-02.js]
-skip-if = true # os != "mac" || debug || !nightly_build
-
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js
+++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-console.js
@@ -1,16 +1,16 @@
 add_task(async function() {
   Services.prefs.setBoolPref("devtools.toolbox.splitconsoleEnabled", true);
   const dbg = await initDebugger("doc-script-switching.html", "switching-01");
 
   await selectSource(dbg, "switching-01");
 
   // open the console
-  await getSplitConsole(dbg);
+  await getDebuggerSplitConsole(dbg);
   ok(dbg.toolbox.splitConsole, "Split console is shown.");
 
   // close the console
   await clickElement(dbg, "codeMirror");
   // First time to focus out of text area
   pressKey(dbg, "Escape");
 
   // Second time to hide console
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-01.js
+++ /dev/null
@@ -1,42 +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/ */
-
-// Test basic breakpoint functionality in web replay.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger(
-    "doc_rr_basic.html",
-    { waitForRecording: true }
-  );
-  const {threadClient, tab, toolbox} = dbg;
-
-  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
-
-  // Visit a lot of breakpoints so that we are sure we have crossed major
-  // checkpoint boundaries.
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 9);
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 8);
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 7);
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 6);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 7);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 8);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 9);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-
-  await toolbox.closeToolbox();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-02.js
+++ /dev/null
@@ -1,25 +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/ */
-
-// Test unhandled divergence while evaluating at a breakpoint with Web Replay.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger("doc_rr_basic.html", { waitForRecording: true });
-  const {threadClient, tab, toolbox} = dbg;
-
-  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
-  await rewindToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
-  await checkEvaluateInTopFrame(threadClient, "number", 10);
-  await checkEvaluateInTopFrame(threadClient, "testStepping2()", undefined);
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-03.js
+++ /dev/null
@@ -1,27 +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/ */
-
-// Test some issues when stepping around after hitting a breakpoint while recording.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
-  const {threadClient, tab, toolbox} = dbg;
-
-  await threadClient.interrupt();
-  await setBreakpoint(threadClient, "doc_rr_continuous.html", 19);
-  await resumeToLine(threadClient, 19);
-  await reverseStepOverToLine(threadClient, 18);
-  await checkEvaluateInTopFrame(threadClient, "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)", undefined);
-  await stepInToLine(threadClient, 22);
-  await setBreakpoint(threadClient, "doc_rr_continuous.html", 24);
-  await resumeToLine(threadClient, 24);
-  await setBreakpoint(threadClient, "doc_rr_continuous.html", 22);
-  await rewindToLine(threadClient, 22);
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-04.js
+++ /dev/null
@@ -1,33 +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/ */
-
-// Test navigating back to earlier breakpoints while recording, then resuming
-// recording.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
-  const {threadClient, tab, toolbox} = dbg;
-
-  await setBreakpoint(threadClient, "doc_rr_continuous.html", 14);
-  await resumeToLine(threadClient, 14);
-  let value = await evaluateInTopFrame(threadClient, "number");
-  await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
-  await rewindToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value);
-  await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
-  await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
-  await resumeToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 3);
-  await rewindToLine(threadClient, 14);
-  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_breakpoints-05.js
+++ /dev/null
@@ -1,30 +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/ */
-
-// Test hitting breakpoints when rewinding past the point where the breakpoint
-// script was created.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger(
-    "doc_rr_basic.html", 
-    { waitForRecording: true }
-  );
-
-  const {threadClient, tab, toolbox} = dbg;
-
-  // Rewind to the beginning of the recording.
-  await rewindToLine(threadClient, undefined);
-
-  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 1);
-  await resumeToLine(threadClient, 21);
-  await checkEvaluateInTopFrame(threadClient, "number", 2);
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-01.js
+++ /dev/null
@@ -1,53 +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/ */
-
-// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
-// since this test straddles both the web console and the debugger. I couldn't
-// figure out how to load that script directly here.
-function waitForThreadEvents(threadClient, eventName) {
-  info(`Waiting for thread event '${eventName}' to fire.`);
-
-  return new Promise(function(resolve, reject) {
-    threadClient.addListener(eventName, function onEvent(eventName, ...args) {
-      info(`Thread event '${eventName}' fired.`);
-      threadClient.removeListener(eventName, onEvent);
-      resolve.apply(resolve, args);
-    });
-  });
-}
-
-
-// Test basic console time warping functionality in web replay.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger(
-    "doc_rr_error.html", 
-    { waitForRecording: true }
-  );
-
-  const {tab, toolbox, threadClient} = dbg;
-  const console = await getSplitConsole(dbg);
-  const hud = console.hud;
-
-  await warpToMessage(hud, threadClient, "Number 5");
-  await threadClient.interrupt();
-
-  await checkEvaluateInTopFrame(threadClient, "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(threadClient, 19);
-  await reverseStepOverToLine(threadClient, 18);
-  await setBreakpoint(threadClient, "doc_rr_error.html", 12);
-  await rewindToLine(threadClient, 12);
-  await checkEvaluateInTopFrame(threadClient, "number", 4);
-  await resumeToLine(threadClient, 12);
-  await checkEvaluateInTopFrame(threadClient, "number", 5);
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_console_warp-02.js
+++ /dev/null
@@ -1,48 +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/ */
-
-
-// This functionality was copied from devtools/client/webconsole/test/mochitest/head.js,
-// since this test straddles both the web console and the debugger. I couldn't
-// figure out how to load that script directly here.
-function waitForThreadEvents(threadClient, eventName) {
-  info(`Waiting for thread event '${eventName}' to fire.`);
-
-  return new Promise(function(resolve, reject) {
-    threadClient.addListener(eventName, function onEvent(eventName, ...args) {
-      info(`Thread event '${eventName}' fired.`);
-      threadClient.removeListener(eventName, onEvent);
-      resolve.apply(resolve, args);
-    });
-  });
-}
-
-
-// Test basic console time warping functionality in web replay.
-async function test() {
-  waitForExplicitFinish();
-
-  const dbg = await attachRecordingDebugger(
-    "doc_rr_logs.html", 
-    { waitForRecording: true }
-  );
-
-  const {tab, toolbox, threadClient} = dbg;
-  const console = await getSplitConsole(dbg);
-  const hud = console.hud;
-
-  let message = await warpToMessage(hud, threadClient, "number: 1");
-  ok(!message.classList.contains("paused-before"), "paused before message is not shown");
-
-  await stepOverToLine(threadClient, 18);
-  await reverseStepOverToLine(threadClient, 17);
-
-  message = findMessage(hud, "number: 1")
-  ok(message.classList.contains("paused-before"), "paused before message is shown");
-
-  await toolbox.destroy();
-  await gBrowser.removeTab(tab);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_record.js
+++ /dev/null
@@ -1,19 +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/ */
-
-// Test basic recording of a tab without any debugging.
-async function test() {
-  waitForExplicitFinish();
-
-  var recordingTab = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_recovery-01.js
+++ /dev/null
@@ -1,28 +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/ */
-
-// Test basic recovery of crashed child processes in web replay.
-async function test() {
-  waitForExplicitFinish();
-
-  let tab = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-01.js
+++ /dev/null
@@ -1,39 +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/ */
-
-// Basic test for saving a recording and then replaying it in a new tab.
-async function test() {
-  waitForExplicitFinish();
-
-  let recordingFile = newRecordingFile();
-  let recordingTab = BrowserTestUtils.addTab(gBrowser, 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 = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-02.js
+++ /dev/null
@@ -1,49 +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/ */
-
-// Test ending a recording at a breakpoint and then separately replaying to the end.
-async function test() {
-  waitForExplicitFinish();
-
-  let recordingFile = newRecordingFile();
-  let recordingTab = BrowserTestUtils.addTab(gBrowser, 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 = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_replay-03.js
+++ /dev/null
@@ -1,32 +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/ */
-
-// 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 = BrowserTestUtils.addTab(gBrowser, 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 = 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);
-  finish();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-01.js
+++ /dev/null
@@ -1,29 +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/ */
-
-// Test basic step-over/back functionality in web replay.
-async function test() {
-  waitForExplicitFinish();
-
-  let tab = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-02.js
+++ /dev/null
@@ -1,30 +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/ */
-
-// Test fixes for some simple stepping bugs.
-async function test() {
-  waitForExplicitFinish();
-
-  let tab = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-03.js
+++ /dev/null
@@ -1,28 +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/ */
-
-// Test stepping back while recording, then resuming recording.
-async function test() {
-  waitForExplicitFinish();
-
-  let tab = BrowserTestUtils.addTab(gBrowser, 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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/browser_dbg_rr_stepping-04.js
+++ /dev/null
@@ -1,42 +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/ */
-
-// Stepping past the beginning or end of a frame should act like a step-out.
-async function test() {
-  waitForExplicitFinish();
-
-  let tab = BrowserTestUtils.addTab(gBrowser, 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);
-  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();
-}
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_basic.html
+++ /dev/null
@@ -1,38 +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, 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>
deleted file mode 100644
--- a/devtools/client/debugger/new/test/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/debugger/new/test/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/debugger/new/test/mochitest/examples/doc_rr_logs.html
+++ /dev/null
@@ -1,28 +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);
-// Simulate a longer recording by marking major checkpoints whenever possible.
-SpecialPowers.Cu.recordReplayDirective(/* AlwaysMarkMajorCheckpoints */ 4);
-</script>
-</html>
deleted file mode 100644
--- a/devtools/client/debugger/new/test/mochitest/examples/doc_rr_recovery.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<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>
--- a/devtools/client/debugger/new/test/mochitest/head.js
+++ b/devtools/client/debugger/new/test/mochitest/head.js
@@ -42,221 +42,21 @@ Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/devtools/client/debugger/new/test/mochitest/helpers.js",
   this
 );
 
 const EXAMPLE_URL =
   "http://example.com/browser/devtools/client/debugger/new/test/mochitest/examples/";
 
 
-async function waitUntilPredicate(predicate) {
-  let result;
-  await waitUntil(() => {
-    result = predicate();
-    return result;
-  })
-
-  return result;
-}
-
 // NOTE: still experimental, the screenshots might not be exactly correct
 async function takeScreenshot(dbg) {
   let canvas = dbg.win.document.createElementNS(
     "http://www.w3.org/1999/xhtml",
     "html:canvas"
   );
   let context = canvas.getContext("2d");
   canvas.width = dbg.win.innerWidth;
   canvas.height = dbg.win.innerHeight;
   context.drawWindow(dbg.win, 0, 0, canvas.width, canvas.height, "white");
   await waitForTime(1000);
   dump(`[SCREENSHOT] ${canvas.toDataURL()}\n`);
 }
-
-// Attach a debugger to a tab, returning a promise that resolves with the
-// debugger's toolbox.
-async function attachDebugger(tab) {
-  let target = await TargetFactory.forTab(tab);
-  let toolbox = await gDevTools.showToolbox(target, "jsdebugger");
-  return toolbox;
-}
-
-async function attachRecordingDebugger(url, { waitForRecording } = { waitForRecording: false }) {
-  let tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
-  gBrowser.selectedTab = tab;
-  openTrustedLinkIn(EXAMPLE_URL + url, "current");
-  
-  if (waitForRecording) {
-    await once(Services.ppmm, "RecordingFinished");
-  }
-  const toolbox = await attachDebugger(tab);
-  const dbg = createDebuggerContext(toolbox)
-  const threadClient = dbg.toolbox.threadClient;
-
-  await threadClient.interrupt();
-  return {...dbg, tab, threadClient};
-}
-
-
-// Return a promise with a reference to jsterm, opening the split
-// console if necessary.  This cleans up the split console pref so
-// it won't pollute other tests.
-async function getSplitConsole(dbg) {
-  const { toolbox, win } = dbg;
-
-  if (!win) {
-    win = toolbox.win;
-  }
-
-  if (!toolbox.splitConsole) {
-    pressKey(dbg, "Escape");
-  }
-
-  await toolbox.openSplitConsole();
-  return toolbox.getPanel("webconsole");
-}
-
-
-// Return a promise that resolves when a breakpoint has been set.
-async function setBreakpoint(threadClient, expectedFile, lineno) {
-  let {sources} = await threadClient.getSources();
-  ok(sources.length == 1, "Got one source");
-  ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
-  let sourceClient = threadClient.source(sources[0]);
-  await sourceClient.setBreakpoint({ line: lineno });
-}
-
-function resumeThenPauseAtLineFunctionFactory(method) {
-  return async function(threadClient, lineno) {
-    threadClient[method]();
-    await threadClient.addOneTimeListener("paused", async function(event, packet) {
-      let {frames} = await threadClient.getFrames(0, 1);
-      let frameLine = frames[0] ? frames[0].where.line : undefined;
-      ok(frameLine == lineno, "Paused at line " + frameLine + " 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 reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn");
-var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
-var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut");
-var stepOutToLine = resumeThenPauseAtLineFunctionFactory("stepOut");
-
-// Return a promise that resolves with the result of a thread evaluating a
-// string in the topmost frame.
-async function evaluateInTopFrame(threadClient, text) {
-  let {frames} = await threadClient.getFrames(0, 1);
-  ok(frames.length == 1, "Got one frame");
-  let response = await threadClient.eval(frames[0].actor, text);
-  ok(response.type == "resumed", "Got resume response from eval");
-  let rval;
-  await threadClient.addOneTimeListener("paused", function(event, packet) {
-    ok(packet.type == "paused" &&
-       packet.why.type == "clientEvaluated" &&
-       "return" in packet.why.frameFinished, "Eval returned a value");
-    rval = packet.why.frameFinished["return"];
-  });
-  return (rval.type == "undefined") ? undefined : rval;
-}
-
-// Return a promise that resolves when a thread evaluates a string in the
-// topmost frame, ensuring the result matches the expected value.
-async function checkEvaluateInTopFrame(threadClient, text, expected) {
-  let rval = await evaluateInTopFrame(threadClient, text);
-  ok(rval == expected, "Eval returned " + expected);
-}
-
-// Return a promise that resolves when a thread evaluates a string in the
-// topmost frame, with the result throwing an exception.
-async function checkEvaluateInTopFrameThrows(threadClient, text) {
-  let {frames} = await threadClient.getFrames(0, 1);
-  ok(frames.length == 1, "Got one frame");
-  let response = await threadClient.eval(frames[0].actor, text);
-  ok(response.type == "resumed", "Got resume response from eval");
-  await threadClient.addOneTimeListener("paused", function(event, packet) {
-    ok(packet.type == "paused" &&
-       packet.why.type == "clientEvaluated" &&
-       "throw" in packet.why.frameFinished, "Eval threw an exception");
-  });
-}
-
-// Return a pathname that can be used for a new recording file.
-function newRecordingFile() {
-  ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
-  return OS.Path.join(OS.Constants.Path.tmpDir,
-                      "MochitestRecording" + Math.round(Math.random() * 1000000000));
-}
-
-
-async function warpToMessage(hud, threadClient, text) {
-  let messages = await waitForMessages(hud, text);
-  ok(messages.length == 1, "Found one message");
-  let message = messages.pop();
-
-  let menuPopup = await openConsoleContextMenu(hud, message);
-  console.log(`.>> menu`, menuPopup);
-
-
-  let timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
-  ok(timeWarpItem, "Time warp menu item is available");
-
-  timeWarpItem.click();
-
-  await Promise.all([
-    hideConsoleContextMenu(hud),
-    once(Services.ppmm, "TimeWarpFinished"),
-    waitForThreadEvents(threadClient, 'paused')
-  ]);
-
-  messages = findMessages(hud, "", ".paused");
-  ok(messages.length == 1, "Found one paused message");
-
-  return message;
-}
-
-
-function findMessage(hud, text, selector = ".message") {
-  return findMessages(hud, text, selector)[0]
-}
-
-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 waitForMessages(hud, text, selector = ".message") {
-  return waitUntilPredicate(() => findMessages(hud, text, selector))
-}
-
-async function openConsoleContextMenu(hud, element) {
-  const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
-  synthesizeContextMenuEvent(element);
-  await onConsoleMenuOpened;
-  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
-  return doc.getElementById("webconsole-menu");
-}
-
-function hideConsoleContextMenu(hud) {
-  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
-  const popup = doc.getElementById("webconsole-menu");
-  if (!popup) {
-    return Promise.resolve();
-  }
-
-  const onPopupHidden = once(popup, "popuphidden");
-  popup.hidePopup();
-  return onPopupHidden;
-}
--- a/devtools/client/debugger/new/test/mochitest/helpers.js
+++ b/devtools/client/debugger/new/test/mochitest/helpers.js
@@ -1457,8 +1457,80 @@ async function editExpression(dbg, input
   dblClickElement(dbg, "expressionNode", 1);
   // Position cursor reliably at the end of the text.
   pressKey(dbg, "End");
   type(dbg, input);
   const evaluated = waitForDispatch(dbg, "EVALUATE_EXPRESSIONS");
   pressKey(dbg, "Enter");
   await evaluated;
 }
+
+async function waitUntilPredicate(predicate) {
+  let result;
+  await waitUntil(() => {
+    result = predicate();
+    return result;
+  })
+
+  return result;
+}
+
+// Return a promise with a reference to jsterm, opening the split
+// console if necessary.  This cleans up the split console pref so
+// it won't pollute other tests.
+async function getDebuggerSplitConsole(dbg) {
+  const { toolbox, win } = dbg;
+
+  if (!win) {
+    win = toolbox.win;
+  }
+
+  if (!toolbox.splitConsole) {
+    pressKey(dbg, "Escape");
+  }
+
+  await toolbox.openSplitConsole();
+  return toolbox.getPanel("webconsole");
+}
+
+async function openConsoleContextMenu(hud, element) {
+  const onConsoleMenuOpened = hud.ui.consoleOutput.once("menu-open");
+  synthesizeContextMenuEvent(element);
+  await onConsoleMenuOpened;
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  return doc.getElementById("webconsole-menu");
+}
+
+function hideConsoleContextMenu(hud) {
+  const doc = hud.ui.consoleOutput.owner.chromeWindow.document;
+  const popup = doc.getElementById("webconsole-menu");
+  if (!popup) {
+    return Promise.resolve();
+  }
+
+  const onPopupHidden = once(popup, "popuphidden");
+  popup.hidePopup();
+  return onPopupHidden;
+}
+
+// Return a promise that resolves with the result of a thread evaluating a
+// string in the topmost frame.
+async function evaluateInTopFrame(threadClient, text) {
+  const {frames} = await threadClient.getFrames(0, 1);
+  ok(frames.length == 1, "Got one frame");
+  const response = await threadClient.eval(frames[0].actor, text);
+  ok(response.type == "resumed", "Got resume response from eval");
+  let rval;
+  await threadClient.addOneTimeListener("paused", function(event, packet) {
+    ok(packet.type == "paused" &&
+       packet.why.type == "clientEvaluated" &&
+       "return" in packet.why.frameFinished, "Eval returned a value");
+    rval = packet.why.frameFinished.return;
+  });
+  return (rval.type == "undefined") ? undefined : rval;
+}
+
+// Return a promise that resolves when a thread evaluates a string in the
+// topmost frame, ensuring the result matches the expected value.
+async function checkEvaluateInTopFrame(threadClient, text, expected) {
+  const rval = await evaluateInTopFrame(threadClient, text);
+  ok(rval == expected, "Eval returned " + expected);
+}
--- a/devtools/client/themes/dark-theme.css
+++ b/devtools/client/themes/dark-theme.css
@@ -233,17 +233,17 @@ div.CodeMirror span.marked-text {
 }
 
 /* Highlight for evaluating current statement. */
 div.CodeMirror span.eval-text {
   background-color: #556;
 }
 
 .cm-s-mozilla .CodeMirror-linenumber { /* line number text */
-  color: var(--theme-content-color3);
+  color: var(--grey-40);
 }
 
 .cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
   border-right-color: var(--theme-toolbar-background);
   background-color: var(--theme-sidebar-background);
 }
 
 .cm-s-markup-view pre {
--- a/devtools/client/themes/light-theme.css
+++ b/devtools/client/themes/light-theme.css
@@ -224,17 +224,17 @@ div.CodeMirror span.marked-text {
 }
 
 /* Highlight for evaluating current statement. */
 div.CodeMirror span.eval-text {
   background-color: #ccd;
 }
 
 .cm-s-mozilla .CodeMirror-linenumber { /* line number text */
-  color: var(--theme-content-color3);
+  color: var(--grey-50);
 }
 
 .cm-s-mozilla .CodeMirror-gutters { /* vertical line next to line numbers */
   border-right-color: var(--theme-splitter-color);
   background-color: var(--theme-sidebar-background);
 }
 
 .cm-s-markup-view pre {
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser.ini
@@ -0,0 +1,38 @@
+[DEFAULT]
+tags = devtools
+subsuite = devtools
+
+# Feel free to set this to true if an impending change breaks Web Replay and
+# fixing it would be annoying or difficult. This will avoid running all tests
+# that use Web Replay; we don't want this experimental feature to impede
+# development in the rest of Gecko.
+#
+# Please file a bug against the 'Core > Web Replay' component if you do so,
+# so that the problem can be fixed and tests reenabled.
+skip-if = os != "mac" || debug || !nightly_build
+
+support-files =
+  head.js
+  examples/doc_rr_basic.html
+  examples/doc_rr_continuous.html
+  examples/doc_rr_logs.html
+  examples/doc_rr_recovery.html
+  examples/doc_rr_error.html
+
+[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_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_recovery-01.js]
+skip-if = true # See bug 1481009
+[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]
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-01.js
@@ -0,0 +1,44 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic breakpoint functionality in web replay.
+add_task(async function() {
+  const dbg = await attachRecordingDebugger(
+    "doc_rr_basic.html",
+    { waitForRecording: true }
+  );
+  const {threadClient, tab, toolbox} = dbg;
+
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
+
+  // Visit a lot of breakpoints so that we are sure we have crossed major
+  // checkpoint boundaries.
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 6);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 7);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 8);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 9);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+
+  await toolbox.closeToolbox();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-02.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// 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 });
+  const {threadClient, tab, toolbox} = dbg;
+
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
+  await rewindToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrameThrows(threadClient, "window.alert(3)");
+  await checkEvaluateInTopFrame(threadClient, "number", 10);
+  await checkEvaluateInTopFrame(threadClient, "testStepping2()", undefined);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-03.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test some issues when stepping around after hitting a breakpoint while recording.
+add_task(async function() {
+  const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
+  const {threadClient, tab, toolbox} = dbg;
+
+  await threadClient.interrupt();
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 19);
+  await resumeToLine(threadClient, 19);
+  await reverseStepOverToLine(threadClient, 18);
+  await checkEvaluateInTopFrame(threadClient,
+    "SpecialPowers.Cu.recordReplayDirective(/* AlwaysTakeTemporarySnapshots */ 3)",
+    undefined);
+  await stepInToLine(threadClient, 22);
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 24);
+  await resumeToLine(threadClient, 24);
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 22);
+  await rewindToLine(threadClient, 22);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-04.js
@@ -0,0 +1,35 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test navigating back to earlier breakpoints while recording, then resuming
+// recording.
+add_task(async function() {
+  const dbg = await attachRecordingDebugger("doc_rr_continuous.html");
+  const {threadClient, tab, toolbox} = dbg;
+
+  await setBreakpoint(threadClient, "doc_rr_continuous.html", 14);
+  await resumeToLine(threadClient, 14);
+  const value = await evaluateInTopFrame(threadClient, "number");
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await rewindToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 1);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
+  await resumeToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 3);
+  await rewindToLine(threadClient, 14);
+  await checkEvaluateInTopFrame(threadClient, "number", value + 2);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_breakpoints-05.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// 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 }
+  );
+
+  const {threadClient, tab, toolbox} = dbg;
+
+  // Rewind to the beginning of the recording.
+  await rewindToLine(threadClient, undefined);
+
+  await setBreakpoint(threadClient, "doc_rr_basic.html", 21);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 1);
+  await resumeToLine(threadClient, 21);
+  await checkEvaluateInTopFrame(threadClient, "number", 2);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic console time warping functionality in web replay.
+add_task(async function() {
+  const dbg = await attachRecordingDebugger(
+    "doc_rr_error.html",
+    { waitForRecording: true }
+  );
+
+  const {tab, toolbox, threadClient} = dbg;
+  const console = await getDebuggerSplitConsole(dbg);
+  const hud = console.hud;
+
+  await warpToMessage(hud, dbg, "Number 5");
+  await threadClient.interrupt();
+
+  await checkEvaluateInTopFrame(threadClient, "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(threadClient, 19);
+  await reverseStepOverToLine(threadClient, 18);
+  await setBreakpoint(threadClient, "doc_rr_error.html", 12);
+  await rewindToLine(threadClient, 12);
+  await checkEvaluateInTopFrame(threadClient, "number", 4);
+  await resumeToLine(threadClient, 12);
+  await checkEvaluateInTopFrame(threadClient, "number", 5);
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_console_warp-02.js
@@ -0,0 +1,33 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic console time warping functionality in web replay.
+add_task(async function() {
+  const dbg = await attachRecordingDebugger(
+    "doc_rr_logs.html",
+    { waitForRecording: true }
+  );
+
+  const {tab, toolbox, threadClient} = dbg;
+  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 not shown");
+
+  await stepOverToLine(threadClient, 18);
+  await reverseStepOverToLine(threadClient, 17);
+
+  message = findMessage(hud, "number: 1");
+  ok(message.classList.contains("paused-before"), "paused before message is shown");
+
+  await toolbox.destroy();
+  await gBrowser.removeTab(tab);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_record.js
@@ -0,0 +1,21 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic recording of a tab without any debugging.
+add_task(async function() {
+  const recordingTab = BrowserTestUtils.addTab(gBrowser, 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");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_recovery-01.js
@@ -0,0 +1,33 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic recovery of crashed child processes in web replay.
+add_task(async function() {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_recovery.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Basic test for saving a recording and then replaying it in a new tab.
+add_task(async function() {
+  const recordingFile = newRecordingFile();
+  const recordingTab = BrowserTestUtils.addTab(gBrowser, null,
+                                               { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
+  ok(tabParent, "Found recording tab parent");
+  ok(tabParent.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 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-02.js
@@ -0,0 +1,54 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test ending a recording at a breakpoint and then separately replaying to the end.
+add_task(async function() {
+  waitForExplicitFinish();
+
+  const recordingFile = newRecordingFile();
+  const recordingTab = BrowserTestUtils.addTab(gBrowser, 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);
+  const lastNumberValue = await evaluateInTopFrame(client, "number");
+
+  const 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);
+
+  const replayingTab = BrowserTestUtils.addTab(gBrowser, 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_replay-03.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// 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 = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = recordingTab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const tabParent = recordingTab.linkedBrowser.frameLoader.tabParent;
+  ok(tabParent, "Found recording tab parent");
+  ok(tabParent.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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-01.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test basic step-over/back functionality in web replay.
+add_task(async function() {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-02.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test fixes for some simple stepping bugs.
+add_task(async function() {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-03.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/ */
+/* eslint-disable no-undef */
+
+"use strict";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Test stepping back while recording, then resuming recording.
+add_task(async function() {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_continuous.html", "current");
+
+  const toolbox = await attachDebugger(tab), client = toolbox.threadClient;
+  await client.interrupt();
+  await setBreakpoint(client, "doc_rr_continuous.html", 13);
+  await resumeToLine(client, 13);
+  const 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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/browser_dbg_rr_stepping-04.js
@@ -0,0 +1,44 @@
+/* -*- 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";
+
+// To disable all Web Replay tests, see browser.ini
+
+// Stepping past the beginning or end of a frame should act like a step-out.
+add_task(async function() {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + "doc_rr_basic.html", "current");
+  await once(Services.ppmm, "RecordingFinished");
+
+  const 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);
+  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);
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/examples/doc_rr_basic.html
@@ -0,0 +1,38 @@
+<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, 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/webreplay/mochitest/examples/doc_rr_continuous.html
@@ -0,0 +1,28 @@
+<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>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/examples/doc_rr_error.html
@@ -0,0 +1,23 @@
+<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>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/examples/doc_rr_logs.html
@@ -0,0 +1,28 @@
+<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);
+// 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/webreplay/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>
new file mode 100644
--- /dev/null
+++ b/devtools/client/webreplay/mochitest/head.js
@@ -0,0 +1,144 @@
+/* -*- 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";
+
+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/new/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");
+  return toolbox;
+}
+
+async function attachRecordingDebugger(url,
+    { waitForRecording } = { waitForRecording: false }) {
+  const tab = BrowserTestUtils.addTab(gBrowser, null, { recordExecution: "*" });
+  gBrowser.selectedTab = tab;
+  openTrustedLinkIn(EXAMPLE_URL + url, "current");
+
+  if (waitForRecording) {
+    await once(Services.ppmm, "RecordingFinished");
+  }
+  const toolbox = await attachDebugger(tab);
+  const dbg = createDebuggerContext(toolbox);
+  const threadClient = dbg.toolbox.threadClient;
+
+  await threadClient.interrupt();
+  return {...dbg, tab, threadClient};
+}
+
+// Return a promise that resolves when a breakpoint has been set.
+async function setBreakpoint(threadClient, expectedFile, lineno) {
+  const {sources} = await threadClient.getSources();
+  ok(sources.length == 1, "Got one source");
+  ok(RegExp(expectedFile).test(sources[0].url), "Source is " + expectedFile);
+  const sourceClient = threadClient.source(sources[0]);
+  await sourceClient.setBreakpoint({ line: lineno });
+}
+
+function resumeThenPauseAtLineFunctionFactory(method) {
+  return async function(threadClient, lineno) {
+    threadClient[method]();
+    await threadClient.addOneTimeListener("paused", async function(event, packet) {
+      const {frames} = await threadClient.getFrames(0, 1);
+      const frameLine = frames[0] ? frames[0].where.line : undefined;
+      ok(frameLine == lineno, "Paused at line " + frameLine + " 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 reverseStepInToLine = resumeThenPauseAtLineFunctionFactory("reverseStepIn");
+var stepInToLine = resumeThenPauseAtLineFunctionFactory("stepIn");
+var reverseStepOutToLine = resumeThenPauseAtLineFunctionFactory("reverseStepOut");
+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(threadClient, text) {
+  const {frames} = await threadClient.getFrames(0, 1);
+  ok(frames.length == 1, "Got one frame");
+  const response = await threadClient.eval(frames[0].actor, text);
+  ok(response.type == "resumed", "Got resume response from eval");
+  await threadClient.addOneTimeListener("paused", function(event, packet) {
+    ok(packet.type == "paused" &&
+       packet.why.type == "clientEvaluated" &&
+       "throw" in packet.why.frameFinished, "Eval threw an exception");
+  });
+}
+
+// Return a pathname that can be used for a new recording file.
+function newRecordingFile() {
+  ChromeUtils.import("resource://gre/modules/osfile.jsm", this);
+  return OS.Path.join(OS.Constants.Path.tmpDir,
+                      "MochitestRecording" + Math.round(Math.random() * 1000000000));
+}
+
+function findMessage(hud, text, selector = ".message") {
+  return findMessages(hud, text, selector)[0];
+}
+
+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 waitForMessages(hud, text, selector = ".message") {
+  return waitUntilPredicate(() => findMessages(hud, text, selector));
+}
+
+async function warpToMessage(hud, threadClient, text) {
+  let messages = await waitForMessages(hud, text);
+  ok(messages.length == 1, "Found one message");
+  const message = messages.pop();
+
+  const menuPopup = await openConsoleContextMenu(hud, message);
+  console.log(`.>> menu`, menuPopup);
+
+  const timeWarpItem = menuPopup.querySelector("#console-menu-time-warp");
+  ok(timeWarpItem, "Time warp menu item is available");
+
+  timeWarpItem.click();
+
+  await Promise.all([
+    hideConsoleContextMenu(hud),
+    once(Services.ppmm, "TimeWarpFinished"),
+    waitForThreadEvents(threadClient, "paused"),
+  ]);
+
+  messages = findMessages(hud, "", ".paused");
+  ok(messages.length == 1, "Found one paused message");
+
+  return message;
+}
--- a/devtools/client/webreplay/moz.build
+++ b/devtools/client/webreplay/moz.build
@@ -9,8 +9,10 @@ DIRS += [
 ]
 
 DevToolsModules(
     'menu.js',
 )
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Web Replay')
+
+BROWSER_CHROME_MANIFESTS += [ 'mochitest/browser.ini' ]
--- a/devtools/shared/css/generated/properties-db.js
+++ b/devtools/shared/css/generated/properties-db.js
@@ -3679,16 +3679,55 @@ exports.CSS_PROPERTIES = {
       "ridge",
       "solid",
       "thick",
       "thin",
       "transparent",
       "unset"
     ]
   },
+  "border-block": {
+    "isInherited": false,
+    "subproperties": [
+      "border-block-start-width",
+      "border-block-end-width",
+      "border-block-start-style",
+      "border-block-end-style",
+      "border-block-start-color",
+      "border-block-end-color"
+    ],
+    "supports": [
+      2
+    ],
+    "values": [
+      "COLOR",
+      "currentColor",
+      "dashed",
+      "dotted",
+      "double",
+      "groove",
+      "hidden",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "inset",
+      "medium",
+      "none",
+      "outset",
+      "rgb",
+      "rgba",
+      "ridge",
+      "solid",
+      "thick",
+      "thin",
+      "transparent",
+      "unset"
+    ]
+  },
   "border-block-end": {
     "isInherited": false,
     "subproperties": [
       "border-block-end-color",
       "border-block-end-style",
       "border-block-end-width"
     ],
     "supports": [
@@ -4149,16 +4188,55 @@ exports.CSS_PROPERTIES = {
     "supports": [],
     "values": [
       "auto",
       "inherit",
       "initial",
       "unset"
     ]
   },
+  "border-inline": {
+    "isInherited": false,
+    "subproperties": [
+      "border-inline-start-width",
+      "border-inline-end-width",
+      "border-inline-start-style",
+      "border-inline-end-style",
+      "border-inline-start-color",
+      "border-inline-end-color"
+    ],
+    "supports": [
+      2
+    ],
+    "values": [
+      "COLOR",
+      "currentColor",
+      "dashed",
+      "dotted",
+      "double",
+      "groove",
+      "hidden",
+      "hsl",
+      "hsla",
+      "inherit",
+      "initial",
+      "inset",
+      "medium",
+      "none",
+      "outset",
+      "rgb",
+      "rgba",
+      "ridge",
+      "solid",
+      "thick",
+      "thin",
+      "transparent",
+      "unset"
+    ]
+  },
   "border-inline-end": {
     "isInherited": false,
     "subproperties": [
       "border-inline-end-color",
       "border-inline-end-style",
       "border-inline-end-width"
     ],
     "supports": [
@@ -6440,16 +6518,30 @@ exports.CSS_PROPERTIES = {
       "auto",
       "inherit",
       "initial",
       "max-content",
       "min-content",
       "unset"
     ]
   },
+  "inset-block": {
+    "isInherited": false,
+    "subproperties": [
+      "inset-block-start",
+      "inset-block-end"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "inherit",
+      "initial",
+      "unset"
+    ]
+  },
   "inset-block-end": {
     "isInherited": false,
     "subproperties": [
       "inset-block-end"
     ],
     "supports": [],
     "values": [
       "auto",
@@ -6466,16 +6558,30 @@ exports.CSS_PROPERTIES = {
     "supports": [],
     "values": [
       "auto",
       "inherit",
       "initial",
       "unset"
     ]
   },
+  "inset-inline": {
+    "isInherited": false,
+    "subproperties": [
+      "inset-inline-start",
+      "inset-inline-end"
+    ],
+    "supports": [],
+    "values": [
+      "auto",
+      "inherit",
+      "initial",
+      "unset"
+    ]
+  },
   "inset-inline-end": {
     "isInherited": false,
     "subproperties": [
       "inset-inline-end"
     ],
     "supports": [],
     "values": [
       "auto",
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/file_bug1495363.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<form id="form" method="POST" action="file_bug1495363.sjs" enctype="multipart/form-data">
+  <input type="text" name="post" id="post" />
+  <input type="file" name="file" id="file" />
+  <input id="btn" type="submit" />
+</form>
+
+<script type="application/javascript">
+
+let p = new Promise(resolve => {
+  let url = SimpleTest.getTestFileURL("../../../../dom/filesystem/tests/script_fileList.js");
+  let script = SpecialPowers.loadChromeScript(url);
+
+  function onOpened(message) {
+    SpecialPowers.wrap(document.getElementById("file")).mozSetFileArray([message.file]);
+    resolve();
+  }
+
+  script.addMessageListener("file.opened", onOpened);
+  script.sendAsyncMessage("file.open");
+});
+
+p.then(() => {
+  let form = document.getElementById("form");
+  form.onsubmit = function() {
+    setTimeout(() => {
+      document.getElementById("post").value = "TIMEOUT";
+      form.submit();
+      parent.timeoutExpired();
+    }, 0);
+    parent.formSubmitted();
+    return true;
+  }
+
+  document.getElementById("post").value = "CLICK";
+  document.getElementById("btn").click();
+});
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/file_bug1495363.sjs
@@ -0,0 +1,45 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+
+function handleRequest(aRequest, aResponse) {
+  aResponse.setStatusLine(aRequest.httpVersion, 200);
+
+  // This returns number of requests received so far.
+  if (aRequest.queryString.includes("result")) {
+    let hints = getState("hints") || 0;
+    setState("hints", "0");
+
+    let submitter = getState("submitter");
+    setState("submitter", "");
+
+    aResponse.write(hints + "-" + submitter);
+    return;
+  }
+
+  // Here we count the number of requests and we store who was the last
+  // submitter.
+
+  let bodyStream = new BinaryInputStream(aRequest.bodyInputStream);
+  let requestBody = "";
+  while ((bodyAvail = bodyStream.available()) > 0) {
+    requestBody += bodyStream.readBytes(bodyAvail);
+  }
+
+  let lines = requestBody.split("\n");
+  let submitter = "";
+  for (let i = 0; i < lines.length; ++i) {
+    if (lines[i].trim() == 'Content-Disposition: form-data; name="post"') {
+      submitter = lines[i+2].trim();
+      break;
+    }
+  }
+
+  let hints = parseInt(getState("hints") || 0) + 1;
+  setState("hints", hints.toString());
+  setState("submitter", submitter);
+
+  aResponse.setHeader("Content-Type", "text/html", false);
+  aResponse.write("Hello World!");
+}
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -111,8 +111,13 @@ skip-if = android_version == '18' || os 
 [test_stepup_stepdown.html]
 [test_textarea_attributes_reflection.html]
 [test_validation.html]
 [test_valueAsDate_pref.html]
 [test_valueasdate_attribute.html]
 [test_valueasnumber_attribute.html]
 [test_validation_not_in_doc.html]
 [test_reportValidation_preventDefault.html]
+[test_bug1495363.html]
+support-files =
+  file_bug1495363.html
+  file_bug1495363.sjs
+  !/dom/filesystem/tests/script_fileList.js
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_bug1495363.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Bug 1495363</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<script type="application/javascript">
+
+let readyToCheck = 0;
+window.timeoutExpired = () => {
+  ok(true, "Timeout expired");
+  readyToCheck++;
+  maybeCheckResults();
+}
+
+window.formSubmitted = () => {
+  ok(true, "Form submited!");
+  ifr.addEventListener("load", () => {
+    readyToCheck++;
+    maybeCheckResults();
+  }, {once: true});
+}
+
+SimpleTest.waitForExplicitFinish();
+
+var ifr = document.createElement('iframe');
+ifr.src = "file_bug1495363.html";
+document.body.appendChild(ifr);
+
+function maybeCheckResults() {
+  ok(readyToCheck <= 2, "So far so good");
+  if (readyToCheck < 2) {
+    return;
+  }
+
+  SimpleTest.executeSoon(() => {
+    fetch("file_bug1495363.sjs?result").then(r => r.text()).then(text => {
+      let parts = text.split("-");
+      is(parts[0], "1", "We have 1 request only");
+      is(parts[1], "TIMEOUT", "The request comes from the timer");
+      SimpleTest.finish();
+    });
+  });
+}
+
+</script>
+</body>
+</html>
--- a/js/src/gc/Memory.cpp
+++ b/js/src/gc/Memory.cpp
@@ -258,16 +258,17 @@ static inline uint64_t GetNumberInRange(
   const uint64_t MaxRand = UINT64_C(0xffffffffffffffff);
   maxNum -= minNum;
   uint64_t binSize = 1 + (MaxRand - maxNum) / (maxNum + 1);
 
   uint64_t rndNum;
   do {
     mozilla::Maybe<uint64_t> result;
     do {
+      mozilla::recordreplay::AutoPassThroughThreadEvents pt;
       result = mozilla::RandomUint64();
     } while (!result);
     rndNum = result.value() / binSize;
   } while (rndNum > maxNum);
 
   return minNum + rndNum;
 }
 
--- a/js/src/jit/arm64/Assembler-arm64.cpp
+++ b/js/src/jit/arm64/Assembler-arm64.cpp
@@ -372,16 +372,30 @@ void PatchJump(CodeLocationJump& jump_, 
   // FIXME: That assumption implies that the branch target is always in-range.
   if (branch->IsTargetReachable((Instruction*)label.raw())) {
     branch->SetImmPCOffsetTarget((Instruction*)label.raw());
   } else {
     MOZ_CRASH("PatchJump target not reachable");
   }
 }
 
+void Assembler::PatchWrite_NearCall(CodeLocationLabel start,
+                                    CodeLocationLabel toCall) {
+  Instruction* dest = (Instruction*)start.raw();
+  ptrdiff_t relTarget = (Instruction*)toCall.raw() - dest;
+  ptrdiff_t relTarget00 = relTarget >> 2;
+  MOZ_RELEASE_ASSERT((relTarget & 0x3) == 0);
+  MOZ_RELEASE_ASSERT(vixl::is_int26(relTarget00));
+
+  // printf("patching %p with call to %p\n", start.raw(), toCall.raw());
+  bl(dest, relTarget00);
+
+  AutoFlushICache::flush(uintptr_t(dest), 4);
+}
+
 void Assembler::PatchDataWithValueCheck(CodeLocationLabel label,
                                         PatchedImmPtr newValue,
                                         PatchedImmPtr expected) {
   Instruction* i = (Instruction*)label.raw();
   void** pValue = i->LiteralAddress<void**>();
   MOZ_ASSERT(*pValue == expected.value);
   *pValue = newValue.value;
 }
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -314,21 +314,17 @@ class Assembler : public vixl::Assembler
   size_t addPatchableJump(BufferOffset src, RelocationKind kind);
 
  public:
   static uint32_t PatchWrite_NearCallSize() { return 4; }
 
   static uint32_t NopSize() { return 4; }
 
   static void PatchWrite_NearCall(CodeLocationLabel start,
-                                  CodeLocationLabel toCall) {
-    Instruction* dest = (Instruction*)start.raw();
-    // printf("patching %p with call to %p\n", start.raw(), toCall.raw());
-    bl(dest, ((Instruction*)toCall.raw() - dest) >> 2);
-  }
+                                  CodeLocationLabel toCall);
   static void PatchDataWithValueCheck(CodeLocationLabel label,
                                       PatchedImmPtr newValue,
                                       PatchedImmPtr expected);
 
   static void PatchDataWithValueCheck(CodeLocationLabel label, ImmPtr newValue,
                                       ImmPtr expected);
 
   static void PatchWrite_Imm32(CodeLocationLabel label, Imm32 imm) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -661,17 +661,22 @@ void MacroAssembler::call(JitCode* c) {
 
 CodeOffset MacroAssembler::callWithPatch() {
   bl(0, LabelDoc());
   return CodeOffset(currentOffset());
 }
 void MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
   Instruction* inst = getInstructionAt(BufferOffset(callerOffset - 4));
   MOZ_ASSERT(inst->IsBL());
-  bl(inst, ((int)calleeOffset - ((int)callerOffset - 4)) >> 2);
+  ptrdiff_t relTarget = (int)calleeOffset - ((int)callerOffset - 4);
+  ptrdiff_t relTarget00 = relTarget >> 2;
+  MOZ_RELEASE_ASSERT((relTarget & 0x3) == 0);
+  MOZ_RELEASE_ASSERT(vixl::is_int26(relTarget00));
+  bl(inst, relTarget00);
+  AutoFlushICache::flush(uintptr_t(inst), 4);
 }
 
 CodeOffset MacroAssembler::farJumpWithPatch() {
   vixl::UseScratchRegisterScope temps(this);
   const ARMRegister scratch = temps.AcquireX();
   const ARMRegister scratch2 = temps.AcquireX();
 
   AutoForbidPools afp(this, /* max number of instructions in scope = */ 7);
--- a/layout/generic/ScrollAnchorContainer.cpp
+++ b/layout/generic/ScrollAnchorContainer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/. */
 
 #include "ScrollAnchorContainer.h"
 
+#include "GeckoProfiler.h"
 #include "mozilla/dom/Text.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsGfxScrollFrame.h"
 #include "nsLayoutUtils.h"
 
 #define ANCHOR_LOG(...)
 // #define ANCHOR_LOG(...) printf_stderr("ANCHOR: " __VA_ARGS__)
 
@@ -118,16 +119,17 @@ static nsRect FindScrollAnchoringBoundin
 void ScrollAnchorContainer::SelectAnchor() {
   MOZ_ASSERT(mScrollFrame->mScrolledFrame);
   MOZ_ASSERT(mAnchorNodeIsDirty);
 
   if (!StaticPrefs::layout_css_scroll_anchoring_enabled()) {
     return;
   }
 
+  AUTO_PROFILER_LABEL("ScrollAnchorContainer::SelectAnchor", LAYOUT);
   ANCHOR_LOG("Selecting anchor for %p with scroll-port [%d %d x %d %d].\n",
              this, mScrollFrame->mScrollPort.x, mScrollFrame->mScrollPort.y,
              mScrollFrame->mScrollPort.width, mScrollFrame->mScrollPort.height);
 
   const nsStyleDisplay* disp = Frame()->StyleDisplay();
 
   // Don't select a scroll anchor if the scroll frame has `overflow-anchor:
   // none`.
--- a/layout/reftests/font-face/font-display-1.html
+++ b/layout/reftests/font-face/font-display-1.html
@@ -27,17 +27,17 @@ body { margin: 20px }
 @font-face {
   font-family: test1-fallback;
   src: url(../fonts/markA.woff);
   font-display: fallback;
 }
 
 @font-face {
   font-family: test1-optional;
-  src: url(../fonts/markfonts-delay.sjs?font=markA&delay=200&test=font-display-1-optional);
+  src: url(../fonts/markfonts-delay.sjs?font=markA&delay=2000&test=font-display-1-optional);
   font-display: optional;
 }
 
 div.test {
   font-size: 600%;
   line-height: 1.3em;
   width: 500px;
 }
--- a/layout/reftests/font-face/reftest.list
+++ b/layout/reftests/font-face/reftest.list
@@ -173,19 +173,18 @@ HTTP(..) == reflow-sanity-1-data.html re
 HTTP(..) == reflow-sanity-1.html reflow-sanity-1-data.html
 HTTP(..) == reflow-sanity-delay-1a.html reflow-sanity-1-ref.html
 HTTP(..) == reflow-sanity-delay-1b.html reflow-sanity-1-ref.html
 HTTP(..) == reflow-sanity-delay-1c.html reflow-sanity-1-ref.html
 HTTP(..) == reflow-sanity-delay-1-metrics.html reflow-sanity-1-ref.html
 
 HTTP(..) == bug-1481905-cancel-load.html bug-1481905-cancel-load-ref.html
 
-# font-display
-skip-if(/^Linux\x20i686/.test(http.oscpu)) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) HTTP(..) == font-display-1.html font-display-1-ref.html # normal font load (~500ms), bug 1392106
-# ^ disabled due to intermittents due to timing issues -- Bug 1238222
+# font-display, with the timeouts extended so that slow (debug) builds have a better chance to keep up
+pref(gfx.downloadable_fonts.fallback_delay,10000) pref(gfx.downloadable_fonts.fallback_delay_short,1000) HTTP(..) == font-display-1.html font-display-1-ref.html # normal font load (~500ms), bug 1392106
 fuzzy-if(OSX==1010,0-3,0-5) HTTP(..) == font-display-2.html font-display-2-ref.html # font load takes 4500ms
 
 # Testing hack for Meiryo
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == meiryo-en.html meiryo-ja.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == meiryo-en-bold.html meiryo-ja-bold.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == meiryo-en-italic.html meiryo-ja-italic.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == meiryo-en-oblique.html meiryo-ja-oblique.html # Bug 1392106
 random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == meiryo-en-bolditalic.html meiryo-ja-bolditalic.html # Bug 1392106
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -1294,16 +1294,26 @@ var gCSSProperties = {
   "-moz-binding": {
     domProp: "MozBinding",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "none" ],
     other_values: [ "url(foo.xml)" ],
     invalid_values: []
   },
+  "border-inline": {
+    domProp: "borderInline",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-inline-start-color", "border-inline-start-style", "border-inline-start-width",
+                     "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ],
+    initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+    other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+    invalid_values: [ "5%", "5", "5 solid green" ]
+  },
   "border-inline-end": {
     domProp: "borderInlineEnd",
     inherited: false,
     type: CSS_TYPE_TRUE_SHORTHAND,
     subproperties: [ "border-inline-end-color", "border-inline-end-style", "border-inline-end-width" ],
     initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
     other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
     invalid_values: [ "5%", "5", "5 green none" ]
@@ -6039,16 +6049,26 @@ var gCSSProperties = {
       "calc(2px)",
       "calc(50%)",
       "calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
     ],
     invalid_values: [ "none" ],
   },
+  "border-block": {
+    domProp: "borderBlock",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "border-block-start-color", "border-block-start-style", "border-block-start-width",
+                     "border-block-end-color", "border-block-end-style", "border-block-end-width" ],
+    initial_values: [ "none", "medium", "currentColor", "thin", "none medium currentcolor" ],
+    other_values: [ "solid", "green", "medium solid", "green solid", "10px solid", "thick solid", "5px green none" ],
+    invalid_values: [ "5%", "5", "5 solid green" ]
+  },
   "border-block-end-color": {
     domProp: "borderBlockEndColor",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     applies_to_first_letter: true,
     logical: true,
     initial_values: [ "currentColor" ],
     other_values: [ "green", "rgba(255,128,0,0.5)", "transparent" ],
@@ -6381,16 +6401,34 @@ var gCSSProperties = {
       "calc(2px)",
       "calc(50%)",
       "calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
     ],
     invalid_values: [ "none", "5" ]
   },
+  "inset-block": {
+    domProp: "insetBlock",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "inset-block-start", "inset-block-end" ],
+    /* FIXME: run tests with multiple prerequisites */
+    prerequisites: { "position": "relative" },
+    initial_values: [ "auto", "auto auto" ],
+    other_values: [ "32px", "-3em", "12%", "32px auto", "auto -3em", "12% auto",
+      "calc(2px)", "calc(2px) auto",
+      "calc(-2px)", "auto calc(-2px)",
+      "calc(50%)", "auto calc(50%)",
+      "calc(3*25px)", "calc(3*25px) auto",
+      "calc(25px*3)", "auto calc(25px*3)",
+      "calc(3*25px + 50%)", "auto calc(3*25px + 50%)",
+    ],
+    invalid_values: [ "none" ]
+  },
   "inset-block-end": {
     domProp: "insetBlockEnd",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     /* FIXME: run tests with multiple prerequisites */
     prerequisites: { "position": "relative" },
     /* XXX 0 may or may not be equal to auto */
@@ -6419,16 +6457,34 @@ var gCSSProperties = {
       "calc(-2px)",
       "calc(50%)",
       "calc(3*25px)",
       "calc(25px*3)",
       "calc(3*25px + 50%)",
     ],
     invalid_values: []
   },
+  "inset-inline": {
+    domProp: "insetInline",
+    inherited: false,
+    type: CSS_TYPE_TRUE_SHORTHAND,
+    subproperties: [ "inset-inline-start", "inset-inline-end" ],
+    /* FIXME: run tests with multiple prerequisites */
+    prerequisites: { "position": "relative" },
+    initial_values: [ "auto", "auto auto" ],
+    other_values: [ "32px", "-3em", "12%", "32px auto", "auto -3em", "12% auto",
+      "calc(2px)", "calc(2px) auto",
+      "calc(-2px)", "auto calc(-2px)",
+      "calc(50%)", "auto calc(50%)",
+      "calc(3*25px)", "calc(3*25px) auto",
+      "calc(25px*3)", "auto calc(25px*3)",
+      "calc(3*25px + 50%)", "auto calc(3*25px + 50%)",
+    ],
+    invalid_values: [ "none" ]
+  },
   "inset-inline-end": {
     domProp: "insetInlineEnd",
     inherited: false,
     type: CSS_TYPE_LONGHAND,
     logical: true,
     /* FIXME: run tests with multiple prerequisites */
     prerequisites: { "position": "relative" },
     /* XXX 0 may or may not be equal to auto */
--- a/layout/tools/reftest/mach_commands.py
+++ b/layout/tools/reftest/mach_commands.py
@@ -233,12 +233,12 @@ class MachCommands(MachCommandBase):
         kwargs["topsrcdir"] = self.topsrcdir
         process_test_objects(kwargs)
         reftest = self._spawn(ReftestRunner)
         # Unstructured logging must be enabled prior to calling
         # adb which uses an unstructured logger in its constructor.
         reftest.log_manager.enable_unstructured()
         if conditions.is_android(self):
             from mozrunner.devices.android_device import verify_android_device
-            verify_android_device(self, install=True, xre=True, app=kwargs["app"],
-                                  device_serial=kwargs["deviceSerial"])
+            verify_android_device(self, install=True, xre=True, network=True,
+                                  app=kwargs["app"], device_serial=kwargs["deviceSerial"])
             return reftest.run_android_test(**kwargs)
         return reftest.run_desktop_test(**kwargs)
--- a/mobile/android/chrome/moz.build
+++ b/mobile/android/chrome/moz.build
@@ -4,17 +4,17 @@
 # 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/.
 
 # NOTE: I think there are a few other possible components in this directory
 with Files('**'):
     BUG_COMPONENT = ('Firefox for Android', 'General')
 
 with Files('geckoview/**'):
-    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+    BUG_COMPONENT = ('GeckoView', 'General')
 
 DIRS += ['geckoview']
 
 DEFINES['AB_CD'] = CONFIG['MOZ_UI_LOCALE']
 DEFINES['PACKAGE'] = 'browser'
 DEFINES['MOZ_APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 DEFINES['MOZ_APP_VERSION_DISPLAY'] = CONFIG['MOZ_APP_VERSION_DISPLAY']
 DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
--- a/mobile/android/modules/moz.build
+++ b/mobile/android/modules/moz.build
@@ -10,17 +10,17 @@ with Files('**'):
 
 with Files('DownloadNotifications.jsm'):
     BUG_COMPONENT = ('Firefox for Android', 'Download Manager')
 
 with Files('HomeProvider.jsm'):
     BUG_COMPONENT = ('Firefox for Android', 'Data Providers')
 
 with Files('geckoview/**'):
-    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+    BUG_COMPONENT = ('GeckoView', 'General')
 
 DIRS += ['geckoview']
 
 EXTRA_JS_MODULES += [
     'Accounts.jsm',
     'ActionBarHandler.jsm',
     'BrowserActions.jsm',
     'dbg-browser-actors.js',
--- a/mobile/android/moz.build
+++ b/mobile/android/moz.build
@@ -13,26 +13,26 @@ with Files('branding/**'):
 
 with Files('config/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
 
 with Files('docs/**'):
     BUG_COMPONENT = ('Firefox for Android', 'General')
 
 with Files('geckoview/**'):
-    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+    BUG_COMPONENT = ('GeckoView', 'General')
 
 with Files('geckoview/src/main/aidl/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
 
 with Files('geckoview/src/main/java/org/mozilla/gecko/mozglue/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Audio/Video')
 
 with Files('geckoview_example/**'):
-    BUG_COMPONENT = ('Firefox for Android', 'GeckoView')
+    BUG_COMPONENT = ('GeckoView', 'General')
 
 with Files('gradle/**'):
     BUG_COMPONENT = ('Firefox for Android', 'Build Config & IDE Support')
 
 with Files('services/**'):
     BUG_COMPONENT = ('Android Background Services', 'Android Sync')
 
 with Files('themes/**'):
--- a/servo/components/style/properties/shorthands/border.mako.rs
+++ b/servo/components/style/properties/shorthands/border.mako.rs
@@ -350,8 +350,55 @@ pub fn parse_border<'i, 't>(
             self.border_image_width.to_css(dest)?;
             dest.write_str(" / ")?;
             self.border_image_outset.to_css(dest)?;
             dest.write_str(" ")?;
             self.border_image_repeat.to_css(dest)
         }
     }
 </%helpers:shorthand>
+
+% for axis in ["block", "inline"]:
+    <%
+        spec = "https://drafts.csswg.org/css-logical/#propdef-border-%s" % (axis)
+    %>
+    <%helpers:shorthand
+        name="border-${axis}"
+        sub_properties="${' '.join(
+            'border-%s-%s-width' % (axis, side)
+            for side in ['start', 'end']
+        )} ${' '.join(
+            'border-%s-%s-style' % (axis, side)
+            for side in ['start', 'end']
+        )} ${' '.join(
+            'border-%s-%s-color' % (axis, side)
+            for side in ['start', 'end']
+        )}"
+        spec="${spec}">
+
+        use crate::properties::shorthands::border_${axis}_start;
+        pub fn parse_value<'i, 't>(
+            context: &ParserContext,
+            input: &mut Parser<'i, 't>,
+        ) -> Result<Longhands, ParseError<'i>> {
+            let start_value = border_${axis}_start::parse_value(context, input)?;
+            Ok(expanded! {
+                border_${axis}_start_width: start_value.border_${axis}_start_width.clone(),
+                border_${axis}_end_width: start_value.border_${axis}_start_width,
+                border_${axis}_start_style: start_value.border_${axis}_start_style.clone(),
+                border_${axis}_end_style: start_value.border_${axis}_start_style,
+                border_${axis}_start_color: start_value.border_${axis}_start_color.clone(),
+                border_${axis}_end_color: start_value.border_${axis}_start_color,
+            })
+        }
+
+        impl<'a> ToCss for LonghandsToSerialize<'a>  {
+            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+                super::serialize_directional_border(
+                    dest,
+                    self.border_${axis}_start_width,
+                    self.border_${axis}_start_style,
+                    self.border_${axis}_start_color
+                )
+            }
+        }
+    </%helpers:shorthand>
+% endfor
--- a/servo/components/style/properties/shorthands/position.mako.rs
+++ b/servo/components/style/properties/shorthands/position.mako.rs
@@ -757,8 +757,51 @@
                 dest.write_str(" ")?;
                 self.justify_items.to_css(dest)?;
             }
 
             Ok(())
         }
     }
 </%helpers:shorthand>
+
+% for axis in ["block", "inline"]:
+    <%
+        spec = "https://drafts.csswg.org/css-logical/#propdef-inset-%s" % axis
+    %>
+    <%helpers:shorthand
+        name="inset-${axis}"
+        sub_properties="${' '.join(
+            'inset-%s-%s' % (axis, side)
+            for side in ['start', 'end']
+        )}"
+        spec="${spec}">
+
+        use crate::parser::Parse;
+        use crate::values::specified::length::LengthPercentageOrAuto;
+        pub fn parse_value<'i, 't>(
+            context: &ParserContext,
+            input: &mut Parser<'i, 't>,
+        ) -> Result<Longhands, ParseError<'i>> {
+            let start_value = LengthPercentageOrAuto::parse(context, input)?;
+            let end_value =
+                input.try(|input| LengthPercentageOrAuto::parse(context, input)).unwrap_or_else(|_| start_value.clone());
+
+            Ok(expanded! {
+                inset_${axis}_start: start_value,
+                inset_${axis}_end: end_value,
+            })
+        }
+
+        impl<'a> ToCss for LonghandsToSerialize<'a>  {
+            fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result where W: fmt::Write {
+                self.inset_${axis}_start.to_css(dest)?;
+
+                if self.inset_${axis}_end != self.inset_${axis}_start {
+                    dest.write_str(" ")?;
+                    self.inset_${axis}_end.to_css(dest)?;
+                }
+
+                Ok(())
+            }
+        }
+    </%helpers:shorthand>
+% endfor
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -279,17 +279,17 @@ def setup_junit_argument_parser():
 
         with open(path, 'r') as fh:
             imp.load_module('mochitest', fh, path,
                             ('.py', 'r', imp.PY_SOURCE))
 
         import runjunit
 
         from mozrunner.devices.android_device import verify_android_device
-        verify_android_device(build_obj, install=False, xre=True)
+        verify_android_device(build_obj, install=False, xre=True, network=True)
 
     global parser
     parser = runjunit.JunitArgumentParser()
     return parser
 
 
 # condition filters
 
@@ -451,18 +451,18 @@ class MachCommands(MachCommandBase):
             from mozrunner.devices.android_device import grant_runtime_permissions
             from mozrunner.devices.android_device import verify_android_device
             app = kwargs.get('app')
             if not app:
                 app = self.substs["ANDROID_PACKAGE_NAME"]
             device_serial = kwargs.get('deviceSerial')
 
             # verify installation
-            verify_android_device(self, install=True, xre=False, app=app,
-                                  device_serial=device_serial)
+            verify_android_device(self, install=True, xre=False, network=True,
+                                  app=app, device_serial=device_serial)
             grant_runtime_permissions(self, app, device_serial=device_serial)
             run_mochitest = mochitest.run_android_test
         else:
             run_mochitest = mochitest.run_desktop_test
 
         overall = None
         for (flavor, subsuite), tests in sorted(suites.items()):
             fobj = ALL_FLAVORS[flavor]
@@ -563,18 +563,18 @@ class RobocopCommands(MachCommandBase):
 
         from mozrunner.devices.android_device import grant_runtime_permissions, get_adb_path
         from mozrunner.devices.android_device import verify_android_device
         # verify installation
         app = kwargs.get('app')
         if not app:
             app = self.substs["ANDROID_PACKAGE_NAME"]
         device_serial = kwargs.get('deviceSerial')
-        verify_android_device(self, install=True, xre=False, app=app,
-                              device_serial=device_serial)
+        verify_android_device(self, install=True, xre=False, network=True,
+                              app=app, device_serial=device_serial)
         grant_runtime_permissions(self, app, device_serial=device_serial)
 
         if not kwargs['adbPath']:
             kwargs['adbPath'] = get_adb_path(self)
 
         mochitest = self._spawn(MochitestRunner)
         return mochitest.run_robocop_test(self._mach_context, tests, 'robocop', **kwargs)
 
--- a/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
+++ b/testing/mozbase/mozrunner/mozrunner/devices/android_device.py
@@ -7,16 +7,17 @@ from __future__ import absolute_import, 
 import fileinput
 import glob
 import os
 import platform
 import psutil
 import re
 import shutil
 import signal
+import subprocess
 import sys
 import telnetlib
 import time
 import urlparse
 import urllib2
 from distutils.spawn import find_executable
 
 from mozdevice import ADBHost, ADBDevice
@@ -170,28 +171,30 @@ def _maybe_update_host_utils(build_obj):
                 parts = os.path.split(existing_path)
                 backup_dir = '_backup-' + parts[1]
                 backup_path = os.path.join(parts[0], backup_dir)
                 shutil.move(existing_path, backup_path)
                 _install_host_utils(build_obj)
 
 
 def verify_android_device(build_obj, install=False, xre=False, debugger=False,
-                          verbose=False, app=None, device_serial=None):
+                          network=False, verbose=False, app=None, device_serial=None):
     """
        Determine if any Android device is connected via adb.
        If no device is found, prompt to start an emulator.
        If a device is found or an emulator started and 'install' is
        specified, also check whether Firefox is installed on the
        device; if not, prompt to install Firefox.
        If 'xre' is specified, also check with MOZ_HOST_BIN is set
        to a valid xre/host-utils directory; if not, prompt to set
        one up.
        If 'debugger' is specified, also check that JimDB is installed;
        if JimDB is not found, prompt to set up JimDB.
+       If 'network' is specified, also check that the device has basic
+       network connectivity.
        Returns True if the emulator was started or another device was
        already connected.
     """
     device_verified = False
     emulator = AndroidEmulator('*', substs=build_obj.substs, verbose=verbose)
     adb_path = _find_sdk_exe(build_obj.substs, 'adb', False)
     if not adb_path:
         adb_path = 'adb'
@@ -300,16 +303,38 @@ def verify_android_device(build_obj, ins
                     break
         if err:
             _log_info("Host utilities not found: %s" % err)
             response = raw_input(
                 "Download and setup your host utilities? (Y/n) ").strip()
             if response.lower().startswith('y') or response == '':
                 _install_host_utils(build_obj)
 
+    if device_verified and network:
+        # Optionally check the network: If on a device that does not look like
+        # an emulator, verify that the device IP address can be obtained
+        # and check that this host can ping the device.
+        serial = device_serial or os.environ.get('DEVICE_SERIAL')
+        if not serial or ('emulator' not in serial):
+            device = _get_device(build_obj.substs, serial)
+            try:
+                addr = device.get_ip_address()
+                if not addr:
+                    _log_warning("unable to get Android device's IP address!")
+                    _log_warning("tests may fail without network connectivity to the device!")
+                else:
+                    _log_info("Android device's IP address: %s" % addr)
+                    response = subprocess.check_output(["ping", "-c", "1", addr])
+                    _log_debug(response)
+            except Exception as e:
+                _log_warning("unable to verify network connection to device: %s" % str(e))
+                _log_warning("tests may fail without network connectivity to the device!")
+        else:
+            _log_debug("network check skipped on emulator")
+
     if debugger:
         # Optionally set up JimDB. See https://wiki.mozilla.org/Mobile/Fennec/Android/GDB.
         build_platform = _get_device_platform(build_obj.substs)
         jimdb_path = os.path.join(EMULATOR_HOME_DIR, 'jimdb-%s' % build_platform)
         jimdb_utils_path = os.path.join(jimdb_path, 'utils')
         gdb_path = os.path.join(jimdb_path, 'bin', 'gdb')
         err = None
         if not os.path.isdir(jimdb_path):
--- a/testing/xpcshell/mach_commands.py
+++ b/testing/xpcshell/mach_commands.py
@@ -243,17 +243,17 @@ class MachCommands(MachCommandBase):
                 "XPCShellTests", params, log_defaults, fmt_defaults)
 
         if not params['threadCount']:
             params['threadCount'] = int((cpu_count() * 3) / 2)
 
         if conditions.is_android(self):
             from mozrunner.devices.android_device import verify_android_device, get_adb_path
             device_serial = params.get('deviceSerial')
-            verify_android_device(self, device_serial=device_serial)
+            verify_android_device(self, network=True, device_serial=device_serial)
             if not params['adbPath']:
                 params['adbPath'] = get_adb_path(self)
             xpcshell = self._spawn(AndroidXPCShellRunner)
         else:
             xpcshell = self._spawn(XPCShellRunner)
         xpcshell.cwd = self._mach_context.cwd
 
         try:
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -2163,32 +2163,28 @@ nsresult nsUrlClassifierDBService::Looku
   nsCOMPtr<nsIUrlClassifierUtils> utilsService =
       do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
   rv = utilsService->GetKeyForURI(uri, key);
   if (NS_FAILED(rv)) return rv;
 
   if (forceLookup) {
     *didLookup = true;
   } else {
-    bool clean = false;
-
-    if (!clean) {
-      nsCOMPtr<nsIPermissionManager> permissionManager =
-          services::GetPermissionManager();
-
-      if (permissionManager) {
-        uint32_t perm;
-        rv = permissionManager->TestPermissionFromPrincipal(
-            aPrincipal, "safe-browsing", &perm);
-        NS_ENSURE_SUCCESS(rv, rv);
-
-        clean |= (perm == nsIPermissionManager::ALLOW_ACTION);
-      }
+    nsCOMPtr<nsIPermissionManager> permissionManager =
+        services::GetPermissionManager();
+    if (NS_WARN_IF(!permissionManager)) {
+      return NS_ERROR_FAILURE;
     }
 
+    uint32_t perm;
+    rv = permissionManager->TestPermissionFromPrincipal(
+        aPrincipal, "safe-browsing", &perm);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    bool clean = (perm == nsIPermissionManager::ALLOW_ACTION);
     *didLookup = !clean;
     if (clean) {
       return NS_OK;
     }
   }
 
   // Create an nsUrlClassifierLookupCallback object.  This object will
   // take care of confirming partial hash matches if necessary before
--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
@@ -97,12 +97,23 @@ endif
 	cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) uninstaller.nsi
 
 uninstaller:: $(CONFIG_DIR)/helper.exe
 	$(NSINSTALL) -D $(DIST)/bin/uninstall
 	cp $(CONFIG_DIR)/helper.exe $(DIST)/bin/uninstall
 
 ifdef MOZ_MAINTENANCE_SERVICE
 maintenanceservice_installer::
+	$(RM) -r $(CONFIG_DIR)
+	$(MKDIR) $(CONFIG_DIR)
+	$(INSTALL) $(addprefix $(srcdir)/,$(INSTALLER_FILES)) $(CONFIG_DIR)
+	$(INSTALL) $(addprefix $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/,$(BRANDING_FILES)) $(CONFIG_DIR)
+	$(call py_action,preprocessor,-Fsubstitution $(DEFINES) $(ACDEFINES) \
+	  $(srcdir)/nsis/defines.nsi.in -o $(CONFIG_DIR)/defines.nsi)
+	$(PYTHON) $(topsrcdir)/toolkit/mozapps/installer/windows/nsis/preprocess-locale.py \
+	  --preprocess-locale $(topsrcdir) \
+	  $(PPL_LOCALE_ARGS) $(AB_CD) $(CONFIG_DIR)
+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/toolkit/mozapps/installer/windows/nsis/,$(TOOLKIT_NSIS_FILES)) $(CONFIG_DIR)
+	$(INSTALL) $(addprefix $(MOZILLA_DIR)/other-licenses/nsis/Plugins/,$(CUSTOM_NSIS_PLUGINS)) $(CONFIG_DIR)
 	cd $(CONFIG_DIR) && $(MAKENSISU) $(MAKENSISU_FLAGS) maintenanceservice_installer.nsi
 	$(NSINSTALL) -D $(DIST)/bin/
 	cp $(CONFIG_DIR)/maintenanceservice_installer.exe $(DIST)/bin
 endif
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -388,23 +388,22 @@ nsDocLoader::OnStartRequest(nsIRequest* 
 
   //
   // Only fire a doStartDocumentLoad(...) if the document loader
   // has initiated a load...  Otherwise, this notification has
   // resulted from a request being added to the load group.
   //
   if (mIsLoadingDocument) {
     if (loadFlags & nsIChannel::LOAD_DOCUMENT_URI) {
-      //
-      // Make sure that the document channel is null at this point...
-      // (unless its been redirected)
-      //
-      NS_ASSERTION(
-          (loadFlags & nsIChannel::LOAD_REPLACE) || !(mDocumentRequest.get()),
-          "Overwriting an existing document channel!");
+      // If we have a document request channel, and this is not a redirect, we
+      // must abort it and replace it with the new one.
+      if (!(loadFlags & nsIChannel::LOAD_REPLACE) && mDocumentRequest) {
+        mDocumentRequest->Cancel(NS_ERROR_ABORT);
+        mDocumentRequest = nullptr;
+      }
 
       // This request is associated with the entire document...
       mDocumentRequest = request;
       mLoadGroup->SetDefaultLoadRequest(request);
 
       // Only fire the start document load notification for the first
       // document URI...  Do not fire it again for redirections
       //
--- a/widget/android/fennec/moz.build
+++ b/widget/android/fennec/moz.build
@@ -1,16 +1,16 @@
 # -*- 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/.
 
 with Files("**"):
-    BUG_COMPONENT = ("Firefox for Android", "GeckoView")
+    BUG_COMPONENT = ('GeckoView', 'General')
 
 EXPORTS += [
     '!FennecJNINatives.h',
     '!FennecJNIWrappers.h',
 ]
 
 SOURCES += [
     '!FennecJNIWrappers.cpp',
--- a/xpcom/ds/nsTArray.cpp
+++ b/xpcom/ds/nsTArray.cpp
@@ -6,17 +6,20 @@
 
 #include <string.h>
 #include "nsTArray.h"
 #include "nsXPCOM.h"
 #include "nsDebug.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/IntegerPrintfMacros.h"
 
-nsTArrayHeader sEmptyTArrayHeader = {0, 0, 0};
+// Ensure this is sufficiently aligned so that Elements() and co don't create
+// unaligned pointers, or slices with unaligned pointers for empty arrays, see
+// https://github.com/servo/servo/issues/22613.
+alignas(8) nsTArrayHeader sEmptyTArrayHeader = {0, 0, 0};
 
 bool IsTwiceTheRequiredBytesRepresentableAsUint32(size_t aCapacity,
                                                   size_t aElemSize) {
   using mozilla::CheckedUint32;
   return ((CheckedUint32(aCapacity) * aElemSize) * 2).isValid();
 }
 
 MOZ_NORETURN MOZ_COLD void InvalidArrayIndex_CRASH(size_t aIndex,