Merge mozilla-inbound to mozilla-central. a=merge
authorAndreea Pavel <apavel@mozilla.com>
Fri, 19 Apr 2019 00:36:23 +0300
changeset 528866 b44914767f72367a7e4b01c9fd0ba9258c41570c
parent 528831 74dc09642c222e18634f4f13e7b0bd6b7f91cbc2 (current diff)
parent 528865 c305a816937d6da174ffa5c3b36884e5d560ed5e (diff)
child 528867 2ccc6648064315964dd23039ad28ebf7d9f82999
child 528871 e0a826fcd85be5a510e8b093aa56e53bc27e75ac
child 528931 e6eaf8ab500a17c4ed1bb2ea84e525d9e825ce3a
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone68.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-inbound to mozilla-central. a=merge
devtools/shared/DevToolsUtils.js
js/src/gc/Nursery.cpp
layout/generic/nsBulletFrame.cpp
testing/raptor/raptor/raptor.py
toolkit/components/passwordmgr/test/mochitest/test_autocomplete_https_upgrade.html
toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_open.html
toolkit/components/passwordmgr/test/mochitest/test_autocomplete_sandboxed.html
toolkit/components/passwordmgr/test/mochitest/test_autofill_different_formSubmitURL.html
toolkit/components/passwordmgr/test/mochitest/test_autofill_from_bfcache.html
toolkit/components/passwordmgr/test/mochitest/test_autofill_https_upgrade.html
toolkit/components/passwordmgr/test/mochitest/test_autofill_sandboxed.html
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_formSubmitURL.html
toolkit/components/passwordmgr/test/mochitest/test_case_differences.html
toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_no_saved_login.html
--- a/browser/themes/shared/incontentprefs/dialog.inc.css
+++ b/browser/themes/shared/incontentprefs/dialog.inc.css
@@ -2,18 +2,18 @@
 /* - 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/. */
 %endif
 
 window,
 dialog {
   -moz-appearance: none;
-  background-color: #fbfbfb;
-  color: #424e5a;
+  background-color: var(--in-content-page-background);
+  color: var(--in-content-page-color);
   margin: 0;
   padding: 0;
 }
 
 .contentPane {
   -moz-box-flex: 1;
 }
 
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -1,15 +1,31 @@
 %if 0
 /* - This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this file,
    - You can obtain one at http://mozilla.org/MPL/2.0/. */
 %endif
 @namespace html "http://www.w3.org/1999/xhtml";
 
+:root {
+  --in-content-dialogtitle-background: #f1f1f1;
+  --in-content-dialogtitle-border: #c1c1c1;
+  --in-content-warning-container: var(--grey-20);
+}
+
+@supports -moz-bool-pref("browser.in-content.dark-mode") {
+@media (prefers-color-scheme: dark) {
+  :root {
+    --in-content-dialogtitle-background: rgba(249,249,250,0.05);
+    --in-content-dialogtitle-border: rgba(0,0,0,0.5);
+    --in-content-warning-container: var(--grey-90-a30);
+  }
+}
+}
+
 * {
   -moz-user-select: text;
 }
 
 .main-content {
   padding-top: 0;
 }
 
@@ -464,19 +480,19 @@ button > hbox > label {
   visibility: hidden;
 }
 
 .dialogOverlay[topmost="true"] {
   background-color: rgba(0,0,0,0.5);
 }
 
 .dialogBox {
-  background-color: #fbfbfb;
+  background-color: var(--in-content-page-background);
   background-clip: content-box;
-  color: #424e5a;
+  color: var(--in-content-page-color);
   /* `transparent` will use the dialogText color in high-contrast themes and
      when page colors are disabled */
   border: 1px solid transparent;
   border-radius: 3.5px;
   box-shadow: 0 2px 6px 0 rgba(0,0,0,0.3);
   display: -moz-box;
   margin: 0;
   padding: 0;
@@ -487,18 +503,18 @@ button > hbox > label {
   overflow: hidden;
   min-height: 20em;
   min-width: 66ch;
 }
 
 .dialogTitleBar {
   margin-top: 0;
   padding: 3.5px 0;
-  background-color: #F1F1F1;
-  border-bottom: 1px solid #C1C1C1;
+  background-color: var(--in-content-dialogtitle-background);
+  border-bottom: 1px solid var(--in-content-dialogtitle-border);
 }
 
 .dialogTitle {
   font-size: .9em;
   font-weight: 600;
   text-align: center;
   -moz-user-select: none;
 }
@@ -894,17 +910,17 @@ menulist[indicator=true] > menupopup men
 }
 
 /* Menulist styles */
 .label-item {
   font-size: 0.8em;
 }
 
 .updateSettingCrossUserWarningContainer {
-  background: var(--grey-20);
+  background: var(--in-content-warning-container);
   border-radius: 5px;
   padding-inline: 8px;
   padding-block-start: 2px;
   padding-block-end: 8px;
   margin-block-end: 17px;
 }
 
 #updateSettingCrossUserWarning {
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -656,16 +656,18 @@ support-files =
   examples/pause-points.js
   examples/script-mutate.js
   examples/script-switching-02.js
   examples/script-switching-01.js
   examples/times2.js
   examples/doc-windowless-workers.html
   examples/doc-windowless-workers-early-breakpoint.html
   examples/simple-worker.js
+  examples/doc-worker-scopes.html
+  examples/scopes-worker.js
   examples/doc-event-handler.html
   examples/doc-eval-throw.html
   examples/doc-sourceURL-breakpoint.html
 
 [browser_dbg-asm.js]
 [browser_dbg-audiocontext.js]
 [browser_dbg-async-stepping.js]
 [browser_dbg-sourcemapped-breakpoint-console.js]
@@ -781,12 +783,13 @@ skip-if = os == "win"
 [browser_dbg-tabs-without-urls.js]
 [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-windowless-workers-early-breakpoint.js]
+[browser_dbg-worker-scopes.js]
 [browser_dbg-event-handler.js]
 [browser_dbg-eval-throw.js]
 [browser_dbg-sourceURL-breakpoint.js]
 [browser_dbg-old-breakpoint.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg-worker-scopes.js
@@ -0,0 +1,85 @@
+/* -*- 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/ */
+
+function findNode(dbg, text) {
+  for (let index = 0;; index++) {
+    var elem = findElement(dbg, "scopeNode", index);
+    if (elem && elem.innerText == text) {
+      return elem;
+    }
+  }
+}
+
+function toggleNode(dbg, text) {
+  return toggleObjectInspectorNode(findNode(dbg, text));
+}
+
+function findNodeValue(dbg, text) {
+  for (let index = 0;; index++) {
+    var elem = findElement(dbg, "scopeNode", index);
+    if (elem && elem.innerText == text) {
+      return findElement(dbg, "scopeValue", index).innerText;
+    }
+  }
+}
+
+// Test that unusual objects have their contents shown in worker thread scopes.
+add_task(async function() {
+  const dbg = await initDebugger("doc-worker-scopes.html", "scopes-worker.js");
+  const workerSource = findSource(dbg, "scopes-worker.js");
+
+  await addBreakpoint(dbg, workerSource, 11);
+  invokeInTab("startWorker");
+  await waitForPaused(dbg, "scopes-worker.js");
+  await removeBreakpoint(dbg, workerSource.id, 11);
+
+  // We should be paused at the first line of simple-worker.js
+  assertPausedAtSourceAndLine(dbg, workerSource.id, 11);
+
+  await toggleNode(dbg, "var_array");
+  ok(findNodeValue(dbg, "0") == "\"mango\"", "array elem0");
+  ok(findNodeValue(dbg, "1") == "\"pamplemousse\"", "array elem1");
+  ok(findNodeValue(dbg, "2") == "\"pineapple\"", "array elem2");
+  await toggleNode(dbg, "var_array");
+
+  await toggleNode(dbg, "var_tarray");
+  ok(findNodeValue(dbg, "0") == "42", "tarray elem0");
+  ok(findNodeValue(dbg, "1") == "43", "tarray elem1");
+  ok(findNodeValue(dbg, "2") == "44", "tarray elem2");
+  await toggleNode(dbg, "var_tarray");
+
+  await toggleNode(dbg, "var_set");
+  await toggleNode(dbg, "<entries>");
+
+  ok(findNodeValue(dbg, "0") == "\"papaya\"", "set elem0");
+  ok(findNodeValue(dbg, "1") == "\"banana\"", "set elem1");
+  await toggleNode(dbg, "var_set");
+
+  await toggleNode(dbg, "var_map");
+  await toggleNode(dbg, "<entries>");
+  await toggleNode(dbg, "0");
+  ok(findNodeValue(dbg, "<key>"), "1");
+  ok(findNodeValue(dbg, "<value>"), "\"one\"");
+  await toggleNode(dbg, "0");
+  await toggleNode(dbg, "1");
+  ok(findNodeValue(dbg, "<key>"), "2");
+  ok(findNodeValue(dbg, "<value>"), "\"two\"");
+  await toggleNode(dbg, "var_map");
+
+  await toggleNode(dbg, "var_weakmap");
+  await toggleNode(dbg, "<entries>");
+  await toggleNode(dbg, "0");
+  await toggleNode(dbg, "<key>");
+  ok(findNodeValue(dbg, "foo"), "foo");
+  await toggleNode(dbg, "<value>");
+  ok(findNodeValue(dbg, "bar"), "bar");
+  await toggleNode(dbg, "var_weakmap");
+
+  await toggleNode(dbg, "var_weakset");
+  await toggleNode(dbg, "<entries>");
+  await toggleNode(dbg, "0");
+  ok(findNodeValue(dbg, "foo"), "foo");
+  await toggleNode(dbg, "var_weakset");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/doc-worker-scopes.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+
+<script>
+startWorker();
+
+function startWorker() {
+  w = new Worker("scopes-worker.js");
+}
+</script>
+
+<body>
+Hello World!
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/examples/scopes-worker.js
@@ -0,0 +1,13 @@
+function f() {
+  var var_array = ["mango","pamplemousse","pineapple"];
+  var var_tarray = new Uint8Array([42,43,44]);
+  var var_set = new Set(["papaya","banana"]);
+  var var_map = new Map([[1,"one"],[2,"two"]]);
+  var var_weakmap = new WeakMap();
+  var key = { foo: "foo" }, value = { bar: "bar" };
+  var_weakmap.set(key, value);
+  var var_weakset = new WeakSet();
+  var_weakset.add(key);
+  return 0;
+}
+f();
--- a/devtools/client/inspector/index.xhtml
+++ b/devtools/client/inspector/index.xhtml
@@ -43,26 +43,29 @@
 <body class="theme-body" role="application">
   <div class="inspector-responsive-container theme-body inspector"
        data-localization-bundle="devtools/client/locales/inspector.properties">
 
     <!-- Main Panel Content -->
     <div id="inspector-main-content" class="devtools-main-content" style="visibility: hidden;">
       <!-- Toolbar -->
       <div id="inspector-toolbar" class="devtools-toolbar" nowindowdrag="true">
-        <button id="inspector-element-add-button" class="devtools-button"
-                data-localization="title=inspectorAddNode.label"></button>
-        <div class="devtools-toolbar-spacer"></div>
-        <span id="inspector-searchlabel"></span>
         <div id="inspector-search" class="devtools-searchbox has-clear-btn">
           <input id="inspector-searchbox" class="devtools-searchinput"
                  type="search"
                  data-localization="placeholder=inspectorSearchHTML.label3"/>
           <button id="inspector-searchinput-clear" class="devtools-searchinput-clear" hidden="true" tabindex="-1"></button>
         </div>
+        <div id="inspector-searchlabel-container" hidden="true">
+          <div class="devtools-separator"></div>
+          <span id="inspector-searchlabel"></span>
+        </div>
+        <div class="devtools-separator"></div>
+        <button id="inspector-element-add-button" class="devtools-button"
+                data-localization="title=inspectorAddNode.label"></button>
         <button id="inspector-eyedropper-toggle" class="devtools-button"></button>
       </div>
 
       <!-- Markup Container -->
       <div id="markup-box"></div>
       <div id="inspector-breadcrumbs-toolbar" class="devtools-toolbar">
         <div id="inspector-breadcrumbs" class="breadcrumbs-widget-container"
              role="toolbar" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
@@ -89,16 +92,17 @@
           <div id="ruleview-toolbar">
             <div class="devtools-searchbox has-clear-btn">
               <input id="ruleview-searchbox"
                      class="devtools-filterinput devtools-rule-searchbox"
                      type="search"
                      data-localization="placeholder=inspector.filterStyles.placeholder"/>
               <button id="ruleview-searchinput-clear" class="devtools-searchinput-clear"></button>
             </div>
+            <div class="devtools-separator"></div>
             <div id="ruleview-command-toolbar">
               <button id="ruleview-add-rule-button" data-localization="title=inspector.addRule.tooltip" class="devtools-button"></button>
               <button id="pseudo-class-panel-toggle" data-localization="title=inspector.togglePseudo.tooltip" class="devtools-button"></button>
               <button id="class-panel-toggle" data-localization="title=inspector.classPanel.toggleClass.tooltip" class="devtools-button"></button>
               <button id="print-simulation-toggle" data-localization="title=inspector.printSimulation.tooltip" class="devtools-button" hidden="true"></button>
             </div>
           </div>
           <div id="pseudo-class-panel" class="ruleview-reveal-panel" hidden="true">
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -422,16 +422,18 @@ Inspector.prototype = {
   },
 
   /**
    * Hooks the searchbar to show result and auto completion suggestions.
    */
   setupSearchBox: function() {
     this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
     this.searchClearButton = this.panelDoc.getElementById("inspector-searchinput-clear");
+    this.searchResultsContainer =
+      this.panelDoc.getElementById("inspector-searchlabel-container");
     this.searchResultsLabel = this.panelDoc.getElementById("inspector-searchlabel");
 
     this.searchBox.addEventListener("focus", () => {
       this.search.on("search-cleared", this._clearSearchResultsLabel);
       this.search.on("search-result", this._updateSearchResultsLabel);
     }, { once: true });
 
     const shortcuts = new KeyShortcuts({
@@ -461,16 +463,20 @@ Inspector.prototype = {
     let str = "";
     if (!clear) {
       if (result) {
         str = INSPECTOR_L10N.getFormatStr(
           "inspector.searchResultsCount2", result.resultsIndex + 1, result.resultsLength);
       } else {
         str = INSPECTOR_L10N.getStr("inspector.searchResultsNone");
       }
+
+      this.searchResultsContainer.hidden = false;
+    } else {
+      this.searchResultsContainer.hidden = true;
     }
 
     this.searchResultsLabel.textContent = str;
   },
 
   get React() {
     return this._toolbox.React;
   },
--- a/devtools/client/inspector/markup/test/browser.ini
+++ b/devtools/client/inspector/markup/test/browser.ini
@@ -35,16 +35,17 @@ support-files =
   doc_markup_links.html
   doc_markup_mutation.html
   doc_markup_navigation.html
   doc_markup_not_displayed.html
   doc_markup_pagesize_01.html
   doc_markup_pagesize_02.html
   doc_markup_pseudo.html
   doc_markup_search.html
+  doc_markup_subgrid.html
   doc_markup_svg_attributes.html
   doc_markup_toggle.html
   doc_markup_tooltip.png
   doc_markup_void_elements.html
   doc_markup_void_elements.xhtml
   doc_markup_whitespace.html
   doc_markup_xul.xul
   doc_markup_update-on-navigtion_1.html
@@ -211,16 +212,17 @@ subsuite = clipboard
 [browser_markup_shadowdom_open_debugger.js]
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533507
 [browser_markup_shadowdom_shadowroot_mode.js]
 [browser_markup_shadowdom_show_nodes_button.js]
 [browser_markup_shadowdom_slotted_keyboard_focus.js]
 [browser_markup_shadowdom_slotupdate.js]
 [browser_markup_shadowdom_ua_widgets.js]
 [browser_markup_shadowdom_ua_widgets_with_nac.js]
+[browser_markup_subgrid_display_badge.js]
 [browser_markup_tag_delete_whitespace_node.js]
 [browser_markup_tag_edit_01.js]
 [browser_markup_tag_edit_02.js]
 [browser_markup_tag_edit_03.js]
 [browser_markup_tag_edit_04-backspace.js]
 [browser_markup_tag_edit_04-delete.js]
 [browser_markup_tag_edit_05.js]
 [browser_markup_tag_edit_06.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/browser_markup_subgrid_display_badge.js
@@ -0,0 +1,58 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URL = URL_ROOT + "doc_markup_subgrid.html";
+
+add_task(async function() {
+  info("Enable subgrid in order to see the subgrid display type.");
+  await pushPref("layout.css.grid-template-subgrid-value.enabled", true);
+  const { inspector } = await openInspectorForURL(TEST_URL);
+  const { highlighters, store } = inspector;
+
+  info("Check the subgrid display badge is shown and not active.");
+  await selectNode("main", inspector);
+  const eltContainer = await getContainerForSelector("main", inspector);
+  const subgridDisplayBadge = eltContainer.elt.querySelector(
+    ".inspector-badge.interactive[data-display]");
+  ok(!subgridDisplayBadge.classList.contains("active"),
+    "subgrid display badge is not active.");
+  ok(subgridDisplayBadge.classList.contains("interactive"),
+    "subgrid display badge is interactive.");
+
+  info("Check the initial state of the grid highlighter.");
+  ok(!highlighters.gridHighlighters.size,
+    "No CSS grid highlighter exists in the highlighters overlay.");
+
+  info("Toggling ON the CSS grid highlighter from the subgrid display badge.");
+  const onHighlighterShown = highlighters.once("grid-highlighter-shown");
+  let onCheckboxChange = waitUntilState(store, state =>
+    state.grids.length === 2 &&
+    state.grids[1].highlighted);
+  subgridDisplayBadge.click();
+  await onHighlighterShown;
+  await onCheckboxChange;
+
+  info("Check that the CSS grid highlighter is created and the display badge state.");
+  is(highlighters.gridHighlighters.size, 1,
+    "CSS grid highlighter is created in the highlighters overlay.");
+  ok(subgridDisplayBadge.classList.contains("active"),
+    "subgrid display badge is active.");
+  ok(subgridDisplayBadge.classList.contains("interactive"),
+   "subgrid display badge is interactive.");
+
+  info("Toggling OFF the CSS grid highlighter from the subgrid display badge.");
+  const onHighlighterHidden = highlighters.once("grid-highlighter-hidden");
+  onCheckboxChange = waitUntilState(store, state =>
+     state.grids.length == 2 &&
+     !state.grids[1].highlighted);
+  subgridDisplayBadge.click();
+  await onHighlighterHidden;
+  await onCheckboxChange;
+
+  ok(!subgridDisplayBadge.classList.contains("active"),
+    "subgrid display badge is not active.");
+  ok(subgridDisplayBadge.classList.contains("interactive"),
+    "subgrid display badge is interactive.");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/markup/test/doc_markup_subgrid.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta charset="utf-8" />
+  <style>
+    .container {
+      display: grid;
+      grid-gap: 5px;
+      grid-template: auto / 1fr 3fr 1fr;
+      background: lightyellow;
+    }
+
+    header, aside, section, footer {
+      background: lightblue;
+      font-family: sans-serif;
+      font-size: 3em;
+    }
+
+    header, footer {
+      grid-column: span 3;
+    }
+
+    main {
+      grid-column: span 3;
+      display: grid;
+      grid: subgrid;
+    }
+
+    .aside1 {
+      grid-column: 1;
+    }
+
+    .aside2 {
+      grid-column: 3;
+    }
+
+    section {
+      grid-column: 2;
+    }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <header>Header</header>
+    <main>
+      <aside class="aside1">aside</aside>
+      <section>section</section>
+      <aside class="aside2">aside2</aside>
+    </main>
+    <footer>footer</footer>
+  </div>
+</body>
+</html>
--- a/devtools/client/inspector/markup/views/element-editor.js
+++ b/devtools/client/inspector/markup/views/element-editor.js
@@ -355,17 +355,19 @@ ElementEditor.prototype = {
     this._displayBadge.dataset.display = displayType;
     this._displayBadge.title = DISPLAY_TYPES[displayType];
     this._displayBadge.classList.toggle("active",
       this.highlighters.flexboxHighlighterShown === this.node ||
       this.highlighters.gridHighlighters.has(this.node));
 
     if (displayType === "flex" || displayType === "inline-flex") {
       this._displayBadge.classList.toggle("interactive", true);
-    } else if (displayType === "grid" || displayType === "inline-grid") {
+    } else if (displayType === "grid" ||
+               displayType === "inline-grid" ||
+               displayType === "subgrid") {
       this._displayBadge.classList.toggle("interactive",
         this.highlighters.canGridHighlighterToggle(this.node));
     } else {
       this._displayBadge.classList.remove("interactive");
     }
   },
 
   /**
@@ -768,17 +770,18 @@ ElementEditor.prototype = {
       this.stopTrackingFlexboxHighlighterEvents();
 
       this._displayBadge.classList.toggle("active");
       await this.highlighters.toggleFlexboxHighlighter(this.node, "markup");
 
       this.startTrackingFlexboxHighlighterEvents();
     }
 
-    if (target.dataset.display === "grid" || target.dataset.display === "inline-grid") {
+    if (target.dataset.display === "grid" || target.dataset.display === "inline-grid" ||
+        target.dataset.display === "subgrid") {
       // Don't toggle the grid highlighter if the max number of new grid highlighters
       // allowed has been reached.
       if (!this.highlighters.canGridHighlighterToggle(this.node)) {
         return;
       }
 
       // Stop tracking highlighter events to avoid flickering of the active class.
       this.stopTrackingGridHighlighterEvents();
--- a/devtools/client/inspector/rules/components/Toolbar.js
+++ b/devtools/client/inspector/rules/components/Toolbar.js
@@ -84,16 +84,17 @@ class Toolbar extends PureComponent {
     return (
       dom.div(
         {
           id: "ruleview-toolbar-container",
           className: "devtools-toolbar",
         },
         dom.div({ id: "ruleview-toolbar" },
           SearchBox({}),
+          dom.div({ className: "devtools-separator" }),
           dom.div({ id: "ruleview-command-toolbar" },
             dom.button({
               id: "ruleview-add-rule-button",
               className: "devtools-button",
               disabled: !isAddRuleEnabled,
               onClick: this.onAddRuleClick,
               title: getStr("rule.addRule.tooltip"),
             }),
--- a/devtools/client/themes/inspector.css
+++ b/devtools/client/themes/inspector.css
@@ -64,46 +64,59 @@ window {
   background-image: url("chrome://devtools/skin/images/close-3-pane.svg");
   transform: unset;
 }
 
 #inspector-splitter-box .sidebar-toggle.pane-collapsed::before {
   background-image: url("chrome://devtools/skin/images/open-3-pane.svg");
 }
 
-/* Use flex layout for the Inspector toolbar. For now, it's done
-   specifically for the Inspector toolbar since general rule applied
-   on .devtools-toolbar breaks breadcrumbs and also toolbars in other
-   panels (e.g. webconsole, debugger), these are not ready for HTML
-   layout yet. */
-#inspector-toolbar.devtools-toolbar {
+.devtools-toolbar {
+  background-color: var(--theme-body-background);
   display: flex;
 }
 
-#inspector-toolbar.devtools-toolbar .devtools-toolbar-spacer {
-  flex-grow: 1;
-  display: inline-block;
+.devtools-toolbar .devtools-searchbox {
+  border: 1px solid transparent;
+}
+
+.devtools-toolbar .devtools-searchbox:focus-within {
+  border: 1px solid var(--blue-50);
+  margin-bottom: 0;
+  margin-top: 0;
+  box-shadow: none;
+}
+
+.devtools-toolbar .devtools-filterinput,
+.devtools-toolbar .devtools-searchinput {
+  border: none;
+  border-radius: 0 !important;
+  box-shadow: none;
+  background-color: var(--theme-body-background);
+}
+
+#inspector-searchlabel-container {
+  display: flex;
+}
+
+#inspector-searchlabel-container[hidden] {
+  display: none;
+}
+
+#inspector-searchlabel {
+  padding: 0 3px;
 }
 
 /* Add element toolbar button */
 #inspector-element-add-button::before {
   background-image: url("chrome://devtools/skin/images/add.svg");
   list-style-image: url("chrome://devtools/skin/images/add.svg");
   -moz-user-focus: normal;
 }
 
-#inspector-searchlabel {
-  overflow: hidden;
-  margin-inline-end: 2px;
-}
-
-#inspector-search {
-  flex: unset;
-}
-
 /* Eyedropper toolbar button */
 
 #inspector-eyedropper-toggle {
   /* Required to display tooltip when eyedropper is disabled in non-HTML documents */
   pointer-events: auto;
 }
 
 #inspector-eyedropper-toggle::before {
--- a/devtools/client/themes/rules.css
+++ b/devtools/client/themes/rules.css
@@ -34,72 +34,79 @@
   /* Override the min-width from .inspector-tabpanel, as the rule panel can support small
      widths */
   min-width: 100px;
 }
 
 /* Rule View Toolbar */
 
 #ruleview-toolbar-container {
-  display: flex;
   flex-direction: column;
   height: auto;
+  padding: 0;
 }
 
 #ruleview-toolbar {
   display: flex;
 }
 
 #ruleview-toolbar > .devtools-searchbox:first-child {
   padding-inline-start: 0px;
 }
 
 #ruleview-command-toolbar {
   display: flex;
 }
 
 .ruleview-reveal-panel {
+  background: var(--rule-header-background-color);
+  border-top: 1px solid var(--theme-splitter-color);
   display: flex;
   overflow: hidden;
   flex-wrap: wrap;
 }
 
 .ruleview-reveal-panel[hidden] {
   display: none;
 }
 
 .ruleview-reveal-panel label {
   -moz-user-select: none;
   flex-grow: 1;
   display: flex;
   align-items: center;
 }
 
+#ruleview-toolbar,
+#pseudo-class-panel,
+#ruleview-class-panel .classes {
+  padding: 0 3px;
+}
+
 /* Class toggle panel */
-#ruleview-class-panel:not([hidden]) {
-  /* The class panel can contain 0 to N classes, so we can't hardcode a height here like
-     we do for the pseudo-class panel. Unfortunately, that means we don't get the height
-     transition when toggling the panel */
+
+#ruleview-class-panel {
   flex-direction: column;
 }
 
 #ruleview-class-panel .add-class {
+  border: 1px solid transparent;
+  border-radius: 0;
   margin: 0;
-  border-width: 0 0 1px 0;
-  padding: 2px 6px;
-  border-radius: 0;
+}
+
+#ruleview-class-panel .add-class:focus {
+  border: 1px solid var(--blue-50);
 }
 
 #ruleview-class-panel .classes {
+  border-top: 1px solid var(--theme-splitter-color);
   display: flex;
   flex-direction: row;
   flex-wrap: wrap;
-}
-
-#ruleview-class-panel .classes {
   max-height: 100px;
   overflow-y: auto;
 }
 
 #ruleview-class-panel .classes label {
   flex: 0 0;
   max-width: 50%;
 }
--- a/devtools/server/actors/object/property-iterator.js
+++ b/devtools/server/actors/object/property-iterator.js
@@ -343,33 +343,45 @@ function enumWeakMapEntries(objectActor)
   // Xraying those temporary objects, and filtering access to |it.value|
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
   // waive Xrays on the iterable, and relying on the Debugger machinery to
   // make sure we handle the resulting objects carefully.
   const raw = objectActor.obj.unsafeDereference();
-  const keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakMapKeys(raw));
+  const basekeys = ChromeUtils.nondeterministicGetWeakMapKeys(raw);
+  const keys = isWorker ? basekeys : Cu.waiveXrays(basekeys);
+
+  const values = [];
+  if (isWorker) {
+    for (const k of keys) {
+      const nk = ObjectUtils.makeDebuggeeValueIfNeeded(objectActor.obj, k);
+      const v = DevToolsUtils.callPropertyOnObject(objectActor.obj, "get", nk);
+      values.push(ObjectUtils.unwrapDebuggeeValue(v));
+    }
+  } else {
+    for (const k of keys) {
+      values.push(WeakMap.prototype.get.call(raw, k));
+    }
+  }
 
   return {
     [Symbol.iterator]: function* () {
-      for (const key of keys) {
-        const value = WeakMap.prototype.get.call(raw, key);
-        yield [ key, value ].map(val => gripFromEntry(objectActor, val));
+      for (let i = 0; i < keys.length; i++) {
+        yield [ keys[i], values[i] ].map(val => gripFromEntry(objectActor, val));
       }
     },
     size: keys.length,
     propertyName(index) {
       return index;
     },
     propertyDescription(index) {
       const key = keys[index];
-      const val = WeakMap.prototype.get.call(raw, key);
+      const val = values[index];
       return {
         enumerable: true,
         value: {
           type: "mapEntry",
           preview: {
             key: gripFromEntry(objectActor, key),
             value: gripFromEntry(objectActor, val),
           },
@@ -426,18 +438,18 @@ function enumWeakSetEntries(objectActor)
   // Xraying those temporary objects, and filtering access to |it.value|
   // based on whether or not it's Xrayable and/or callable, which breaks
   // the for/of iteration.
   //
   // This code is designed to handle untrusted objects, so we can safely
   // waive Xrays on the iterable, and relying on the Debugger machinery to
   // make sure we handle the resulting objects carefully.
   const raw = objectActor.obj.unsafeDereference();
-  const keys = Cu.waiveXrays(
-    ChromeUtils.nondeterministicGetWeakSetKeys(raw));
+  const basekeys = ChromeUtils.nondeterministicGetWeakSetKeys(raw);
+  const keys = isWorker ? basekeys : Cu.waiveXrays(basekeys);
 
   return {
     [Symbol.iterator]: function* () {
       for (const item of keys) {
         yield gripFromEntry(objectActor, item);
       }
     },
     size: keys.length,
--- a/devtools/server/actors/object/utils.js
+++ b/devtools/server/actors/object/utils.js
@@ -54,16 +54,26 @@ function getPromiseState(obj) {
 function makeDebuggeeValueIfNeeded(obj, value) {
   if (value && (typeof value == "object" || typeof value == "function")) {
     return obj.makeDebuggeeValue(value);
   }
   return value;
 }
 
 /**
+ * Convert a debuggee value into the underlying raw object, if needed.
+ */
+function unwrapDebuggeeValue(value) {
+  if (value && typeof value == "object") {
+    return value.unsafeDereference();
+  }
+  return value;
+}
+
+/**
  * Create a grip for the given debuggee value.  If the value is an
  * object, will create an actor with the given lifetime.
  */
 function createValueGrip(value, pool, makeObjectGrip) {
   switch (typeof value) {
     case "boolean":
       return value;
 
@@ -227,16 +237,17 @@ function getStorageLength(object) {
     throw new Error("Expected a storage object, got a " + object.class);
   }
   return DevToolsUtils.getProperty(object, "length");
 }
 
 module.exports = {
   getPromiseState,
   makeDebuggeeValueIfNeeded,
+  unwrapDebuggeeValue,
   createValueGrip,
   stringIsLong,
   isTypedArray,
   isArray,
   isStorage,
   getArrayLength,
   getStorageLength,
   isArrayIndex,
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -762,17 +762,17 @@ errorOnFlag(exports, "wantVerbose");
 
 // Calls the property with the given `name` on the given `object`, where
 // `name` is a string, and `object` a Debugger.Object instance.
 //
 // This function uses only the Debugger.Object API to call the property. It
 // avoids the use of unsafeDeference. This is useful for example in workers,
 // where unsafeDereference will return an opaque security wrapper to the
 // referent.
-function callPropertyOnObject(object, name) {
+function callPropertyOnObject(object, name, ...args) {
   // Find the property.
   let descriptor;
   let proto = object;
   do {
     descriptor = proto.getOwnPropertyDescriptor(name);
     if (descriptor !== undefined) {
       break;
     }
@@ -782,17 +782,17 @@ function callPropertyOnObject(object, na
     throw new Error("No such property");
   }
   const value = descriptor.value;
   if (typeof value !== "object" || value === null || !("callable" in value)) {
     throw new Error("Not a callable object.");
   }
 
   // Call the property.
-  const result = value.call(object);
+  const result = value.call(object, ...args);
   if (result === null) {
     throw new Error("Code was terminated.");
   }
   if ("throw" in result) {
     throw result.throw;
   }
   return result.return;
 }
--- a/devtools/shared/worker/loader.js
+++ b/devtools/shared/worker/loader.js
@@ -570,16 +570,17 @@ this.worker = new WorkerDebuggerLoader({
     "atob": this.atob,
   },
   loadSubScript: loadSubScript,
   modules: {
     "Debugger": Debugger,
     "Services": Object.create(null),
     "chrome": chrome,
     "xpcInspector": xpcInspector,
+    "ChromeUtils": ChromeUtils,
   },
   paths: {
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "devtools": "resource://devtools",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "promise": "resource://gre/modules/Promise-backend.js",
     // ⚠ DISCUSSION ON DEV-DEVELOPER-TOOLS REQUIRED BEFORE MODIFYING ⚠
     "xpcshell-test": "resource://test",
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1764,22 +1764,21 @@ bool InterSliceGCRunnerFired(TimeStamp a
       }
     }
 
     uint32_t percent =
         uint32_t(idleDuration.ToSeconds() / duration.ToSeconds() * 100);
     Telemetry::Accumulate(Telemetry::GC_SLICE_DURING_IDLE, percent);
   }
 
-  // If we didn't use the whole budget, we're past the foreground sweeping slice
-  // and will need to wait for the background tasks to finish, or we're at the
-  // last slice. Return value on the latter case doesn't matter, and on the
-  // former we want to wait a bit before polling again.
-  // Returning false makes IdleTaskRunner postpone the next call a bit.
-  return int64_t(sliceDuration.ToMilliseconds()) >= budget;
+  // If the GC doesn't have any more work to do on the foreground thread (and
+  // e.g. is waiting for background sweeping to finish) then return false to
+  // make IdleTaskRunner postpone the next call a bit.
+  JSContext* cx = danger::GetJSContext();
+  return JS::IncrementalGCHasForegroundWork(cx);
 }
 
 // static
 void GCTimerFired(nsITimer* aTimer, void* aClosure) {
   nsJSContext::KillGCTimer();
   if (sShuttingDown) {
     nsJSContext::KillInterSliceGCRunner();
     return;
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -139,17 +139,18 @@ class CacheQuotaClient final : public Cl
   }
 
   virtual Type GetType() override { return DOMCACHE; }
 
   virtual nsresult InitOrigin(PersistenceType aPersistenceType,
                               const nsACString& aGroup,
                               const nsACString& aOrigin,
                               const AtomicBool& aCanceled,
-                              UsageInfo* aUsageInfo) override {
+                              UsageInfo* aUsageInfo,
+                              bool aForGetUsage) override {
     AssertIsOnIOThread();
 
     // The QuotaManager passes a nullptr UsageInfo if there is no quota being
     // enforced against the origin.
     if (!aUsageInfo) {
       return NS_OK;
     }
 
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -7849,17 +7849,18 @@ class QuotaClient final : public mozilla
 
   nsresult UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory) override;
 
   nsresult UpgradeStorageFrom2_1To2_2(nsIFile* aDirectory) override;
 
   nsresult InitOrigin(PersistenceType aPersistenceType,
                       const nsACString& aGroup, const nsACString& aOrigin,
                       const AtomicBool& aCanceled,
-                      UsageInfo* aUsageInfo) override;
+                      UsageInfo* aUsageInfo,
+                      bool aForGetUsage) override;
 
   nsresult GetUsageForOrigin(PersistenceType aPersistenceType,
                              const nsACString& aGroup,
                              const nsACString& aOrigin,
                              const AtomicBool& aCanceled,
                              UsageInfo* aUsageInfo) override;
 
   void OnOriginClearCompleted(PersistenceType aPersistenceType,
@@ -15947,17 +15948,18 @@ nsresult QuotaClient::UpgradeStorageFrom
 
   return NS_OK;
 }
 
 nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                                  const nsACString& aGroup,
                                  const nsACString& aOrigin,
                                  const AtomicBool& aCanceled,
-                                 UsageInfo* aUsageInfo) {
+                                 UsageInfo* aUsageInfo,
+                                 bool aForGetUsage) {
   AssertIsOnIOThread();
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv =
       GetDirectory(aPersistenceType, aOrigin, getter_AddRefs(directory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     REPORT_TELEMETRY_INIT_ERR(kExternalError, IDB_GetDirectory);
     return rv;
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -2640,18 +2640,18 @@ class QuotaClient final : public mozilla
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::QuotaClient, override)
 
   Type GetType() override;
 
   nsresult InitOrigin(PersistenceType aPersistenceType,
                       const nsACString& aGroup, const nsACString& aOrigin,
-                      const AtomicBool& aCanceled,
-                      UsageInfo* aUsageInfo) override;
+                      const AtomicBool& aCanceled, UsageInfo* aUsageInfo,
+                      bool aForGetUsage) override;
 
   nsresult GetUsageForOrigin(PersistenceType aPersistenceType,
                              const nsACString& aGroup,
                              const nsACString& aOrigin,
                              const AtomicBool& aCanceled,
                              UsageInfo* aUsageInfo) override;
 
   nsresult AboutToClearOrigins(
@@ -5146,26 +5146,33 @@ void Database::RequestAllowToClose() {
   }
 
   mRequestedAllowToClose = true;
 
   // Send the RequestAllowToClose message to the child to avoid racing with the
   // child actor. Except the case when the actor was already destroyed.
   if (mActorDestroyed) {
     MOZ_ASSERT(mAllowedToClose);
-  } else if (NS_WARN_IF(!SendRequestAllowToClose())) {
-    // Allow to close immediately if sending failed.
+    return;
+  }
+
+  if (NS_WARN_IF(!SendRequestAllowToClose()) && !mSnapshot) {
+    // This is not necessary, because there should be a runnable scheduled that
+    // will call ActorDestroy which calls AllowToClose. However we can speedup
+    // the shutdown a bit if we do it here directly, but only if there's no
+    // registered snapshot.
     AllowToClose();
   }
 }
 
 void Database::AllowToClose() {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mAllowedToClose);
   MOZ_ASSERT(mDatastore);
+  MOZ_ASSERT(!mSnapshot);
 
   mAllowedToClose = true;
 
   mDatastore->NoteFinishedDatabase(this);
 
   mDatastore = nullptr;
 
   MOZ_ASSERT(gLiveDatabases);
@@ -7519,17 +7526,17 @@ nsresult QuotaClient::Initialize() {
 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
   return QuotaClient::LS;
 }
 
 nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                                  const nsACString& aGroup,
                                  const nsACString& aOrigin,
                                  const AtomicBool& aCanceled,
-                                 UsageInfo* aUsageInfo) {
+                                 UsageInfo* aUsageInfo, bool aForGetUsage) {
   AssertIsOnIOThread();
   MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<nsIFile> directory;
   nsresult rv = quotaManager->GetDirectoryForOrigin(aPersistenceType, aOrigin,
@@ -7684,17 +7691,19 @@ nsresult QuotaClient::InitOrigin(Persist
       if (NS_WARN_IF(NS_FAILED(rv))) {
         REPORT_TELEMETRY_INIT_ERR(kExternalError, LS_Remove3);
         return rv;
       }
     }
 
     MOZ_ASSERT(usage >= 0);
 
-    InitUsageForOrigin(aOrigin, usage);
+    if (!aForGetUsage) {
+      InitUsageForOrigin(aOrigin, usage);
+    }
 
     aUsageInfo->AppendToDatabaseUsage(uint64_t(usage));
   } else if (usageFileExists) {
     rv = usageFile->Remove(false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       REPORT_TELEMETRY_INIT_ERR(kExternalError, LS_Remove4);
       return rv;
     }
--- a/dom/localstorage/LocalStorageManager2.cpp
+++ b/dom/localstorage/LocalStorageManager2.cpp
@@ -9,30 +9,97 @@
 #include "LSObject.h"
 #include "mozilla/dom/Promise.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
-class RequestResolver final : public LSRequestChildCallback {
+class AsyncRequestHelper final : public Runnable,
+                                 public LSRequestChildCallback {
+  enum class State {
+    /**
+     * The AsyncRequestHelper has been created and dispatched to the DOM File
+     * Thread.
+     */
+    Initial,
+    /**
+     * Start() has been invoked on the DOM File Thread and
+     * LocalStorageManager2::StartRequest has been invoked from there, sending
+     * an IPC message to PBackground to service the request.  We stay in this
+     * state until a response is received.
+     */
+    ResponsePending,
+    /**
+     * A response has been received and AsyncRequestHelper has been dispatched
+     * back to the owning event target to call Finish().
+     */
+    Finishing,
+    /**
+     * Finish() has been called on the main thread. The promise will be resolved
+     * according to the received response.
+     */
+    Complete
+  };
+
+  // The object we are issuing a request on behalf of.  Present because of the
+  // need to invoke LocalStorageManager2::StartRequest off the main thread.
+  // Dropped on return to the main-thread in Finish().
+  RefPtr<LocalStorageManager2> mManager;
+  // The thread the AsyncRequestHelper was created on.  This should be the main
+  // thread.
+  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
+  // The IPC actor handling the request with standard IPC allocation rules.
+  // Our reference is nulled in OnResponse which corresponds to the actor's
+  // __destroy__ method.
+  LSRequestChild* mActor;
   RefPtr<Promise> mPromise;
+  const LSRequestParams mParams;
+  LSRequestResponse mResponse;
+  nsresult mResultCode;
+  State mState;
 
  public:
-  explicit RequestResolver(Promise* aPromise) : mPromise(aPromise) {}
+  AsyncRequestHelper(LocalStorageManager2* aManager, Promise* aPromise,
+                     const LSRequestParams& aParams)
+      : Runnable("dom::LocalStorageManager2::AsyncRequestHelper"),
+        mManager(aManager),
+        mOwningEventTarget(GetCurrentThreadEventTarget()),
+        mActor(nullptr),
+        mPromise(aPromise),
+        mParams(aParams),
+        mResultCode(NS_OK),
+        mState(State::Initial) {}
 
-  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::RequestResolver, override);
+  bool IsOnOwningThread() const {
+    MOZ_ASSERT(mOwningEventTarget);
+
+    bool current;
+    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
+           current;
+  }
+
+  void AssertIsOnOwningThread() const {
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(IsOnOwningThread());
+  }
+
+  nsresult Dispatch();
 
  private:
-  ~RequestResolver() = default;
+  ~AsyncRequestHelper() = default;
+
+  nsresult Start();
 
-  void HandleResponse(nsresult aResponse);
+  void Finish();
 
-  void HandleResponse();
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIRUNNABLE
 
   // LSRequestChildCallback
   void OnResponse(const LSRequestResponse& aResponse) override;
 };
 
 class SimpleRequestResolver final : public LSSimpleRequestChildCallback {
   RefPtr<Promise> mPromise;
 
@@ -215,17 +282,30 @@ LocalStorageManager2::Preload(nsIPrincip
   }
 
   LSRequestCommonParams commonParams;
   commonParams.principalInfo() = *principalInfo;
   commonParams.originKey() = originKey;
 
   LSRequestPreloadDatastoreParams params(commonParams);
 
-  rv = StartRequest(promise, params);
+  RefPtr<AsyncRequestHelper> helper =
+      new AsyncRequestHelper(this, promise, params);
+
+  // This will start and finish the async request on the DOM File thread.
+  // This must be done on DOM File Thread because it's very likely that a
+  // content process will issue a prepare datastore request for the same
+  // principal while blocking the content process on the main thread.
+  // There would be a potential for deadlock if the preloading was initialized
+  // from the main thread of the parent process and a11y issued a synchronous
+  // message from the parent process to the content process (approximately at
+  // the same time) because the preload request wouldn't be able to respond
+  // to the Ready message by sending the Finish message which is needed to
+  // finish the preload request and unblock the prepare datastore request.
+  rv = helper->Dispatch();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   promise.forget(_retval);
   return NS_OK;
 }
 
@@ -253,35 +333,33 @@ LocalStorageManager2::IsPreloaded(nsIPri
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   promise.forget(_retval);
   return NS_OK;
 }
 
-nsresult LocalStorageManager2::StartRequest(Promise* aPromise,
-                                            const LSRequestParams& aParams) {
-  MOZ_ASSERT(NS_IsMainThread());
+LSRequestChild* LocalStorageManager2::StartRequest(
+    const LSRequestParams& aParams, LSRequestChildCallback* aCallback) {
+  AssertIsOnDOMFileThread();
 
   PBackgroundChild* backgroundActor =
       BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!backgroundActor)) {
-    return NS_ERROR_FAILURE;
+    return nullptr;
   }
 
-  RefPtr<RequestResolver> resolver = new RequestResolver(aPromise);
-
-  auto actor = new LSRequestChild(resolver);
+  auto actor = new LSRequestChild(aCallback);
 
   if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
-    return NS_ERROR_FAILURE;
+    return nullptr;
   }
 
-  return NS_OK;
+  return actor;
 }
 
 nsresult LocalStorageManager2::StartSimpleRequest(
     Promise* aPromise, const LSSimpleRequestParams& aParams) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aPromise);
 
   PBackgroundChild* backgroundActor =
@@ -297,50 +375,128 @@ nsresult LocalStorageManager2::StartSimp
   if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
                                                                   aParams)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
-void RequestResolver::HandleResponse(nsresult aResponse) {
-  MOZ_ASSERT(NS_IsMainThread());
+nsresult AsyncRequestHelper::Dispatch() {
+  AssertIsOnOwningThread();
 
-  if (!mPromise) {
-    return;
+  nsCOMPtr<nsIEventTarget> domFileThread =
+      IPCBlobInputStreamThread::GetOrCreate();
+  if (NS_WARN_IF(!domFileThread)) {
+    return NS_ERROR_FAILURE;
   }
 
-  mPromise->MaybeReject(aResponse);
+  nsresult rv = domFileThread->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
 }
 
-void RequestResolver::HandleResponse() {
-  MOZ_ASSERT(NS_IsMainThread());
+nsresult AsyncRequestHelper::Start() {
+  AssertIsOnDOMFileThread();
+  MOZ_ASSERT(mState == State::Initial);
 
-  if (!mPromise) {
-    return;
+  mState = State::ResponsePending;
+
+  LSRequestChild* actor = mManager->StartRequest(mParams, this);
+  if (NS_WARN_IF(!actor)) {
+    return NS_ERROR_FAILURE;
   }
 
-  mPromise->MaybeResolveWithUndefined();
+  mActor = actor;
+
+  return NS_OK;
+}
+
+void AsyncRequestHelper::Finish() {
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mState == State::Finishing);
+
+  if (NS_WARN_IF(NS_FAILED(mResultCode))) {
+    if (mPromise) {
+      mPromise->MaybeReject(mResultCode);
+    }
+  } else {
+    switch (mResponse.type()) {
+      case LSRequestResponse::Tnsresult:
+        if (mPromise) {
+          mPromise->MaybeReject(mResponse.get_nsresult());
+        }
+        break;
+
+      case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
+        if (mPromise) {
+          mPromise->MaybeResolveWithUndefined();
+        }
+        break;
+      default:
+        MOZ_CRASH("Unknown response type!");
+    }
+  }
+
+  mManager = nullptr;
+
+  mState = State::Complete;
 }
 
-void RequestResolver::OnResponse(const LSRequestResponse& aResponse) {
-  MOZ_ASSERT(NS_IsMainThread());
+NS_IMPL_ISUPPORTS_INHERITED0(AsyncRequestHelper, Runnable)
 
-  switch (aResponse.type()) {
-    case LSRequestResponse::Tnsresult:
-      HandleResponse(aResponse.get_nsresult());
+NS_IMETHODIMP
+AsyncRequestHelper::Run() {
+  nsresult rv;
+
+  switch (mState) {
+    case State::Initial:
+      rv = Start();
       break;
 
-    case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
-      HandleResponse();
-      break;
+    case State::Finishing:
+      Finish();
+      return NS_OK;
+
     default:
-      MOZ_CRASH("Unknown response type!");
+      MOZ_CRASH("Bad state!");
   }
+
+  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
+    if (NS_SUCCEEDED(mResultCode)) {
+      mResultCode = rv;
+    }
+
+    mState = State::Finishing;
+
+    if (IsOnOwningThread()) {
+      Finish();
+    } else {
+      MOZ_ALWAYS_SUCCEEDS(
+          mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
+    }
+  }
+
+  return NS_OK;
+}
+
+void AsyncRequestHelper::OnResponse(const LSRequestResponse& aResponse) {
+  AssertIsOnDOMFileThread();
+  MOZ_ASSERT(mState == State::ResponsePending);
+
+  mActor = nullptr;
+
+  mResponse = aResponse;
+
+  mState = State::Finishing;
+
+  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
 }
 
 void SimpleRequestResolver::HandleResponse(nsresult aResponse) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mPromise);
 
   mPromise->MaybeReject(aResponse);
 }
--- a/dom/localstorage/LocalStorageManager2.h
+++ b/dom/localstorage/LocalStorageManager2.h
@@ -8,16 +8,18 @@
 #define mozilla_dom_localstorage_LocalStorageManager2_h
 
 #include "nsIDOMStorageManager.h"
 #include "nsILocalStorageManager.h"
 
 namespace mozilla {
 namespace dom {
 
+class LSRequestChild;
+class LSRequestChildCallback;
 class LSRequestParams;
 class LSSimpleRequestParams;
 class Promise;
 
 /**
  * Under LSNG this exposes nsILocalStorageManager::Preload to ContentParent to
  * trigger preloading.  Otherwise, this is basically just a place for test logic
  * that doesn't make sense to put directly on the Storage WebIDL interface.
@@ -34,26 +36,27 @@ class LocalStorageManager2 final : publi
                                    public nsILocalStorageManager {
  public:
   LocalStorageManager2();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMSTORAGEMANAGER
   NS_DECL_NSILOCALSTORAGEMANAGER
 
- private:
-  ~LocalStorageManager2();
-
   /**
    * Helper to trigger an LSRequest and resolve/reject the provided promise when
    * the result comes in.  This routine is notable because the LSRequest
    * mechanism is normally used synchronously from content, but here it's
    * exposed asynchronously.
    */
-  nsresult StartRequest(Promise* aPromise, const LSRequestParams& aParams);
+  LSRequestChild* StartRequest(const LSRequestParams& aParams,
+                               LSRequestChildCallback* aCallback);
+
+ private:
+  ~LocalStorageManager2();
 
   /**
    * Helper to trigger an LSSimpleRequst and resolve/reject the provided promise
    * when the result comes in.
    */
   nsresult StartSimpleRequest(Promise* aPromise,
                               const LSSimpleRequestParams& aParams);
 };
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -4087,17 +4087,18 @@ nsresult QuotaManager::InitializeOrigin(
         MOZ_DIAGNOSTIC_ASSERT(true, "Found a deprecated client");
       }
 
       CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(NS_ERROR_UNEXPECTED);
     }
 
     Atomic<bool> dummy(false);
     rv = mClients[clientType]->InitOrigin(aPersistenceType, aGroup, aOrigin,
-                                          /* aCanceled */ dummy, usageInfo);
+                                          /* aCanceled */ dummy, usageInfo,
+                                          /* aForGetUsage */ false);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       // error should have reported in InitOrigin
       RECORD_IN_NIGHTLY(statusKeeper, rv);
       CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(rv);
     }
   }
   if (NS_WARN_IF(NS_FAILED(rv))) {
     REPORT_TELEMETRY_INIT_ERR(kInternalError, Ori_GetNextFile);
@@ -7075,17 +7076,17 @@ nsresult QuotaUsageRequestBase::GetUsage
       Client* client = aQuotaManager->GetClient(clientType);
       MOZ_ASSERT(client);
 
       if (initialized) {
         rv = client->GetUsageForOrigin(aPersistenceType, aGroup, aOrigin,
                                        mCanceled, aUsageInfo);
       } else {
         rv = client->InitOrigin(aPersistenceType, aGroup, aOrigin, mCanceled,
-                                aUsageInfo);
+                                aUsageInfo, /* aForGetUsage */ true);
       }
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
--- a/dom/quota/Client.h
+++ b/dom/quota/Client.h
@@ -138,17 +138,18 @@ class Client {
   virtual nsresult UpgradeStorageFrom2_1To2_2(nsIFile* aDirectory) {
     return NS_OK;
   }
 
   virtual nsresult InitOrigin(PersistenceType aPersistenceType,
                               const nsACString& aGroup,
                               const nsACString& aOrigin,
                               const AtomicBool& aCanceled,
-                              UsageInfo* aUsageInfo) = 0;
+                              UsageInfo* aUsageInfo,
+                              bool aForGetUsage) = 0;
 
   virtual nsresult GetUsageForOrigin(PersistenceType aPersistenceType,
                                      const nsACString& aGroup,
                                      const nsACString& aOrigin,
                                      const AtomicBool& aCanceled,
                                      UsageInfo* aUsageInfo) = 0;
 
   // This method is called when origins are about to be cleared
--- a/dom/simpledb/ActorsParent.cpp
+++ b/dom/simpledb/ActorsParent.cpp
@@ -438,17 +438,18 @@ class QuotaClient final : public mozilla
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
 
   Type GetType() override;
 
   nsresult InitOrigin(PersistenceType aPersistenceType,
                       const nsACString& aGroup, const nsACString& aOrigin,
                       const AtomicBool& aCanceled,
-                      UsageInfo* aUsageInfo) override;
+                      UsageInfo* aUsageInfo,
+                      bool aForGetUsage) override;
 
   nsresult GetUsageForOrigin(PersistenceType aPersistenceType,
                              const nsACString& aGroup,
                              const nsACString& aOrigin,
                              const AtomicBool& aCanceled,
                              UsageInfo* aUsageInfo) override;
 
   void OnOriginClearCompleted(PersistenceType aPersistenceType,
@@ -1605,17 +1606,18 @@ QuotaClient::~QuotaClient() {
 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
   return QuotaClient::SDB;
 }
 
 nsresult QuotaClient::InitOrigin(PersistenceType aPersistenceType,
                                  const nsACString& aGroup,
                                  const nsACString& aOrigin,
                                  const AtomicBool& aCanceled,
-                                 UsageInfo* aUsageInfo) {
+                                 UsageInfo* aUsageInfo,
+                                 bool aForGetUsage) {
   AssertIsOnIOThread();
 
   if (!aUsageInfo) {
     return NS_OK;
   }
 
   return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aCanceled,
                            aUsageInfo);
--- a/dom/smil/SMILBoolType.h
+++ b/dom/smil/SMILBoolType.h
@@ -34,14 +34,14 @@ class SMILBoolType : public SMILType {
   virtual nsresult ComputeDistance(const SMILValue& aFrom, const SMILValue& aTo,
                                    double& aDistance) const override;
   virtual nsresult Interpolate(const SMILValue& aStartVal,
                                const SMILValue& aEndVal, double aUnitDistance,
                                SMILValue& aResult) const override;
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILBoolType() {}
+  constexpr SMILBoolType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILBoolType_h
--- a/dom/smil/SMILCSSValueType.h
+++ b/dom/smil/SMILCSSValueType.h
@@ -120,14 +120,14 @@ class SMILCSSValueType : public SMILType
    * @param aValueToMatch A SMILValue (of type SMILCSSValueType) for which
    *                      a corresponding zero value will be created if |aValue|
    *                      is empty.
    */
   static void FinalizeValue(SMILValue& aValue, const SMILValue& aValueToMatch);
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILCSSValueType() {}
+  constexpr SMILCSSValueType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILCSSValueType_h
--- a/dom/smil/SMILCompositor.h
+++ b/dom/smil/SMILCompositor.h
@@ -33,17 +33,16 @@ class SMILCompositor : public PLDHashEnt
 
   explicit SMILCompositor(KeyTypePointer aKey)
       : mKey(*aKey), mForceCompositing(false) {}
   SMILCompositor(SMILCompositor&& toMove)
       : PLDHashEntryHdr(std::move(toMove)),
         mKey(std::move(toMove.mKey)),
         mAnimationFunctions(std::move(toMove.mAnimationFunctions)),
         mForceCompositing(false) {}
-  ~SMILCompositor() {}
 
   // PLDHashEntryHdr methods
   KeyTypeRef GetKey() const { return mKey; }
   bool KeyEquals(KeyTypePointer aKey) const;
   static KeyTypePointer KeyToPointer(KeyTypeRef aKey) { return &aKey; }
   static PLDHashNumber HashKey(KeyTypePointer aKey);
   enum { ALLOW_MEMMOVE = false };
 
--- a/dom/smil/SMILEnumType.h
+++ b/dom/smil/SMILEnumType.h
@@ -34,14 +34,14 @@ class SMILEnumType : public SMILType {
   virtual nsresult ComputeDistance(const SMILValue& aFrom, const SMILValue& aTo,
                                    double& aDistance) const override;
   virtual nsresult Interpolate(const SMILValue& aStartVal,
                                const SMILValue& aEndVal, double aUnitDistance,
                                SMILValue& aResult) const override;
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILEnumType() {}
+  constexpr SMILEnumType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILEnumType_h
--- a/dom/smil/SMILFloatType.h
+++ b/dom/smil/SMILFloatType.h
@@ -34,14 +34,14 @@ class SMILFloatType : public SMILType {
   virtual nsresult ComputeDistance(const SMILValue& aFrom, const SMILValue& aTo,
                                    double& aDistance) const override;
   virtual nsresult Interpolate(const SMILValue& aStartVal,
                                const SMILValue& aEndVal, double aUnitDistance,
                                SMILValue& aResult) const override;
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILFloatType() {}
+  constexpr SMILFloatType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILFloatType_h
--- a/dom/smil/SMILIntegerType.h
+++ b/dom/smil/SMILIntegerType.h
@@ -29,14 +29,14 @@ class SMILIntegerType : public SMILType 
                                SMILValue& aResult) const override;
 
   static SMILIntegerType* Singleton() {
     static SMILIntegerType sSingleton;
     return &sSingleton;
   }
 
  private:
-  constexpr SMILIntegerType() {}
+  constexpr SMILIntegerType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILIntegerType_h
--- a/dom/smil/SMILKeySpline.cpp
+++ b/dom/smil/SMILKeySpline.cpp
@@ -76,21 +76,21 @@ double SMILKeySpline::GetTForX(double aX
   double guessForT = intervalStart + dist * kSampleStepSize;
 
   // Check the slope to see what strategy to use. If the slope is too small
   // Newton-Raphson iteration won't converge on a root so we use bisection
   // instead.
   double initialSlope = GetSlope(guessForT, mX1, mX2);
   if (initialSlope >= NEWTON_MIN_SLOPE) {
     return NewtonRaphsonIterate(aX, guessForT);
-  } else if (initialSlope == 0.0) {
+  }
+  if (initialSlope == 0.0) {
     return guessForT;
-  } else {
-    return BinarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
   }
+  return BinarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
 }
 
 double SMILKeySpline::NewtonRaphsonIterate(double aX, double aGuessT) const {
   // Refine guess with Newton-Raphson iteration
   for (uint32_t i = 0; i < NEWTON_ITERATIONS; ++i) {
     // We're trying to find where f(t) = aX,
     // so we're actually looking for a root for: CalcBezier(t) - aX
     double currentX = CalcBezier(aGuessT, mX1, mX2) - aX;
--- a/dom/smil/SMILNullType.h
+++ b/dom/smil/SMILNullType.h
@@ -34,14 +34,14 @@ class SMILNullType : public SMILType {
   virtual nsresult ComputeDistance(const SMILValue& aFrom, const SMILValue& aTo,
                                    double& aDistance) const override;
   virtual nsresult Interpolate(const SMILValue& aStartVal,
                                const SMILValue& aEndVal, double aUnitDistance,
                                SMILValue& aResult) const override;
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILNullType() {}
+  constexpr SMILNullType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILNullType_h
--- a/dom/smil/SMILParserUtils.cpp
+++ b/dom/smil/SMILParserUtils.cpp
@@ -435,20 +435,20 @@ bool SMILParserUtils::ParseKeySplines(
   nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
       controlPointTokenizer(aSpec, ';');
   while (controlPointTokenizer.hasMoreTokens()) {
     nsCharSeparatedTokenizerTemplate<nsContentUtils::IsHTMLWhitespace>
         tokenizer(controlPointTokenizer.nextToken(), ',',
                   nsCharSeparatedTokenizer::SEPARATOR_OPTIONAL);
 
     double values[4];
-    for (int i = 0; i < 4; i++) {
+    for (auto& value : values) {
       if (!tokenizer.hasMoreTokens() ||
-          !SVGContentUtils::ParseNumber(tokenizer.nextToken(), values[i]) ||
-          values[i] > 1.0 || values[i] < 0.0) {
+          !SVGContentUtils::ParseNumber(tokenizer.nextToken(), value) ||
+          value > 1.0 || value < 0.0) {
         return false;
       }
     }
     if (tokenizer.hasMoreTokens() || tokenizer.separatorAfterCurrentToken() ||
         !aKeySplines.AppendElement(
             SMILKeySpline(values[0], values[1], values[2], values[3]),
             fallible)) {
       return false;
--- a/dom/smil/SMILSetAnimationFunction.cpp
+++ b/dom/smil/SMILSetAnimationFunction.cpp
@@ -11,25 +11,21 @@ namespace mozilla {
 inline bool SMILSetAnimationFunction::IsDisallowedAttribute(
     const nsAtom* aAttribute) const {
   //
   // A <set> element is similar to <animate> but lacks:
   //   AnimationValue.attrib(calcMode, values, keyTimes, keySplines, from, to,
   //                         by) -- BUT has 'to'
   //   AnimationAddition.attrib(additive, accumulate)
   //
-  if (aAttribute == nsGkAtoms::calcMode || aAttribute == nsGkAtoms::values ||
-      aAttribute == nsGkAtoms::keyTimes ||
-      aAttribute == nsGkAtoms::keySplines || aAttribute == nsGkAtoms::from ||
-      aAttribute == nsGkAtoms::by || aAttribute == nsGkAtoms::additive ||
-      aAttribute == nsGkAtoms::accumulate) {
-    return true;
-  }
-
-  return false;
+  return aAttribute == nsGkAtoms::calcMode || aAttribute == nsGkAtoms::values ||
+         aAttribute == nsGkAtoms::keyTimes ||
+         aAttribute == nsGkAtoms::keySplines || aAttribute == nsGkAtoms::from ||
+         aAttribute == nsGkAtoms::by || aAttribute == nsGkAtoms::additive ||
+         aAttribute == nsGkAtoms::accumulate;
 }
 
 bool SMILSetAnimationFunction::SetAttr(nsAtom* aAttribute,
                                        const nsAString& aValue,
                                        nsAttrValue& aResult,
                                        nsresult* aParseResult) {
   if (IsDisallowedAttribute(aAttribute)) {
     aResult.SetTo(aValue);
--- a/dom/smil/SMILStringType.h
+++ b/dom/smil/SMILStringType.h
@@ -34,14 +34,14 @@ class SMILStringType : public SMILType {
   virtual nsresult ComputeDistance(const SMILValue& aFrom, const SMILValue& aTo,
                                    double& aDistance) const override;
   virtual nsresult Interpolate(const SMILValue& aStartVal,
                                const SMILValue& aEndVal, double aUnitDistance,
                                SMILValue& aResult) const override;
 
  private:
   // Private constructor: prevent instances beyond my singleton.
-  constexpr SMILStringType() {}
+  constexpr SMILStringType() = default;
 };
 
 }  // namespace mozilla
 
 #endif  // mozilla_SMILStringType_h
--- a/dom/svg/DOMSVGAnimatedEnumeration.h
+++ b/dom/svg/DOMSVGAnimatedEnumeration.h
@@ -25,17 +25,17 @@ class DOMSVGAnimatedEnumeration : public
 
   virtual uint16_t BaseVal() = 0;
   virtual void SetBaseVal(uint16_t aBaseVal, ErrorResult& aRv) = 0;
   virtual uint16_t AnimVal() = 0;
 
  protected:
   explicit DOMSVGAnimatedEnumeration(SVGElement* aSVGElement)
       : mSVGElement(aSVGElement) {}
-  virtual ~DOMSVGAnimatedEnumeration(){};
+  virtual ~DOMSVGAnimatedEnumeration() = default;
 
   RefPtr<SVGElement> mSVGElement;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_DOMSVGAnimatedEnumeration_h
--- a/dom/svg/DOMSVGAnimatedInteger.h
+++ b/dom/svg/DOMSVGAnimatedInteger.h
@@ -25,17 +25,17 @@ class DOMSVGAnimatedInteger : public nsI
 
   virtual int32_t BaseVal() = 0;
   virtual void SetBaseVal(int32_t aBaseVal) = 0;
   virtual int32_t AnimVal() = 0;
 
  protected:
   explicit DOMSVGAnimatedInteger(SVGElement* aSVGElement)
       : mSVGElement(aSVGElement) {}
-  virtual ~DOMSVGAnimatedInteger(){};
+  virtual ~DOMSVGAnimatedInteger() = default;
 
   RefPtr<SVGElement> mSVGElement;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_DOMSVGAnimatedInteger_h
--- a/dom/svg/DOMSVGAnimatedNumber.h
+++ b/dom/svg/DOMSVGAnimatedNumber.h
@@ -26,17 +26,17 @@ class DOMSVGAnimatedNumber : public nsIS
 
   virtual float BaseVal() = 0;
   virtual void SetBaseVal(float aBaseVal) = 0;
   virtual float AnimVal() = 0;
 
  protected:
   explicit DOMSVGAnimatedNumber(SVGElement* aSVGElement)
       : mSVGElement(aSVGElement) {}
-  virtual ~DOMSVGAnimatedNumber(){};
+  virtual ~DOMSVGAnimatedNumber() = default;
 
   RefPtr<SVGElement> mSVGElement;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_DOMSVGAnimatedNumber_h
--- a/dom/svg/SVGAElement.cpp
+++ b/dom/svg/SVGAElement.cpp
@@ -49,18 +49,16 @@ NS_IMPL_ADDREF_INHERITED(SVGAElement, SV
 NS_IMPL_RELEASE_INHERITED(SVGAElement, SVGAElementBase)
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGAElement::SVGAElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGAElementBase(std::move(aNodeInfo)), Link(this) {}
 
-SVGAElement::~SVGAElement() {}
-
 already_AddRefed<DOMSVGAnimatedString> SVGAElement::Href() {
   return mStringAttributes[HREF].IsExplicitlySet()
              ? mStringAttributes[HREF].ToDOMAnimatedString(this)
              : mStringAttributes[XLINK_HREF].ToDOMAnimatedString(this);
 }
 
 //----------------------------------------------------------------------
 // Link methods
--- a/dom/svg/SVGAElement.h
+++ b/dom/svg/SVGAElement.h
@@ -88,17 +88,17 @@ class SVGAElement final : public SVGAEle
   void SetText(const nsAString& aText, mozilla::ErrorResult& rv);
 
   void NodeInfoChanged(Document* aOldDoc) final {
     ClearHasPendingLinkUpdate();
     SVGAElementBase::NodeInfoChanged(aOldDoc);
   }
 
  protected:
-  virtual ~SVGAElement();
+  virtual ~SVGAElement() = default;
 
   virtual StringAttributesInfo GetStringInfo() override;
 
   enum { HREF, XLINK_HREF, TARGET };
   SVGAnimatedString mStringAttributes[3];
   static StringInfo sStringInfo[3];
 
   RefPtr<nsDOMTokenList> mRelList;
--- a/dom/svg/SVGAnimatedClass.cpp
+++ b/dom/svg/SVGAnimatedClass.cpp
@@ -32,17 +32,17 @@ struct DOMAnimatedString final : public 
 
   void SetBaseVal(const nsAString& aValue) override {
     mVal->SetBaseValue(aValue, mSVGElement, true);
   }
 
   void GetAnimVal(nsAString& aResult) override;
 
  private:
-  ~DOMAnimatedString() {}
+  ~DOMAnimatedString() = default;
 };
 
 NS_SVG_VAL_IMPL_CYCLE_COLLECTION_WRAPPERCACHED(DOMAnimatedString, mSVGElement)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMAnimatedString)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMAnimatedString)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMAnimatedString)
--- a/dom/svg/SVGAnimatedViewBox.h
+++ b/dom/svg/SVGAnimatedViewBox.h
@@ -29,22 +29,16 @@ class SVGElement;
 struct SVGViewBox {
   float x, y;
   float width, height;
   bool none;
 
   SVGViewBox() : x(0.0), y(0.0), width(0.0), height(0.0), none(true) {}
   SVGViewBox(float aX, float aY, float aWidth, float aHeight)
       : x(aX), y(aY), width(aWidth), height(aHeight), none(false) {}
-  SVGViewBox(const SVGViewBox& rhs)
-      : x(rhs.x),
-        y(rhs.y),
-        width(rhs.width),
-        height(rhs.height),
-        none(rhs.none) {}
   bool operator==(const SVGViewBox& aOther) const;
 
   static nsresult FromString(const nsAString& aStr, SVGViewBox* aViewBox);
 };
 
 class SVGAnimatedViewBox {
  public:
   typedef mozilla::dom::SVGElement SVGElement;
--- a/dom/svg/SVGAnimationElement.cpp
+++ b/dom/svg/SVGAnimationElement.cpp
@@ -33,18 +33,16 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(SVGAn
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGAnimationElement::SVGAnimationElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGAnimationElementBase(std::move(aNodeInfo)), mHrefTarget(this) {}
 
-SVGAnimationElement::~SVGAnimationElement() {}
-
 nsresult SVGAnimationElement::Init() {
   nsresult rv = SVGAnimationElementBase::Init();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mTimedElement.SetAnimationElement(this);
   AnimationFunction().SetAnimationElement(this);
   mTimedElement.SetTimeClient(&AnimationFunction());
 
--- a/dom/svg/SVGAnimationElement.h
+++ b/dom/svg/SVGAnimationElement.h
@@ -18,17 +18,17 @@ namespace dom {
 
 typedef SVGElement SVGAnimationElementBase;
 
 class SVGAnimationElement : public SVGAnimationElementBase, public SVGTests {
  protected:
   explicit SVGAnimationElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
   nsresult Init();
-  virtual ~SVGAnimationElement();
+  virtual ~SVGAnimationElement() = default;
 
  public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAnimationElement,
                                            SVGAnimationElementBase)
 
--- a/dom/svg/SVGGraphicsElement.cpp
+++ b/dom/svg/SVGGraphicsElement.cpp
@@ -21,18 +21,16 @@ NS_INTERFACE_MAP_END_INHERITING(SVGGraph
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGGraphicsElement::SVGGraphicsElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGGraphicsElementBase(std::move(aNodeInfo)) {}
 
-SVGGraphicsElement::~SVGGraphicsElement() {}
-
 bool SVGGraphicsElement::IsSVGFocusable(bool* aIsFocusable,
                                         int32_t* aTabIndex) {
   Document* doc = GetComposedDoc();
   if (!doc || doc->HasFlag(NODE_IS_EDITABLE)) {
     // In designMode documents we only allow focusing the document.
     if (aTabIndex) {
       *aTabIndex = -1;
     }
--- a/dom/svg/SVGGraphicsElement.h
+++ b/dom/svg/SVGGraphicsElement.h
@@ -14,17 +14,17 @@ namespace mozilla {
 namespace dom {
 
 typedef SVGTransformableElement SVGGraphicsElementBase;
 
 class SVGGraphicsElement : public SVGGraphicsElementBase, public SVGTests {
  protected:
   explicit SVGGraphicsElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
-  ~SVGGraphicsElement();
+  ~SVGGraphicsElement() = default;
 
  public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
   bool IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) override;
   SVGElement* AsSVGElement() final { return this; }
 
--- a/dom/svg/SVGLength.h
+++ b/dom/svg/SVGLength.h
@@ -38,25 +38,16 @@ class SVGLength {
         mUnit(dom::SVGLength_Binding::SVG_LENGTHTYPE_UNKNOWN)  // caught by
                                                                // IsValid()
   {}
 
   SVGLength(float aValue, uint8_t aUnit) : mValue(aValue), mUnit(aUnit) {
     NS_ASSERTION(IsValid(), "Constructed an invalid length");
   }
 
-  SVGLength(const SVGLength &aOther)
-      : mValue(aOther.mValue), mUnit(aOther.mUnit) {}
-
-  SVGLength &operator=(const SVGLength &rhs) {
-    mValue = rhs.mValue;
-    mUnit = rhs.mUnit;
-    return *this;
-  }
-
   bool operator==(const SVGLength &rhs) const {
     return mValue == rhs.mValue && mUnit == rhs.mUnit;
   }
 
   void GetValueAsString(nsAString &aValue) const;
 
   /**
    * This method returns true, unless there was a parse failure, in which
--- a/dom/svg/SVGPoint.h
+++ b/dom/svg/SVGPoint.h
@@ -24,24 +24,16 @@ class SVGPoint {
 
  public:
   SVGPoint() : mX(0.0f), mY(0.0f) {}
 
   SVGPoint(float aX, float aY) : mX(aX), mY(aY) {
     NS_ASSERTION(IsValid(), "Constructed an invalid SVGPoint");
   }
 
-  SVGPoint(const SVGPoint& aOther) : mX(aOther.mX), mY(aOther.mY) {}
-
-  SVGPoint& operator=(const SVGPoint& rhs) {
-    mX = rhs.mX;
-    mY = rhs.mY;
-    return *this;
-  }
-
   bool operator==(const SVGPoint& rhs) const {
     return mX == rhs.mX && mY == rhs.mY;
   }
 
   SVGPoint& operator+=(const SVGPoint& rhs) {
     mX += rhs.mX;
     mY += rhs.mY;
     return *this;
--- a/dom/svg/SVGPolyElement.cpp
+++ b/dom/svg/SVGPolyElement.cpp
@@ -16,18 +16,16 @@ namespace dom {
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGPolyElement::SVGPolyElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGPolyElementBase(std::move(aNodeInfo)) {}
 
-SVGPolyElement::~SVGPolyElement() {}
-
 already_AddRefed<DOMSVGPointList> SVGPolyElement::Points() {
   void* key = mPoints.GetBaseValKey();
   RefPtr<DOMSVGPointList> points =
       DOMSVGPointList::GetDOMWrapper(key, this, false);
   return points.forget();
 }
 
 already_AddRefed<DOMSVGPointList> SVGPolyElement::AnimatedPoints() {
--- a/dom/svg/SVGPolyElement.h
+++ b/dom/svg/SVGPolyElement.h
@@ -17,17 +17,17 @@ class DOMSVGPointList;
 namespace dom {
 
 typedef SVGGeometryElement SVGPolyElementBase;
 
 class SVGPolyElement : public SVGPolyElementBase {
  protected:
   explicit SVGPolyElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
 
-  virtual ~SVGPolyElement();
+  virtual ~SVGPolyElement() = default;
 
  public:
   // interfaces
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(SVGPolyElement, SVGPolyElementBase)
 
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* name) const override;
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -128,18 +128,16 @@ SVGSVGElement::SVGSVGElement(
       mCurrentScale(1.0f),
       mPreviousTranslate(0.0f, 0.0f),
       mPreviousScale(1.0f),
       mStartAnimationOnBindToTree(aFromParser == NOT_FROM_PARSER ||
                                   aFromParser == FROM_PARSER_FRAGMENT ||
                                   aFromParser == FROM_PARSER_XSLT),
       mImageNeedsTransformInvalidation(false) {}
 
-SVGSVGElement::~SVGSVGElement() {}
-
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT_AND_PARSER(SVGSVGElement)
 
 //----------------------------------------------------------------------
 // nsIDOMSVGSVGElement methods:
 
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -83,17 +83,17 @@ class SVGSVGElement final : public SVGSV
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) override;
 
   friend nsresult(::NS_NewSVGSVGElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
       mozilla::dom::FromParser aFromParser));
 
-  ~SVGSVGElement();
+  ~SVGSVGElement() = default;
 
  public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGSVGElement, SVGSVGElementBase)
 
   /**
    * For use by zoom controls to allow currentScale, currentTranslate.x and
--- a/dom/svg/SVGScriptElement.cpp
+++ b/dom/svg/SVGScriptElement.cpp
@@ -38,18 +38,16 @@ NS_IMPL_ISUPPORTS_INHERITED(SVGScriptEle
 
 SVGScriptElement::SVGScriptElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
     FromParser aFromParser)
     : SVGScriptElementBase(std::move(aNodeInfo)), ScriptElement(aFromParser) {
   AddMutationObserver(this);
 }
 
-SVGScriptElement::~SVGScriptElement() {}
-
 //----------------------------------------------------------------------
 // nsINode methods
 
 nsresult SVGScriptElement::Clone(dom::NodeInfo* aNodeInfo,
                                  nsINode** aResult) const {
   *aResult = nullptr;
 
   SVGScriptElement* it =
--- a/dom/svg/SVGScriptElement.h
+++ b/dom/svg/SVGScriptElement.h
@@ -67,17 +67,17 @@ class SVGScriptElement final : public SV
   // WebIDL
   void GetType(nsAString& aType);
   void SetType(const nsAString& aType, ErrorResult& rv);
   void GetCrossOrigin(nsAString& aCrossOrigin);
   void SetCrossOrigin(const nsAString& aCrossOrigin, ErrorResult& aError);
   already_AddRefed<DOMSVGAnimatedString> Href();
 
  protected:
-  ~SVGScriptElement();
+  ~SVGScriptElement() = default;
 
   virtual StringAttributesInfo GetStringInfo() override;
 
   // SVG Script elements don't have the ability to set async properties on
   // themselves, so this will always return false.
   virtual bool GetAsyncState() override { return false; }
 
   enum { HREF, XLINK_HREF };
--- a/dom/svg/SVGStyleElement.cpp
+++ b/dom/svg/SVGStyleElement.cpp
@@ -45,18 +45,16 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 // Implementation
 
 SVGStyleElement::SVGStyleElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGStyleElementBase(std::move(aNodeInfo)) {
   AddMutationObserver(this);
 }
 
-SVGStyleElement::~SVGStyleElement() {}
-
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGStyleElement)
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
--- a/dom/svg/SVGStyleElement.h
+++ b/dom/svg/SVGStyleElement.h
@@ -24,17 +24,17 @@ class SVGStyleElement final : public SVG
                               public nsStyleLinkElement,
                               public nsStubMutationObserver {
  protected:
   friend nsresult(::NS_NewSVGStyleElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGStyleElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
-  ~SVGStyleElement();
+  ~SVGStyleElement() = default;
 
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) override;
 
  public:
   NS_DECL_ISUPPORTS_INHERITED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGStyleElement, SVGStyleElementBase)
--- a/dom/svg/SVGSwitchElement.cpp
+++ b/dom/svg/SVGSwitchElement.cpp
@@ -37,18 +37,16 @@ NS_INTERFACE_MAP_END_INHERITING(SVGSwitc
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGSwitchElement::SVGSwitchElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGSwitchElementBase(std::move(aNodeInfo)) {}
 
-SVGSwitchElement::~SVGSwitchElement() {}
-
 void SVGSwitchElement::MaybeInvalidate() {
   // We must not change mActiveChild until after
   // InvalidateAndScheduleBoundsUpdate has been called, otherwise
   // it will not correctly invalidate the old mActiveChild area.
 
   nsIContent* newActiveChild = FindActiveChild();
 
   if (newActiveChild == mActiveChild) {
--- a/dom/svg/SVGSwitchElement.h
+++ b/dom/svg/SVGSwitchElement.h
@@ -24,17 +24,17 @@ class SVGSwitchElement final : public SV
   friend class ::nsSVGSwitchFrame;
 
  protected:
   friend nsresult(::NS_NewSVGSwitchElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGSwitchElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
-  ~SVGSwitchElement();
+  ~SVGSwitchElement() = default;
   virtual JSObject* WrapNode(JSContext* aCx,
                              JS::Handle<JSObject*> aGivenProto) override;
 
  public:
   nsIContent* GetActiveChild() const { return mActiveChild; }
   void MaybeInvalidate();
 
   // interfaces:
--- a/dom/svg/SVGSymbolElement.cpp
+++ b/dom/svg/SVGSymbolElement.cpp
@@ -25,17 +25,15 @@ NS_IMPL_ISUPPORTS_INHERITED(SVGSymbolEle
 
 //----------------------------------------------------------------------
 // Implementation
 
 SVGSymbolElement::SVGSymbolElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGSymbolElementBase(std::move(aNodeInfo)) {}
 
-SVGSymbolElement::~SVGSymbolElement() {}
-
 //----------------------------------------------------------------------
 // nsINode methods
 
 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGSymbolElement)
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/svg/SVGSymbolElement.h
+++ b/dom/svg/SVGSymbolElement.h
@@ -19,17 +19,17 @@ typedef SVGViewportElement SVGSymbolElem
 
 class SVGSymbolElement final : public SVGSymbolElementBase {
  protected:
   friend nsresult(::NS_NewSVGSymbolElement(
       nsIContent** aResult,
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo));
   explicit SVGSymbolElement(
       already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
-  ~SVGSymbolElement();
+  ~SVGSymbolElement() = default;
   virtual JSObject* WrapNode(JSContext* cx,
                              JS::Handle<JSObject*> aGivenProto) override;
 
  public:
   // interfaces:
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual nsresult Clone(dom::NodeInfo*, nsINode** aResult) const override;
--- a/dom/svg/SVGTitleElement.cpp
+++ b/dom/svg/SVGTitleElement.cpp
@@ -27,18 +27,16 @@ NS_IMPL_ISUPPORTS_INHERITED(SVGTitleElem
 // Implementation
 
 SVGTitleElement::SVGTitleElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGTitleElementBase(std::move(aNodeInfo)) {
   AddMutationObserver(this);
 }
 
-SVGTitleElement::~SVGTitleElement() {}
-
 void SVGTitleElement::CharacterDataChanged(nsIContent* aContent,
                                            const CharacterDataChangeInfo&) {
   SendTitleChangeEvent(false);
 }
 
 void SVGTitleElement::ContentAppended(nsIContent* aFirstNewContent) {
   SendTitleChangeEvent(false);
 }
--- a/dom/svg/SVGTitleElement.h
+++ b/dom/svg/SVGTitleElement.h
@@ -21,17 +21,17 @@ typedef SVGElement SVGTitleElementBase;
 class SVGTitleElement final : public SVGTitleElementBase,
                               public nsStubMutationObserver {
  protected:
   friend nsresult(::NS_NewSVGTitleElement(
       nsIContent **aResult,
       already_AddRefed<mozilla::dom::NodeInfo> &&aNodeInfo));
   explicit SVGTitleElement(
       already_AddRefed<mozilla::dom::NodeInfo> &&aNodeInfo);
-  ~SVGTitleElement();
+  ~SVGTitleElement() = default;
 
   virtual JSObject *WrapNode(JSContext *aCx,
                              JS::Handle<JSObject *> aGivenProto) override;
 
  public:
   // interfaces:
 
   NS_DECL_ISUPPORTS_INHERITED
--- a/dom/svg/SVGViewportElement.cpp
+++ b/dom/svg/SVGViewportElement.cpp
@@ -54,18 +54,16 @@ SVGElement::LengthInfo SVGViewportElemen
 
 SVGViewportElement::SVGViewportElement(
     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
     : SVGGraphicsElement(std::move(aNodeInfo)),
       mViewportWidth(0),
       mViewportHeight(0),
       mHasChildrenOnlyTransform(false) {}
 
-SVGViewportElement::~SVGViewportElement() {}
-
 //----------------------------------------------------------------------
 
 already_AddRefed<SVGAnimatedRect> SVGViewportElement::ViewBox() {
   return mViewBox.ToSVGAnimatedRect(this);
 }
 
 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
 SVGViewportElement::PreserveAspectRatio() {
--- a/dom/svg/SVGViewportElement.h
+++ b/dom/svg/SVGViewportElement.h
@@ -43,17 +43,17 @@ class svgFloatSize {
 };
 
 class SVGViewportElement : public SVGGraphicsElement {
   friend class ::nsSVGOuterSVGFrame;
   friend class ::nsSVGViewportFrame;
 
  protected:
   SVGViewportElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo);
-  ~SVGViewportElement();
+  ~SVGViewportElement() = default;
 
  public:
   // nsIContent interface
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* aAttribute) const override;
 
   // SVGElement specializations:
   virtual gfxMatrix PrependLocalTransformsTo(
       const gfxMatrix& aMatrix,
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -43,14 +43,18 @@ bool WorkerPrivate::RegisterBindings(JSC
 
 bool WorkerPrivate::RegisterDebuggerBindings(JSContext* aCx,
                                              JS::Handle<JSObject*> aGlobal) {
   // Init Web IDL bindings
   if (!RegisterWorkerDebuggerBindings(aCx, aGlobal)) {
     return false;
   }
 
+  if (!ChromeUtils_Binding::GetConstructorObject(aCx)) {
+    return false;
+  }
+
   if (!JS_DefineDebuggerObject(aCx, aGlobal)) {
     return false;
   }
 
   return true;
 }
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -561,16 +561,24 @@ extern JS_PUBLIC_API void StartIncrement
  *
  * Note: SpiderMonkey's GC is not realtime. Slices in practice may be longer or
  *       shorter than the requested interval.
  */
 extern JS_PUBLIC_API void IncrementalGCSlice(JSContext* cx, GCReason reason,
                                              int64_t millis = 0);
 
 /**
+ * Return whether an incremental GC has work to do on the foreground thread and
+ * would make progress if a slice was run now. If this returns false then the GC
+ * is waiting for background threads to finish their work and a slice started
+ * now would return immediately.
+ */
+extern JS_PUBLIC_API bool IncrementalGCHasForegroundWork(JSContext* cx);
+
+/**
  * If IsIncrementalGCInProgress(cx), this call finishes the ongoing collection
  * by performing an arbitrarily long slice. If !IsIncrementalGCInProgress(cx),
  * this is equivalent to NonIncrementalGC. When this function returns,
  * IsIncrementalGCInProgress(cx) will always be false.
  */
 extern JS_PUBLIC_API void FinishIncrementalGC(JSContext* cx, GCReason reason);
 
 /**
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -7185,16 +7185,33 @@ void GCRuntime::incrementalSlice(SliceBu
       incrementalState = State::NotActive;
       break;
   }
 
   MOZ_ASSERT(safeToYield);
   MOZ_ASSERT(marker.markColor() == MarkColor::Black);
 }
 
+bool GCRuntime::hasForegroundWork() const {
+  switch (incrementalState) {
+    case State::NotActive:
+      // Incremental GC is not running and no work is pending.
+      return false;
+    case State::Finalize:
+      // We yield in the Finalize state to wait for background sweeping.
+      return !isBackgroundSweeping();
+    case State::Decommit:
+      // We yield in the Decommit state to wait for background decommit.
+      return !decommitTask.isRunning();
+    default:
+      // In all other states there is still work to do.
+      return true;
+  }
+}
+
 gc::AbortReason gc::IsIncrementalGCUnsafe(JSRuntime* rt) {
   MOZ_ASSERT(!rt->mainContextFromOwnThread()->suppressGC);
 
   if (!rt->gc.isIncrementalGCAllowed()) {
     return gc::AbortReason::IncrementalDisabled;
   }
 
   return gc::AbortReason::None;
@@ -8559,16 +8576,22 @@ JS_PUBLIC_API void JS::StartIncrementalG
   cx->runtime()->gc.startGC(gckind, reason, millis);
 }
 
 JS_PUBLIC_API void JS::IncrementalGCSlice(JSContext* cx, GCReason reason,
                                           int64_t millis) {
   cx->runtime()->gc.gcSlice(reason, millis);
 }
 
+JS_PUBLIC_API bool JS::IncrementalGCHasForegroundWork(JSContext* cx) {
+  MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
+  CHECK_THREAD(cx);
+  return cx->runtime()->gc.hasForegroundWork();
+}
+
 JS_PUBLIC_API void JS::FinishIncrementalGC(JSContext* cx, GCReason reason) {
   cx->runtime()->gc.finishGC(reason);
 }
 
 JS_PUBLIC_API void JS::AbortIncrementalGC(JSContext* cx) {
   if (IsIncrementalGCInProgress(cx)) {
     cx->runtime()->gc.abortGC();
   }
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -303,17 +303,17 @@ class GCRuntime {
   bool shutdownCollectedEverything() const { return arenasEmptyAtShutdown; }
 #endif
 
  public:
   // Internal public interface
   State state() const { return incrementalState; }
   bool isHeapCompacting() const { return state() == State::Compact; }
   bool isForegroundSweeping() const { return state() == State::Sweep; }
-  bool isBackgroundSweeping() { return sweepTask.isRunning(); }
+  bool isBackgroundSweeping() const { return sweepTask.isRunning(); }
   void waitBackgroundSweepEnd();
   void waitBackgroundAllocEnd() { allocTask.cancelAndWait(); }
   void waitBackgroundFreeEnd();
 
   void lockGC() { lock.lock(); }
 
   void unlockGC() { lock.unlock(); }
 
@@ -327,16 +327,17 @@ class GCRuntime {
   void disallowIncrementalGC() { incrementalAllowed = false; }
 
   bool isIncrementalGCEnabled() const {
     return (mode == JSGC_MODE_INCREMENTAL ||
             mode == JSGC_MODE_ZONE_INCREMENTAL) &&
            incrementalAllowed;
   }
   bool isIncrementalGCInProgress() const { return state() != State::NotActive; }
+  bool hasForegroundWork() const;
 
   bool isCompactingGCEnabled() const;
 
   bool isShrinkingGC() const { return invocationKind == GC_SHRINK; }
 
   bool initSweepActions();
 
   void setGrayRootsTracer(JSTraceDataOp traceOp, void* data);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1255,51 +1255,19 @@ void WasmInstanceScope::Data::trace(JSTr
   TraceBindingNames(trc, trailingNames.start(), length);
 }
 void WasmFunctionScope::Data::trace(JSTracer* trc) {
   TraceBindingNames(trc, trailingNames.start(), length);
 }
 void Scope::traceChildren(JSTracer* trc) {
   TraceNullableEdge(trc, &enclosing_, "scope enclosing");
   TraceNullableEdge(trc, &environmentShape_, "scope env shape");
-  switch (kind_) {
-    case ScopeKind::Function:
-      as<FunctionScope>().data().trace(trc);
-      break;
-    case ScopeKind::FunctionBodyVar:
-    case ScopeKind::ParameterExpressionVar:
-      as<VarScope>().data().trace(trc);
-      break;
-    case ScopeKind::Lexical:
-    case ScopeKind::SimpleCatch:
-    case ScopeKind::Catch:
-    case ScopeKind::NamedLambda:
-    case ScopeKind::StrictNamedLambda:
-      as<LexicalScope>().data().trace(trc);
-      break;
-    case ScopeKind::Global:
-    case ScopeKind::NonSyntactic:
-      as<GlobalScope>().data().trace(trc);
-      break;
-    case ScopeKind::Eval:
-    case ScopeKind::StrictEval:
-      as<EvalScope>().data().trace(trc);
-      break;
-    case ScopeKind::Module:
-      as<ModuleScope>().data().trace(trc);
-      break;
-    case ScopeKind::With:
-      break;
-    case ScopeKind::WasmInstance:
-      as<WasmInstanceScope>().data().trace(trc);
-      break;
-    case ScopeKind::WasmFunction:
-      as<WasmFunctionScope>().data().trace(trc);
-      break;
-  }
+  applyScopeDataTyped([trc](auto data) {
+                        data->trace(trc);
+                      });
 }
 inline void js::GCMarker::eagerlyMarkChildren(Scope* scope) {
   do {
     if (scope->environmentShape_) {
       traverseEdge(scope, scope->environmentShape_.get());
     }
     TrailingNamesArray* names = nullptr;
     uint32_t length = 0;
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -808,16 +808,17 @@ void js::Nursery::collect(JS::GCReason r
         previousGC.nurseryUsedBytes >= 4 * 1024 * 1024) ||
        IsFullStoreBufferReason(reason));
 
   if (shouldPretenure) {
     JSContext* cx = rt->mainContextFromOwnThread();
     for (auto& entry : tenureCounts.entries) {
       if (entry.count >= tunables().pretenureGroupThreshold()) {
         ObjectGroup* group = entry.group;
+        AutoMaybeLeaveAtomsZone leaveAtomsZone(cx);
         AutoRealm ar(cx, group);
         AutoSweepObjectGroup sweep(group);
         if (group->canPreTenure(sweep)) {
           group->setShouldPreTenure(sweep, cx);
           pretenureCount++;
         }
       }
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1543589.js
@@ -0,0 +1,15 @@
+// |jit-test| skip-if: getBuildConfiguration()["arm64-simulator"] === true
+// This test times out in ARM64 simulator builds.
+
+gczeal(0);
+gcparam('minNurseryBytes', 16 * 1024 * 1024);
+
+let a = [];
+for (var i = 0; i < 20000; i++) {
+    a.push(import("nonexistent.js"));
+    Symbol();
+}
+
+for (let p of a) {
+    p.catch(() => {});
+}
--- a/js/src/vm/JSContext.h
+++ b/js/src/vm/JSContext.h
@@ -26,16 +26,17 @@
 #include "vm/MallocProvider.h"
 #include "vm/Runtime.h"
 
 struct DtoaState;
 
 namespace js {
 
 class AutoAllocInAtomsZone;
+class AutoMaybeLeaveAtomsZone;
 class AutoRealm;
 
 namespace jit {
 class JitContext;
 class DebugModeOSRVolatileJitFrameIter;
 }  // namespace jit
 
 namespace gc {
@@ -307,16 +308,17 @@ struct JSContext : public JS::RootingCon
   inline void enterRealm(JS::Realm* realm);
 
   inline void enterAtomsZone();
   inline void leaveAtomsZone(JS::Realm* oldRealm);
   enum IsAtomsZone { AtomsZone, NotAtomsZone };
   inline void setZone(js::Zone* zone, IsAtomsZone isAtomsZone);
 
   friend class js::AutoAllocInAtomsZone;
+  friend class js::AutoMaybeLeaveAtomsZone;
   friend class js::AutoRealm;
 
  public:
   inline void enterRealmOf(JSObject* target);
   inline void enterRealmOf(JSScript* target);
   inline void enterRealmOf(js::ObjectGroup* target);
   inline void enterNullRealm();
 
--- a/js/src/vm/Realm-inl.h
+++ b/js/src/vm/Realm-inl.h
@@ -80,16 +80,29 @@ js::AutoAllocInAtomsZone::AutoAllocInAto
     : cx_(cx), origin_(cx->realm()) {
   cx_->enterAtomsZone();
 }
 
 js::AutoAllocInAtomsZone::~AutoAllocInAtomsZone() {
   cx_->leaveAtomsZone(origin_);
 }
 
+js::AutoMaybeLeaveAtomsZone::AutoMaybeLeaveAtomsZone(JSContext* cx)
+    : cx_(cx), wasInAtomsZone_(cx->zone() && cx->zone()->isAtomsZone()) {
+  if (wasInAtomsZone_) {
+    cx_->leaveAtomsZone(nullptr);
+  }
+}
+
+js::AutoMaybeLeaveAtomsZone::~AutoMaybeLeaveAtomsZone() {
+  if (wasInAtomsZone_) {
+    cx_->enterAtomsZone();
+  }
+}
+
 js::AutoRealmUnchecked::AutoRealmUnchecked(JSContext* cx, JS::Realm* target)
     : AutoRealm(cx, target) {}
 
 MOZ_ALWAYS_INLINE bool js::ObjectRealm::objectMaybeInIteration(JSObject* obj) {
   MOZ_ASSERT(&ObjectRealm::get(obj) == this);
 
   // If the list is empty we're not iterating any objects.
   js::NativeIterator* next = enumerators->next();
--- a/js/src/vm/Realm.h
+++ b/js/src/vm/Realm.h
@@ -883,16 +883,29 @@ class MOZ_RAII AutoAllocInAtomsZone {
   AutoAllocInAtomsZone(const AutoAllocInAtomsZone&) = delete;
   AutoAllocInAtomsZone& operator=(const AutoAllocInAtomsZone&) = delete;
 
  public:
   inline explicit AutoAllocInAtomsZone(JSContext* cx);
   inline ~AutoAllocInAtomsZone();
 };
 
+// For the one place where we need to enter a realm when we may have been
+// allocating in the the atoms zone, this leaves the atoms zone temporarily.
+class MOZ_RAII AutoMaybeLeaveAtomsZone {
+  JSContext* const cx_;
+  bool wasInAtomsZone_;
+  AutoMaybeLeaveAtomsZone(const AutoMaybeLeaveAtomsZone&) = delete;
+  AutoMaybeLeaveAtomsZone& operator=(const AutoMaybeLeaveAtomsZone&) = delete;
+
+ public:
+  inline explicit AutoMaybeLeaveAtomsZone(JSContext* cx);
+  inline ~AutoMaybeLeaveAtomsZone();
+};
+
 // Enter a realm directly. Only use this where there's no target GC thing
 // to pass to AutoRealm or where you need to avoid the assertions in
 // JS::Compartment::enterCompartmentOf().
 class AutoRealmUnchecked : protected AutoRealm {
  public:
   inline AutoRealmUnchecked(JSContext* cx, JS::Realm* target);
 };
 
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -448,22 +448,20 @@ Scope* Scope::clone(JSContext* cx, Handl
       break;
   }
 
   return nullptr;
 }
 
 void Scope::finalize(FreeOp* fop) {
   MOZ_ASSERT(CurrentThreadIsGCSweeping());
-  if (data_) {
-    // We don't need to call the destructors for any GCPtrs in Data because
-    // this only happens during a GC.
-    fop->free_(data_);
-    data_ = nullptr;
-  }
+  applyScopeDataTyped([fop](auto data) {
+                        fop->delete_(data);
+                      });
+  data_ = nullptr;
 }
 
 size_t Scope::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
   if (data_) {
     return mallocSizeOf(data_);
   }
   return 0;
 }
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -306,16 +306,19 @@ class Scope : public js::gc::TenuredCell
       XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
       MutableHandle<typename ConcreteScope::Data*> data);
 
   Shape* maybeCloneEnvironmentShape(JSContext* cx);
 
   template <typename ConcreteScope>
   void initData(MutableHandle<UniquePtr<typename ConcreteScope::Data>> data);
 
+  template <typename F>
+  void applyScopeDataTyped(F&& f);
+
  public:
   static const JS::TraceKind TraceKind = JS::TraceKind::Scope;
 
   template <typename T>
   bool is() const {
     return kind_ == T::classScopeKind_;
   }
 
@@ -1010,16 +1013,59 @@ class WasmFunctionScope : public Scope {
   const Data& data() const { return *static_cast<Data*>(data_); }
 
  public:
   uint32_t funcIndex() const { return data().funcIndex; }
 
   static Shape* getEmptyEnvironmentShape(JSContext* cx);
 };
 
+template <typename F>
+void Scope::applyScopeDataTyped(F&& f) {
+  switch (kind()) {
+    case ScopeKind::Function: {
+      f(&as<FunctionScope>().data());
+      break;
+    case ScopeKind::FunctionBodyVar:
+    case ScopeKind::ParameterExpressionVar:
+      f(&as<VarScope>().data());
+      break;
+    case ScopeKind::Lexical:
+    case ScopeKind::SimpleCatch:
+    case ScopeKind::Catch:
+    case ScopeKind::NamedLambda:
+    case ScopeKind::StrictNamedLambda:
+      f(&as<LexicalScope>().data());
+      break;
+    case ScopeKind::With:
+      // With scopes do not have data.
+      break;
+    case ScopeKind::Eval:
+    case ScopeKind::StrictEval:
+      f(&as<EvalScope>().data());
+      break;
+    case ScopeKind::Global:
+    case ScopeKind::NonSyntactic:
+      f(&as<GlobalScope>().data());
+      break;
+    case ScopeKind::Module:
+      f(&as<ModuleScope>().data());
+      break;
+    case ScopeKind::WasmInstance:
+      f(&as<WasmInstanceScope>().data());
+      break;
+    case ScopeKind::WasmFunction:
+      f(&as<WasmFunctionScope>().data());
+      break;
+    default:
+      MOZ_CRASH("Unexpected scope type in ApplyScopeDataTyped");
+    }
+  }
+}
+
 //
 // An iterator for a Scope's bindings. This is the source of truth for frame
 // and environment object layout.
 //
 // It may be placed in GC containers; for example:
 //
 //   for (Rooted<BindingIter> bi(cx, BindingIter(scope)); bi; bi++) {
 //     use(bi);
new file mode 100644
--- /dev/null
+++ b/layout/generic/crashtests/1542441.html
@@ -0,0 +1,37 @@
+<style>
+.multicol-a {
+  width: 300px;
+  column-width: 100px;
+  column-gap: 0;
+  height: 100px;
+}
+.multicol-b {
+  border: 1px solid silver;
+  width: 200px;
+  column-width: 51px;
+  column-gap: 0;
+  height: 50px;
+}
+
+.step {
+  height: 1px;
+}
+.float-L {
+  width: 1px;
+  height: 1px;
+  float: left;
+}
+.float-R {
+  width: 1px;
+  height: 26px; /* 25 -> 26 crash */
+}
+</style>
+
+<div class="multicol-a">
+  <div class="float-R"></div>
+  <div class="multicol-b">
+    <div class="step"></div>
+    <div class="float-L"></div>
+  </div>
+</div>
+
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -725,8 +725,9 @@ pref(layout.css.column-span.enabled,true
 load 1515124.html
 pref(layout.css.column-span.enabled,true) load 1517033.html
 pref(layout.css.column-span.enabled,true) load 1517297.html
 load 1520798-1.xul
 load 1520798-2.html
 load 1539656.html
 load 1544060-1.html
 load 1544060-2.html
+load 1542441.html
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -1278,17 +1278,24 @@ void nsBlockFrame::Reflow(nsPresContext*
       // We have some lines to align the ::marker with.
 
       // Doing the alignment using the baseline will also cater for
       // ::markers that are placed next to a child block (bug 92896)
 
       // Tall ::markers won't look particularly nice here...
       LogicalRect bbox =
           marker->GetLogicalRect(wm, reflowOutput.PhysicalSize());
-      bbox.BStart(wm) = position.mBaseline - reflowOutput.BlockStartAscent();
+      const auto baselineGroup = BaselineSharingGroup::eFirst;
+      nscoord markerBaseline;
+      if (MOZ_UNLIKELY(wm.IsOrthogonalTo(marker->GetWritingMode()) ||
+                       !marker->GetNaturalBaselineBOffset(wm, baselineGroup, &markerBaseline))) {
+        // ::marker has no baseline in this axis: align with its margin-box end.
+        markerBaseline = bbox.BSize(wm) + marker->GetLogicalUsedMargin(wm).BEnd(wm);
+      }
+      bbox.BStart(wm) = position.mBaseline - markerBaseline;
       marker->SetRect(wm, bbox, reflowOutput.PhysicalSize());
     }
     // Otherwise just leave the ::marker where it is, up against our
     // block-start padding.
   }
 
   CheckFloats(state);
 
@@ -4725,28 +4732,34 @@ bool nsBlockFrame::DrainOverflowLines() 
             break;
           }
         }
       }
 
       // Make the overflow out-of-flow frames mine too.
       nsAutoOOFFrameList oofs(prevBlock);
       if (oofs.mList.NotEmpty()) {
-        // In case we own a next-in-flow of any of the drained frames, then
-        // those are now not PUSHED_FLOATs anymore.
+        // In case we own any next-in-flows of any of the drained frames, then
+        // move those to the PushedFloat list.
+        nsFrameList pushedFloats;
         for (nsFrameList::Enumerator e(oofs.mList); !e.AtEnd(); e.Next()) {
           nsIFrame* nif = e.get()->GetNextInFlow();
           for (; nif && nif->GetParent() == this; nif = nif->GetNextInFlow()) {
-            MOZ_ASSERT(mFloats.ContainsFrame(nif));
-            nif->RemoveStateBits(NS_FRAME_IS_PUSHED_FLOAT);
+            MOZ_ASSERT(nif->HasAnyStateBits(NS_FRAME_IS_PUSHED_FLOAT));
+            RemoveFloat(nif);
+            pushedFloats.AppendFrame(nullptr, nif);
           }
         }
         ReparentFrames(oofs.mList, prevBlock, this,
                        ReparentingDirection::Forwards);
         mFloats.InsertFrames(nullptr, nullptr, oofs.mList);
+        if (!pushedFloats.IsEmpty()) {
+          nsFrameList* pf = EnsurePushedFloats();
+          pf->InsertFrames(nullptr, nullptr, pushedFloats);
+        }
       }
 
       if (!mLines.empty()) {
         // Remember to recompute the margins on the first line. This will
         // also recompute the correct deltaBCoord if necessary.
         mLines.front()->MarkPreviousMarginDirty();
       }
       // The overflow lines have already been marked dirty and their previous
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -1258,16 +1258,29 @@ nscoord nsBulletFrame::GetLogicalBaselin
   if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
     ascent = BSize(aWritingMode);
   } else {
     ascent = GetListStyleAscent();
   }
   return ascent + GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
 }
 
+bool nsBulletFrame::GetNaturalBaselineBOffset(WritingMode aWM,
+                                              BaselineSharingGroup,
+                                              nscoord* aBaseline) const {
+  nscoord ascent = 0;
+  if (GetStateBits() & BULLET_FRAME_IMAGE_LOADING) {
+    ascent = BSize(aWM);
+  } else {
+    ascent = GetListStyleAscent();
+  }
+  *aBaseline = ascent;
+  return true;
+}
+
 #ifdef ACCESSIBILITY
 void nsBulletFrame::GetSpokenText(nsAString& aText) {
   CounterStyle* style =
       PresContext()->CounterStyleManager()->ResolveCounterStyle(
           StyleList()->mCounterStyle);
   bool isBullet;
   style->GetSpokenCounterText(Ordinal(true), GetWritingMode(), aText, isBullet);
   if (isBullet) {
--- a/layout/generic/nsBulletFrame.h
+++ b/layout/generic/nsBulletFrame.h
@@ -100,19 +100,28 @@ class nsBulletFrame final : public nsFra
   Maybe<BulletRenderer> CreateBulletRenderer(gfxContext& aRenderingContext,
                                              nsPoint aPt);
   ImgDrawResult PaintBullet(gfxContext& aRenderingContext, nsPoint aPt,
                             const nsRect& aDirtyRect, uint32_t aFlags,
                             bool aDisableSubpixelAA);
 
   virtual bool IsEmpty() override;
   virtual bool IsSelfEmpty() override;
+
+  // XXXmats note that this method returns a non-standard baseline that includes
+  // the ::marker block-start margin.  New code should probably use
+  // GetNaturalBaselineBOffset instead, which returns a normal baseline offset
+  // as documented in nsIFrame.h.
   virtual nscoord GetLogicalBaseline(
       mozilla::WritingMode aWritingMode) const override;
 
+  bool GetNaturalBaselineBOffset(WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord* aBaseline) const override;
+
   float GetFontSizeInflation() const;
   bool HasFontSizeInflation() const {
     return (GetStateBits() & BULLET_FRAME_HAS_FONT_INFLATION) != 0;
   }
   void SetFontSizeInflation(float aInflation);
 
   // aDebugFromA11y should not be used
   int32_t Ordinal(bool aDebugFromA11y = false) const;
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -266,22 +266,28 @@ class nsFrameList {
 
   nsIFrame* FrameAt(int32_t aIndex) const;
   int32_t IndexOf(nsIFrame* aFrame) const;
 
   bool IsEmpty() const { return nullptr == mFirstChild; }
 
   bool NotEmpty() const { return nullptr != mFirstChild; }
 
+  /**
+   * Return true if aFrame is on this list.
+   * @note this method has O(n) time complexity over the length of the list
+   * XXXmats: ideally, we should make this function #ifdef DEBUG
+   */
   bool ContainsFrame(const nsIFrame* aFrame) const;
 
   /**
    * Get the number of frames in this list. Note that currently the
    * implementation has O(n) time complexity. Do not call it repeatedly in hot
    * code.
+   * XXXmats: ideally, we should make this function #ifdef DEBUG
    */
   int32_t GetLength() const;
 
   /**
    * If this frame list has only one frame, return that frame.
    * Otherwise, return null.
    */
   nsIFrame* OnlyChild() const {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/css-display-ref.html
@@ -0,0 +1,54 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Reference: File input with CSS display</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+input {
+  height: 3em;
+}
+.grid  { display: block; }
+.flex  { display: block; }
+.block { display: block; }
+.table { display: block; }
+.columns { display: block; }
+.n { -webkit-appearance:none; appearance:none; }
+</style>
+<body>
+  <input type="file" class="grid">
+  <input type="file" class="flex">
+  <input type="file" class="block">
+  <input type="file" class="table">
+  <input type="file" class="columns">
+
+  A<input type="file">
+  <input type="file" class="inline">
+  <input type="file" class="inline-grid">
+  <input type="file" class="inline-flex">
+  <input type="file" class="inline-block">
+  <input type="file" class="inline-table">
+  <input type="file" class="inline-columns">
+
+  A<input disabled type="file">
+  <input disabled type="file" class="inline">
+  <input disabled type="file" class="inline-grid">
+  <input disabled type="file" class="inline-flex">
+  <input disabled type="file" class="inline-block">
+  <input disabled type="file" class="inline-table">
+  <input disabled type="file" class="inline-columns">
+
+  A<input type="file" class="n">
+  <input type="file" class="n inline">
+  <input type="file" class="n inline-grid">
+  <input type="file" class="n inline-flex">
+  <input type="file" class="n inline-block">
+  <input type="file" class="n inline-table">
+  <input type="file" class="n inline-columns">
+
+  A<input disabled type="file" class="n">
+  <input disabled type="file" class="n inline">
+  <input disabled type="file" class="n inline-grid">
+  <input disabled type="file" class="n inline-flex">
+  <input disabled type="file" class="n inline-block">
+  <input disabled type="file" class="n inline-table">
+  <input disabled type="file" class="n inline-columns">
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/css-display.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test: File input with CSS display</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#widgets">
+<link rel="help" href="https://github.com/whatwg/html/issues/4082">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#propdef-overflow">
+<link rel="match" href="css-overflow-ref.html">
+<style>
+input {
+  height: 3em;
+}
+.inline       { display: inline }
+.inline-grid  { display: inline-grid; grid: 500px/500px; align-items: end; }
+.inline-flex  { display: inline-flex; flex-flow: column; align-items: end; }
+.inline-block { display: inline-block }
+.inline-table { display: inline-table }
+.inline-columns { display: inline-block; column-width: 500px; column-rule: 10px solid; }
+.grid  { display: grid; grid: 500px/500px; align-items: end; }
+.flex  { display: flex; flex-flow: column; align-items: end; }
+.block { display: block }
+.table { display: table }
+.columns { display: block; column-width: 500px; column-rule: 10px solid; }
+.n { -webkit-appearance:none; appearance:none; }
+</style>
+<body>
+  <input type="file" class="grid">
+  <input type="file" class="flex">
+  <input type="file" class="block">
+  <input type="file" class="table">
+  <input type="file" class="columns">
+
+  A<input type="file">
+  <input type="file" class="inline">
+  <input type="file" class="inline-grid">
+  <input type="file" class="inline-flex">
+  <input type="file" class="inline-block">
+  <input type="file" class="inline-table">
+  <input type="file" class="inline-columns">
+
+  A<input disabled type="file">
+  <input disabled type="file" class="inline">
+  <input disabled type="file" class="inline-grid">
+  <input disabled type="file" class="inline-flex">
+  <input disabled type="file" class="inline-block">
+  <input disabled type="file" class="inline-table">
+  <input disabled type="file" class="inline-columns">
+
+  A<input type="file" class="n">
+  <input type="file" class="n inline">
+  <input type="file" class="n inline-grid">
+  <input type="file" class="n inline-flex">
+  <input type="file" class="n inline-block">
+  <input type="file" class="n inline-table">
+  <input type="file" class="n inline-columns">
+
+  A<input disabled type="file" class="n">
+  <input disabled type="file" class="n inline">
+  <input disabled type="file" class="n inline-grid">
+  <input disabled type="file" class="n inline-flex">
+  <input disabled type="file" class="n inline-block">
+  <input disabled type="file" class="n inline-table">
+  <input disabled type="file" class="n inline-columns">
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/css-overflow-ref.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Reference: File input with CSS overflow</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+input {
+  display: inline-block;
+  width: 100px;
+  height: 30px;
+  font-size: 48px;
+  overflow: visible;
+}
+.n { -webkit-appearance:none; appearance:none; }
+</style>
+<body>
+  A<input type="file">
+  <input type="file" class="ov">
+  <input type="file" class="oh">
+  <input type="file" class="os">
+  <input type="file" class="oa">
+
+  A<input disabled type="file">
+  <input disabled type="file" class="ov">
+  <input disabled type="file" class="oh">
+  <input disabled type="file" class="os">
+  <input disabled type="file" class="oa">
+
+  A<input type="file" class="n">
+  <input type="file" class="n ov">
+  <input type="file" class="n oh">
+  <input type="file" class="n os">
+  <input type="file" class="n oa">
+
+  A<input disabled type="file" class="n">
+  <input disabled type="file" class="n ov">
+  <input disabled type="file" class="n oh">
+  <input disabled type="file" class="n os">
+  <input disabled type="file" class="n oa">
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/file/css-overflow.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test: File input with CSS overflow</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/rendering.html#widgets">
+<link rel="help" href="https://github.com/whatwg/html/issues/4082">
+<link rel="help" href="https://drafts.csswg.org/css-overflow-3/#propdef-overflow">
+<link rel="match" href="css-overflow-ref.html">
+<style>
+input {
+  display: inline-block;
+  width: 100px;
+  height: 30px;
+  font-size: 48px;
+}
+.ov { overflow: visible; }
+.oh { overflow: hidden; }
+.os { overflow: scroll; }
+.oa { overflow: auto; }
+.n { -webkit-appearance:none; appearance:none; }
+</style>
+<body>
+  A<input type="file">
+  <input type="file" class="ov">
+  <input type="file" class="oh">
+  <input type="file" class="os">
+  <input type="file" class="oa">
+
+  A<input disabled type="file">
+  <input disabled type="file" class="ov">
+  <input disabled type="file" class="oh">
+  <input disabled type="file" class="os">
+  <input disabled type="file" class="oa">
+
+  A<input type="file" class="n">
+  <input type="file" class="n ov">
+  <input type="file" class="n oh">
+  <input type="file" class="n os">
+  <input type="file" class="n oa">
+
+  A<input disabled type="file" class="n">
+  <input disabled type="file" class="n ov">
+  <input disabled type="file" class="n oh">
+  <input disabled type="file" class="n os">
+  <input disabled type="file" class="n oa">
+</body>
--- a/layout/reftests/forms/input/file/reftest.list
+++ b/layout/reftests/forms/input/file/reftest.list
@@ -2,8 +2,10 @@ fuzzy-if(gtkWidget||webrender,0-1,0-34) 
 fuzzy-if(gtkWidget||webrender,0-1,0-17) fails-if(Android) == rtl.html rtl-ref.xul
 fuzzy-if(gtkWidget||webrender,0-1,0-34) fails-if(Android) == size.html simple-ref.xul
 fuzzy-if(gtkWidget||webrender,0-1,0-10) fails-if(Android) == background.html background-ref.xul
 fuzzy-if(gtkWidget,0-1,0-10) fails-if(Android) == style.html style-ref.xul
 != width-clip.html width-clip-ref.html
 fails-if(Android) == color-inherit.html color-inherit-ref.html
 fuzzy-if(Android,0-2,0-2) fails-if(webrender&&!cocoaWidget) == dynamic-max-width.html dynamic-max-width-ref.html # bug 1496542 for webrender.
 == label-min-inline-size.html label-min-inline-size-ref.html
+== css-overflow.html css-overflow-ref.html
+== css-display.html css-display-ref.html
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3642,27 +3642,28 @@ void nsStyleContent::TriggerImageLoads(D
             : nullptr;
     mContents[i].Resolve(aDocument, oldData);
   }
 }
 
 nsStyleContent::nsStyleContent(const nsStyleContent& aSource)
     : mContents(aSource.mContents),
       mIncrements(aSource.mIncrements),
-      mResets(aSource.mResets) {
+      mResets(aSource.mResets),
+      mSets(aSource.mSets) {
   MOZ_COUNT_CTOR(nsStyleContent);
 }
 
 nsChangeHint nsStyleContent::CalcDifference(
     const nsStyleContent& aNewData) const {
   // Unfortunately we need to reframe even if the content lengths are the same;
   // a simple reflow will not pick up different text or different image URLs,
   // since we set all that up in the CSSFrameConstructor
   if (mContents != aNewData.mContents || mIncrements != aNewData.mIncrements ||
-      mResets != aNewData.mResets) {
+      mResets != aNewData.mResets || mSets != aNewData.mSets) {
     return nsChangeHint_ReconstructFrame;
   }
 
   return nsChangeHint(0);
 }
 
 // --------------------
 // nsStyleTextReset
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -462,17 +462,17 @@ input[type="image"]:-moz-focusring {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
 
 /* file selector */
 input[type="file"] {
   display: inline-block;
   white-space: nowrap !important;
-  overflow: hidden;
+  overflow: hidden !important;
   overflow-clip-box: padding-box;
   color: unset;
 
   /* Revert rules which apply on all inputs. */
   -moz-appearance: none;
   cursor: default;
 
   border: none;
--- a/layout/style/test/test_grid_container_shorthands.html
+++ b/layout/style/test/test_grid_container_shorthands.html
@@ -198,17 +198,17 @@ grid_test_cases = grid_template_test_cas
     {
         specified: "auto-flow / 0",
         gridAutoFlow: "row",
         gridAutoRows: "auto",
         gridTemplateColumns: "0px",
     },
     {
         specified: "auto-flow dense / 0",
-        gridAutoFlow: "row dense",
+        gridAutoFlow: "dense",
         gridAutoRows: "auto",
         gridTemplateColumns: "0px",
     },
     {
         specified: "auto-flow minmax(auto,1fr) / none",
         gridAutoFlow: "row",
         gridAutoRows: "1fr",
     },
@@ -233,17 +233,17 @@ grid_test_cases = grid_template_test_cas
         specified: "0 / auto-flow dense auto",
         gridAutoFlow: "column dense",
         gridAutoRows: "auto",
         gridAutoColumns: "auto",
         gridTemplateRows: "0px",
     },
     {
         specified: "dense auto-flow minmax(min-content, 2fr) / 0",
-        gridAutoFlow: "row dense",
+        gridAutoFlow: "dense",
         gridAutoRows: "minmax(min-content, 2fr)",
         gridAutoColumns: "auto",
         gridTemplateColumns: "0px",
     },
     {
         specified: "auto-flow 40px / 100px",
         gridAutoFlow: "row",
         gridAutoRows: "40px",
--- a/layout/svg/SVGContextPaint.h
+++ b/layout/svg/SVGContextPaint.h
@@ -57,17 +57,17 @@ class SVGContextPaint : public RefCounte
   typedef mozilla::gfx::Float Float;
   typedef mozilla::image::imgDrawingParams imgDrawingParams;
 
   SVGContextPaint() : mDashOffset(0.0f), mStrokeWidth(0.0f) {}
 
  public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(SVGContextPaint)
 
-  virtual ~SVGContextPaint() {}
+  virtual ~SVGContextPaint() = default;
 
   virtual already_AddRefed<gfxPattern> GetFillPattern(
       const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
       imgDrawingParams& aImgParams) = 0;
   virtual already_AddRefed<gfxPattern> GetStrokePattern(
       const DrawTarget* aDrawTarget, float aOpacity, const gfxMatrix& aCTM,
       imgDrawingParams& aImgParams) = 0;
   virtual float GetFillOpacity() const = 0;
--- a/layout/svg/SVGImageContext.h
+++ b/layout/svg/SVGImageContext.h
@@ -20,17 +20,17 @@ class ComputedStyle;
 
 // SVG image-specific rendering context. For imgIContainer::Draw.
 // Used to pass information such as
 //  - viewport information from CSS, and
 //  - overridden attributes from an SVG <image> element
 // to the image's internal SVG document when it's drawn.
 class SVGImageContext {
  public:
-  SVGImageContext() {}
+  SVGImageContext() = default;
 
   /**
    * Currently it seems that the aViewportSize parameter ends up being used
    * for different things by different pieces of code, and probably in some
    * cases being used incorrectly (specifically in the case of pixel snapping
    * under the nsLayoutUtils::Draw*Image() methods).  An unfortunate result of
    * the messy code is that aViewportSize is currently a Maybe<T> since it
    * is difficult to create a utility function that consumers can use up
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -182,17 +182,17 @@ class SVGTextFrame final : public nsSVGD
         mTrailingUndisplayedCharacters(0),
         mFontSizeScaleFactor(1.0f),
         mLastContextScale(1.0f),
         mLengthAdjustScaleFactor(1.0f) {
     AddStateBits(NS_STATE_SVG_TEXT_CORRESPONDENCE_DIRTY |
                  NS_STATE_SVG_POSITIONING_DIRTY);
   }
 
-  ~SVGTextFrame() {}
+  ~SVGTextFrame() = default;
 
  public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS(SVGTextFrame)
 
   // nsIFrame:
   virtual void Init(nsIContent* aContent, nsContainerFrame* aParent,
                     nsIFrame* aPrevInFlow) override;
--- a/layout/svg/nsSVGImageFrame.h
+++ b/layout/svg/nsSVGImageFrame.h
@@ -36,17 +36,17 @@ class nsSVGImageListener final : public 
   explicit nsSVGImageListener(nsSVGImageFrame* aFrame);
 
   NS_DECL_ISUPPORTS
   NS_DECL_IMGINOTIFICATIONOBSERVER
 
   void SetFrame(nsSVGImageFrame* frame) { mFrame = frame; }
 
  private:
-  ~nsSVGImageListener() {}
+  ~nsSVGImageListener() = default;
 
   nsSVGImageFrame* mFrame;
 };
 
 class nsSVGImageFrame final : public mozilla::SVGGeometryFrame,
                               public nsIReflowCallback {
   friend nsIFrame* NS_NewSVGImageFrame(mozilla::PresShell* aPresShell,
                                        ComputedStyle* aStyle);
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1099,19 +1099,16 @@ impl ${style_struct.gecko_struct_name} {
         unsafe {
             Gecko_Construct_Default_${style_struct.gecko_ffi_name}(
                 &mut Arc::get_mut(&mut result).unwrap().gecko,
                 document,
             );
         }
         result
     }
-    pub fn get_gecko(&self) -> &${style_struct.gecko_ffi_name} {
-        &self.gecko
-    }
 }
 impl Drop for ${style_struct.gecko_struct_name} {
     fn drop(&mut self) {
         unsafe {
             Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut self.gecko);
         }
     }
 }
--- a/servo/components/style/properties/properties.mako.rs
+++ b/servo/components/style/properties/properties.mako.rs
@@ -22,17 +22,17 @@ use std::mem::{self, ManuallyDrop};
 use cssparser::{Parser, RGBA, TokenSerializationType};
 use cssparser::ParserInput;
 #[cfg(feature = "servo")] use euclid::SideOffsets2D;
 use crate::context::QuirksMode;
 #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{self, nsCSSPropertyID};
 #[cfg(feature = "servo")] use crate::logical_geometry::LogicalMargin;
 #[cfg(feature = "servo")] use crate::computed_values;
 use crate::logical_geometry::WritingMode;
-#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use crate::media_queries::Device;
 use crate::parser::ParserContext;
 use crate::properties::longhands::system_font::SystemFont;
 use crate::selector_parser::PseudoElement;
 use selectors::parser::SelectorParseErrorKind;
 #[cfg(feature = "servo")] use servo_config::prefs;
 use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
 use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
@@ -371,17 +371,16 @@ impl PartialEq for PropertyDeclaration {
                     *this == other_repr.value
                 }
                 % endfor
             }
         }
     }
 }
 
-#[cfg(feature = "gecko")]
 impl MallocSizeOf for PropertyDeclaration {
     #[inline]
     fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
         use self::PropertyDeclaration::*;
 
         match *self {
             % for ty, vs in groupby(variants, key=lambda x: x["type"]):
             ${" | ".join("{}(ref value)".format(v["name"]) for v in vs)} => {
@@ -3740,18 +3739,17 @@ pub fn adjust_border_width(style: &mut S
         if style.get_border().clone_border_${side}_style().none_or_hidden() &&
            style.get_border().border_${side}_has_nonzero_width() {
             style.set_border_${side}_width(NonNegativeLength::zero());
         }
     % endfor
 }
 
 /// An identifier for a given alias property.
-#[derive(Clone, Copy, Eq, PartialEq)]
-#[cfg_attr(feature = "servo", derive(MallocSizeOf))]
+#[derive(Clone, Copy, Eq, PartialEq, MallocSizeOf)]
 #[repr(u16)]
 pub enum AliasId {
     % for i, property in enumerate(data.all_aliases()):
         /// ${property.name}
         ${property.camel_case} = ${i},
     % endfor
 }
 
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -455,16 +455,21 @@ pub enum AutoFlow {
     /// The auto-placement algorithm places items by filling each row in turn,
     /// adding new rows as necessary.
     Row,
     /// The auto-placement algorithm places items by filling each column in turn,
     /// adding new columns as necessary.
     Column,
 }
 
+/// If `dense` is specified, `row` is implied.
+fn is_row_dense(autoflow: &AutoFlow, dense: &bool) -> bool {
+    *autoflow == AutoFlow::Row && *dense
+}
+
 #[derive(
     Clone,
     Copy,
     Debug,
     Eq,
     MallocSizeOf,
     PartialEq,
     SpecifiedValueInfo,
@@ -472,16 +477,17 @@ pub enum AutoFlow {
     ToCss,
     ToResolvedValue,
     ToShmem,
 )]
 /// Controls how the auto-placement algorithm works
 /// specifying exactly how auto-placed items get flowed into the grid
 pub struct GridAutoFlow {
     /// Specifiy how auto-placement algorithm fills each `row` or `column` in turn
+    #[css(contextual_skip_if = "is_row_dense")]
     pub autoflow: AutoFlow,
     /// Specify use `dense` packing algorithm or not
     #[css(represents_keyword)]
     pub dense: bool,
 }
 
 impl GridAutoFlow {
     #[inline]
--- a/testing/web-platform/meta/css/css-grid/grid-layout-properties.html.ini
+++ b/testing/web-platform/meta/css/css-grid/grid-layout-properties.html.ini
@@ -15,19 +15,16 @@
     expected: FAIL
 
   [grid-template.<string><track-size>+]
     expected: FAIL
 
   [grid-template.reset]
     expected: FAIL
 
-  [grid-auto-flow.dense]
-    expected: FAIL
-
   [grid-row-start.span <custom-ident>]
     expected: FAIL
 
   [grid-column-start.span <custom-ident>]
     expected: FAIL
 
   [grid-row-end.span <custom-ident>]
     expected: FAIL
--- a/testing/web-platform/tests/css/css-grid/grid-layout-properties.html
+++ b/testing/web-platform/tests/css/css-grid/grid-layout-properties.html
@@ -137,18 +137,18 @@
         '<track-size>.<track-breadth>.max-content': ['max-content', 'max-content'],
         '<track-size>.<track-breadth>.minmax()': ['minmax(100px, 200px)', 'minmax(100px, 200px)'],
         'reset': ['auto', 'auto'],
       },
       'grid-auto-flow': {
         initial: 'row',
         'row': ['row', 'row'],
         'column': ['column', 'column'],
-        'dense': ['dense', 'row dense'],
-        'row dense': ['row dense', 'row dense'],
+        'dense': ['dense', 'dense'],
+        'row dense': ['dense', 'dense'],
         'column dense': ['column dense', 'column dense'],
         'reset': ['row', 'row'],
       },
       'grid-row-start': {
         initial: 'auto',
         'auto': ['auto', 'auto'],
         '<custom-ident>': ['a', 'a'],
         '<integer>': ['1', '1'],
--- a/testing/web-platform/tests/css/css-grid/parsing/grid-auto-flow-computed.html
+++ b/testing/web-platform/tests/css/css-grid/parsing/grid-auto-flow-computed.html
@@ -8,18 +8,18 @@
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/computed-testcommon.js"></script>
 </head>
 <body>
 <div id="target"></div>
 <script>
 test_computed_value("grid-auto-flow", "row");
 test_computed_value("grid-auto-flow", "column");
-test_computed_value("grid-auto-flow", "row dense");
+test_computed_value("grid-auto-flow", "row dense", "dense");
 test_computed_value("grid-auto-flow", "column dense");
 
-test_computed_value("grid-auto-flow", "dense row", "row dense");
+test_computed_value("grid-auto-flow", "dense row", "dense");
 test_computed_value("grid-auto-flow", "dense column", "column dense");
 
-test_computed_value("grid-auto-flow", "dense", "row dense");
+test_computed_value("grid-auto-flow", "dense", "dense");
 </script>
 </body>
 </html>
--- a/testing/web-platform/tests/css/css-grid/parsing/grid-auto-flow-valid.html
+++ b/testing/web-platform/tests/css/css-grid/parsing/grid-auto-flow-valid.html
@@ -9,16 +9,16 @@
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <script src="/css/support/parsing-testcommon.js"></script>
 </head>
 <body>
 <script>
 test_valid_value("grid-auto-flow", "row");
 test_valid_value("grid-auto-flow", "column");
-test_valid_value("grid-auto-flow", "row dense");
+test_valid_value("grid-auto-flow", "row dense", "dense");
+test_valid_value("grid-auto-flow", "dense row", "dense");
+test_valid_value("grid-auto-flow", "dense");
+test_valid_value("grid-auto-flow", "column dense");
 test_valid_value("grid-auto-flow", "dense column", "column dense");
-
-// Blink/WebKit "dense", Edge/Firefox "row dense"
-test_valid_value("grid-auto-flow", "dense", ["dense", "row dense"]);
 </script>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-lists/counter-set-002-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Lists: dynamic update test for 'counter-set'</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+</head>
+<body>
+<ol><li></li></ol>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-lists/counter-set-002.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Lists: dynamic update test for 'counter-set'</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+  <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-set">
+  <link rel="match" href="counter-set-002-ref.html">
+</head>
+<body onload="document.getElementById('item').style=''">
+<noscript>Test not run - javascript required.</noscript>
+<ol><li id="item" style="counter-increment: list-item 3"></li></ol>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-009-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Reference: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+.h m { display:inline-block; width:0; line-height:0; height:0; position:relative; left: -2ch; }
+.v m { writing-mode: vertical-lr; width:0; position:relative; left: -1em; height:0; text-indent:-2ch; }
+.lr { writing-mode: vertical-lr; }
+.big { font-size:xx-large; }
+.big-marker m { font-size:xx-large; }
+li { display: block; }
+</style>
+</head><body>
+<ol class="h">
+  <li><div class="big"><m style="font-size:initial">AB</m>C<br>D</div></li>
+  <li><div></div><div class="big"><m style="font-size:initial">AB</m>C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker h">
+  <li><div><m>AB</m>C<br>D</div></li>
+  <li><div></div><div><m>AB</m>C<br>D</div></li>
+</ol>
+<ol class="v">
+  <li><div><m>AB</m>C<br>D</div></li>
+  <li><div></div><div><m>AB</m>C<br>D</div></li>
+  <li><div class="big"><m style="font-size:initial">AB</m>C<br>D</div></li>
+</ol>
+<ol class="v big-marker">
+  <li><div><m>AB</m>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-009.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Test: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="match" href="marker-content-009-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#marker-pseudo">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+li::marker { content: 'AB'; }
+.v > li::marker { writing-mode: vertical-lr; }
+.lr { writing-mode: vertical-lr; }
+.big { font-size:xx-large; }
+.big-marker > li::marker { font-size:xx-large; }
+f { float: left; margin-right: 20px; }
+</style>
+</head><body>
+<ol>
+  <li><div class="big">C<br>D</div></li>
+  <li><div></div><div class="big">C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker">
+  <li><div>C<br>D</div></li>
+  <li><div></div><div>C<br>D</div></li>
+</ol>
+<ol class=v>
+  <li><div>C<br>D</div></li>
+  <li><div></div><div>C<br>D</div></li>
+  <li><div class="big">C<br>D</div></li>
+</ol>
+<ol class="v big-marker">
+  <li><div>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-010-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Reference: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+.h m { display:inline-block; width:0; line-height:0; height:0; position:relative; left: -3ch; }
+.big { font-size:xx-large; }
+.big-marker m { font-size:xx-large; }
+li { display: block; }
+</style>
+</head><body>
+<ol class="h">
+  <li><div class="big"><m style="font-size:initial">1.</m>C<br>D</div></li>
+  <li><div></div><div class="big"><m style="font-size:initial">2.</m>C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker h">
+  <li><div><m>1.</m>C<br>D</div></li>
+  <li><div></div><div><m>2.</m>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-010.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Test: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="match" href="marker-content-010-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#marker-pseudo">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+.big { font-size:xx-large; }
+.big-marker > li::marker { font-size:xx-large; }
+</style>
+</head><body>
+<ol>
+  <li><div class="big">C<br>D</div></li>
+  <li><div></div><div class="big">C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker">
+  <li><div>C<br>D</div></li>
+  <li><div></div><div>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-011-ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Reference: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+.h m { display:inline-block; width:0; line-height:0; height:0; position:relative; left: -3ch; }
+.v m { writing-mode: vertical-lr; width:0; position:relative; left: -2em; height:0; text-indent:-3ch; }
+.lr { writing-mode: vertical-lr; }
+.big { font-size:xx-large; }
+.big-marker m { font-size:xx-large; }
+li { display: block; }
+</style>
+</head><body>
+<ol class="h">
+  <li><div class="big"><m style="font-size:initial">AB</m>C<br>D</div></li>
+  <li><div></div><div class="big"><m style="font-size:initial">AB</m>C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker h">
+  <li><div><m>AB</m>C<br>D</div></li>
+  <li><div></div><div><m>AB</m>C<br>D</div></li>
+</ol>
+<ol class="v">
+  <li><div><m>AB</m>C<br>D</div></li>
+  <li><div></div><div><m>AB</m>C<br>D</div></li>
+  <li><div class="big"><m style="font-size:initial">AB</m>C<br>D</div></li>
+</ol>
+<ol class="v big-marker">
+  <li><div><m>AB</m>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-pseudo/marker-content-011.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html><head>
+<meta charset="utf-8">
+<title>CSS Test: ::marker pseudo elements styled with 'content' property</title>
+<link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.com">
+<link rel="match" href="marker-content-011-ref.html">
+<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#marker-pseudo">
+<style>
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+body { margin-left: 40px; }
+li::marker { content: 'AB'; margin-bottom: 1ch; margin-right: 1ch; }
+.v > li::marker { writing-mode: vertical-lr; margin-right: 1em; }
+.lr { writing-mode: vertical-lr; }
+.big { font-size:xx-large; }
+.big-marker > li::marker { font-size:xx-large; }
+f { float: left; margin-right: 20px; }
+</style>
+</head><body>
+<ol>
+  <li><div class="big">C<br>D</div></li>
+  <li><div></div><div class="big">C<br>D</div>
+  </li>
+</ol>
+<ol class="big-marker">
+  <li><div>C<br>D</div></li>
+  <li><div></div><div>C<br>D</div></li>
+</ol>
+<ol class=v>
+  <li><div>C<br>D</div></li>
+  <li><div></div><div>C<br>D</div></li>
+  <li><div class="big">C<br>D</div></li>
+</ol>
+<ol class="v big-marker">
+  <li><div>C<br>D</div></li>
+</ol>
+</body></html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>HTML LI element: dynamic update test for LI @value</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+</head>
+<body onload="document.getElementById('item').removeAttribute('value');">
+<ol><li id="item" value="3"></li></ol>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/semantics/grouping-content/the-li-element/grouping-li-reftest-003.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>HTML LI element: dynamic update test for LI @value</title>
+  <link rel="author" title="Mats Palmgren" href="mailto:mats@mozilla.org">
+  <link rel="help" href="https://html.spec.whatwg.org/multipage/grouping-content.html#the-li-element">
+  <link rel="help" href="https://drafts.csswg.org/css-lists/#propdef-counter-set">
+  <link rel="match" href="grouping-li-reftest-003-ref.html">
+</head>
+<body onload="document.getElementById('item').removeAttribute('value');">
+<noscript>Test not run - javascript required.</noscript>
+<ol><li id="item" value="3"></li></ol>
+</body>
+</html>
--- a/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
+++ b/toolkit/components/passwordmgr/test/mochitest/pwmgr_common.js
@@ -390,8 +390,15 @@ this.LoginManager = new Proxy({}, {
       return PWMGR_COMMON_PARENT.sendSyncMessage("proxyLoginManager", {
         args: cloneableArgs,
         loginInfoIndices,
         methodName: prop,
       })[0][0];
     };
   },
 });
+
+// Check for expected username/password in form.
+function checkLoginForm(usernameField, expectedUsername, passwordField, expectedPassword) {
+  let formID = usernameField.parentNode.id;
+  is(usernameField.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
+  is(passwordField.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
+}
--- a/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_https_upgrade.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_https_upgrade.html
@@ -62,23 +62,16 @@ let pword;
 // Restore the form to the default state.
 function restoreForm() {
   pword.focus();
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  let formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username");
-  is(pword.value, expectedPassword, "Checking " + formID + " password");
-}
-
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set": [["signon.schemeUpgrades", true]]});
 
   iframe.src = "https://example.org/tests/toolkit/components/passwordmgr/test/mochitest/form_basic.html";
   await new Promise(resolve => {
     iframe.addEventListener("load", function() {
       resolve();
     }, {once: true});
@@ -87,65 +80,65 @@ add_task(async function setup() {
   iframeDoc = iframe.contentDocument;
   hostname = iframeDoc.documentURIObject.host;
   uname = iframeDoc.getElementById("form-basic-username");
   pword = iframeDoc.getElementById("form-basic-password");
 });
 
 add_task(async function test_empty_first_entry() {
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   // Trigger autocomplete popup
   restoreForm();
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown");
   let results = await shownPromise;
   popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected");
   checkAutoCompleteResults(results, ["name", "name1", "name2"], hostname, "initial");
 
   // Check first entry
   let index0Promise = notifySelectedIndex(0);
   synthesizeKey("KEY_ArrowDown");
   await index0Promise;
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("name", "pass");
+  checkLoginForm(uname, "name", pword, "pass");
 });
 
 add_task(async function test_empty_second_entry() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("name1", "pass1");
+  checkLoginForm(uname, "name1", pword, "pass1");
 });
 
 add_task(async function test_search() {
   restoreForm();
   let shownPromise = promiseACShown();
   // We need to blur for the autocomplete controller to notice the forced value below.
   uname.blur();
   uname.value = "name";
   uname.focus();
   sendChar("1");
   synthesizeKey("KEY_ArrowDown"); // open
   let results = await shownPromise;
   checkAutoCompleteResults(results, ["name1"], hostname, "check result deduping for 'name1'");
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("name1", "pass1");
+  checkLoginForm(uname, "name1", pword, "pass1");
 
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is now closed");
 });
 
 add_task(async function test_delete_first_entry() {
   restoreForm();
   uname.focus();
@@ -157,17 +150,17 @@ add_task(async function test_delete_firs
   synthesizeKey("KEY_ArrowDown");
   await index0Promise;
 
   let deletionPromise = promiseStorageChanged(["removeLogin"]);
   // On OS X, shift-backspace and shift-delete work, just delete does not.
   // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await deletionPromise;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 
   let results = await notifyMenuChanged(3, "name1");
 
   checkAutoCompleteResults(results, ["name1", "name2"], hostname, "two logins should remain after deleting the first");
   let popupState = await getPopupState();
   is(popupState.open, true, "Check popup stays open after deleting");
   synthesizeKey("KEY_Escape");
   popupState = await getPopupState();
@@ -185,17 +178,17 @@ add_task(async function test_delete_dupl
   synthesizeKey("KEY_ArrowDown");
   await index0Promise;
 
   let deletionPromise = promiseStorageChanged(["removeLogin"]);
   // On OS X, shift-backspace and shift-delete work, just delete does not.
   // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await deletionPromise;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 
   is(LoginManager.countLogins("http://example.org", "http://example.org", null), 1,
      "Check that the HTTP login remains");
   is(LoginManager.countLogins("https://example.org", "https://example.org", null), 0,
      "Check that the HTTPS login was deleted");
 
   // Two menu items should remain as the HTTPS login should have been deleted but
   // the HTTP would remain.
--- a/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_open.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_password_open.html
@@ -45,26 +45,19 @@ var setupScript = runInParent(function s
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: Test password field autocomplete footer with and without logins **/
 
 let uname = document.getElementById("uname");
 let pword = document.getElementById("pword");
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 add_task(async function test_no_autofill() {
   // Make sure initial form is empty as autofill shouldn't happen in the sandboxed frame.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 add_task(async function test_two_logins() {
   let shownPromise = promiseACShown();
   uname.focus();
   await shownPromise;
@@ -77,17 +70,17 @@ add_task(async function test_two_logins(
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   let expectedMenuItems = [
     "tempuser1",
     "tempuser2",
   ];
   checkAutoCompleteResults(results, expectedMenuItems, "example.com", "Check all menuitems are displayed correctly.");
 
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let removedPromise = promiseStorageChanged(["removeAllLogins"]);
   LoginManager.removeAllLogins();
   await removedPromise;
 });
 
 add_task(async function test_zero_logins() {
   uname.focus();
 
@@ -98,20 +91,20 @@ add_task(async function test_zero_logins
   let results = await Promise.race([
     shownPromise,
     new Promise(resolve => setTimeout(resolve, 2000)), // Wait 2s for the popup to appear
   ]);
 
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is still closed");
 
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   shownPromise = promiseACShown();
   info("arrow down should still open the popup");
   synthesizeKey("KEY_ArrowDown");
   results = await shownPromise;
   checkAutoCompleteResults(results, [], "example.com", "Check only footer is displayed.");
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_sandboxed.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autocomplete_sandboxed.html
@@ -49,23 +49,16 @@ var setupScript = runInParent(function s
 
 /** Test for Login Manager: form field autocomplete in sandboxed documents (null principal) **/
 
 let sandboxed = document.getElementById("sandboxed");
 let uname;
 let pword;
 let hostname;
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set": [
     ["security.insecure_field_warning.contextual.enabled", true],
   ]});
 
   let frameWindow = SpecialPowers.wrap(sandboxed).contentWindow;
   // Can't use SimpleTest.promiseFocus as it doesn't work with the sandbox.
   await SimpleTest.promiseWaitForCondition(() => {
@@ -74,17 +67,17 @@ add_task(async function setup() {
   let frameDoc = SpecialPowers.wrap(sandboxed).contentDocument;
   uname = frameDoc.getElementById("form-basic-username");
   pword = frameDoc.getElementById("form-basic-password");
   hostname = frameDoc.documentURIObject.host;
 });
 
 add_task(async function test_no_autofill() {
   // Make sure initial form is empty as autofill shouldn't happen in the sandboxed frame.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 add_task(async function test_autocomplete_warning_no_logins() {
   pword.focus();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open the popup
@@ -93,14 +86,14 @@ add_task(async function test_autocomplet
   let popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   let expectedMenuItems = [
     "This connection is not secure. Logins entered here could be compromised. Learn More",
   ];
   checkAutoCompleteResults(results, expectedMenuItems, hostname, "Check all menuitems are displayed correctly.");
 
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_autofill_different_formSubmitURL.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofill_different_formSubmitURL.html
@@ -24,25 +24,16 @@ let nsLoginInfo = SpecialPowers.wrap(Spe
 <div id="content">
   <iframe></iframe>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  let iframeDoc = iframe.contentDocument;
-  let uname = iframeDoc.getElementById("form-basic-username");
-  let pword = iframeDoc.getElementById("form-basic-password");
-  let formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username");
-  is(pword.value, expectedPassword, "Checking " + formID + " password");
-}
 async function prepareLoginsAndProcessForm(url, logins = []) {
   LoginManager.removeAllLogins();
 
   let dates = Date.now();
   for (let login of logins) {
     SpecialPowers.do_QueryInterface(login, SpecialPowers.Ci.nsILoginMetaInfo);
     // Force all dates to be the same so they don't affect things like deduping.
     login.timeCreated = login.timePasswordChanged = login.timeLastUsed = dates;
@@ -54,24 +45,30 @@ async function prepareLoginsAndProcessFo
 }
 
 add_task(async function test_formSubmitURL_wildcard_should_autofill() {
   await prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
     new nsLoginInfo("https://example.com", "", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("name2", "pass2");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name2", pword, "pass2");
 });
 
 add_task(async function test_formSubmitURL_different_shouldnt_autofill() {
   await prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
     new nsLoginInfo("https://example.com", "https://another.domain", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("", "");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "", pword, "");
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_autofill_from_bfcache.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofill_from_bfcache.html
@@ -25,44 +25,38 @@ runInParent(function initLogins() {
 
 <div id="content">
   <a id="next" href="https://example.org/" target="loginWin">Next</a>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 let win;
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  let doc = win.document;
-  let uname = doc.getElementById("form-basic-username");
-  let pword = doc.getElementById("form-basic-password");
-  let formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username");
-  is(pword.value, expectedPassword, "Checking " + formID + " password");
-}
 
 add_task(async function test_crossOriginBfcacheRestore() {
   let processedPromise = promiseFormsProcessed();
   win = window.open("form_basic.html", "loginWin");
   await processedPromise;
-  checkACForm("autofilled", "pass1");
+  let doc = win.document;
+  let uname = doc.getElementById("form-basic-username");
+  let pword = doc.getElementById("form-basic-password");
+  checkLoginForm(uname, "autofilled", pword, "pass1");
 
   let pageHidePromise = new Promise(resolve => {
     win.addEventListener("pagehide", resolve, {once: true});
   });
   info("Navigate to another page on a different origin");
   document.getElementById("next").click();
   let pageHideEvent = await pageHidePromise;
   await SimpleTest.promiseFocus(win);
   ok(pageHideEvent.persisted, "First document should have been persisted in bfcache");
 
   info("go back to the first page (login form)");
   processedPromise = promiseFormsProcessed();
   SpecialPowers.wrap(win).history.back();
   await processedPromise;
-  checkACForm("autofilled", "pass1");
+  checkLoginForm(uname, "autofilled", pword, "pass1");
   win.close();
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_autofill_https_upgrade.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofill_https_upgrade.html
@@ -25,25 +25,16 @@ let nsLoginInfo = SpecialPowers.wrap(Spe
 <div id="content">
   <iframe></iframe>
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 let iframe = SpecialPowers.wrap(document.getElementsByTagName("iframe")[0]);
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  let iframeDoc = iframe.contentDocument;
-  let uname = iframeDoc.getElementById("form-basic-username");
-  let pword = iframeDoc.getElementById("form-basic-password");
-  let formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username");
-  is(pword.value, expectedPassword, "Checking " + formID + " password");
-}
 async function prepareLoginsAndProcessForm(url, logins = []) {
   LoginManager.removeAllLogins();
 
   let dates = Date.now();
   for (let login of logins) {
     SpecialPowers.do_QueryInterface(login, SpecialPowers.Ci.nsILoginMetaInfo);
     // Force all dates to be the same so they don't affect things like deduping.
     login.timeCreated = login.timePasswordChanged = login.timeLastUsed = dates;
@@ -59,57 +50,72 @@ add_task(async function setup() {
 });
 
 add_task(async function test_simpleNoDupesNoAction() {
   await prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
     new nsLoginInfo("http://example.com", "http://example.com", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("name2", "pass2");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name2", pword, "pass2");
 });
 
 add_task(async function test_simpleNoDupesUpgradeOriginAndAction() {
   await prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
     new nsLoginInfo("http://example.com", "http://another.domain", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("name2", "pass2");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name2", pword, "pass2");
 });
 
 add_task(async function test_simpleNoDupesUpgradeOriginOnly() {
   await prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
     new nsLoginInfo("http://example.com", "https://another.domain", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("name2", "pass2");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name2", pword, "pass2");
 });
 
 add_task(async function test_simpleNoDupesUpgradeActionOnly() {
   await prepareLoginsAndProcessForm("https://example.com" + CROSS_ORIGIN_SECURE_PATH, [
     new nsLoginInfo("https://example.com", "http://another.domain", null,
                     "name2", "pass2", "uname", "pword"),
   ]);
 
-  checkACForm("name2", "pass2");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name2", pword, "pass2");
 });
 
 add_task(async function test_dedupe() {
   await prepareLoginsAndProcessForm("https://example.com" + MISSING_ACTION_PATH, [
     new nsLoginInfo("https://example.com", "https://example.com", null,
                     "name1", "passHTTPStoHTTPS", "uname", "pword"),
     new nsLoginInfo("http://example.com", "http://example.com", null,
                     "name1", "passHTTPtoHTTP", "uname", "pword"),
     new nsLoginInfo("http://example.com", "https://example.com", null,
                     "name1", "passHTTPtoHTTPS", "uname", "pword"),
     new nsLoginInfo("https://example.com", "http://example.com", null,
                     "name1", "passHTTPStoHTTP", "uname", "pword"),
   ]);
 
-  checkACForm("name1", "passHTTPStoHTTPS");
+  let iframeDoc = iframe.contentDocument;
+  let uname = iframeDoc.getElementById("form-basic-username");
+  let pword = iframeDoc.getElementById("form-basic-password");
+  checkLoginForm(uname, "name1", pword, "passHTTPStoHTTPS");
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_autofill_sandboxed.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_autofill_sandboxed.html
@@ -43,23 +43,16 @@ var setupScript = runInParent(function s
 <script class="testbody" type="text/javascript">
 
 /** Test for Login Manager: form field autofill in sandboxed documents (null principal) **/
 
 let sandboxed = document.getElementById("sandboxed");
 let uname;
 let pword;
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 function promiseExecuteSoon() {
   return new Promise(SimpleTest.executeSoon);
 }
 
 add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set": [
     ["security.insecure_field_warning.contextual.enabled", true],
   ]});
@@ -84,26 +77,26 @@ add_task(async function test_no_autofill
   let frameDoc = SpecialPowers.wrap(sandboxed).contentDocument;
 
   uname = frameDoc.getElementById("form-basic-username");
   pword = frameDoc.getElementById("form-basic-password");
 
   await promiseExecuteSoon();
   // Autofill shouldn't happen in the sandboxed frame but would have happened by
   // now since DOMFormHasPassword was observed above.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 
   info("blurring the username field after typing the username");
   uname.focus();
   uname.setUserInput("tempuser1");
   synthesizeKey("VK_TAB", {}, frameWindow);
   await promiseExecuteSoon();
   await promiseExecuteSoon();
   await promiseExecuteSoon();
-  checkACForm("tempuser1", "");
+  checkLoginForm(uname, "tempuser1", pword, "");
 });
 
 add_task(async function test_no_autofill_outside_form() {
   sandboxed.src = "formless_basic.html";
   let frameWindow = SpecialPowers.wrap(sandboxed).contentWindow;
   let DOMInputPasswordAddedPromise = new Promise(resolve => {
     SpecialPowers.addChromeEventListener("DOMInputPasswordAdded", function onDIPA() {
       SpecialPowers.removeChromeEventListener("DOMInputPasswordAdded", onDIPA);
@@ -120,14 +113,14 @@ add_task(async function test_no_autofill
   let frameDoc = SpecialPowers.wrap(sandboxed).contentDocument;
 
   uname = frameDoc.getElementById("form-basic-username");
   pword = frameDoc.getElementById("form-basic-password");
 
   await promiseExecuteSoon();
   // Autofill shouldn't happen in the sandboxed frame but would have happened by
   // now since DOMInputPasswordAdded was observed above.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html
@@ -117,23 +117,16 @@ var pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 function sendFakeAutocompleteEvent(element) {
   var acEvent = document.createEvent("HTMLEvents");
   acEvent.initEvent("DOMAutoComplete", true, false);
   element.dispatchEvent(acEvent);
 }
 
 function spinEventLoop() {
   return Promise.resolve();
@@ -142,17 +135,17 @@ function spinEventLoop() {
 add_task(async function setup() {
   listenForUnexpectedPopupShown();
 });
 
 add_task(async function test_form1_initial_empty() {
   await SimpleTest.promiseFocus(window);
 
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 add_task(async function test_form1_menuitems() {
   await SimpleTest.promiseFocus(window);
   // Trigger autocomplete popup
   restoreForm();
@@ -164,83 +157,83 @@ add_task(async function test_form1_menui
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   let expectedMenuItems = ["tempuser1",
                            "testuser2",
                            "testuser3",
                            "zzzuser4"];
   checkAutoCompleteResults(results, expectedMenuItems, "example.com", "Check all menuitems are displayed correctly.");
 
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
   synthesizeKey("KEY_Enter");
   await spinEventLoop(); // let focus happen
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 
 add_task(async function test_form1_first_entry() {
   await SimpleTest.promiseFocus(window);
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   let popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   synthesizeKey("KEY_ArrowDown"); // first
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_second_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_third_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser3", "testpass3");
+  checkLoginForm(uname, "testuser3", pword, "testpass3");
 });
 
 add_task(async function test_form1_fourth_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_ArrowDown"); // fourth
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_first_entry() {
   // Trigger autocomplete popup
   restoreForm();
   await spinEventLoop(); // let focus happen
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
@@ -250,47 +243,47 @@ add_task(async function test_form1_wrapa
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_ArrowDown"); // fourth
   synthesizeKey("KEY_ArrowDown"); // footer
   synthesizeKey("KEY_ArrowDown"); // deselects
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_wraparound_up_last_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowUp"); // footer
   synthesizeKey("KEY_ArrowUp"); // last (fourth)
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_down_up_up() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // select first entry
   synthesizeKey("KEY_ArrowUp"); // selects nothing!
   synthesizeKey("KEY_ArrowUp"); // footer
   synthesizeKey("KEY_ArrowUp"); // select last entry
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_up_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
@@ -301,85 +294,85 @@ add_task(async function test_form1_wrapa
   synthesizeKey("KEY_ArrowUp");
   synthesizeKey("KEY_ArrowUp");
   synthesizeKey("KEY_ArrowUp"); // first entry
   synthesizeKey("KEY_ArrowUp"); // deselects
   synthesizeKey("KEY_ArrowUp"); // footer
   synthesizeKey("KEY_ArrowUp"); // last entry
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_fill_username_without_autofill_right() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Set first entry w/o triggering autocomplete
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowRight");
   await spinEventLoop();
-  checkACForm("tempuser1", ""); // empty password
+  checkLoginForm(uname, "tempuser1", pword, ""); // empty password
 });
 
 add_task(async function test_form1_fill_username_without_autofill_left() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Set first entry w/o triggering autocomplete
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowLeft");
-  checkACForm("tempuser1", ""); // empty password
+  checkLoginForm(uname, "tempuser1", pword, ""); // empty password
 });
 
 add_task(async function test_form1_pageup_first() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry (page up)
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_PageUp"); // first
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_pagedown_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   /* test 13 */
   // Check last entry (page down)
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_PageDown"); // footer
   synthesizeKey("KEY_ArrowUp"); // last
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_untrusted_event() {
   restoreForm();
   await spinEventLoop();
 
   // Send a fake (untrusted) event.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   uname.value = "zzzuser4";
   sendFakeAutocompleteEvent(uname);
   await spinEventLoop();
-  checkACForm("zzzuser4", "");
+  checkLoginForm(uname, "zzzuser4", pword, "");
 });
 
 add_task(async function test_form1_delete() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
@@ -395,114 +388,114 @@ add_task(async function test_form1_delet
 
   let countChangedPromise = notifyMenuChanged(4);
   var deletionPromise = promiseStorageChanged(["removeLogin"]);
   // On OS X, shift-backspace and shift-delete work, just delete does not.
   // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await deletionPromise;
 
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   numLogins = LoginManager.countLogins("https://example.com", "https://autocomplete:8888", null);
   is(numLogins, 4, "Correct number of logins after deleting one");
   await countChangedPromise;
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_first_after_deletion() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check the new first entry (of 3)
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_delete_second() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Delete the second entry (of 3), "testuser3"
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Delete", {shiftKey: true});
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("https://example.com", "https://autocomplete:8888", null);
   is(numLogins, 3, "Correct number of logins after deleting one");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_first_after_deletion2() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check the new first entry (of 2)
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_delete_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   /* test 54 */
   // Delete the last entry (of 2), "zzzuser4"
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Delete", {shiftKey: true});
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("https://example.com", "https://autocomplete:8888", null);
   is(numLogins, 2, "Correct number of logins after deleting one");
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_first_after_3_deletions() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check the only remaining entry
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_check_only_entry_remaining() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   /* test 56 */
   // Delete the only remaining entry, "testuser2"
   synthesizeKey("KEY_ArrowDown");
   let storageChanged = promiseStorageChanged(["removeLogin", "removeLogin", "addLogin"]);
   synthesizeKey("KEY_Delete", {shiftKey: true});
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("https://example.com", "https://autocomplete:8888", null);
   is(numLogins, 1, "Correct number of logins after deleting one");
 
   // remove the logins for the previous tests
   setupScript.sendSyncMessage("removeLogin", "login0");
   setupScript.sendSyncMessage("addLogin", "login5");
   await storageChanged;
 });
@@ -514,129 +507,129 @@ add_task(async function test_form2() {
       <input  type="text"       name="uname">
       <input  type="password"   name="pword" autocomplete="off">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   // Turn our attention to form2
   uname = $_(2, "uname");
   pword = $_(2, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form3() {
   await setFormAndWaitForFieldFilled(`
     <form id="form3" action="https://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname" autocomplete="off">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(3, "uname");
   pword = $_(3, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form4() {
   await setFormAndWaitForFieldFilled(`
     <form id="form4" action="https://autocomplete2" onsubmit="return false;" autocomplete="off">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(4, "uname");
   pword = $_(4, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form5() {
   await setFormAndWaitForFieldFilled(`
     <form id="form5" action="https://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname" autocomplete="off">
       <input  type="password"   name="pword" autocomplete="off">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(5, "uname");
   pword = $_(5, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form6() {
   await setFormAndWaitForFieldFilled(`
     <!-- control -->
     <form id="form6" action="https://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   // (this is a control, w/o autocomplete=off, to ensure the login
   // that was being suppressed would have been filled in otherwise)
   uname = $_(6, "uname");
   pword = $_(6, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form6_changeUsername() {
   // Test that the password field remains filled in after changing
   // the username.
   uname.focus();
   synthesizeKey("KEY_ArrowRight");
   synthesizeKey("X", {shiftKey: true});
   // Trigger the 'blur' event on uname
   pword.focus();
   await spinEventLoop();
-  checkACForm("singleuser5X", "singlepass5");
+  checkLoginForm(uname, "singleuser5X", pword, "singlepass5");
   uname.focus();
 
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login5");
   await storageChanged;
 });
 
 add_task(async function test_form7() {
@@ -649,17 +642,17 @@ add_task(async function test_form7() {
     <form id="form7" action="https://autocomplete3" onsubmit="return false;">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: ""});
 
   uname = $_(7, "uname");
   pword = $_(7, "pword");
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 
   // Insert a new username field into the form. We'll then make sure
   // that invoking the autocomplete doesn't try to fill the form.
   var newField = document.createElement("input");
   newField.setAttribute("type", "text");
   newField.setAttribute("name", "uname2");
   pword.parentNode.insertBefore(newField, pword);
   is($_(7, "uname2").value, "", "Verifying empty uname2");
@@ -677,26 +670,27 @@ add_task(async function test_form7_2() {
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   let results = await shownPromise;
   checkAutoCompleteResults(results,
                            ["form7user1"],
                            "example.com",
                            "Check dropdown is showing all logins while field is blank");
 
+
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   // The form changes, so we expect the old username field to get the
   // selected autocomplete value, but neither the new username field nor
   // the password field should have any values filled in.
   await SimpleTest.promiseWaitForCondition(() => uname.value == "form7user1",
                                            "Wait for username to get filled");
-  checkACForm("form7user1", "");
+  checkLoginForm(uname, "form7user1", pword, "");
   is($_(7, "uname2").value, "", "Verifying empty uname2");
   restoreForm(); // clear field, so reloading test doesn't fail
 
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login6A");
   await storageChanged;
 });
 
@@ -715,33 +709,33 @@ add_task(async function test_form8() {
     <form id="form8" action="https://autocomplete4" onsubmit="return false;">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "form8user", formId: "form8"});
 
   uname = $_(8, "uname");
   pword = $_(8, "pword");
-  checkACForm("form8user", "form8pass");
+  checkLoginForm(uname, "form8user", pword, "form8pass");
   restoreForm();
 });
 
 add_task(async function test_form8_blur() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   // Focus the previous form to trigger a blur.
   $_(7, "uname").focus();
 });
 
 add_task(async function test_form8_2() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   restoreForm();
 });
 
 add_task(async function test_form8_3() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let storageChanged = promiseStorageChanged(["removeLogin", "addLogin", "addLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login7");
   setupScript.sendSyncMessage("addLogin", "login8A");
   setupScript.sendSyncMessage("addLogin", "login8B");
   await storageChanged;
 });
 
 add_task(async function test_form9_filtering() {
@@ -768,29 +762,29 @@ add_task(async function test_form9_filte
   shownPromise = promiseACShown();
   sendString("form9userAB");
   results = await shownPromise;
   checkAutoCompleteResults(results,
                            ["form9userAB"],
                            "example.com",
                            "Check dropdown is showing login with only one 'A'");
 
-  checkACForm("form9userAB", "");
+  checkLoginForm(uname, "form9userAB", pword, "");
   uname.focus();
   synthesizeKey("KEY_ArrowLeft");
   shownPromise = promiseACShown();
   synthesizeKey("A", {shiftKey: true});
   results = await shownPromise;
 
-  checkACForm("form9userAAB", "");
+  checkLoginForm(uname, "form9userAAB", pword, "");
   checkAutoCompleteResults(results, ["form9userAAB"], "example.com", "Check dropdown is updated after inserting 'A'");
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("form9userAAB", "form9pass");
+  checkLoginForm(uname, "form9userAAB", pword, "form9pass");
 });
 
 add_task(async function test_form9_autocomplete_cache() {
   // Note that this addLogin call will only be seen by the autocomplete
   // attempt for the synthesizeKey if we do not successfully cache the
   // autocomplete results.
   let storageChanged = promiseStorageChanged(["addLogin"]);
   setupScript.sendSyncMessage("addLogin", "login8C");
@@ -822,55 +816,55 @@ add_task(async function test_form11_form
       <input  type="password"   name="pword" id="pword">
       <button type="submit">Submit</button>
     </div>`, {fieldSelector: `input[name="uname"]`, fieldValue: "testuser11"});
 
   // Test form-less autocomplete
   uname = $_(11, "uname");
   pword = $_(11, "pword");
   restoreForm();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Trigger autocomplete
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   let processedPromise = promiseFormsProcessed();
   synthesizeKey("KEY_Enter");
   await processedPromise;
-  checkACForm("testuser11", "testpass11");
+  checkLoginForm(uname, "testuser11", pword, "testpass11");
 });
 
 add_task(async function test_form11_open_on_trusted_focus() {
   uname = $_(11, "uname");
   pword = $_(11, "pword");
   uname.value = "";
   pword.value = "";
 
   // Move focus to the password field so we can test the first click on the
   // username field.
   pword.focus();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   const firePrivEventPromise = new Promise((resolve) => {
     uname.addEventListener("click", (e) => {
       ok(e.isTrusted, "Ensure event is trusted");
       resolve();
     });
   });
   const shownPromise = promiseACShown();
   synthesizeMouseAtCenter(uname, {});
   await firePrivEventPromise;
   await shownPromise;
   synthesizeKey("KEY_ArrowDown");
   const processedPromise = promiseFormsProcessed();
   synthesizeKey("KEY_Enter");
   await processedPromise;
-  checkACForm("testuser11", "testpass11");
+  checkLoginForm(uname, "testuser11", pword, "testpass11");
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login11");
   await storageChanged;
 });
 
 add_task(async function test_form12_recipes() {
   let storageChanged = promiseStorageChanged(["addLogin"]);
   setupScript.sendSyncMessage("addLogin", "login10");
@@ -894,69 +888,69 @@ add_task(async function test_form12_reci
   pword = $_(12, "2");
 
   // First test DOMAutocomplete
   // Switch the password field to type=password so _fillForm marks the username
   // field for autocomplete.
   pword.type = "password";
   await promiseFormsProcessed();
   restoreForm();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser10", "testpass10");
+  checkLoginForm(uname, "testuser10", pword, "testpass10");
 
   // Now test recipes with blur on the username field.
   restoreForm();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   uname.value = "testuser10";
-  checkACForm("testuser10", "");
+  checkLoginForm(uname, "testuser10", pword, "");
   synthesizeKey("KEY_Tab");
   await promiseFormsProcessed();
-  checkACForm("testuser10", "testpass10");
+  checkLoginForm(uname, "testuser10", pword, "testpass10");
   await resetRecipes();
 });
 
 add_task(async function test_form13_stays_open_upon_empty_search() {
   await setFormAndWaitForFieldFilled(`
     <!-- test not closing when the search string (.value) becomes empty -->
     <form id="form13" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
       <input  type="text"       name="uname" value="prefilled">
       <input  type="password"   name="pword" value="prefilled">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "prefilled"});
 
   uname = $_(13, "uname");
   pword = $_(13, "pword");
-  checkACForm("prefilled", "prefilled");
+  checkLoginForm(uname, "prefilled", pword, "prefilled");
 
   uname.scrollIntoView();
   let shownPromise = promiseACShown();
   synthesizeMouseAtCenter(uname, {});
   await shownPromise;
   uname.select();
   synthesizeKey("KEY_Delete");
 
   await spinEventLoop();
   let popupState = await getPopupState();
   is(popupState.open, true, "Check popup is still open");
-  checkACForm("", "prefilled");
+  checkLoginForm(uname, "", pword, "prefilled");
 
   info("testing password field");
   synthesizeMouseAtCenter(pword, {});
   pword.select();
   popupState = await getPopupState();
   is(popupState.open, false, "Check popup closed since password field isn't empty");
   shownPromise = promiseACShown();
   synthesizeKey("KEY_Delete");
   await shownPromise;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_formSubmitURL.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete_formSubmitURL.html
@@ -51,36 +51,29 @@ var pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 function spinEventLoop() {
   return Promise.resolve();
 }
 
 add_task(async function setup() {
   listenForUnexpectedPopupShown();
 });
 
 add_task(async function test_form1_initial_empty() {
   await SimpleTest.promiseFocus(window);
 
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 /* For this testcase, the only login that exists for this origin
  * is one with a different formSubmitURL, so the login will appear
  * in the autocomplete popup.
  */
@@ -94,18 +87,18 @@ add_task(async function test_form1_menu_
 
   let popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   let expectedMenuItems = ["dfsu1"];
   checkAutoCompleteResults(results, expectedMenuItems, "example.com", "Check all menuitems are displayed correctly.");
 
   synthesizeKey("KEY_ArrowDown"); // first item
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
 
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("dfsu1", "dfsp1");
+  checkLoginForm(uname, "dfsu1", pword, "dfsp1");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_case_differences.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_case_differences.html
@@ -64,82 +64,76 @@ var pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username");
-  is(pword.value, expectedPassword, "Checking " + formID + " password");
-}
 
 add_task(async function test_empty_first_entry() {
   /* test 1 */
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   // Trigger autocomplete popup
   restoreForm();
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown");
   let results = await shownPromise;
   popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected");
   checkAutoCompleteResults(results, ["name", "Name", "USER"], "example.com", "initial");
 
   // Check first entry
   let index0Promise = notifySelectedIndex(0);
   synthesizeKey("KEY_ArrowDown");
   await index0Promise;
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("name", "pass");
+  checkLoginForm(uname, "name", pword, "pass");
 });
 
 add_task(async function test_empty_second_entry() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("Name", "Pass");
+  checkLoginForm(uname, "Name", pword, "Pass");
 });
 
 add_task(async function test_empty_third_entry() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("USER", "PASS");
+  checkLoginForm(uname, "USER", pword, "PASS");
 });
 
 add_task(async function test_preserve_matching_username_case() {
   restoreForm();
   uname.value = "user";
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check that we don't clobber user-entered text when tabbing away
   // (even with no autocomplete entry selected)
   synthesizeKey("KEY_Tab");
   await promiseFormsProcessed();
-  checkACForm("user", "PASS");
+  checkLoginForm(uname, "user", pword, "PASS");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_autocomplete.html
@@ -112,23 +112,16 @@ var pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  var formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 function sendFakeAutocompleteEvent(element) {
   var acEvent = document.createEvent("HTMLEvents");
   acEvent.initEvent("DOMAutoComplete", true, false);
   element.dispatchEvent(acEvent);
 }
 
 function spinEventLoop() {
   return Promise.resolve();
@@ -138,17 +131,17 @@ add_task(async function setup() {
   await SpecialPowers.pushPrefEnv({"set": [["security.insecure_field_warning.contextual.enabled", true]]});
   listenForUnexpectedPopupShown();
 });
 
 add_task(async function test_form1_initial_empty() {
   await SimpleTest.promiseFocus(window);
 
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 add_task(async function test_form1_warning_entry() {
   await SimpleTest.promiseFocus(window);
   // Trigger autocomplete popup
   restoreForm();
@@ -162,87 +155,87 @@ add_task(async function test_form1_warni
   let expectedMenuItems = ["This connection is not secure. Logins entered here could be compromised. Learn More",
                            "tempuser1",
                            "testuser2",
                            "testuser3",
                            "zzzuser4"];
   checkAutoCompleteResults(results, expectedMenuItems, "mochi.test", "Check all menuitems are displayed correctly.");
 
   synthesizeKey("KEY_ArrowDown"); // select insecure warning
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
   synthesizeKey("KEY_Enter");
   await spinEventLoop(); // let focus happen
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 
 add_task(async function test_form1_first_entry() {
   await SimpleTest.promiseFocus(window);
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   let popupState = await getPopupState();
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_second_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_third_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser3", "testpass3");
+  checkLoginForm(uname, "testuser3", pword, "testpass3");
 });
 
 add_task(async function test_form1_fourth_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_ArrowDown"); // fourth
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_first_entry() {
   // Trigger autocomplete popup
   restoreForm();
   await spinEventLoop(); // let focus happen
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
@@ -254,47 +247,47 @@ add_task(async function test_form1_wrapa
   synthesizeKey("KEY_ArrowDown"); // third
   synthesizeKey("KEY_ArrowDown"); // fourth
   synthesizeKey("KEY_ArrowDown"); // footer
   synthesizeKey("KEY_ArrowDown"); // deselects
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_wraparound_up_last_entry() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowUp"); // footer
   synthesizeKey("KEY_ArrowUp"); // last (fourth)
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_down_up_up() {
   // Trigger autocomplete popup
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // select first entry
   synthesizeKey("KEY_ArrowUp"); // selects nothing!
   synthesizeKey("KEY_ArrowUp"); // selects footer
   synthesizeKey("KEY_ArrowUp"); // select last entry
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_wraparound_up_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
@@ -306,88 +299,88 @@ add_task(async function test_form1_wrapa
   synthesizeKey("KEY_ArrowUp");
   synthesizeKey("KEY_ArrowUp"); // skip insecure warning
   synthesizeKey("KEY_ArrowUp"); // first entry
   synthesizeKey("KEY_ArrowUp"); // deselects
   synthesizeKey("KEY_ArrowUp"); // footer
   synthesizeKey("KEY_ArrowUp"); // last entry
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_fill_username_without_autofill_right() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Set first entry w/o triggering autocomplete
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowRight");
   await spinEventLoop();
-  checkACForm("tempuser1", ""); // empty password
+  checkLoginForm(uname, "tempuser1", pword, ""); // empty password
 });
 
 add_task(async function test_form1_fill_username_without_autofill_left() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Set first entry w/o triggering autocomplete
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowLeft");
-  checkACForm("tempuser1", ""); // empty password
+  checkLoginForm(uname, "tempuser1", pword, ""); // empty password
 });
 
 add_task(async function test_form1_pageup_first() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // Check first entry (page up)
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_ArrowDown"); // second
   synthesizeKey("KEY_PageUp"); // first
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("tempuser1", "temppass1");
+  checkLoginForm(uname, "tempuser1", pword, "temppass1");
 });
 
 add_task(async function test_form1_pagedown_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   // test 13
   // Check last entry (page down)
   synthesizeKey("KEY_ArrowDown"); // first
   synthesizeKey("KEY_PageDown"); // last
   synthesizeKey("KEY_ArrowUp"); // skip footer
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_untrusted_event() {
   restoreForm();
   await spinEventLoop();
 
   // Send a fake (untrusted) event.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   uname.value = "zzzuser4";
   sendFakeAutocompleteEvent(uname);
   await spinEventLoop();
-  checkACForm("zzzuser4", "");
+  checkLoginForm(uname, "zzzuser4", pword, "");
 });
 
 add_task(async function test_form1_delete() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
@@ -405,125 +398,125 @@ add_task(async function test_form1_delet
 
   let countChangedPromise = notifyMenuChanged(5);
   var deletionPromise = promiseStorageChanged(["removeLogin"]);
   // On OS X, shift-backspace and shift-delete work, just delete does not.
   // On Win/Linux, shift-backspace does not work, delete and shift-delete do.
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await deletionPromise;
 
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   numLogins = LoginManager.countLogins("http://mochi.test:8888", "", null);
   is(numLogins, 4, "Correct number of logins after deleting one");
   await countChangedPromise;
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_first_after_deletion() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check the new first entry (of 3)
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_delete_second() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Delete the second entry (of 3), "testuser3"
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_ArrowDown");
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await storageChanged;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("http://mochi.test:8888", "", null);
   is(numLogins, 3, "Correct number of logins after deleting one");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("zzzuser4", "zzzpass4");
+  checkLoginForm(uname, "zzzuser4", pword, "zzzpass4");
 });
 
 add_task(async function test_form1_first_after_deletion2() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check the new first entry (of 2)
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_delete_last() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // test 54
   // Delete the last entry (of 2), "zzzuser4"
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_ArrowDown");
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   synthesizeKey("KEY_Delete", {shiftKey: true});
   await storageChanged;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("http://mochi.test:8888", "", null);
   is(numLogins, 2, "Correct number of logins after deleting one");
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_first_after_3_deletions() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check the only remaining entry
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser2", "testpass2");
+  checkLoginForm(uname, "testuser2", pword, "testpass2");
 });
 
 add_task(async function test_form1_check_only_entry_remaining() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // test 56
   // Delete the only remaining entry, "testuser2"
   synthesizeKey("KEY_ArrowDown");
   let storageChanged = promiseStorageChanged(["removeLogin", "removeLogin"]);
   synthesizeKey("KEY_Delete", {shiftKey: true});
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let numLogins = LoginManager.countLogins("http://mochi.test:8888", "", null);
   is(numLogins, 1, "Correct number of logins after deleting one");
 
   // remove the login that's not shown in the list.
   setupScript.sendSyncMessage("removeLogin", "login0");
   await storageChanged;
 });
 
@@ -538,133 +531,133 @@ add_task(async function test_form2() {
       <input  type="text"       name="uname">
       <input  type="password"   name="pword" autocomplete="off">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   // Turn our attention to form2
   uname = $_(2, "uname");
   pword = $_(2, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form3() {
   await setFormAndWaitForFieldFilled(`
     <form id="form3" action="http://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname" autocomplete="off">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(3, "uname");
   pword = $_(3, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form4() {
   await setFormAndWaitForFieldFilled(`
     <form id="form4" action="http://autocomplete2" onsubmit="return false;" autocomplete="off">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(4, "uname");
   pword = $_(4, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form5() {
   await setFormAndWaitForFieldFilled(`
     <form id="form5" action="http://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname" autocomplete="off">
       <input  type="password"   name="pword" autocomplete="off">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   uname = $_(5, "uname");
   pword = $_(5, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form6() {
   await setFormAndWaitForFieldFilled(`
     <!-- control -->
     <form id="form6" action="http://autocomplete2" onsubmit="return false;">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "singleuser5"});
 
   // (this is a control, w/o autocomplete=off, to ensure the login
   // that was being suppressed would have been filled in otherwise)
   uname = $_(6, "uname");
   pword = $_(6, "pword");
-  checkACForm("singleuser5", "singlepass5");
+  checkLoginForm(uname, "singleuser5", pword, "singlepass5");
 });
 
 add_task(async function test_form6_changeUsername() {
   // Test that the password field remains filled in after changing
   // the username.
   uname.focus();
   synthesizeKey("KEY_ArrowRight");
   synthesizeKey("X", {shiftKey: true});
   // Trigger the 'blur' event on uname
   pword.focus();
   await spinEventLoop();
-  checkACForm("singleuser5X", "singlepass5");
+  checkLoginForm(uname, "singleuser5X", pword, "singlepass5");
   uname.focus();
 
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login5");
   await storageChanged;
 });
 
 add_task(async function test_form7() {
@@ -677,17 +670,17 @@ add_task(async function test_form7() {
     <form id="form7" action="http://autocomplete3" onsubmit="return false;">
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: ""});
 
   uname = $_(7, "uname");
   pword = $_(7, "pword");
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 
   // Insert a new username field into the form. We'll then make sure
   // that invoking the autocomplete doesn't try to fill the form.
   var newField = document.createElement("input");
   newField.setAttribute("type", "text");
   newField.setAttribute("name", "uname2");
   pword.parentNode.insertBefore(newField, pword);
   is($_(7, "uname2").value, "", "Verifying empty uname2");
@@ -704,23 +697,23 @@ add_task(async function test_form7_2() {
   restoreForm();
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   // Check first entry
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   // The form changes, so we expect the old username field to get the
   // selected autocomplete value, but neither the new username field nor
   // the password field should have any values filled in.
   await spinEventLoop();
-  checkACForm("form7user1", "");
+  checkLoginForm(uname, "form7user1", pword, "");
   is($_(7, "uname2").value, "", "Verifying empty uname2");
   restoreForm(); // clear field, so reloading test doesn't fail
 
   let storageChanged = promiseStorageChanged(["removeLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login6A");
   await storageChanged;
 });
 
@@ -740,33 +733,33 @@ add_task(async function test_form8() {
       <input  type="text"       name="uname">
       <input  type="password"   name="pword">
       <button type="submit">Submit</button>
     </form>
     `, {fieldSelector: `#form8 input[name="uname"]`, fieldValue: "form8user"});
 
   uname = $_(8, "uname");
   pword = $_(8, "pword");
-  checkACForm("form8user", "form8pass");
+  checkLoginForm(uname, "form8user", pword, "form8pass");
   restoreForm();
 });
 
 add_task(async function test_form8_blur() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   // Focus the previous form to trigger a blur.
   $_(7, "uname").focus();
 });
 
 add_task(async function test_form8_2() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   restoreForm();
 });
 
 add_task(async function test_form8_3() {
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 
 add_task(async function test_form9_filtering() {
   let storageChanged = promiseStorageChanged(["removeLogin", "addLogin", "addLogin"]);
   setupScript.sendSyncMessage("removeLogin", "login7");
   setupScript.sendSyncMessage("addLogin", "login8A");
   setupScript.sendSyncMessage("addLogin", "login8B");
   await storageChanged;
@@ -792,33 +785,33 @@ add_task(async function test_form9_filte
   shownPromise = promiseACShown();
   sendString("form9userAB");
   results = await shownPromise;
   checkAutoCompleteResults(results,
                            ["This connection is not secure. Logins entered here could be compromised. Learn More", "form9userAB"],
                            "mochi.test",
                            "Check dropdown is showing login with only one 'A'");
 
-  checkACForm("form9userAB", "");
+  checkLoginForm(uname, "form9userAB", pword, "");
   uname.focus();
   synthesizeKey("KEY_ArrowLeft");
   shownPromise = promiseACShown();
   synthesizeKey("A", {shiftKey: true});
   results = await shownPromise;
 
-  checkACForm("form9userAAB", "");
+  checkLoginForm(uname, "form9userAAB", pword, "");
   checkAutoCompleteResults(results,
                            ["This connection is not secure. Logins entered here could be compromised. Learn More", "form9userAAB"],
                            "mochi.test",
                            "Check dropdown is updated after inserting 'A'");
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown");
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("form9userAAB", "form9pass");
+  checkLoginForm(uname, "form9userAAB", pword, "form9pass");
 });
 
 add_task(async function test_form9_autocomplete_cache() {
   // Note that this addLogin call will only be seen by the autocomplete
   // attempt for the synthesizeKey if we do not successfully cache the
   // autocomplete results.
   let storageChanged = promiseStorageChanged(["addLogin"]);
   setupScript.sendSyncMessage("addLogin", "login8C");
@@ -871,36 +864,36 @@ add_task(async function test_form11_reci
   pword = $_(11, "2");
 
   // First test DOMAutocomplete
   // Switch the password field to type=password so _fillForm marks the username
   // field for autocomplete.
   pword.type = "password";
   await promiseFormsProcessed();
   restoreForm();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let shownPromise = promiseACShown();
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   synthesizeKey("KEY_Enter");
   await promiseFormsProcessed();
-  checkACForm("testuser10", "testpass10");
+  checkLoginForm(uname, "testuser10", pword, "testpass10");
 
   // Now test recipes with blur on the username field.
   restoreForm();
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   uname.value = "testuser10";
-  checkACForm("testuser10", "");
+  checkLoginForm(uname, "testuser10", pword, "");
   synthesizeKey("KEY_Tab");
   await promiseFormsProcessed();
-  checkACForm("testuser10", "testpass10");
+  checkLoginForm(uname, "testuser10", pword, "testpass10");
   await resetRecipes();
 });
 
 add_task(async function test_form12_formless() {
   // Test form-less autocomplete
   await setFormAndWaitForFieldFilled(`
     <!-- tests <form>-less autocomplete -->
     <div id="form12">
@@ -919,54 +912,54 @@ add_task(async function test_form12_form
   synthesizeKey("KEY_ArrowDown"); // open
   let results = await shownPromise;
 
   let expectedMenuItems = ["This connection is not secure. Logins entered here could be compromised. Learn More",
                            "testuser10"];
   checkAutoCompleteResults(results, expectedMenuItems, "mochi.test", "Check all menuitems are displayed correctly.");
   synthesizeKey("KEY_ArrowDown"); // skip insecure warning
   synthesizeKey("KEY_ArrowDown");
-  checkACForm("", ""); // value shouldn't update
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update
   let processedPromise = promiseFormsProcessed();
   synthesizeKey("KEY_Enter");
   await processedPromise;
-  checkACForm("testuser10", "testpass10");
+  checkLoginForm(uname, "testuser10", pword, "testpass10");
 });
 
 add_task(async function test_form13_stays_open_upon_empty_search() {
   await setFormAndWaitForFieldFilled(`
     <!-- test not closing when the search string (.value) becomes empty -->
     <form id="form13" action="http://autocomplete:8888/formtest.js" onsubmit="return false;">
       <input  type="text"       name="uname" value="prefilled">
       <input  type="password"   name="pword" value="prefilled">
       <button type="submit">Submit</button>
     </form>`, {fieldSelector: `input[name="uname"]`, fieldValue: "prefilled"});
 
   uname = $_(13, "uname");
   pword = $_(13, "pword");
-  checkACForm("prefilled", "prefilled");
+  checkLoginForm(uname, "prefilled", pword, "prefilled");
 
   uname.scrollIntoView();
   let shownPromise = promiseACShown();
   synthesizeMouseAtCenter(uname, {});
   await shownPromise;
   uname.select();
   synthesizeKey("KEY_Delete");
 
   await spinEventLoop();
   let popupState = await getPopupState();
   is(popupState.open, true, "Check popup is still open");
-  checkACForm("", "prefilled");
+  checkLoginForm(uname, "", pword, "prefilled");
 
   info("testing password field");
   synthesizeMouseAtCenter(pword, {});
   pword.select();
   popupState = await getPopupState();
   is(popupState.open, false, "Check popup closed since password field isn't empty");
   shownPromise = promiseACShown();
   synthesizeKey("KEY_Delete");
   await shownPromise;
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_no_saved_login.html
+++ b/toolkit/components/passwordmgr/test/mochitest/test_insecure_form_field_no_saved_login.html
@@ -47,36 +47,29 @@ let pword = $_(1, "pword");
 
 // Restore the form to the default state.
 function restoreForm() {
   uname.value = "";
   pword.value = "";
   uname.focus();
 }
 
-// Check for expected username/password in form.
-function checkACForm(expectedUsername, expectedPassword) {
-  let formID = uname.parentNode.id;
-  is(uname.value, expectedUsername, "Checking " + formID + " username is: " + expectedUsername);
-  is(pword.value, expectedPassword, "Checking " + formID + " password is: " + expectedPassword);
-}
-
 function spinEventLoop() {
   return Promise.resolve();
 }
 
 add_task(async function setup() {
   listenForUnexpectedPopupShown();
 });
 
 add_task(async function test_form1_initial_empty() {
   await SimpleTest.promiseFocus(window);
 
   // Make sure initial form is empty.
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
   let popupState = await getPopupState();
   is(popupState.open, false, "Check popup is initially closed");
 });
 
 add_task(async function test_form1_warning_entry() {
   await SimpleTest.promiseFocus(window);
   // Trigger autocomplete popup
   restoreForm();
@@ -84,18 +77,18 @@ add_task(async function test_form1_warni
   synthesizeKey("KEY_ArrowDown"); // open
   await shownPromise;
 
   let popupState = await getPopupState();
   is(popupState.open, true, "Check popup is opened");
   is(popupState.selectedIndex, -1, "Check no entries are selected upon opening");
 
   synthesizeKey("KEY_ArrowDown"); // select insecure warning
-  checkACForm("", ""); // value shouldn't update just by selecting
+  checkLoginForm(uname, "", pword, ""); // value shouldn't update just by selecting
   synthesizeKey("KEY_Enter");
   await spinEventLoop(); // let focus happen
-  checkACForm("", "");
+  checkLoginForm(uname, "", pword, "");
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -212,16 +212,21 @@ function expectPopup() {
 }
 
 function popupShownListener() {
   info("popup shown for test " + testNum);
   if (expectingPopup) {
     expectingPopup = false;
     SimpleTest.executeSoon(runTest);
   } else {
+    if (testNum == 253) {
+      // Bug 1420103
+      todo(false, "Autocomplete popup not expected during test " + testNum);
+      return;
+    }
     ok(false, "Autocomplete popup not expected during test " + testNum);
   }
 }
 
 registerPopupShownListener(popupShownListener);
 
 /*
  * Main section of test...
@@ -1067,16 +1072,15 @@ function checkMenuEntries(expectedValues
 }
 
 function startTest() {
   setupFormHistory(function() {
     runTest();
   });
 }
 
-window.onload = startTest;
-
+SimpleTest.waitForFocus(startTest);
 SimpleTest.waitForExplicitFinish();
 SimpleTest.requestFlakyTimeout("untriaged");
 </script>
 </pre>
 </body>
 </html>
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -118,16 +118,18 @@
 
     --in-content-category-text-selected: var(--blue-40);
     --in-content-category-text-selected-active: var(--blue-50);
     --in-content-link-color: var(--blue-40);
     --in-content-link-color-hover: var(--blue-50);
     --in-content-link-color-active: var(--blue-60);
 
     --in-content-tab-color: var(--in-content-page-color);
+
+    scrollbar-color: rgba(249,249,250,.4) rgba(20,20,25,.3);
   }
 }
 }
 
 html|html,
 xul|page,
 xul|window {
   font: message-box;