Browse Source

make it actually work

Frank-Rainer Grahl 8 months ago
parent
commit
101a72d35d
100 changed files with 60480 additions and 302 deletions
  1. 30 0
      frg/work-js/comm-release/patches/1466297-62a1.patch
  2. 1 1
      frg/work-js/comm-release/patches/series
  3. 78 0
      frg/work-js/mozilla-release/patches/1339800-58a1.patch
  4. 3 3
      frg/work-js/mozilla-release/patches/1348273-63a1.patch
  5. 2049 0
      frg/work-js/mozilla-release/patches/1351685-61a1.patch
  6. 512 0
      frg/work-js/mozilla-release/patches/1354883-61a1.patch
  7. 995 0
      frg/work-js/mozilla-release/patches/1382581-1-61a1.patch
  8. 272 0
      frg/work-js/mozilla-release/patches/1382581-2-61a1.patch
  9. 54 0
      frg/work-js/mozilla-release/patches/1382581-3-61a1.patch
  10. 75 0
      frg/work-js/mozilla-release/patches/1382581-4-61a1.patch
  11. 286 0
      frg/work-js/mozilla-release/patches/1382581-5-61a1.patch
  12. 122 0
      frg/work-js/mozilla-release/patches/1382581-6-61a1.patch
  13. 343 0
      frg/work-js/mozilla-release/patches/1382581-7-61a1.patch
  14. 130 0
      frg/work-js/mozilla-release/patches/1382581-8-61a1.patch
  15. 72 0
      frg/work-js/mozilla-release/patches/1382585-61a1.patch
  16. 73 0
      frg/work-js/mozilla-release/patches/1382599-61a1.patch
  17. 419 0
      frg/work-js/mozilla-release/patches/1382600-61a1.patch
  18. 441 0
      frg/work-js/mozilla-release/patches/1382601-1-61a1.patch
  19. 31 0
      frg/work-js/mozilla-release/patches/1382601-2-61a1.patch
  20. 243 0
      frg/work-js/mozilla-release/patches/1382602-61a1.patch
  21. 1 1
      frg/work-js/mozilla-release/patches/1394235-57a1.patch
  22. 229 0
      frg/work-js/mozilla-release/patches/1394554-1-57a1.patch
  23. 81 0
      frg/work-js/mozilla-release/patches/1394554-2-57a1.patch
  24. 109 0
      frg/work-js/mozilla-release/patches/1394554-3-57a1.patch
  25. 2951 0
      frg/work-js/mozilla-release/patches/1398981-59a1.patch
  26. 1 1
      frg/work-js/mozilla-release/patches/1399877-64a1.patch
  27. 291 0
      frg/work-js/mozilla-release/patches/1399948-2-58a1.patch
  28. 0 0
      frg/work-js/mozilla-release/patches/1401939-61a1.patch
  29. 1 1
      frg/work-js/mozilla-release/patches/1408790-58a1.patch
  30. 384 0
      frg/work-js/mozilla-release/patches/1411368-2-58a1.patch
  31. 57 0
      frg/work-js/mozilla-release/patches/1414286-1-61a1.patch
  32. 1095 0
      frg/work-js/mozilla-release/patches/1414286-2-61a1.patch
  33. 79 0
      frg/work-js/mozilla-release/patches/1415211-60a1.patch
  34. 0 0
      frg/work-js/mozilla-release/patches/1419094-1-61a1.patch
  35. 0 0
      frg/work-js/mozilla-release/patches/1419094-2-61a1.patch
  36. 2566 0
      frg/work-js/mozilla-release/patches/1419925-59a1.patch
  37. 107 0
      frg/work-js/mozilla-release/patches/1421582-59a1.patch
  38. 1 1
      frg/work-js/mozilla-release/patches/1421992-4c-59a1.patch
  39. 0 0
      frg/work-js/mozilla-release/patches/1422043-2-60a1.patch
  40. 0 0
      frg/work-js/mozilla-release/patches/1422043-3-60a1.patch
  41. 0 0
      frg/work-js/mozilla-release/patches/1422043-4-60a1.patch
  42. 0 0
      frg/work-js/mozilla-release/patches/1422043-5-60a1.patch
  43. 29 0
      frg/work-js/mozilla-release/patches/1424025-59a1.patch
  44. 618 0
      frg/work-js/mozilla-release/patches/1427677-1-59a1.patch
  45. 92 0
      frg/work-js/mozilla-release/patches/1427677-2-59a1.patch
  46. 310 0
      frg/work-js/mozilla-release/patches/1427908-59a1.patch
  47. 1443 0
      frg/work-js/mozilla-release/patches/1428685-59a1.patch
  48. 166 0
      frg/work-js/mozilla-release/patches/1428722-1-59a1.patch
  49. 190 0
      frg/work-js/mozilla-release/patches/1428722-2-59a1.patch
  50. 298 0
      frg/work-js/mozilla-release/patches/1428722-3-59a1.patch
  51. 1 1
      frg/work-js/mozilla-release/patches/1429206-1-60a1.patch
  52. 1 1
      frg/work-js/mozilla-release/patches/1429206-3no2-60a1.patch
  53. 305 0
      frg/work-js/mozilla-release/patches/1429656-59a1.patch
  54. 145 0
      frg/work-js/mozilla-release/patches/1429982-59a1.patch
  55. 142 0
      frg/work-js/mozilla-release/patches/1430299-59a1.patch
  56. 2028 0
      frg/work-js/mozilla-release/patches/1430547-59a1.patch
  57. 1330 0
      frg/work-js/mozilla-release/patches/1431740-59a1.patch
  58. 57 0
      frg/work-js/mozilla-release/patches/1436575-2b-60a1.patch
  59. 1998 0
      frg/work-js/mozilla-release/patches/1436670-60a1.patch
  60. 32 0
      frg/work-js/mozilla-release/patches/1437714-60a1.patch
  61. 31 0
      frg/work-js/mozilla-release/patches/1437728-60a1.patch
  62. 98 0
      frg/work-js/mozilla-release/patches/1439925-60a1.patch
  63. 5273 0
      frg/work-js/mozilla-release/patches/1440388-1-61a1.patch
  64. 200 0
      frg/work-js/mozilla-release/patches/1440388-2-61a1.patch
  65. 134 0
      frg/work-js/mozilla-release/patches/1440388-3-61a1.patch
  66. 82 0
      frg/work-js/mozilla-release/patches/1440388-4-61a1.patch
  67. 125 0
      frg/work-js/mozilla-release/patches/1441462-60a1.patch
  68. 3007 0
      frg/work-js/mozilla-release/patches/1441703-1-61a1.patch
  69. 463 0
      frg/work-js/mozilla-release/patches/1441703-2-61a1.patch
  70. 12441 0
      frg/work-js/mozilla-release/patches/1442465-4_2-61a1.patch
  71. 303 0
      frg/work-js/mozilla-release/patches/1442465-5-61a1.patch
  72. 88 0
      frg/work-js/mozilla-release/patches/1442465-6-61a1.patch
  73. 30 0
      frg/work-js/mozilla-release/patches/1442465-7-61a1.patch
  74. 2 2
      frg/work-js/mozilla-release/patches/1443429-2-66a1.patch
  75. 484 0
      frg/work-js/mozilla-release/patches/1443846-1-61a1.patch
  76. 600 0
      frg/work-js/mozilla-release/patches/1443846-2-61a1.patch
  77. 111 0
      frg/work-js/mozilla-release/patches/1443846-3-61a1.patch
  78. 44 0
      frg/work-js/mozilla-release/patches/1444007-61a1.patch
  79. 7998 0
      frg/work-js/mozilla-release/patches/1444033-61a1.patch
  80. 451 0
      frg/work-js/mozilla-release/patches/1444327-61a1.patch
  81. 87 0
      frg/work-js/mozilla-release/patches/1445153-61a1.patch
  82. 0 0
      frg/work-js/mozilla-release/patches/1445181-1-61a1.patch
  83. 0 0
      frg/work-js/mozilla-release/patches/1445181-2-61a1.patch
  84. 0 0
      frg/work-js/mozilla-release/patches/1445181-3-61a1.patch
  85. 0 0
      frg/work-js/mozilla-release/patches/1445196-61a1.patch
  86. 74 0
      frg/work-js/mozilla-release/patches/1445776-61a1.patch
  87. 382 0
      frg/work-js/mozilla-release/patches/1446064-61a1.patch
  88. 310 0
      frg/work-js/mozilla-release/patches/1446941-61a1.patch
  89. 73 0
      frg/work-js/mozilla-release/patches/1447154-1-61a1.patch
  90. 402 0
      frg/work-js/mozilla-release/patches/1447154-2-61a1.patch
  91. 32 0
      frg/work-js/mozilla-release/patches/1447180-61a1.patch
  92. 94 0
      frg/work-js/mozilla-release/patches/1447528-61a1.patch
  93. 143 0
      frg/work-js/mozilla-release/patches/1448288-61a1.patch
  94. 45 0
      frg/work-js/mozilla-release/patches/1450163-61a1.patch
  95. 1 1
      frg/work-js/mozilla-release/patches/1450242-61a1.patch
  96. 2 2
      frg/work-js/mozilla-release/patches/1463048-64a1.patch
  97. 1 1
      frg/work-js/mozilla-release/patches/1463596-62a1.patch
  98. 564 0
      frg/work-js/mozilla-release/patches/1463924-62a1.patch
  99. 0 286
      frg/work-js/mozilla-release/patches/1465060-1-PARTIAL-js-62a1.patch
  100. 2938 0
      frg/work-js/mozilla-release/patches/1465060-1-std-62a1.patch

+ 30 - 0
frg/work-js/comm-release/patches/1466297-62a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Jorg K <jorgk@jorgk.com>
+# Date 1527919359 -7200
+#      Sat Jun 02 08:02:39 2018 +0200
+# Node ID 16e80af1dc4a4008b5ff07f674fce6427b799a3d
+# Parent  da665e98363c482e694691f611c2e24aae6188e5
+Bug 1466297 - Port bug 1465585: Switch from mozilla::Move to std::move. rs=bustage-fix
+
+diff --git a/mailnews/base/search/src/nsMsgFilterList.cpp b/mailnews/base/search/src/nsMsgFilterList.cpp
+--- a/mailnews/base/search/src/nsMsgFilterList.cpp
++++ b/mailnews/base/search/src/nsMsgFilterList.cpp
+@@ -579,17 +579,17 @@ nsresult nsMsgFilterList::LoadValue(nsCS
+ }
+ 
+ nsresult nsMsgFilterList::LoadTextFilters(already_AddRefed<nsIInputStream> aStream)
+ {
+   nsresult  err = NS_OK;
+   uint64_t bytesAvailable;
+ 
+   nsCOMPtr<nsIInputStream> bufStream;
+-  nsCOMPtr<nsIInputStream> stream = mozilla::Move(aStream);
++  nsCOMPtr<nsIInputStream> stream = std::move(aStream);
+   err = NS_NewBufferedInputStream(getter_AddRefs(bufStream), stream.forget(), FILE_IO_BUFFER_SIZE);
+   NS_ENSURE_SUCCESS(err, err);
+ 
+   nsMsgFilterFileAttribValue attrib;
+   nsCOMPtr<nsIMsgRuleAction> currentFilterAction;
+   // We'd really like to move lot's of these into the objects that they refer to.
+   do
+   {

+ 1 - 1
frg/work-js/comm-release/patches/series

@@ -2145,7 +2145,7 @@ WIP-1687385-mozconfigs-v1_2-253.patch
 WIP-9999999-lintglobals.patch
 TOP-1378089-4-bookmarks-wip-25319.patch
 TOP-1872623-cancelbookmark-25319.patch
-
+1466297-62a1.patch
 
 
 

+ 78 - 0
frg/work-js/mozilla-release/patches/1339800-58a1.patch

@@ -0,0 +1,78 @@
+# HG changeset patch
+# User Patrick Brosset <pbrosset@mozilla.com>
+# Date 1508161185 -7200
+# Node ID 1ba66e0d82a0d03a6b5656ba955b95dd60801b2b
+# Parent  05fd06706f066e6b6a4c3a5124376a30dc3fb377
+Bug 1339800 - Toggle the highlighter between each test case; r=gl
+
+MozReview-Commit-ID: 4TS7JovMaJI
+
+diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-zoom.js b/devtools/client/inspector/test/browser_inspector_highlighter-zoom.js
+--- a/devtools/client/inspector/test/browser_inspector_highlighter-zoom.js
++++ b/devtools/client/inspector/test/browser_inspector_highlighter-zoom.js
+@@ -16,52 +16,40 @@ const TEST_LEVELS = [2, 1, .5];
+ // node, for the values given.
+ const expectedStyle = (w, h, z) =>
+         (z !== 1 ? `transform-origin:top left; transform:scale(${1 / z}); ` : "") +
+         `position:absolute; width:${w * z}px;height:${h * z}px; ` +
+         "overflow:hidden";
+ 
+ add_task(function* () {
+   let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
+-
+-  info("Highlighting the test node");
++  let highlighterUtils = inspector.toolbox.highlighterUtils;
+ 
+-  yield hoverElement("div", inspector);
+-  let isVisible = yield testActor.isHighlighting();
+-  ok(isVisible, "The highlighter is visible");
++  let div = yield getNodeFront("div", inspector);
+ 
+   for (let level of TEST_LEVELS) {
+-    info("Zoom to level " + level +
+-         " and check that the highlighter is correct");
++    info(`Zoom to level ${level}`);
++    yield testActor.zoomPageTo(level, false);
+ 
+-    yield testActor.zoomPageTo(level);
+-    isVisible = yield testActor.isHighlighting();
+-    ok(isVisible, "The highlighter is still visible at zoom level " + level);
++    info("Highlight the test node");
++    yield highlighterUtils.highlightNodeFront(div);
++
++    let isVisible = yield testActor.isHighlighting();
++    ok(isVisible, `The highlighter is visible at zoom level ${level}`);
+ 
+     yield testActor.isNodeCorrectlyHighlighted("div", is);
+ 
+     info("Check that the highlighter root wrapper node was scaled down");
+ 
+     let style = yield getElementsNodeStyle(testActor);
+     let { width, height } = yield testActor.getWindowDimensions();
+     is(style, expectedStyle(width, height, level),
+       "The style attribute of the root element is correct");
++
++    info("Unhighlight the node");
++    yield highlighterUtils.unhighlight();
+   }
+ });
+ 
+-function* hoverElement(selector, inspector) {
+-  info("Hovering node " + selector + " in the markup view");
+-  let container = yield getContainerForSelector(selector, inspector);
+-  yield hoverContainer(container, inspector);
+-}
+-
+-function* hoverContainer(container, inspector) {
+-  let onHighlight = inspector.toolbox.once("node-highlight");
+-  EventUtils.synthesizeMouse(container.tagLine, 2, 2, {type: "mousemove"},
+-      inspector.markup.doc.defaultView);
+-  yield onHighlight;
+-}
+-
+ function* getElementsNodeStyle(testActor) {
+-  let value = yield testActor.getHighlighterNodeAttribute(
+-    "box-model-elements", "style");
++  let value = yield testActor.getHighlighterNodeAttribute("box-model-elements", "style");
+   return value;
+ }

+ 3 - 3
frg/work-js/mozilla-release/patches/1348273-63a1.patch

@@ -421,8 +421,8 @@ diff --git a/gfx/ipc/GPUProcessManager.cpp b/gfx/ipc/GPUProcessManager.cpp
      return;
    }
  
-   mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, Move(vsyncChild));
-   mGPUChild->SendInitVsyncBridge(Move(vsyncParent));
+   mVsyncBridge = VsyncBridgeChild::Create(mVsyncIOThread, mProcessToken, std::move(vsyncChild));
+   mGPUChild->SendInitVsyncBridge(std::move(vsyncParent));
  
    CrashReporter::AnnotateCrashReport(
 -    NS_LITERAL_CSTRING("GPUProcessStatus"),
@@ -1533,7 +1533,7 @@ diff --git a/ipc/mscom/ProxyStream.cpp b/ipc/mscom/ProxyStream.cpp
 +      CrashReporter::Annotation::GetHGlobalFromStreamFailure, hrAsStr);
    }
  
-   mStream = mozilla::Move(stream);
+   mStream = std::move(stream);
  
    if (streamSize) {
 -    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("ProxyStreamSizeFrom"),

+ 2049 - 0
frg/work-js/mozilla-release/patches/1351685-61a1.patch

@@ -0,0 +1,2049 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1520871915 14400
+# Node ID 1d9cd8bb925d25778241064afb178f4084783a62
+# Parent  e662674bbf249891532e00a4b03c02d57bbc8688
+Bug 1351685 - Remove the box model from the computed view. r=pbro
+
+diff --git a/devtools/client/inspector/boxmodel/components/BoxModelApp.js b/devtools/client/inspector/boxmodel/components/BoxModelApp.js
+deleted file mode 100644
+--- a/devtools/client/inspector/boxmodel/components/BoxModelApp.js
++++ /dev/null
+@@ -1,56 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this
+- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-"use strict";
+-
+-const Services = require("Services");
+-const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+-const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+-const { connect } = require("devtools/client/shared/vendor/react-redux");
+-const { LocalizationHelper } = require("devtools/shared/l10n");
+-
+-const Accordion =
+-  createFactory(require("devtools/client/inspector/layout/components/Accordion"));
+-const BoxModel = createFactory(require("./BoxModel"));
+-
+-const Types = require("../types");
+-
+-const BOXMODEL_STRINGS_URI = "devtools/client/locales/boxmodel.properties";
+-const BOXMODEL_L10N = new LocalizationHelper(BOXMODEL_STRINGS_URI);
+-
+-const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";
+-
+-class BoxModelApp extends PureComponent {
+-  static get propTypes() {
+-    return {
+-      boxModel: PropTypes.shape(Types.boxModel).isRequired,
+-      setSelectedNode: PropTypes.func.isRequired,
+-      showBoxModelProperties: PropTypes.bool.isRequired,
+-      onHideBoxModelHighlighter: PropTypes.func.isRequired,
+-      onShowBoxModelEditor: PropTypes.func.isRequired,
+-      onShowBoxModelHighlighter: PropTypes.func.isRequired,
+-      onShowBoxModelHighlighterForNode: PropTypes.func.isRequired,
+-      onToggleGeometryEditor: PropTypes.func.isRequired,
+-    };
+-  }
+-
+-  render() {
+-    return Accordion({
+-      items: [
+-        {
+-          header: BOXMODEL_L10N.getStr("boxmodel.title"),
+-          component: BoxModel,
+-          componentProps: this.props,
+-          opened: Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+-          onToggled: () => {
+-            let opened = Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF);
+-            Services.prefs.setBoolPref(BOXMODEL_OPENED_PREF, !opened);
+-          }
+-        }
+-      ]
+-    });
+-  }
+-}
+-
+-module.exports = connect(state => state)(BoxModelApp);
+diff --git a/devtools/client/inspector/boxmodel/components/moz.build b/devtools/client/inspector/boxmodel/components/moz.build
+--- a/devtools/client/inspector/boxmodel/components/moz.build
++++ b/devtools/client/inspector/boxmodel/components/moz.build
+@@ -1,15 +1,14 @@
+ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+ # vim: set filetype=python:
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ DevToolsModules(
+     'BoxModel.js',
+-    'BoxModelApp.js',
+     'BoxModelEditable.js',
+     'BoxModelInfo.js',
+     'BoxModelMain.js',
+     'BoxModelProperties.js',
+     'ComputedProperty.js',
+ )
+diff --git a/devtools/client/inspector/boxmodel/test/browser.ini b/devtools/client/inspector/boxmodel/test/browser.ini
+--- a/devtools/client/inspector/boxmodel/test/browser.ini
++++ b/devtools/client/inspector/boxmodel/test/browser.ini
+@@ -8,17 +8,16 @@ support-files =
+   !/devtools/client/commandline/test/helpers.js
+   !/devtools/client/inspector/test/head.js
+   !/devtools/client/inspector/test/shared-head.js
+   !/devtools/client/shared/test/shared-head.js
+   !/devtools/client/shared/test/test-actor.js
+   !/devtools/client/shared/test/test-actor-registry.js
+ 
+ [browser_boxmodel.js]
+-[browser_boxmodel_computed-accordion-state.js]
+ [browser_boxmodel_edit-position-visible-position-change.js]
+ [browser_boxmodel_editablemodel.js]
+ [browser_boxmodel_editablemodel_allproperties.js]
+ disabled=too many intermittent failures (bug 1009322)
+ [browser_boxmodel_editablemodel_bluronclick.js]
+ [browser_boxmodel_editablemodel_border.js]
+ [browser_boxmodel_editablemodel_pseudo.js]
+ [browser_boxmodel_editablemodel_stylerules.js]
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel.js
+@@ -152,41 +152,41 @@ var res2 = [
+ 
+ add_task(function* () {
+   let style = "div { position: absolute; top: 42px; left: 42px; " +
+               "height: 100.111px; width: 100px; border: 10px solid black; " +
+               "padding: 20px; margin: 30px auto;}";
+   let html = "<style>" + style + "</style><div></div>";
+ 
+   yield addTab("data:text/html," + encodeURIComponent(html));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+   yield selectNode("div", inspector);
+ 
+-  yield testInitialValues(inspector, view);
+-  yield testChangingValues(inspector, view, testActor);
++  yield testInitialValues(inspector, boxmodel);
++  yield testChangingValues(inspector, boxmodel, testActor);
+ });
+ 
+-function* testInitialValues(inspector, view) {
++function* testInitialValues(inspector, boxmodel) {
+   info("Test that the initial values of the box model are correct");
+-  let viewdoc = view.document;
++  let doc = boxmodel.document;
+ 
+   for (let i = 0; i < res1.length; i++) {
+-    let elt = viewdoc.querySelector(res1[i].selector);
++    let elt = doc.querySelector(res1[i].selector);
+     is(elt.textContent, res1[i].value,
+        res1[i].selector + " has the right value.");
+   }
+ }
+ 
+-function* testChangingValues(inspector, view, testActor) {
++function* testChangingValues(inspector, boxmodel, testActor) {
+   info("Test that changing the document updates the box model");
+-  let viewdoc = view.document;
++  let doc = boxmodel.document;
+ 
+   let onUpdated = waitForUpdate(inspector);
+   yield testActor.setAttribute("div", "style",
+                                "height:150px;padding-right:50px;top:50px");
+   yield onUpdated;
+ 
+   for (let i = 0; i < res2.length; i++) {
+-    let elt = viewdoc.querySelector(res2[i].selector);
++    let elt = doc.querySelector(res2[i].selector);
+     is(elt.textContent, res2[i].value,
+        res2[i].selector + " has the right value after style update.");
+   }
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_computed-accordion-state.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_computed-accordion-state.js
+deleted file mode 100644
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_computed-accordion-state.js
++++ /dev/null
+@@ -1,89 +0,0 @@
+-/* Any copyright is dedicated to the Public Domain.
+- http://creativecommons.org/publicdomain/zero/1.0/ */
+-
+-"use strict";
+-
+-// Tests that the box model's accordion state is persistent through hide/show in the
+-// computed view.
+-
+-// There are shutdown issues for which multiple rejections are left uncaught.
+-// See bug 1018184 for resolving these issues.
+-const { PromiseTestUtils } = scopedCuImport("resource://testing-common/PromiseTestUtils.jsm");
+-PromiseTestUtils.whitelistRejectionsGlobally(/Connection closed/);
+-
+-const TEST_URI = `
+-  <style>
+-    #div1 {
+-      margin: 10px;
+-      padding: 3px;
+-    }
+-  </style>
+-  <div id="div1"></div>
+-`;
+-
+-const BOXMODEL_OPENED_PREF = "devtools.computed.boxmodel.opened";
+-
+-add_task(function* () {
+-  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+-  let { inspector, view, toolbox } = yield openBoxModelView();
+-  let { document: doc } = view;
+-
+-  yield testAccordionStateAfterClickingHeader(doc);
+-  yield testAccordionStateAfterSwitchingSidebars(inspector, doc);
+-  yield testAccordionStateAfterReopeningComputedView(toolbox);
+-
+-  Services.prefs.clearUserPref(BOXMODEL_OPENED_PREF);
+-});
+-
+-function* testAccordionStateAfterClickingHeader(doc) {
+-  let header = doc.querySelector("#computed-container .box-model-pane ._header");
+-  let bContent = doc.querySelector("#computed-container .box-model-pane ._content");
+-
+-  info("Checking initial state of the box model panel.");
+-  is(bContent.style.display, "block", "The box model panel content is 'display: block'.");
+-  ok(Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+-    `${BOXMODEL_OPENED_PREF} is pref on by default.`);
+-
+-  info("Clicking the box model header to hide the box model panel.");
+-  header.click();
+-
+-  info("Checking the new state of the box model panel.");
+-  is(bContent.style.display, "none", "The box model panel content is 'display: none'.");
+-  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+-    `${BOXMODEL_OPENED_PREF} is pref off.`);
+-}
+-
+-function* testAccordionStateAfterSwitchingSidebars(inspector, doc) {
+-  info("Checking the box model accordion state is persistent after switching sidebars.");
+-
+-  let bContent = doc.querySelector("#computed-container .box-model-pane ._content");
+-
+-  info("Selecting the layout view.");
+-  inspector.sidebar.select("layoutview");
+-
+-  info("Selecting the computed view.");
+-  inspector.sidebar.select("computedview");
+-
+-  info("Checking the state of the box model panel.");
+-  is(bContent.style.display, "none", "The box model panel content is 'display: none'.");
+-  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+-    `${BOXMODEL_OPENED_PREF} is pref off.`);
+-}
+-
+-function* testAccordionStateAfterReopeningComputedView(toolbox) {
+-  info("Checking the box model accordion state is persistent after closing and "
+-  + "re-opening the layout view.");
+-
+-  info("Closing the toolbox.");
+-  yield toolbox.destroy();
+-
+-  info("Re-opening the layout view.");
+-  let { view } = yield openBoxModelView();
+-  let { document: doc } = view;
+-  let bContent = doc.querySelector("#computed-container .box-model-pane ._content");
+-
+-  info("Checking the state of the box model panel.");
+-  ok(!bContent, "The box model panel content is not rendered.");
+-  ok(!Services.prefs.getBoolPref(BOXMODEL_OPENED_PREF),
+-    `${BOXMODEL_OPENED_PREF} is pref off.`);
+-}
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_edit-position-visible-position-change.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_edit-position-visible-position-change.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_edit-position-visible-position-change.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_edit-position-visible-position-change.js
+@@ -13,31 +13,32 @@ const TEST_URI = `
+     top:10px;
+     left:10px;
+     width:100px;
+     height:100px">
+ `;
+ 
+ add_task(function* () {
+   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+ 
+   yield selectNode("#mydiv", inspector);
+-  let editPositionButton = view.document.querySelector(".layout-geometry-editor");
++  let editPositionButton = boxmodel.document.querySelector(".layout-geometry-editor");
+ 
+   ok(isNodeVisible(editPositionButton), "Edit Position button is visible initially");
+ 
+-  let positionLeftTextbox = view.document.querySelector(
++  let positionLeftTextbox = boxmodel.document.querySelector(
+       ".boxmodel-editable[title=position-left]"
+   );
+   ok(isNodeVisible(positionLeftTextbox), "Position-left edit box exists");
+ 
+   info("Change the value of position-left and submit");
+   let onUpdate = waitForUpdate(inspector);
+-  EventUtils.synthesizeMouseAtCenter(positionLeftTextbox, {}, view.document.defaultView);
+-  EventUtils.synthesizeKey("8", {}, view.document.defaultView);
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeMouseAtCenter(positionLeftTextbox, {},
++    boxmodel.document.defaultView);
++  EventUtils.synthesizeKey("8", {}, boxmodel.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   yield onUpdate;
+-  editPositionButton = view.document.querySelector(".layout-geometry-editor");
++  editPositionButton = boxmodel.document.querySelector(".layout-geometry-editor");
+   ok(isNodeVisible(editPositionButton),
+     "Edit Position button is still visible after layout change");
+ });
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel.js
+@@ -13,182 +13,186 @@ const TEST_URI = "<style>" +
+   "#div2 { border-bottom: 1em solid black; }" +
+   "#div3 { padding: 2em; }" +
+   "#div4 { margin: 1px; }" +
+   "</style>" +
+   "<div id='div1'></div><div id='div2'></div>" +
+   "<div id='div3'></div><div id='div4'></div>";
+ 
+ add_task(function* () {
+-  yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  // Make sure the toolbox is tall enough to have empty space below the
++  // boxmodel-container.
++  yield pushPref("devtools.toolbox.footer.height", 500);
+ 
+-  yield testEditingMargins(inspector, view, testActor);
+-  yield testKeyBindings(inspector, view, testActor);
+-  yield testEscapeToUndo(inspector, view, testActor);
+-  yield testDeletingValue(inspector, view, testActor);
+-  yield testRefocusingOnClick(inspector, view, testActor);
++  yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
++
++  yield testEditingMargins(inspector, boxmodel, testActor);
++  yield testKeyBindings(inspector, boxmodel, testActor);
++  yield testEscapeToUndo(inspector, boxmodel, testActor);
++  yield testDeletingValue(inspector, boxmodel, testActor);
++  yield testRefocusingOnClick(inspector, boxmodel, testActor);
+ });
+ 
+-function* testEditingMargins(inspector, view, testActor) {
++function* testEditingMargins(inspector, boxmodel, testActor) {
+   info("Test that editing margin dynamically updates the document, pressing " +
+        "escape cancels the changes");
+ 
+   is((yield getStyle(testActor, "#div1", "margin-top")), "",
+      "Should be no margin-top on the element.");
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "5px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("3", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("3", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "margin-top")), "3px",
+      "Should have updated the margin.");
+ 
+-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_ESCAPE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "margin-top")), "",
+      "Should be no margin-top on the element.");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ }
+ 
+-function* testKeyBindings(inspector, view, testActor) {
++function* testKeyBindings(inspector, boxmodel, testActor) {
+   info("Test that arrow keys work correctly and pressing enter commits the " +
+        "changes");
+ 
+   is((yield getStyle(testActor, "#div1", "margin-left")), "",
+      "Should be no margin-top on the element.");
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-left > span");
+   is(span.textContent, 10, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "10px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_UP", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "11px", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "margin-left")), "11px",
+      "Should have updated the margin.");
+ 
+-  EventUtils.synthesizeKey("VK_DOWN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_DOWN", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "10px", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "margin-left")), "10px",
+      "Should have updated the margin.");
+ 
+-  EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_UP", { shiftKey: true }, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "20px", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
+      "Should have updated the margin.");
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
+      "Should be the right margin-top on the element.");
+   is(span.textContent, 20, "Should have the right value in the box model.");
+ }
+ 
+-function* testEscapeToUndo(inspector, view, testActor) {
++function* testEscapeToUndo(inspector, boxmodel, testActor) {
+   info("Test that deleting the value removes the property but escape undoes " +
+        "that");
+ 
+   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
+      "Should be the right margin-top on the element.");
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-left > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-left > span");
+   is(span.textContent, 20, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "20px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_DELETE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "margin-left")), "",
+      "Should have updated the margin.");
+ 
+-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_ESCAPE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "margin-left")), "20px",
+      "Should be the right margin-top on the element.");
+   is(span.textContent, 20, "Should have the right value in the box model.");
+ }
+ 
+-function* testDeletingValue(inspector, view, testActor) {
++function* testDeletingValue(inspector, boxmodel, testActor) {
+   info("Test that deleting the value removes the property");
+ 
+   yield setStyle(testActor, "#div1", "marginRight", "15px");
+   yield waitForUpdate(inspector);
+ 
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-right > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-right > span");
+   is(span.textContent, 15, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "15px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_DELETE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "margin-right")), "",
+      "Should have updated the margin.");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div1", "margin-right")), "",
+      "Should be the right margin-top on the element.");
+   is(span.textContent, 10, "Should have the right value in the box model.");
+ }
+ 
+-function* testRefocusingOnClick(inspector, view, testActor) {
++function* testRefocusingOnClick(inspector, boxmodel, testActor) {
+   info("Test that clicking in the editor input does not remove focus");
+ 
+   yield selectNode("#div4", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+   is(span.textContent, 1, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+ 
+   info("Click in the already opened editor input");
+-  EventUtils.synthesizeMouseAtCenter(editor, {}, view.document.defaultView);
+-  is(editor, view.document.activeElement,
++  EventUtils.synthesizeMouseAtCenter(editor, {}, boxmodel.document.defaultView);
++  is(editor, boxmodel.document.activeElement,
+     "Inplace editor input should still have focus.");
+ 
+   info("Check the input can still be used as expected");
+-  EventUtils.synthesizeKey("VK_UP", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_UP", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "2px", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
+      "Should have updated the margin.");
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div4", "margin-top")), "2px",
+      "Should be the right margin-top on the element.");
+   is(span.textContent, 2, "Should have the right value in the box model.");
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_allproperties.js
+@@ -11,136 +11,136 @@ const TEST_URI = "<style>" +
+   "#div1 { margin-top: 5px }" +
+   "#div2 { border-bottom: 1em solid black; }" +
+   "#div3 { padding: 2em; }" +
+   "</style>" +
+   "<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+-  yield testEditing(inspector, view, testActor);
+-  yield testEditingAndCanceling(inspector, view, testActor);
+-  yield testDeleting(inspector, view, testActor);
+-  yield testDeletingAndCanceling(inspector, view, testActor);
++  yield testEditing(inspector, boxmodel, testActor);
++  yield testEditingAndCanceling(inspector, boxmodel, testActor);
++  yield testDeleting(inspector, boxmodel, testActor);
++  yield testDeletingAndCanceling(inspector, boxmodel, testActor);
+ });
+ 
+-function* testEditing(inspector, view, testActor) {
++function* testEditing(inspector, boxmodel, testActor) {
+   info("When all properties are set on the node editing one should work");
+ 
+   yield setStyle(testActor, "#div1", "padding", "5px");
+   yield waitForUpdate(inspector);
+ 
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "5px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("7", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("7", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "7", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
+      "Should have updated the padding");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-bottom")), "7px",
+      "Should be the right padding.");
+   is(span.textContent, 7, "Should have the right value in the box model.");
+ }
+ 
+-function* testEditingAndCanceling(inspector, view, testActor) {
++function* testEditingAndCanceling(inspector, boxmodel, testActor) {
+   info("When all properties are set on the node editing one and then " +
+        "cancelling with ESCAPE should work");
+ 
+   yield setStyle(testActor, "#div1", "padding", "5px");
+   yield waitForUpdate(inspector);
+ 
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-left > span");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "5px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("8", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("8", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "8", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "padding-left")), "8px",
+      "Should have updated the padding");
+ 
+-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_ESCAPE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
+      "Should be the right padding.");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ }
+ 
+-function* testDeleting(inspector, view, testActor) {
++function* testDeleting(inspector, boxmodel, testActor) {
+   info("When all properties are set on the node deleting one should work");
+ 
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-left > span");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "5px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_DELETE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "padding-left")), "",
+      "Should have updated the padding");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-left")), "",
+      "Should be the right padding.");
+   is(span.textContent, 3, "Should have the right value in the box model.");
+ }
+ 
+-function* testDeletingAndCanceling(inspector, view, testActor) {
++function* testDeletingAndCanceling(inspector, boxmodel, testActor) {
+   info("When all properties are set on the node deleting one then cancelling " +
+        "should work");
+ 
+   yield setStyle(testActor, "#div1", "padding", "5px");
+   yield waitForUpdate(inspector);
+ 
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-left > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-left > span");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "5px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_DELETE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_DELETE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "padding-left")), "",
+      "Should have updated the padding");
+ 
+-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_ESCAPE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-left")), "5px",
+      "Should be the right padding.");
+   is(span.textContent, 5, "Should have the right value in the box model.");
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_bluronclick.js
+@@ -16,59 +16,59 @@ const TEST_URI =
+   <div id="div1"></div>`;
+ 
+ add_task(function* () {
+   // Make sure the toolbox is tall enough to have empty space below the
+   // boxmodel-container.
+   yield pushPref("devtools.toolbox.footer.height", 500);
+ 
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+ 
+   yield selectNode("#div1", inspector);
+-  yield testClickingOutsideEditor(view);
+-  yield testClickingBelowContainer(view);
++  yield testClickingOutsideEditor(boxmodel);
++  yield testClickingBelowContainer(boxmodel);
+ });
+ 
+-function* testClickingOutsideEditor(view) {
++function* testClickingOutsideEditor(boxmodel) {
+   info("Test that clicking outside the editor blurs it");
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+   is(span.textContent, 10, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+ 
+   info("Click next to the opened editor input.");
+   let onBlur = once(editor, "blur");
+   let rect = editor.getBoundingClientRect();
+   EventUtils.synthesizeMouse(editor, rect.width + 10, rect.height / 2, {},
+-    view.document.defaultView);
++    boxmodel.document.defaultView);
+   yield onBlur;
+ 
+-  is(view.document.querySelector(".styleinspector-propertyeditor"), null,
++  is(boxmodel.document.querySelector(".styleinspector-propertyeditor"), null,
+     "Inplace editor has been removed.");
+ }
+ 
+-function* testClickingBelowContainer(view) {
++function* testClickingBelowContainer(boxmodel) {
+   info("Test that clicking below the box-model container blurs it");
+-  let span = view.document.querySelector(".boxmodel-margin.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-margin.boxmodel-top > span");
+   is(span.textContent, 10, "Should have the right value in the box model.");
+ 
+   info("Test that clicking below the boxmodel-container blurs the opened editor");
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+ 
+   let onBlur = once(editor, "blur");
+-  let container = view.document.querySelector(".boxmodel-container");
++  let container = boxmodel.document.querySelector(".boxmodel-container");
+   // Using getBoxQuads here because getBoundingClientRect (and therefore synthesizeMouse)
+   // use an erroneous height of ~50px for the boxmodel-container.
+-  let bounds = container.getBoxQuads({relativeTo: view.doc})[0].bounds;
++  let bounds = container.getBoxQuads({relativeTo: boxmodel.document})[0].bounds;
+   EventUtils.synthesizeMouseAtPoint(
+     bounds.left + 10,
+     bounds.top + bounds.height + 10,
+-    {}, view.document.defaultView);
++    {}, boxmodel.document.defaultView);
+   yield onBlur;
+ 
+-  is(view.document.querySelector(".styleinspector-propertyeditor"), null,
++  is(boxmodel.document.querySelector(".styleinspector-propertyeditor"), null,
+     "Inplace editor has been removed.");
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_border.js
+@@ -11,42 +11,42 @@ const TEST_URI = "<style>" +
+   "#div1 { margin-top: 5px }" +
+   "#div2 { border-bottom: 1em solid black; }" +
+   "#div3 { padding: 2em; }" +
+   "</style>" +
+   "<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+   is((yield getStyle(testActor, "#div1", "border-top-width")), "",
+      "Should have the right border");
+   is((yield getStyle(testActor, "#div1", "border-top-style")), "",
+      "Should have the right border");
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-border.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-border.boxmodel-top > span");
+   is(span.textContent, 0, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "0", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("1", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("1", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "1", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "border-top-width")), "1px",
+      "Should have the right border");
+   is((yield getStyle(testActor, "#div1", "border-top-style")), "solid",
+      "Should have the right border");
+ 
+-  EventUtils.synthesizeKey("VK_ESCAPE", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_ESCAPE", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "border-top-width")), "",
+      "Should be the right padding.");
+   is((yield getStyle(testActor, "#div1", "border-top-style")), "",
+      "Should have the right border");
+   is(span.textContent, 0, "Should have the right value in the box model.");
+ });
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_pseudo.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_pseudo.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_pseudo.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_pseudo.js
+@@ -17,34 +17,35 @@ const TEST_URI =
+     }
+   </style>
+   <div style='width:200px;'>
+     <div class=test></div>
+   </div>`;
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+   yield selectNode(".test", inspector);
+ 
+   // No margin-top defined.
+   info("Test that margins are not impacted by a pseudo element");
+   is((yield getStyle(testActor, ".test", "margin-top")), "", "margin-top is correct");
+-  yield checkValueInBoxModel(".boxmodel-margin.boxmodel-top", "0", view.document);
++  yield checkValueInBoxModel(".boxmodel-margin.boxmodel-top", "0", boxmodel.document);
+ 
+   // No padding-top defined.
+   info("Test that paddings are not impacted by a pseudo element");
+   is((yield getStyle(testActor, ".test", "padding-top")), "", "padding-top is correct");
+-  yield checkValueInBoxModel(".boxmodel-padding.boxmodel-top", "0", view.document);
++  yield checkValueInBoxModel(".boxmodel-padding.boxmodel-top", "0", boxmodel.document);
+ 
+   // Width should be driven by the parent div.
+   info("Test that dimensions are not impacted by a pseudo element");
+   is((yield getStyle(testActor, ".test", "width")), "", "width is correct");
+-  yield checkValueInBoxModel(".boxmodel-content.boxmodel-width", "200", view.document);
++  yield checkValueInBoxModel(".boxmodel-content.boxmodel-width", "200",
++    boxmodel.document);
+ });
+ 
+ function* checkValueInBoxModel(selector, expectedValue, doc) {
+   let span = doc.querySelector(selector + " > span");
+   is(span.textContent, expectedValue, "Should have the right value in the box model.");
+ 
+   EventUtils.synthesizeMouseAtCenter(span, {}, doc.defaultView);
+   let editor = doc.querySelector(".styleinspector-propertyeditor");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_editablemodel_stylerules.js
+@@ -12,102 +12,102 @@ const TEST_URI = "<style>" +
+   "#div1 { margin-top: 5px }" +
+   "#div2 { border-bottom: 1em solid black; }" +
+   "#div3 { padding: 2em; }" +
+   "</style>" +
+   "<div id='div1'></div><div id='div2'></div><div id='div3'></div>";
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+-  yield testUnits(inspector, view, testActor);
+-  yield testValueComesFromStyleRule(inspector, view, testActor);
+-  yield testShorthandsAreParsed(inspector, view, testActor);
++  yield testUnits(inspector, boxmodel, testActor);
++  yield testValueComesFromStyleRule(inspector, boxmodel, testActor);
++  yield testShorthandsAreParsed(inspector, boxmodel, testActor);
+ });
+ 
+-function* testUnits(inspector, view, testActor) {
++function* testUnits(inspector, boxmodel, testActor) {
+   info("Test that entering units works");
+ 
+   is((yield getStyle(testActor, "#div1", "padding-top")), "",
+      "Should have the right padding");
+   yield selectNode("#div1", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-top > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-top > span");
+   is(span.textContent, 3, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "3px", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("1", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("1", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+-  EventUtils.synthesizeKey("e", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("e", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-top")), "",
+      "An invalid value is handled cleanly");
+ 
+-  EventUtils.synthesizeKey("m", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("m", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "1em", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div1", "padding-top")),
+      "1em", "Should have updated the padding.");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div1", "padding-top")), "1em",
+      "Should be the right padding.");
+   is(span.textContent, 16, "Should have the right value in the box model.");
+ }
+ 
+-function* testValueComesFromStyleRule(inspector, view, testActor) {
++function* testValueComesFromStyleRule(inspector, boxmodel, testActor) {
+   info("Test that we pick up the value from a higher style rule");
+ 
+   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "",
+      "Should have the right border-bottom-width");
+   yield selectNode("#div2", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-border.boxmodel-bottom > span");
++  let span = boxmodel.document.querySelector(".boxmodel-border.boxmodel-bottom > span");
+   is(span.textContent, 16, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "1em", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("0", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("0", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+ 
+   is(editor.value, "0", "Should have the right value in the editor.");
+   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
+      "Should have updated the border.");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div2", "border-bottom-width")), "0px",
+      "Should be the right border-bottom-width.");
+   is(span.textContent, 0, "Should have the right value in the box model.");
+ }
+ 
+-function* testShorthandsAreParsed(inspector, view, testActor) {
++function* testShorthandsAreParsed(inspector, boxmodel, testActor) {
+   info("Test that shorthand properties are parsed correctly");
+ 
+   is((yield getStyle(testActor, "#div3", "padding-right")), "",
+      "Should have the right padding");
+   yield selectNode("#div3", inspector);
+ 
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-right > span");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-right > span");
+   is(span.textContent, 32, "Should have the right value in the box model.");
+ 
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+   ok(editor, "Should have opened the editor.");
+   is(editor.value, "2em", "Should have the right value in the editor.");
+ 
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   is((yield getStyle(testActor, "#div3", "padding-right")), "",
+      "Should be the right padding.");
+   is(span.textContent, 32, "Should have the right value in the box model.");
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_guides.js
+@@ -14,36 +14,36 @@ const STYLE = "div { position: absolute;
+               "padding: 10px; margin: 10px;}";
+ const HTML = "<style>" + STYLE + "</style><div></div>";
+ const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
+ 
+ var highlightedNodeFront, highlighterOptions;
+ 
+ add_task(async function() {
+   await addTab(TEST_URL);
+-  let {toolbox, inspector, view} = await openBoxModelView();
++  let {toolbox, inspector, boxmodel} = await openLayoutView();
+   await selectNode("div", inspector);
+ 
+   // Mock the highlighter by replacing the showBoxModel method.
+   toolbox.highlighter.showBoxModel = function(nodeFront, options) {
+     highlightedNodeFront = nodeFront;
+     highlighterOptions = options;
+   };
+ 
+-  let elt = view.document.querySelector(".boxmodel-margins");
+-  await testGuideOnLayoutHover(elt, "margin", inspector, view);
++  let elt = boxmodel.document.querySelector(".boxmodel-margins");
++  await testGuideOnLayoutHover(elt, "margin", inspector);
+ 
+-  elt = view.document.querySelector(".boxmodel-borders");
+-  await testGuideOnLayoutHover(elt, "border", inspector, view);
++  elt = boxmodel.document.querySelector(".boxmodel-borders");
++  await testGuideOnLayoutHover(elt, "border", inspector);
+ 
+-  elt = view.document.querySelector(".boxmodel-paddings");
+-  await testGuideOnLayoutHover(elt, "padding", inspector, view);
++  elt = boxmodel.document.querySelector(".boxmodel-paddings");
++  await testGuideOnLayoutHover(elt, "padding", inspector);
+ 
+-  elt = view.document.querySelector(".boxmodel-content");
+-  await testGuideOnLayoutHover(elt, "content", inspector, view);
++  elt = boxmodel.document.querySelector(".boxmodel-content");
++  await testGuideOnLayoutHover(elt, "content", inspector);
+ });
+ 
+ async function testGuideOnLayoutHover(elt, expectedRegion, inspector) {
+   info("Synthesizing mouseover on the boxmodel-view");
+   EventUtils.synthesizeMouse(elt, 2, 2, {type: "mouseover"},
+     elt.ownerDocument.defaultView);
+ 
+   info("Waiting for the node-highlight event from the toolbox");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_navigation.js
+@@ -12,109 +12,109 @@ const TEST_URI = `
+   div { position: absolute; top: 42px; left: 42px;
+   height: 100.111px; width: 100px; border: 10px solid black;
+   padding: 20px; margin: 30px auto;}
+   </style><div></div>
+ `;
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+   yield selectNode("div", inspector);
+ 
+-  yield testInitialFocus(inspector, view);
+-  yield testChangingLevels(inspector, view);
+-  yield testTabbingWrapAround(inspector, view);
+-  yield testChangingLevelsByClicking(inspector, view);
++  yield testInitialFocus(inspector, boxmodel);
++  yield testChangingLevels(inspector, boxmodel);
++  yield testTabbingWrapAround(inspector, boxmodel);
++  yield testChangingLevelsByClicking(inspector, boxmodel);
+ });
+ 
+-function* testInitialFocus(inspector, view) {
+-  info("Test that the focus is on margin layout.");
+-  let viewdoc = view.document;
+-  let boxmodel = viewdoc.querySelector(".boxmodel-container");
+-  boxmodel.focus();
++function* testInitialFocus(inspector, boxmodel) {
++  info("Test that the focus is(on margin layout.");
++  let doc = boxmodel.document;
++  let container = doc.querySelector(".boxmodel-container");
++  container.focus();
+   EventUtils.synthesizeKey("KEY_Enter");
+ 
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
++  is(container.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
+     "Should be set to the position layout.");
+ }
+ 
+-function* testChangingLevels(inspector, view) {
++function* testChangingLevels(inspector, boxmodel) {
+   info("Test that using arrow keys updates level.");
+-  let viewdoc = view.document;
+-  let boxmodel = viewdoc.querySelector(".boxmodel-container");
+-  boxmodel.focus();
++  let doc = boxmodel.document;
++  let container = doc.querySelector(".boxmodel-container");
++  container.focus();
+   EventUtils.synthesizeKey("KEY_Enter");
+   EventUtils.synthesizeKey("KEY_Escape");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowDown");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-margins",
++  is(container.getAttribute("activedescendant"), "boxmodel-margins",
+     "Should be set to the margin layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowDown");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-borders",
++  is(container.getAttribute("activedescendant"), "boxmodel-borders",
+     "Should be set to the border layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowDown");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-paddings",
++  is(container.getAttribute("activedescendant"), "boxmodel-paddings",
+     "Should be set to the padding layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowDown");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-contents",
++  is(container.getAttribute("activedescendant"), "boxmodel-contents",
+     "Should be set to the content layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowUp");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-paddings",
++  is(container.getAttribute("activedescendant"), "boxmodel-paddings",
+     "Should be set to the padding layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowUp");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-borders",
++  is(container.getAttribute("activedescendant"), "boxmodel-borders",
+     "Should be set to the border layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowUp");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-margins",
++  is(container.getAttribute("activedescendant"), "boxmodel-margins",
+     "Should be set to the margin layout.");
+ 
+   EventUtils.synthesizeKey("KEY_ArrowUp");
+-  is(boxmodel.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
++  is(container.getAttribute("activedescendant"), "boxmodel-main devtools-monospace",
+     "Should be set to the position layout.");
+ }
+ 
+-function* testTabbingWrapAround(inspector, view) {
++function* testTabbingWrapAround(inspector, boxmodel) {
+   info("Test that using arrow keys updates level.");
+-  let viewdoc = view.document;
+-  let boxmodel = viewdoc.querySelector(".boxmodel-container");
+-  boxmodel.focus();
++  let doc = boxmodel.document;
++  let container = doc.querySelector(".boxmodel-container");
++  container.focus();
+   EventUtils.synthesizeKey("KEY_Enter");
+ 
+-  let editLevel = boxmodel.getAttribute("activedescendant").split(" ")[0];
+-  let dataLevel = viewdoc.querySelector(`.${editLevel}`).getAttribute("data-box");
+-  let editBoxes = [...viewdoc.querySelectorAll(
++  let editLevel = container.getAttribute("activedescendant").split(" ")[0];
++  let dataLevel = doc.querySelector(`.${editLevel}`).getAttribute("data-box");
++  let editBoxes = [...doc.querySelectorAll(
+     `[data-box="${dataLevel}"].boxmodel-editable`)];
+ 
+   EventUtils.synthesizeKey("KEY_Escape");
+   editBoxes[3].focus();
+   EventUtils.synthesizeKey("KEY_Tab");
+-  is(editBoxes[0], viewdoc.activeElement, "Top edit box should have focus.");
++  is(editBoxes[0], doc.activeElement, "Top edit box should have focus.");
+ 
+   editBoxes[0].focus();
+   EventUtils.synthesizeKey("KEY_Tab", {shiftKey: true});
+-  is(editBoxes[3], viewdoc.activeElement, "Left edit box should have focus.");
++  is(editBoxes[3], doc.activeElement, "Left edit box should have focus.");
+ }
+ 
+-function* testChangingLevelsByClicking(inspector, view) {
++function* testChangingLevelsByClicking(inspector, boxmodel) {
+   info("Test that clicking on levels updates level.");
+-  let viewdoc = view.document;
+-  let boxmodel = viewdoc.querySelector(".boxmodel-container");
+-  boxmodel.focus();
++  let doc = boxmodel.document;
++  let container = doc.querySelector(".boxmodel-container");
++  container.focus();
+ 
+-  let marginLayout = viewdoc.querySelector(".boxmodel-margins");
+-  let borderLayout = viewdoc.querySelector(".boxmodel-borders");
+-  let paddingLayout = viewdoc.querySelector(".boxmodel-paddings");
+-  let contentLayout = viewdoc.querySelector(".boxmodel-contents");
++  let marginLayout = doc.querySelector(".boxmodel-margins");
++  let borderLayout = doc.querySelector(".boxmodel-borders");
++  let paddingLayout = doc.querySelector(".boxmodel-paddings");
++  let contentLayout = doc.querySelector(".boxmodel-contents");
+   let layouts = [contentLayout, paddingLayout, borderLayout, marginLayout];
+ 
+   layouts.forEach(layout => {
+     layout.click();
+-    is(boxmodel.getAttribute("activedescendant"), layout.className,
++    is(container.getAttribute("activedescendant"), layout.className,
+       "Should be set to" + layout.getAttribute("data-box") + "layout.");
+   });
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_positions.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_positions.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_positions.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_positions.js
+@@ -39,27 +39,27 @@ const res1 = [
+   {
+     selector: ".boxmodel-position.boxmodel-left > span",
+     value: 0
+   },
+ ];
+ 
+ add_task(function* () {
+   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+   let node = yield getNodeFront("div", inspector);
+   let children = yield inspector.markup.walker.children(node);
+   let beforeElement = children.nodes[0];
+ 
+   yield selectNode(beforeElement, inspector);
+-  yield testPositionValues(inspector, view);
++  yield testPositionValues(inspector, boxmodel);
+ });
+ 
+-function* testPositionValues(inspector, view) {
++function* testPositionValues(inspector, boxmodel) {
+   info("Test that the position values of the box model are correct");
+-  let viewdoc = view.document;
++  let doc = boxmodel.document;
+ 
+   for (let i = 0; i < res1.length; i++) {
+-    let elt = viewdoc.querySelector(res1[i].selector);
++    let elt = doc.querySelector(res1[i].selector);
+     is(elt.textContent, res1[i].value,
+        res1[i].selector + " has the right value.");
+   }
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_pseudo-element.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_pseudo-element.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_pseudo-element.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_pseudo-element.js
+@@ -94,27 +94,27 @@ const res1 = [
+   {
+     selector: ".boxmodel-border.boxmodel-right > span",
+     value: 0
+   },
+ ];
+ 
+ add_task(function* () {
+   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+   let node = yield getNodeFront("div", inspector);
+   let children = yield inspector.markup.walker.children(node);
+   let beforeElement = children.nodes[0];
+ 
+   yield selectNode(beforeElement, inspector);
+-  yield testInitialValues(inspector, view);
++  yield testInitialValues(inspector, boxmodel);
+ });
+ 
+-function* testInitialValues(inspector, view) {
++function* testInitialValues(inspector, boxmodel) {
+   info("Test that the initial values of the box model are correct");
+-  let viewdoc = view.document;
++  let doc = boxmodel.document;
+ 
+   for (let i = 0; i < res1.length; i++) {
+-    let elt = viewdoc.querySelector(res1[i].selector);
++    let elt = doc.querySelector(res1[i].selector);
+     is(elt.textContent, res1[i].value,
+        res1[i].selector + " has the right value.");
+   }
+ }
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_rotate-labels-on-sides.js
+@@ -27,21 +27,21 @@ const TEST_URI = encodeURIComponent([
+   "margin: 30px auto; }",
+   "</style>",
+   "<div></div>"
+ ].join(""));
+ const LONG_TEXT_ROTATE_LIMIT = 3;
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + TEST_URI);
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+   yield selectNode("div", inspector);
+ 
+   for (let i = 0; i < res1.length; i++) {
+-    let elt = view.document.querySelector(res1[i].selector);
++    let elt = boxmodel.document.querySelector(res1[i].selector);
+     let isLong = elt.textContent.length > LONG_TEXT_ROTATE_LIMIT;
+     let classList = elt.parentNode.classList;
+     let canBeRotated = classList.contains("boxmodel-left") ||
+                        classList.contains("boxmodel-right");
+     let isRotated = classList.contains("boxmodel-rotate");
+ 
+     is(canBeRotated && isLong,
+       isRotated, res1[i].selector + " correctly rotated.");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_sync.js
+@@ -5,31 +5,31 @@
+ "use strict";
+ 
+ // Test editing box model syncs with the rule view.
+ 
+ const TEST_URI = "<p>hello</p>";
+ 
+ add_task(function* () {
+   yield addTab("data:text/html," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+ 
+   info("When a property is edited, it should sync in the rule view");
+ 
+   yield selectNode("p", inspector);
+ 
+   info("Modify padding-bottom in box model view");
+-  let span = view.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
+-  EventUtils.synthesizeMouseAtCenter(span, {}, view.document.defaultView);
+-  let editor = view.document.querySelector(".styleinspector-propertyeditor");
++  let span = boxmodel.document.querySelector(".boxmodel-padding.boxmodel-bottom > span");
++  EventUtils.synthesizeMouseAtCenter(span, {}, boxmodel.document.defaultView);
++  let editor = boxmodel.document.querySelector(".styleinspector-propertyeditor");
+ 
+-  EventUtils.synthesizeKey("7", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("7", {}, boxmodel.document.defaultView);
+   yield waitForUpdate(inspector);
+   is(editor.value, "7", "Should have the right value in the editor.");
+-  EventUtils.synthesizeKey("VK_RETURN", {}, view.document.defaultView);
++  EventUtils.synthesizeKey("VK_RETURN", {}, boxmodel.document.defaultView);
+ 
+   let onRuleViewRefreshed = once(inspector, "rule-view-refreshed");
+   let onRuleViewSelected = once(inspector.sidebar, "ruleview-selected");
+   info("Select the rule view and check that the property was synced there");
+   let ruleView = selectRuleView(inspector);
+ 
+   info("Wait for the rule view to be selected");
+   yield onRuleViewSelected;
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_tooltips.js
+@@ -67,53 +67,53 @@ const VALUES_TEST_DATA = [{
+     name: "padding-left",
+     ruleSelector: "html, body, #div3",
+     styleSheetLocation: "inline:3"
+   }]
+ }];
+ 
+ add_task(function* () {
+   yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
+-  let {inspector, view} = yield openBoxModelView();
++  let {inspector, boxmodel} = yield openLayoutView();
+ 
+   info("Checking the regions tooltips");
+ 
+-  ok(view.document.querySelector(".boxmodel-margins").hasAttribute("title"),
++  ok(boxmodel.document.querySelector(".boxmodel-margins").hasAttribute("title"),
+     "The margin region has a tooltip");
+-  is(view.document.querySelector(".boxmodel-margins").getAttribute("title"), "margin",
++  is(boxmodel.document.querySelector(".boxmodel-margins").getAttribute("title"), "margin",
+     "The margin region has the correct tooltip content");
+ 
+-  ok(view.document.querySelector(".boxmodel-borders").hasAttribute("title"),
++  ok(boxmodel.document.querySelector(".boxmodel-borders").hasAttribute("title"),
+     "The border region has a tooltip");
+-  is(view.document.querySelector(".boxmodel-borders").getAttribute("title"), "border",
++  is(boxmodel.document.querySelector(".boxmodel-borders").getAttribute("title"), "border",
+     "The border region has the correct tooltip content");
+ 
+-  ok(view.document.querySelector(".boxmodel-paddings").hasAttribute("title"),
++  ok(boxmodel.document.querySelector(".boxmodel-paddings").hasAttribute("title"),
+     "The padding region has a tooltip");
+-  is(view.document.querySelector(".boxmodel-paddings").getAttribute("title"), "padding",
+-    "The padding region has the correct tooltip content");
++  is(boxmodel.document.querySelector(".boxmodel-paddings").getAttribute("title"),
++    "padding", "The padding region has the correct tooltip content");
+ 
+-  ok(view.document.querySelector(".boxmodel-content").hasAttribute("title"),
++  ok(boxmodel.document.querySelector(".boxmodel-content").hasAttribute("title"),
+     "The content region has a tooltip");
+-  is(view.document.querySelector(".boxmodel-content").getAttribute("title"), "content",
+-    "The content region has the correct tooltip content");
++  is(boxmodel.document.querySelector(".boxmodel-content").getAttribute("title"),
++    "content", "The content region has the correct tooltip content");
+ 
+   for (let {selector, values} of VALUES_TEST_DATA) {
+     info("Selecting " + selector + " and checking the values tooltips");
+     yield selectNode(selector, inspector);
+ 
+     info("Iterate over all values");
+-    for (let key in view.map) {
++    for (let key in boxmodel.map) {
+       if (key === "position") {
+         continue;
+       }
+ 
+-      let name = view.map[key].property;
++      let name = boxmodel.map[key].property;
+       let expectedTooltipData = values.find(o => o.name === name);
+-      let el = view.document.querySelector(view.map[key].selector);
++      let el = boxmodel.document.querySelector(boxmodel.map[key].selector);
+ 
+       ok(el.hasAttribute("title"), "The " + name + " value has a tooltip");
+ 
+       if (expectedTooltipData) {
+         info("The " + name + " value comes from a css rule");
+         let expectedTooltip = name + "\n" + expectedTooltipData.ruleSelector +
+                               "\n" + expectedTooltipData.styleSheetLocation;
+         is(el.getAttribute("title"), expectedTooltip, "The tooltip is correct");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-navigation.js
+@@ -7,82 +7,82 @@
+ // Test that the box model view continues to work after a page navigation and that
+ // it also works after going back
+ 
+ const IFRAME1 = URL_ROOT + "doc_boxmodel_iframe1.html";
+ const IFRAME2 = URL_ROOT + "doc_boxmodel_iframe2.html";
+ 
+ add_task(function* () {
+   yield addTab(IFRAME1);
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+-  yield testFirstPage(inspector, view, testActor);
++  yield testFirstPage(inspector, boxmodel, testActor);
+ 
+   info("Navigate to the second page");
+   let onMarkupLoaded = waitForMarkupLoaded(inspector);
+   yield testActor.eval(`location.href="${IFRAME2}"`);
+   yield onMarkupLoaded;
+ 
+-  yield testSecondPage(inspector, view, testActor);
++  yield testSecondPage(inspector, boxmodel, testActor);
+ 
+   info("Go back to the first page");
+   onMarkupLoaded = waitForMarkupLoaded(inspector);
+   yield testActor.eval("history.back();");
+   yield onMarkupLoaded;
+ 
+-  yield testBackToFirstPage(inspector, view, testActor);
++  yield testBackToFirstPage(inspector, boxmodel, testActor);
+ });
+ 
+-function* testFirstPage(inspector, view, testActor) {
++function* testFirstPage(inspector, boxmodel, testActor) {
+   info("Test that the box model view works on the first page");
+ 
+   yield selectNode("p", inspector);
+ 
+   info("Checking that the box model view shows the right value");
+-  let paddingElt = view.document.querySelector(
++  let paddingElt = boxmodel.document.querySelector(
+     ".boxmodel-padding.boxmodel-top > span");
+   is(paddingElt.textContent, "50");
+ 
+   info("Listening for box model view changes and modifying the padding");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyle(testActor, "p", "padding", "20px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+ 
+   info("Checking that the box model view shows the right value after update");
+   is(paddingElt.textContent, "20");
+ }
+ 
+-function* testSecondPage(inspector, view, testActor) {
++function* testSecondPage(inspector, boxmodel, testActor) {
+   info("Test that the box model view works on the second page");
+ 
+   yield selectNode("p", inspector);
+ 
+   info("Checking that the box model view shows the right value");
+-  let sizeElt = view.document.querySelector(".boxmodel-size > span");
++  let sizeElt = boxmodel.document.querySelector(".boxmodel-size > span");
+   is(sizeElt.textContent, "100" + "\u00D7" + "100");
+ 
+   info("Listening for box model view changes and modifying the size");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyle(testActor, "p", "width", "200px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+ 
+   info("Checking that the box model view shows the right value after update");
+   is(sizeElt.textContent, "200" + "\u00D7" + "100");
+ }
+ 
+-function* testBackToFirstPage(inspector, view, testActor) {
++function* testBackToFirstPage(inspector, boxmodel, testActor) {
+   info("Test that the box model view works on the first page after going back");
+ 
+   yield selectNode("p", inspector);
+ 
+   info("Checking that the box model view shows the right value, which is the" +
+     "modified value from step one because of the bfcache");
+-  let paddingElt = view.document.querySelector(
++  let paddingElt = boxmodel.document.querySelector(
+     ".boxmodel-padding.boxmodel-top > span");
+   is(paddingElt.textContent, "20");
+ 
+   info("Listening for box model view changes and modifying the padding");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyle(testActor, "p", "padding", "100px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-after-reload.js
+@@ -3,35 +3,35 @@
+  http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ "use strict";
+ 
+ // Test that the box model view continues to work after the page is reloaded
+ 
+ add_task(function* () {
+   yield addTab(URL_ROOT + "doc_boxmodel_iframe1.html");
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+   info("Test that the box model view works on the first page");
+-  yield assertBoxModelView(inspector, view, testActor);
++  yield assertBoxModelView(inspector, boxmodel, testActor);
+ 
+   info("Reload the page");
+   let onMarkupLoaded = waitForMarkupLoaded(inspector);
+   yield testActor.reload();
+   yield onMarkupLoaded;
+ 
+   info("Test that the box model view works on the reloaded page");
+-  yield assertBoxModelView(inspector, view, testActor);
++  yield assertBoxModelView(inspector, boxmodel, testActor);
+ });
+ 
+-function* assertBoxModelView(inspector, view, testActor) {
++function* assertBoxModelView(inspector, boxmodel, testActor) {
+   yield selectNode("p", inspector);
+ 
+   info("Checking that the box model view shows the right value");
+-  let paddingElt = view.document.querySelector(
++  let paddingElt = boxmodel.document.querySelector(
+     ".boxmodel-padding.boxmodel-top > span");
+   is(paddingElt.textContent, "50");
+ 
+   info("Listening for box model view changes and modifying the padding");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyle(testActor, "p", "padding", "20px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+diff --git a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js
+--- a/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js
++++ b/devtools/client/inspector/boxmodel/test/browser_boxmodel_update-in-iframes.js
+@@ -4,56 +4,56 @@
+ 
+ "use strict";
+ 
+ // Test that the box model view for elements within iframes also updates when they
+ // change
+ 
+ add_task(function* () {
+   yield addTab(URL_ROOT + "doc_boxmodel_iframe1.html");
+-  let {inspector, view, testActor} = yield openBoxModelView();
++  let {inspector, boxmodel, testActor} = yield openLayoutView();
+ 
+-  yield testResizingInIframe(inspector, view, testActor);
+-  yield testReflowsAfterIframeDeletion(inspector, view, testActor);
++  yield testResizingInIframe(inspector, boxmodel, testActor);
++  yield testReflowsAfterIframeDeletion(inspector, boxmodel, testActor);
+ });
+ 
+-function* testResizingInIframe(inspector, view, testActor) {
++function* testResizingInIframe(inspector, boxmodel, testActor) {
+   info("Test that resizing an element in an iframe updates its box model");
+ 
+   info("Selecting the nested test node");
+   yield selectNodeInIframe2("div", inspector);
+ 
+   info("Checking that the box model view shows the right value");
+-  let sizeElt = view.document.querySelector(".boxmodel-size > span");
++  let sizeElt = boxmodel.document.querySelector(".boxmodel-size > span");
+   is(sizeElt.textContent, "400\u00D7200");
+ 
+   info("Listening for box model view changes and modifying its size");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyleInIframe2(testActor, "div", "width", "200px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+ 
+   info("Checking that the box model view shows the right value after update");
+   is(sizeElt.textContent, "200\u00D7200");
+ }
+ 
+-function* testReflowsAfterIframeDeletion(inspector, view, testActor) {
++function* testReflowsAfterIframeDeletion(inspector, boxmodel, testActor) {
+   info("Test reflows are still sent to the box model view after deleting an " +
+        "iframe");
+ 
+   info("Deleting the iframe2");
+   let onInspectorUpdated = inspector.once("inspector-updated");
+   yield removeIframe2(testActor);
+   yield onInspectorUpdated;
+ 
+   info("Selecting the test node in iframe1");
+   yield selectNodeInIframe1("p", inspector);
+ 
+   info("Checking that the box model view shows the right value");
+-  let sizeElt = view.document.querySelector(".boxmodel-size > span");
++  let sizeElt = boxmodel.document.querySelector(".boxmodel-size > span");
+   is(sizeElt.textContent, "100\u00D7100");
+ 
+   info("Listening for box model view changes and modifying its size");
+   let onUpdated = waitForUpdate(inspector);
+   yield setStyleInIframe1(testActor, "p", "width", "200px");
+   yield onUpdated;
+   ok(true, "Box model view got updated");
+ 
+diff --git a/devtools/client/inspector/boxmodel/test/head.js b/devtools/client/inspector/boxmodel/test/head.js
+--- a/devtools/client/inspector/boxmodel/test/head.js
++++ b/devtools/client/inspector/boxmodel/test/head.js
+@@ -41,46 +41,16 @@ function* selectAndHighlightNode(selecto
+  * @param {DOMNode}
+  * @return {Boolean}
+  */
+ function isNodeVisible(node) {
+   return !!node.getClientRects().length;
+ }
+ 
+ /**
+- * Open the toolbox, with the inspector tool visible, and the computed view
+- * sidebar tab selected to display the box model view.
+- *
+- * @return {Promise} a promise that resolves when the inspector is ready and the box model
+- *         view is visible and ready.
+- */
+-function openBoxModelView() {
+-  return openInspectorSidebarTab("computedview").then(data => {
+-    // The actual highligher show/hide methods are mocked in box model tests.
+-    // The highlighter is tested in devtools/inspector/test.
+-    function mockHighlighter({highlighter}) {
+-      highlighter.showBoxModel = function() {
+-        return promise.resolve();
+-      };
+-      highlighter.hideBoxModel = function() {
+-        return promise.resolve();
+-      };
+-    }
+-    mockHighlighter(data.toolbox);
+-
+-    return {
+-      toolbox: data.toolbox,
+-      inspector: data.inspector,
+-      view: data.inspector.getPanel("computedview"),
+-      testActor: data.testActor
+-    };
+-  });
+-}
+-
+-/**
+  * Wait for the boxmodel-view-updated event.
+  *
+  * @param  {InspectorPanel} inspector
+  *         The instance of InspectorPanel currently loaded in the toolbox.
+  * @param  {Boolean} waitForSelectionUpdate
+  *         Should the boxmodel-view-updated event come from a new selection.
+  * @return {Promise} a promise
+  */
+diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js
+--- a/devtools/client/inspector/computed/computed.js
++++ b/devtools/client/inspector/computed/computed.js
+@@ -22,22 +22,16 @@ const {
+   VIEW_NODE_IMAGE_URL_TYPE,
+   VIEW_NODE_FONT_TYPE,
+ } = require("devtools/client/inspector/shared/node-types");
+ const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
+ const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ const clipboardHelper = require("devtools/shared/platform/clipboard");
+ 
+-const { createElement, createFactory } = require("devtools/client/shared/vendor/react");
+-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+-const { Provider } = require("devtools/client/shared/vendor/react-redux");
+-
+-const BoxModelApp = createFactory(require("devtools/client/inspector/boxmodel/components/BoxModelApp"));
+-
+ const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
+ 
+ const FILTER_CHANGED_TIMEOUT = 150;
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ 
+ /**
+@@ -151,17 +145,16 @@ UpdateProcess.prototype = {
+  *        The document that will contain the computed view.
+  * @param {PageStyleFront} pageStyle
+  *        Front for the page style actor that will be providing
+  *        the style information.
+  */
+ function CssComputedView(inspector, document, pageStyle) {
+   this.inspector = inspector;
+   this.highlighters = inspector.highlighters;
+-  this.store = inspector.store;
+   this.styleDocument = document;
+   this.styleWindow = this.styleDocument.defaultView;
+   this.pageStyle = pageStyle;
+ 
+   this.propertyViews = [];
+ 
+   let cssProperties = getCssProperties(inspector.toolbox);
+   this._outputParser = new OutputParser(document, cssProperties);
+@@ -172,17 +165,16 @@ function CssComputedView(inspector, docu
+   this._onClick = this._onClick.bind(this);
+   this._onCopy = this._onCopy.bind(this);
+   this._onFilterStyles = this._onFilterStyles.bind(this);
+   this._onClearSearch = this._onClearSearch.bind(this);
+   this._onIncludeBrowserStyles = this._onIncludeBrowserStyles.bind(this);
+ 
+   let doc = this.styleDocument;
+   this.element = doc.getElementById("computed-property-container");
+-  this.boxModelWrapper = doc.getElementById("boxmodel-wrapper");
+   this.searchField = doc.getElementById("computed-searchbox");
+   this.searchClearButton = doc.getElementById("computed-searchinput-clear");
+   this.includeBrowserStylesCheckbox = doc.getElementById("browser-style-checkbox");
+ 
+   this.shortcuts = new KeyShortcuts({ window: this.styleWindow });
+   this._onShortcut = this._onShortcut.bind(this);
+   this.shortcuts.on("CmdOrCtrl+F", event => this._onShortcut("CmdOrCtrl+F", event));
+   this.shortcuts.on("Escape", event => this._onShortcut("Escape", event));
+@@ -204,17 +196,16 @@ function CssComputedView(inspector, docu
+   // original sources changes.
+   this._handlePrefChange = this._handlePrefChange.bind(this);
+   this._prefObserver = new PrefObserver("devtools.");
+   this._prefObserver.on("devtools.defaultColorUnit", this._handlePrefChange);
+ 
+   // The element that we're inspecting, and the document that it comes from.
+   this._viewedElement = null;
+ 
+-  this.createBoxModelView();
+   this.createStyleViews();
+ 
+   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: false });
+ 
+   // Add the tooltips and highlightersoverlay
+   this.tooltips = new TooltipsOverlay(this);
+ 
+   this.highlighters.addToView(this);
+@@ -572,20 +563,18 @@ CssComputedView.prototype = {
+ 
+     let filterTimeout = (this.searchField.value.length > 0)
+       ? FILTER_CHANGED_TIMEOUT : 0;
+     this.searchClearButton.hidden = this.searchField.value.length === 0;
+ 
+     this._filterChangedTimeout = setTimeout(() => {
+       if (this.searchField.value.length > 0) {
+         this.searchField.setAttribute("filled", true);
+-        this.boxModelWrapper.hidden = true;
+       } else {
+         this.searchField.removeAttribute("filled");
+-        this.boxModelWrapper.hidden = false;
+       }
+ 
+       this.refreshPanel();
+       this._filterChangeTimeout = null;
+     }, filterTimeout);
+   },
+ 
+   /**
+@@ -618,48 +607,16 @@ CssComputedView.prototype = {
+   refreshSourceFilter: function() {
+     this._matchedProperties = null;
+     this._sourceFilter = this.includeBrowserStyles ?
+                                  CssLogic.FILTER.UA :
+                                  CssLogic.FILTER.USER;
+   },
+ 
+   /**
+-   * Render the box model view.
+-   */
+-  createBoxModelView: function() {
+-    let {
+-      setSelectedNode,
+-      onShowBoxModelHighlighterForNode,
+-    } = this.inspector.getCommonComponentProps();
+-
+-    let {
+-      onHideBoxModelHighlighter,
+-      onShowBoxModelEditor,
+-      onShowBoxModelHighlighter,
+-      onToggleGeometryEditor,
+-    } = this.inspector.getPanel("boxmodel").getComponentProps();
+-
+-    let provider = createElement(
+-      Provider,
+-      { store: this.store },
+-      BoxModelApp({
+-        setSelectedNode,
+-        showBoxModelProperties: false,
+-        onHideBoxModelHighlighter,
+-        onShowBoxModelEditor,
+-        onShowBoxModelHighlighter,
+-        onShowBoxModelHighlighterForNode,
+-        onToggleGeometryEditor,
+-      })
+-    );
+-    ReactDOM.render(provider, this.boxModelWrapper);
+-  },
+-
+-  /**
+    * The CSS as displayed by the UI.
+    */
+   createStyleViews: function() {
+     if (CssComputedView.propertyNames) {
+       return;
+     }
+ 
+     CssComputedView.propertyNames = [];
+@@ -792,30 +749,28 @@ CssComputedView.prototype = {
+     this.element.removeEventListener("contextmenu", this._onContextMenu);
+     this.searchField.removeEventListener("input", this._onFilterStyles);
+     this.searchClearButton.removeEventListener("click", this._onClearSearch);
+     this.includeBrowserStylesCheckbox.removeEventListener("input",
+       this._onIncludeBrowserStyles);
+ 
+     // Nodes used in templating
+     this.element = null;
+-    this.boxModelWrapper = null;
+     this.searchField = null;
+     this.searchClearButton = null;
+     this.includeBrowserStylesCheckbox = null;
+ 
+     // Property views
+     for (let propView of this.propertyViews) {
+       propView.destroy();
+     }
+     this.propertyViews = null;
+ 
+     this.inspector = null;
+     this.highlighters = null;
+-    this.store = null;
+     this.styleDocument = null;
+     this.styleWindow = null;
+ 
+     this._isDestroyed = true;
+   }
+ };
+ 
+ function PropertyInfo(tree, name) {
+diff --git a/devtools/client/inspector/computed/test/browser_computed_search-filter.js b/devtools/client/inspector/computed/test/browser_computed_search-filter.js
+--- a/devtools/client/inspector/computed/test/browser_computed_search-filter.js
++++ b/devtools/client/inspector/computed/test/browser_computed_search-filter.js
+@@ -28,18 +28,16 @@ function* testToggleDefaultStyles(inspec
+   let checkbox = computedView.includeBrowserStylesCheckbox;
+   let onRefreshed = inspector.once("computed-view-refreshed");
+   checkbox.click();
+   yield onRefreshed;
+ }
+ 
+ function* testAddTextInFilter(inspector, computedView) {
+   info("setting filter text to \"color\"");
+-  let doc = computedView.styleDocument;
+-  let boxModelWrapper = doc.getElementById("boxmodel-wrapper");
+   let searchField = computedView.searchField;
+   let onRefreshed = inspector.once("computed-view-refreshed");
+   let win = computedView.styleWindow;
+ 
+   // First check to make sure that accel + F doesn't focus search if the
+   // container isn't focused
+   inspector.panelWin.focus();
+   EventUtils.synthesizeKey("f", { accelKey: true });
+@@ -48,18 +46,16 @@ function* testAddTextInFilter(inspector,
+ 
+   computedView.element.focus();
+   EventUtils.synthesizeKey("f", { accelKey: true });
+   is(inspector.panelDoc.activeElement, searchField, "Search field is focused");
+ 
+   synthesizeKeys("color", win);
+   yield onRefreshed;
+ 
+-  ok(boxModelWrapper.hidden, "Box model is hidden");
+-
+   info("check that the correct properties are visible");
+ 
+   let propertyViews = computedView.propertyViews;
+   propertyViews.forEach(propView => {
+     let name = propView.name;
+     is(propView.visible, name.indexOf("color") > -1,
+       "span " + name + " property visibility check");
+   });
+diff --git a/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js b/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js
+--- a/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js
++++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_clear.js
+@@ -44,28 +44,24 @@ function* testAddTextInFilter(inspector,
+       "span " + name + " property visibility check");
+   });
+ }
+ 
+ function* testClearSearchFilter(inspector, computedView) {
+   info("Clearing the search filter");
+ 
+   let win = computedView.styleWindow;
+-  let doc = computedView.styleDocument;
+-  let boxModelWrapper = doc.getElementById("boxmodel-wrapper");
+   let propertyViews = computedView.propertyViews;
+   let searchField = computedView.searchField;
+   let searchClearButton = computedView.searchClearButton;
+   let onRefreshed = inspector.once("computed-view-refreshed");
+ 
+   EventUtils.synthesizeMouseAtCenter(searchClearButton, {}, win);
+   yield onRefreshed;
+ 
+-  ok(!boxModelWrapper.hidden, "Box model is displayed");
+-
+   info("Check that the correct properties are visible");
+ 
+   ok(!searchField.value, "Search filter is cleared");
+   propertyViews.forEach((propView) => {
+     is(propView.visible, propView.hasMatchedSelectors,
+       "span " + propView.name + " property visibility check");
+   });
+ }
+diff --git a/devtools/client/inspector/inspector.xhtml b/devtools/client/inspector/inspector.xhtml
+--- a/devtools/client/inspector/inspector.xhtml
++++ b/devtools/client/inspector/inspector.xhtml
+@@ -128,17 +128,16 @@
+                  type="checkbox"
+                  class="includebrowserstyles"/>
+           <label id="browser-style-checkbox-label" for="browser-style-checkbox"
+                  data-localization="content=inspector.browserStyles.label"></label>
+         </div>
+ 
+         <div id="computed-container">
+           <div id="computed-container-focusable" tabindex="-1">
+-            <div id="boxmodel-wrapper"></div>
+             <div id="computed-property-container" class="devtools-monospace" tabindex="0" dir="ltr"></div>
+             <div id="computed-no-results" class="devtools-sidepanel-no-result" hidden="" data-localization="content=inspector.noProperties"></div>
+           </div>
+         </div>
+       </div>
+ 
+       <div id="sidebar-panel-animationinspector" class="theme-sidebar inspector-tabpanel">
+         <iframe class="devtools-inspector-tab-frame"></iframe>
+diff --git a/devtools/client/inspector/test/browser_inspector_textbox-menu.js b/devtools/client/inspector/test/browser_inspector_textbox-menu.js
+--- a/devtools/client/inspector/test/browser_inspector_textbox-menu.js
++++ b/devtools/client/inspector/test/browser_inspector_textbox-menu.js
+@@ -59,20 +59,20 @@ add_task(function* () {
+   info("Testing the rule-view new property");
+   // Tabbing out of the value field triggers a ruleview-changed event that we need to wait
+   // for.
+   let onRuleViewChanged = once(ruleView, "ruleview-changed");
+   EventUtils.sendKey("tab", inspector.panelWin);
+   yield onRuleViewChanged;
+   yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
+ 
+-  info("Switching to the computed-view");
+-  let onComputedViewReady = inspector.once("boxmodel-view-updated");
+-  selectComputedView(inspector);
+-  yield onComputedViewReady;
++  info("Switching to the layout-view");
++  let onBoxModelUpdated = inspector.once("boxmodel-view-updated");
++  selectLayoutView(inspector);
++  yield onBoxModelUpdated;
+ 
+   info("Testing the box-model region");
+   let margin = inspector.panelDoc.querySelector(
+     ".boxmodel-margin.boxmodel-top > span");
+   EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin);
+   yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
+ 
+   // Move the mouse out of the box-model region to avoid triggering the box model
+diff --git a/devtools/client/inspector/test/shared-head.js b/devtools/client/inspector/test/shared-head.js
+--- a/devtools/client/inspector/test/shared-head.js
++++ b/devtools/client/inspector/test/shared-head.js
+@@ -48,17 +48,17 @@ var openInspector = Task.async(function*
+  * visible and ready
+  */
+ var openInspectorSidebarTab = Task.async(function* (id) {
+   let {toolbox, inspector, testActor} = yield openInspector();
+ 
+   info("Selecting the " + id + " sidebar");
+ 
+   let onSidebarSelect = inspector.sidebar.once("select");
+-  if (id === "computedview" || id === "layoutview") {
++  if (id === "layoutview") {
+     // The layout and computed views should wait until the box-model widget is ready.
+     let onBoxModelViewReady = inspector.once("boxmodel-view-updated");
+     // The layout view also needs to wait for the grid panel to be fully updated.
+     let onGridPanelReady = id === "layoutview" ?
+       inspector.once("grid-panel-updated") : Promise.resolve();
+     inspector.sidebar.select(id);
+     yield onBoxModelViewReady;
+     yield onGridPanelReady;
+@@ -165,16 +165,27 @@ function selectRuleView(inspector) {
+  * @return {CssComputedView} the computed view
+  */
+ function selectComputedView(inspector) {
+   inspector.sidebar.select("computedview");
+   return inspector.getPanel("computedview").computedView;
+ }
+ 
+ /**
++ * Select the layout view sidebar tab on an already opened inspector panel.
++ *
++ * @param  {InspectorPanel} inspector
++ * @return {BoxModel} the box model
++ */
++function selectLayoutView(inspector) {
++  inspector.sidebar.select("layoutview");
++  return inspector.getPanel("boxmodel");
++}
++
++/**
+  * Get the NodeFront for a node that matches a given css selector, via the
+  * protocol.
+  * @param {String|NodeFront} selector
+  * @param {InspectorPanel} inspector The instance of InspectorPanel currently
+  * loaded in the toolbox
+  * @return {Promise} Resolves to the NodeFront instance
+  */
+ function getNodeFront(selector, {walker}) {
+diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js
+--- a/devtools/client/preferences/devtools.js
++++ b/devtools/client/preferences/devtools.js
+@@ -74,18 +74,16 @@ pref("devtools.new-animationinspector.en
+ 
+ // Grid highlighter preferences
+ pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
+ pref("devtools.gridinspector.gridOutlineMaxRows", 50);
+ pref("devtools.gridinspector.showGridAreas", false);
+ pref("devtools.gridinspector.showGridLineNumbers", false);
+ pref("devtools.gridinspector.showInfiniteLines", false);
+ 
+-// Whether or not the box model panel is opened in the computed view
+-pref("devtools.computed.boxmodel.opened", true);
+ // Whether or not the box model panel is opened in the layout view
+ pref("devtools.layout.boxmodel.opened", true);
+ // Whether or not the flexbox panel is opened in the layout view
+ pref("devtools.layout.flexbox.opened", true);
+ // Whether or not the grid inspector panel is opened in the layout view
+ pref("devtools.layout.grid.opened", true);
+ 
+ // By how many times eyedropper will magnify pixels

+ 512 - 0
frg/work-js/mozilla-release/patches/1354883-61a1.patch

@@ -0,0 +1,512 @@
+# HG changeset patch
+# User gvso <valentin2507@gmail.com>
+# Date 1521041495 14400
+# Node ID 37243daf113e4f5b5a74c2521b4c3e1af2e3870b
+# Parent  9cf2986f5660facacbbf365b20b75bc7b6f3200d
+Bug 1354883 - convert uses of 'defer' to 'new Promise' in client/canvasdebugger. r=nchevobbe
+
+MozReview-Commit-ID: 9M4CBUc6NCQ
+
+diff --git a/devtools/client/canvasdebugger/canvasdebugger.js b/devtools/client/canvasdebugger/canvasdebugger.js
+--- a/devtools/client/canvasdebugger/canvasdebugger.js
++++ b/devtools/client/canvasdebugger/canvasdebugger.js
+@@ -1,17 +1,16 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+  * You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ const { SideMenuWidget } = require("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
+-const promise = require("promise");
+ const Services = require("Services");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const { CallWatcherFront } = require("devtools/shared/fronts/call-watcher");
+ const { CanvasFront } = require("devtools/shared/fronts/canvas");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ const { extend } = require("devtools/shared/extend");
+ const flags = require("devtools/shared/flags");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+@@ -98,28 +97,28 @@ const CALLS_LIST_SLOW_SAVE_DELAY = 100; 
+  * The current target and the Canvas front, set by this tool's host.
+  */
+ var gToolbox, gTarget, gFront;
+ 
+ /**
+  * Initializes the canvas debugger controller and views.
+  */
+ function startupCanvasDebugger() {
+-  return promise.all([
++  return Promise.all([
+     EventsHandler.initialize(),
+     SnapshotsListView.initialize(),
+     CallsListView.initialize()
+   ]);
+ }
+ 
+ /**
+  * Destroys the canvas debugger controller and views.
+  */
+ function shutdownCanvasDebugger() {
+-  return promise.all([
++  return Promise.all([
+     EventsHandler.destroy(),
+     SnapshotsListView.destroy(),
+     CallsListView.destroy()
+   ]);
+ }
+ 
+ /**
+  * Functions handling target-related lifetime events.
+diff --git a/devtools/client/canvasdebugger/panel.js b/devtools/client/canvasdebugger/panel.js
+--- a/devtools/client/canvasdebugger/panel.js
++++ b/devtools/client/canvasdebugger/panel.js
+@@ -1,17 +1,16 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const { Cc, Ci, Cu, Cr } = require("chrome");
+-const promise = require("promise");
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const { CanvasFront } = require("devtools/shared/fronts/canvas");
+ const DevToolsUtils = require("devtools/shared/DevToolsUtils");
+ 
+ function CanvasDebuggerPanel(iframeWindow, toolbox) {
+   this.panelWin = iframeWindow;
+   this._toolbox = toolbox;
+   this._destroyer = null;
+@@ -30,17 +29,17 @@ CanvasDebuggerPanel.prototype = {
+    */
+   open: function () {
+     let targetPromise;
+ 
+     // Local debugging needs to make the target remote.
+     if (!this.target.isRemote) {
+       targetPromise = this.target.makeRemote();
+     } else {
+-      targetPromise = promise.resolve(this.target);
++      targetPromise = Promise.resolve(this.target);
+     }
+ 
+     return targetPromise
+       .then(() => {
+         this.panelWin.gToolbox = this._toolbox;
+         this.panelWin.gTarget = this.target;
+         this.panelWin.gFront = new CanvasFront(this.target.client, this.target.form);
+         return this.panelWin.startupCanvasDebugger();
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-highlight.js
+@@ -9,17 +9,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.itemCount, 8,
+     "All the function calls should now be displayed in the UI.");
+ 
+   is($(".call-item-view", CallsListView.getItemAtIndex(0).target).hasAttribute("draw-call"), true,
+     "The first item's node should have a draw-call attribute.");
+   is($(".call-item-view", CallsListView.getItemAtIndex(1).target).hasAttribute("draw-call"), false,
+     "The second item's node should not have a draw-call attribute.");
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-list.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.itemCount, 8,
+     "All the function calls should now be displayed in the UI.");
+ 
+   testItem(CallsListView.getItemAtIndex(0),
+     "1", "Object", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
+ 
+   testItem(CallsListView.getItemAtIndex(1),
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-search.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+   let searchbox = $("#calls-searchbox");
+ 
+   await reload(target);
+ 
+   let firstRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([firstRecordingFinished, callListPopulated]);
++  await Promise.all([firstRecordingFinished, callListPopulated]);
+ 
+   is(searchbox.value, "",
+     "The searchbox should be initially empty.");
+   is(CallsListView.visibleItems.length, 8,
+     "All the items should be initially visible in the calls list.");
+ 
+   searchbox.focus();
+   EventUtils.sendString("clear", window);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-01.js
+@@ -15,17 +15,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let locationLink = $(".call-item-location", callItem.target);
+ 
+   is($(".call-item-stack", callItem.target), null,
+     "There should be no stack container available yet for the draw call.");
+ 
+   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-02.js
+@@ -16,17 +16,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let locationLink = $(".call-item-location", callItem.target);
+ 
+   is($(".call-item-stack", callItem.target), null,
+     "There should be no stack container available yet for the draw call.");
+ 
+   let callStackDisplayed = once(window, EVENTS.CALL_STACK_DISPLAYED);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-call-stack-03.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_DEEP_STACK_URL);
+   let { window, $, $all, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   let callItem = CallsListView.getItemAtIndex(2);
+   let view = $(".call-item-view", callItem.target);
+   let contents = $(".call-item-contents", callItem.target);
+ 
+   is(view.hasAttribute("call-stack-populated"), false,
+     "The call item's view should not have the stack populated yet.");
+   is(view.hasAttribute("call-stack-expanded"), false,
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-screenshots.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { window, $, EVENTS, SnapshotsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated, screenshotDisplayed]);
++  await Promise.all([recordingFinished, callListPopulated, screenshotDisplayed]);
+ 
+   is($("#screenshot-container").hidden, false,
+     "The screenshot container should now be visible.");
+ 
+   is($("#screenshot-dimensions").getAttribute("value"), "128" + "\u00D7" + "128",
+     "The screenshot dimensions label has the expected value.");
+ 
+   is($("#screenshot-image").getAttribute("flipped"), "false",
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-01.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { window, $, $all, EVENTS, SnapshotsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
++  await Promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
+ 
+   is($all(".filmstrip-thumbnail").length, 4,
+     "There should be 4 thumbnails displayed in the UI.");
+ 
+   let firstThumbnail = $(".filmstrip-thumbnail[index='0']");
+   ok(firstThumbnail,
+     "The first thumbnail element should be for the function call at index 0.");
+   is(firstThumbnail.width, 50,
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-img-thumbnails-02.js
+@@ -12,17 +12,17 @@ async function ifTestingSupported() {
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([
++  await Promise.all([
+     recordingFinished,
+     callListPopulated,
+     thumbnailsDisplayed,
+     screenshotDisplayed
+   ]);
+ 
+   is($all(".filmstrip-thumbnail[highlighted]").length, 0,
+     "There should be no highlighted thumbnail available yet.");
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-reload-02.js
+@@ -23,17 +23,17 @@ async function ifTestingSupported() {
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([
++  await Promise.all([
+     recordingFinished,
+     callListPopulated,
+     thumbnailsDisplayed,
+     screenshotDisplayed
+   ]);
+ 
+   is(SnapshotsListView.itemCount, 1,
+     "There should be one snapshot displayed in the UI.");
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-01.js
+@@ -9,17 +9,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   is(CallsListView.selectedIndex, -1,
+     "No item in the function calls list should be initially selected.");
+ 
+   is($("#calls-slider").value, 0,
+     "The slider should be moved all the way to the start.");
+   is($("#calls-slider").min, 0,
+     "The slider minimum value should be 0.");
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-slider-02.js
+@@ -10,17 +10,17 @@ async function ifTestingSupported() {
+   let { window, $, EVENTS, gFront, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
++  await Promise.all([recordingFinished, callListPopulated, thumbnailsDisplayed]);
+ 
+   let firstSnapshot = SnapshotsListView.getItemAtIndex(0);
+   let firstSnapshotOverview = await firstSnapshot.attachment.actor.getOverview();
+ 
+   let thumbnails = firstSnapshotOverview.thumbnails;
+   is(thumbnails.length, 4,
+     "There should be 4 thumbnails cached for the snapshot item.");
+ 
+@@ -74,19 +74,19 @@ async function ifTestingSupported() {
+   await once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+   ok(true, "The full-sized screenshot was displayed for the item at index 0.");
+ 
+   await teardown(panel);
+   finish();
+ }
+ 
+ function waitForMozSetImageElement(panel) {
+-  let deferred = defer();
+-  panel._onMozSetImageElement = deferred.resolve;
+-  return deferred.promise;
++  return new Promise((resolve, reject) => {
++    panel._onMozSetImageElement = resolve;
++  });
+ }
+ 
+ function sameArray(a, b) {
+   if (a.length != b.length) {
+     return false;
+   }
+   for (let i = 0; i < a.length; i++) {
+     if (a[i] !== b[i]) {
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-snapshot-select-01.js
+@@ -63,30 +63,30 @@ async function ifTestingSupported() {
+     "The first snapshot should now be re-selected.");
+   is(CallsListView.selectedIndex, -1,
+     "There should still be no call item automatically selected in the snapshot.");
+ 
+   function recordAndWaitForFirstSnapshot() {
+     let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+     let snapshotSelected = waitForSnapshotSelection();
+     SnapshotsListView._onRecordButtonClick();
+-    return promise.all([recordingFinished, snapshotSelected]);
++    return Promise.all([recordingFinished, snapshotSelected]);
+   }
+ 
+   function recordAndWaitForAnotherSnapshot() {
+     let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+     SnapshotsListView._onRecordButtonClick();
+     return recordingFinished;
+   }
+ 
+   function waitForSnapshotSelection() {
+     let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+     let thumbnailsDisplayed = once(window, EVENTS.THUMBNAILS_DISPLAYED);
+     let screenshotDisplayed = once(window, EVENTS.CALL_SCREENSHOT_DISPLAYED);
+-    return promise.all([
++    return Promise.all([
+       callListPopulated,
+       thumbnailsDisplayed,
+       screenshotDisplayed
+     ]);
+   }
+ 
+   await teardown(panel);
+   finish();
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stepping.js
+@@ -9,17 +9,17 @@ async function ifTestingSupported() {
+   let { target, panel } = await initCanvasDebuggerFrontend(SIMPLE_CANVAS_URL);
+   let { window, $, EVENTS, SnapshotsListView, CallsListView } = panel.panelWin;
+ 
+   await reload(target);
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let callListPopulated = once(window, EVENTS.CALL_LIST_POPULATED);
+   SnapshotsListView._onRecordButtonClick();
+-  await promise.all([recordingFinished, callListPopulated]);
++  await Promise.all([recordingFinished, callListPopulated]);
+ 
+   checkSteppingButtons(1, 1, 1, 1);
+   is(CallsListView.selectedIndex, -1,
+     "There should be no selected item in the calls list view initially.");
+ 
+   CallsListView._onResume();
+   checkSteppingButtons(1, 1, 1, 1);
+   is(CallsListView.selectedIndex, 0,
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-01.js
+@@ -18,17 +18,17 @@ async function ifTestingSupported() {
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+   SnapshotsListView._onRecordButtonClick();
+ 
+-  await promise.all([recordingFinished, recordingCancelled]);
++  await Promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+   await teardown(panel);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-02.js
+@@ -17,17 +17,17 @@ async function ifTestingSupported() {
+   await recordingStarted;
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+ 
+-  await promise.all([recordingFinished, recordingCancelled]);
++  await Promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+   await teardown(panel);
+diff --git a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
+--- a/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
++++ b/devtools/client/canvasdebugger/test/browser_canvas-frontend-stop-03.js
+@@ -18,17 +18,17 @@ async function ifTestingSupported() {
+   await recordingStarted;
+ 
+   is($("#empty-notice").hidden, true, "Empty notice not shown");
+   is($("#waiting-notice").hidden, false, "Waiting notice shown");
+ 
+   let recordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
+   let recordingCancelled = once(window, EVENTS.SNAPSHOT_RECORDING_CANCELLED);
+ 
+-  await promise.all([recordingFinished, recordingCancelled]);
++  await Promise.all([recordingFinished, recordingCancelled]);
+ 
+   ok(true, "Recording stopped and was considered failed.");
+ 
+   is(SnapshotsListView.itemCount, 0, "No snapshots in the list.");
+   is($("#empty-notice").hidden, false, "Empty notice shown");
+   is($("#waiting-notice").hidden, true, "Waiting notice not shown");
+ 
+   await teardown(panel);

+ 995 - 0
frg/work-js/mozilla-release/patches/1382581-1-61a1.patch

@@ -0,0 +1,995 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521045072 -3600
+# Node ID 8ef404b01c4aa86801c51097f6a524fc21ca1017
+# Parent  d3873cbded0adbea65bdd390b8b90d21aa22be1f
+Bug 1382581 - Remove old-event-emitter usage from devtools/client/framework; r=bgrins.
+
+MozReview-Commit-ID: 4oQJ3391H9S
+
+diff --git a/devtools/client/framework/ToolboxProcess.jsm b/devtools/client/framework/ToolboxProcess.jsm
+--- a/devtools/client/framework/ToolboxProcess.jsm
++++ b/devtools/client/framework/ToolboxProcess.jsm
+@@ -13,17 +13,17 @@ const { console } = ChromeUtils.import("
+ const { require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ 
+ ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
+ XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
+   return require("devtools/client/shared/telemetry");
+ });
+ XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() {
+-  return require("devtools/shared/old-event-emitter");
++  return require("devtools/shared/event-emitter");
+ });
+ XPCOMUtils.defineLazyGetter(this, "system", function() {
+   return require("devtools/shared/system");
+ });
+ const promise = require("promise");
+ const Services = require("Services");
+ 
+ this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
+diff --git a/devtools/client/framework/devtools-browser.js b/devtools/client/framework/devtools-browser.js
+--- a/devtools/client/framework/devtools-browser.js
++++ b/devtools/client/framework/devtools-browser.js
+@@ -661,26 +661,26 @@ var gDevToolsBrowser = exports.gDevTools
+     gDevTools.destroy({ shuttingDown });
+   },
+ };
+ 
+ // Handle all already registered tools,
+ gDevTools.getToolDefinitionArray()
+          .forEach(def => gDevToolsBrowser._addToolToWindows(def));
+ // and the new ones.
+-gDevTools.on("tool-registered", function(ev, toolId) {
++gDevTools.on("tool-registered", function(toolId) {
+   let toolDefinition = gDevTools._tools.get(toolId);
+   // If the tool has been registered globally, add to all the
+   // available windows.
+   if (toolDefinition) {
+     gDevToolsBrowser._addToolToWindows(toolDefinition);
+   }
+ });
+ 
+-gDevTools.on("tool-unregistered", function(ev, toolId) {
++gDevTools.on("tool-unregistered", function(toolId) {
+   gDevToolsBrowser._removeToolFromWindows(toolId);
+ });
+ 
+ gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
+ gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
+ 
+ Services.obs.addObserver(gDevToolsBrowser, "quit-application");
+ Services.obs.addObserver(gDevToolsBrowser, "browser-delayed-startup-finished");
+diff --git a/devtools/client/framework/devtools.js b/devtools/client/framework/devtools.js
+--- a/devtools/client/framework/devtools.js
++++ b/devtools/client/framework/devtools.js
+@@ -23,17 +23,17 @@ loader.lazyImporter(this, "BrowserToolbo
+ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
+ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
+ 
+ loader.lazyRequireGetter(this, "WebExtensionInspectedWindowFront",
+       "devtools/shared/fronts/webextension-inspected-window", true);
+ 
+ const {defaultTools: DefaultTools, defaultThemes: DefaultThemes} =
+   require("devtools/client/definitions");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {getTheme, setTheme, addThemeObserver, removeThemeObserver} =
+   require("devtools/client/shared/theme");
+ 
+ const FORBIDDEN_IDS = new Set(["toolbox", ""]);
+ const MAX_ORDINAL = 99;
+ 
+ /**
+  * DevTools is a class that represents a set of developer tools, it holds a
+diff --git a/devtools/client/framework/menu.js b/devtools/client/framework/menu.js
+--- a/devtools/client/framework/menu.js
++++ b/devtools/client/framework/menu.js
+@@ -1,17 +1,17 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ 
+ /**
+  * A partial implementation of the Menu API provided by electron:
+  * https://github.com/electron/electron/blob/master/docs/api/menu.md.
+  *
+  * Extra features:
+  *  - Emits an 'open' and 'close' event when the menu is opened/closed
+ 
+diff --git a/devtools/client/framework/selection.js b/devtools/client/framework/selection.js
+--- a/devtools/client/framework/selection.js
++++ b/devtools/client/framework/selection.js
+@@ -2,17 +2,17 @@
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+-var EventEmitter = require("devtools/shared/old-event-emitter");
++var EventEmitter = require("devtools/shared/event-emitter");
+ 
+ /**
+  * API
+  *
+  *   new Selection(walker=null)
+  *   destroy()
+  *   node (readonly)
+  *   setNode(node, origin="unknown")
+diff --git a/devtools/client/framework/source-map-url-service.js b/devtools/client/framework/source-map-url-service.js
+--- a/devtools/client/framework/source-map-url-service.js
++++ b/devtools/client/framework/source-map-url-service.js
+@@ -65,17 +65,17 @@ SourceMapURLService.prototype._getLoadin
+           });
+     }
+ 
+     // Start fetching the sources now.
+     let loadingPromise = this._toolbox.threadClient.getSources().then(({sources}) => {
+       // Ignore errors.  Register the sources we got; we can't rely on
+       // an event to arrive if the source actor already existed.
+       for (let source of sources) {
+-        this._onSourceUpdated(null, {source});
++        this._onSourceUpdated({source});
+       }
+     }, e => {
+       // Also ignore any protocol-based errors.
+     });
+ 
+     this._loadingPromise = Promise.all([styleSheetsLoadingPromise, loadingPromise]);
+   }
+   return this._loadingPromise;
+@@ -105,17 +105,17 @@ SourceMapURLService.prototype.destroy = 
+   }
+   Services.prefs.removeObserver(SOURCE_MAP_PREF, this._onPrefChanged);
+   this._target = this._urls = this._subscriptions = this._idMap = null;
+ };
+ 
+ /**
+  * A helper function that is called when a new source is available.
+  */
+-SourceMapURLService.prototype._onSourceUpdated = function (_, sourceEvent) {
++SourceMapURLService.prototype._onSourceUpdated = function (sourceEvent) {
+   // Maybe we were shut down while waiting.
+   if (!this._urls) {
+     return;
+   }
+ 
+   let { source } = sourceEvent;
+   let { generatedUrl, url, actor: id, sourceMapURL } = source;
+ 
+diff --git a/devtools/client/framework/target.js b/devtools/client/framework/target.js
+--- a/devtools/client/framework/target.js
++++ b/devtools/client/framework/target.js
+@@ -1,17 +1,17 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const { Ci } = require("chrome");
+ const defer = require("devtools/shared/defer");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const Services = require("Services");
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ 
+ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
+ loader.lazyRequireGetter(this, "DebuggerClient",
+   "devtools/shared/client/debugger-client", true);
+ loader.lazyRequireGetter(this, "gDevTools",
+   "devtools/client/framework/devtools", true);
+@@ -617,17 +617,17 @@ TabTarget.prototype = {
+     // remotenesschange events. But we should ignore them as at the end
+     // the content doesn't change its remoteness.
+     if (this._tab.isResponsiveDesignMode) {
+       return;
+     }
+ 
+     // Save a reference to the tab as it will be nullified on destroy
+     let tab = this._tab;
+-    let onToolboxDestroyed = (event, target) => {
++    let onToolboxDestroyed = target => {
+       if (target != this) {
+         return;
+       }
+       gDevTools.off("toolbox-destroyed", target);
+ 
+       // Recreate a fresh target instance as the current one is now destroyed
+       let newTarget = TargetFactory.forTab(tab);
+       gDevTools.showToolbox(newTarget);
+diff --git a/devtools/client/framework/test/browser_devtools_api.js b/devtools/client/framework/test/browser_devtools_api.js
+--- a/devtools/client/framework/test/browser_devtools_api.js
++++ b/devtools/client/framework/test/browser_devtools_api.js
+@@ -36,29 +36,29 @@ function runTests1(tab) {
+   ok(gDevTools.getToolDefinitionMap().has(toolId1),
+     "The tool is registered");
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+ 
+   let events = {};
+ 
+   // Check events on the gDevTools and toolbox objects.
+-  gDevTools.once(toolId1 + "-init", (event, toolbox, iframe) => {
++  gDevTools.once(toolId1 + "-init", (toolbox, iframe) => {
+     ok(iframe, "iframe argument available");
+ 
+-    toolbox.once(toolId1 + "-init", (innerEvent, innerIframe) => {
++    toolbox.once(toolId1 + "-init", innerIframe => {
+       ok(innerIframe, "innerIframe argument available");
+       events.init = true;
+     });
+   });
+ 
+-  gDevTools.once(toolId1 + "-ready", (event, toolbox, panel) => {
++  gDevTools.once(toolId1 + "-ready", (toolbox, panel) => {
+     ok(panel, "panel argument available");
+ 
+-    toolbox.once(toolId1 + "-ready", (innerEvent, innerPanel) => {
++    toolbox.once(toolId1 + "-ready", innerPanel => {
+       ok(innerPanel, "innerPanel argument available");
+       events.ready = true;
+     });
+   });
+ 
+   gDevTools.showToolbox(target, toolId1).then(function (toolbox) {
+     is(toolbox.target, target, "toolbox target is correct");
+     is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct");
+@@ -95,38 +95,38 @@ function runTests2() {
+   ok(gDevTools.getToolDefinitionMap().has(toolId2),
+     "The tool is registered");
+ 
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+ 
+   let events = {};
+ 
+   // Check events on the gDevTools and toolbox objects.
+-  gDevTools.once(toolId2 + "-init", (event, toolbox, iframe) => {
++  gDevTools.once(toolId2 + "-init", (toolbox, iframe) => {
+     ok(iframe, "iframe argument available");
+ 
+-    toolbox.once(toolId2 + "-init", (innerEvent, innerIframe) => {
++    toolbox.once(toolId2 + "-init", innerIframe => {
+       ok(innerIframe, "innerIframe argument available");
+       events.init = true;
+     });
+   });
+ 
+-  gDevTools.once(toolId2 + "-build", (event, toolbox, panel, iframe) => {
++  gDevTools.once(toolId2 + "-build", (toolbox, panel, iframe) => {
+     ok(panel, "panel argument available");
+ 
+-    toolbox.once(toolId2 + "-build", (innerEvent, innerPanel, innerIframe) => {
++    toolbox.once(toolId2 + "-build", (innerPanel, innerIframe) => {
+       ok(innerPanel, "innerPanel argument available");
+       events.build = true;
+     });
+   });
+ 
+-  gDevTools.once(toolId2 + "-ready", (event, toolbox, panel) => {
++  gDevTools.once(toolId2 + "-ready", (toolbox, panel) => {
+     ok(panel, "panel argument available");
+ 
+-    toolbox.once(toolId2 + "-ready", (innerEvent, innerPanel) => {
++    toolbox.once(toolId2 + "-ready", innerPanel => {
+       ok(innerPanel, "innerPanel argument available");
+       events.ready = true;
+     });
+   });
+ 
+   gDevTools.showToolbox(target, toolId2).then(function (toolbox) {
+     is(toolbox.target, target, "toolbox target is correct");
+     is(toolbox.target.tab, gBrowser.selectedTab, "targeted tab is correct");
+@@ -147,30 +147,30 @@ var continueTests = async function (tool
+   ok(toolDefinitions.has(toolId2), "The tool is in gDevTools");
+ 
+   let toolDefinition = toolDefinitions.get(toolId2);
+   is(toolDefinition.id, toolId2, "toolDefinition id is correct");
+ 
+   info("Testing toolbox tool-unregistered event");
+   let toolSelected = toolbox.once("select");
+   let unregisteredTool = await new Promise(resolve => {
+-    toolbox.once("tool-unregistered", (e, id) => resolve(id));
++    toolbox.once("tool-unregistered", id => resolve(id));
+     gDevTools.unregisterTool(toolId2);
+   });
+   await toolSelected;
+ 
+   is(unregisteredTool, toolId2, "Event returns correct id");
+   ok(!toolbox.isToolRegistered(toolId2),
+     "Toolbox: The tool is not registered");
+   ok(!gDevTools.getToolDefinitionMap().has(toolId2),
+     "The tool is no longer registered");
+ 
+   info("Testing toolbox tool-registered event");
+   let registeredTool = await new Promise(resolve => {
+-    toolbox.once("tool-registered", (e, id) => resolve(id));
++    toolbox.once("tool-registered", id => resolve(id));
+     gDevTools.registerTool(toolDefinition);
+   });
+ 
+   is(registeredTool, toolId2, "Event returns correct id");
+   ok(toolbox.isToolRegistered(toolId2),
+     "Toolbox: The tool is registered");
+   ok(gDevTools.getToolDefinitionMap().has(toolId2),
+     "The tool is registered");
+diff --git a/devtools/client/framework/test/browser_devtools_api_destroy.js b/devtools/client/framework/test/browser_devtools_api_destroy.js
+--- a/devtools/client/framework/test/browser_devtools_api_destroy.js
++++ b/devtools/client/framework/test/browser_devtools_api_destroy.js
+@@ -34,30 +34,30 @@ function runTests(aTab) {
+ 
+   let collectedEvents = [];
+ 
+   let target = TargetFactory.forTab(aTab);
+   gDevTools.showToolbox(target, toolDefinition.id).then(function (toolbox) {
+     let panel = toolbox.getPanel(toolDefinition.id);
+     ok(panel, "Tool open");
+ 
+-    gDevTools.once("toolbox-destroy", (event, toolbox, iframe) => {
+-      collectedEvents.push(event);
++    gDevTools.once("toolbox-destroy", (toolbox, iframe) => {
++      collectedEvents.push("toolbox-destroy");
+     });
+ 
+-    gDevTools.once(toolDefinition.id + "-destroy", (event, toolbox, iframe) => {
+-      collectedEvents.push("gDevTools-" + event);
++    gDevTools.once(toolDefinition.id + "-destroy", (toolbox, iframe) => {
++      collectedEvents.push("gDevTools-" + toolDefinition.id + "-destroy");
+     });
+ 
+-    toolbox.once("destroy", (event) => {
+-      collectedEvents.push(event);
++    toolbox.once("destroy", () => {
++      collectedEvents.push("destroy");
+     });
+ 
+-    toolbox.once(toolDefinition.id + "-destroy", (event) => {
+-      collectedEvents.push("toolbox-" + event);
++    toolbox.once(toolDefinition.id + "-destroy", () => {
++      collectedEvents.push("toolbox-" + toolDefinition.id + "-destroy");
+     });
+ 
+     toolbox.destroy().then(function () {
+       is(collectedEvents.join(":"),
+         "toolbox-destroy:destroy:gDevTools-testTool-destroy:toolbox-testTool-destroy",
+         "Found the right amount of collected events.");
+ 
+       gDevTools.unregisterTool(toolDefinition.id);
+diff --git a/devtools/client/framework/test/browser_target_events.js b/devtools/client/framework/test/browser_target_events.js
+--- a/devtools/client/framework/test/browser_target_events.js
++++ b/devtools/client/framework/test/browser_target_events.js
+@@ -26,29 +26,30 @@ function onHidden() {
+   ok(true, "Hidden event received");
+   target.once("visible", onVisible);
+   gBrowser.removeCurrentTab();
+ }
+ 
+ function onVisible() {
+   ok(true, "Visible event received");
+   target.once("will-navigate", onWillNavigate);
+-  let mm = loadFrameScriptUtils();
+-  mm.sendAsyncMessage("devtools:test:navigate", { location: "data:text/html,<meta charset='utf8'/>test navigation" });
++
++  ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
++    content.location = "data:text/html,<meta charset='utf8'/>test navigation";
++  });
+ }
+ 
+-function onWillNavigate(event, request) {
++async function onWillNavigate() {
+   ok(true, "will-navigate event received");
+-  // Wait for navigation handling to complete before removing the tab, in order
+-  // to avoid triggering assertions.
+-  target.once("navigate", executeSoon.bind(null, onNavigate));
++  target.on("navigate", onNavigate);
+ }
+ 
+ function onNavigate() {
+   ok(true, "navigate event received");
++  target.off("navigate", onNavigate);
+   target.once("close", onClose);
+   gBrowser.removeCurrentTab();
+ }
+ 
+ function onClose() {
+   ok(true, "close event received");
+ 
+   target = null;
+diff --git a/devtools/client/framework/test/browser_toolbox_dynamic_registration.js b/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
+--- a/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
++++ b/devtools/client/framework/test/browser_toolbox_dynamic_registration.js
+@@ -24,17 +24,17 @@ function testRegister(aToolbox)
+     id: "test-tool",
+     label: "Test Tool",
+     inMenu: true,
+     isTargetSupported: () => true,
+     build: function () {},
+   });
+ }
+ 
+-function toolRegistered(event, toolId)
++function toolRegistered(toolId)
+ {
+   is(toolId, "test-tool", "tool-registered event handler sent tool id");
+ 
+   ok(gDevTools.getToolDefinitionMap().has(toolId), "tool added to map");
+ 
+   // test that it appeared in the UI
+   let doc = toolbox.doc;
+   let tab = doc.getElementById("toolbox-tab-" + toolId);
+@@ -63,17 +63,17 @@ function getAllBrowserWindows() {
+ 
+ function testUnregister()
+ {
+   gDevTools.once("tool-unregistered", toolUnregistered);
+ 
+   gDevTools.unregisterTool("test-tool");
+ }
+ 
+-function toolUnregistered(event, toolId)
++function toolUnregistered(toolId)
+ {
+   is(toolId, "test-tool", "tool-unregistered event handler sent tool id");
+ 
+   ok(!gDevTools.getToolDefinitionMap().has(toolId), "tool removed from map");
+ 
+   // test that it disappeared from the UI
+   let doc = toolbox.doc;
+   let tab = doc.getElementById("toolbox-tab-" + toolId);
+diff --git a/devtools/client/framework/test/browser_toolbox_options.js b/devtools/client/framework/test/browser_toolbox_options.js
+--- a/devtools/client/framework/test/browser_toolbox_options.js
++++ b/devtools/client/framework/test/browser_toolbox_options.js
+@@ -263,29 +263,29 @@ async function toggleTool(node) {
+       checkRegistered.bind(null, toolId, deferred));
+   }
+   node.scrollIntoView();
+   EventUtils.synthesizeMouseAtCenter(node, {}, panelWin);
+ 
+   await deferred.promise;
+ }
+ 
+-function checkUnregistered(toolId, deferred, event, data) {
++function checkUnregistered(toolId, deferred, data) {
+   if (data == toolId) {
+     ok(true, "Correct tool removed");
+     // checking tab on the toolbox
+     ok(!doc.getElementById("toolbox-tab-" + toolId),
+       "Tab removed for " + toolId);
+   } else {
+     ok(false, "Something went wrong, " + toolId + " was not unregistered");
+   }
+   deferred.resolve();
+ }
+ 
+-function checkRegistered(toolId, deferred, event, data) {
++function checkRegistered(toolId, deferred, data) {
+   if (data == toolId) {
+     ok(true, "Correct tool added back");
+     // checking tab on the toolbox
+     let button = doc.getElementById("toolbox-tab-" + toolId);
+     ok(button, "Tab added back for " + toolId);
+   } else {
+     ok(false, "Something went wrong, " + toolId + " was not registered");
+   }
+diff --git a/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js b/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
+--- a/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
++++ b/devtools/client/framework/test/browser_toolbox_options_disable_buttons.js
+@@ -41,17 +41,17 @@ function testPrefsAreRespectedWhenReopen
+ }
+ 
+ function testSelectTool(devtoolsToolbox) {
+   let deferred = defer();
+   info("Selecting the options panel");
+ 
+   toolbox = devtoolsToolbox;
+   doc = toolbox.doc;
+-  toolbox.once("options-selected", (event, tool) => {
++  toolbox.once("options-selected", tool => {
+     ok(true, "Options panel selected via selectTool method");
+     panelWin = tool.panelWin;
+     deferred.resolve();
+   });
+   toolbox.selectTool("options");
+ 
+   return deferred.promise;
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_theme_registration.js b/devtools/client/framework/test/browser_toolbox_theme_registration.js
+--- a/devtools/client/framework/test/browser_toolbox_theme_registration.js
++++ b/devtools/client/framework/test/browser_toolbox_theme_registration.js
+@@ -14,17 +14,17 @@ const LIGHT_THEME_NAME = "light";
+ var toolbox;
+ 
+ add_task(async function themeRegistration() {
+   let tab = await addTab("data:text/html,test");
+   let target = TargetFactory.forTab(tab);
+   toolbox = await gDevTools.showToolbox(target, "options");
+ 
+   let themeId = await new Promise(resolve => {
+-    gDevTools.once("theme-registered", (e, registeredThemeId) => {
++    gDevTools.once("theme-registered", registeredThemeId => {
+       resolve(registeredThemeId);
+     });
+ 
+     gDevTools.registerTheme({
+       id: TEST_THEME_NAME,
+       label: "Test theme",
+       stylesheets: [CHROME_URL + "doc_theme.css"],
+       classList: ["theme-test"],
+@@ -39,17 +39,17 @@ add_task(async function themeRegistratio
+ add_task(async function themeInOptionsPanel() {
+   let panelWin = toolbox.getCurrentPanel().panelWin;
+   let doc = panelWin.frameElement.contentDocument;
+   let themeBox = doc.getElementById("devtools-theme-box");
+   let testThemeOption = themeBox.querySelector(
+     `input[type=radio][value=${TEST_THEME_NAME}]`);
+   let eventsRecorded = [];
+ 
+-  function onThemeChanged(event, theme) {
++  function onThemeChanged(theme) {
+     eventsRecorded.push(theme);
+   }
+   gDevTools.on("theme-changed", onThemeChanged);
+ 
+   ok(testThemeOption, "new theme exists in the Options panel");
+ 
+   let lightThemeOption = themeBox.querySelector(
+     `input[type=radio][value=${LIGHT_THEME_NAME}]`);
+@@ -96,17 +96,17 @@ add_task(async function themeInOptionsPa
+ });
+ 
+ add_task(async function themeUnregistration() {
+   let panelWin = toolbox.getCurrentPanel().panelWin;
+   let onUnRegisteredTheme = once(gDevTools, "theme-unregistered");
+   let onThemeSwitchComplete = once(panelWin, "theme-switch-complete");
+   let eventsRecorded = [];
+ 
+-  function onThemeChanged(event, theme) {
++  function onThemeChanged(theme) {
+     eventsRecorded.push(theme);
+   }
+   gDevTools.on("theme-changed", onThemeChanged);
+ 
+   gDevTools.unregisterTheme(TEST_THEME_NAME);
+   await onUnRegisteredTheme;
+   await onThemeSwitchComplete;
+ 
+diff --git a/devtools/client/framework/test/browser_toolbox_toggle.js b/devtools/client/framework/test/browser_toolbox_toggle.js
+--- a/devtools/client/framework/test/browser_toolbox_toggle.js
++++ b/devtools/client/framework/test/browser_toolbox_toggle.js
+@@ -44,23 +44,23 @@ async function testToggle(key, modifiers
+ 
+ async function testToggleDockedToolbox(tab, key, modifiers) {
+   let toolbox = getToolboxForTab(tab);
+ 
+   isnot(toolbox.hostType, Toolbox.HostType.WINDOW,
+     "Toolbox is docked in the main window");
+ 
+   info("verify docked toolbox is destroyed when using toggle key");
+-  let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
++  let onToolboxDestroyed = gDevTools.once("toolbox-destroyed");
+   EventUtils.synthesizeKey(key, modifiers);
+   await onToolboxDestroyed;
+   ok(true, "Docked toolbox is destroyed when using a toggle key");
+ 
+   info("verify new toolbox is created when using toggle key");
+-  let onToolboxReady = once(gDevTools, "toolbox-ready");
++  let onToolboxReady = gDevTools.once("toolbox-ready");
+   EventUtils.synthesizeKey(key, modifiers);
+   await onToolboxReady;
+   ok(true, "Toolbox is created by using when toggle key");
+ }
+ 
+ async function testToggleDetachedToolbox(tab, key, modifiers) {
+   let toolbox = getToolboxForTab(tab);
+ 
+@@ -86,23 +86,22 @@ async function testToggleDetachedToolbox
+   let onToolboxWindowFocus = once(toolboxWindow, "focus", true);
+   EventUtils.synthesizeKey(key, modifiers);
+   await onToolboxWindowFocus;
+   ok(true, "Toolbox focused and not destroyed");
+ 
+   info("Verify windowed toolbox is destroyed when using toggle key from its " +
+     "own window");
+ 
+-  let onToolboxDestroyed = once(gDevTools, "toolbox-destroyed");
++  let onToolboxDestroyed = gDevTools.once("toolbox-destroyed");
+   EventUtils.synthesizeKey(key, modifiers, toolboxWindow);
+   await onToolboxDestroyed;
+   ok(true, "Toolbox destroyed");
+ }
+ 
+ function getToolboxForTab(tab) {
+   return gDevTools.getToolbox(TargetFactory.forTab(tab));
+ }
+ 
+ function cleanup() {
+-  Services.prefs.setCharPref("devtools.toolbox.host",
+-    Toolbox.HostType.BOTTOM);
++  Services.prefs.setCharPref("devtools.toolbox.host", Toolbox.HostType.BOTTOM);
+   gBrowser.removeCurrentTab();
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_transport_events.js b/devtools/client/framework/test/browser_toolbox_transport_events.js
+--- a/devtools/client/framework/test/browser_toolbox_transport_events.js
++++ b/devtools/client/framework/test/browser_toolbox_transport_events.js
+@@ -58,53 +58,53 @@ function testPackets(sent, received) {
+     "The first sent packet is for list of tabs");
+ }
+ 
+ // Listen to the transport object that is associated with the
+ // default Toolbox debugger client
+ var sent1 = [];
+ var received1 = [];
+ 
+-function send1(eventId, packet) {
++function send1(packet) {
+   sent1.push(packet);
+ }
+ 
+-function onPacket1(eventId, packet) {
++function onPacket1(packet) {
+   received1.push(packet);
+ }
+ 
+-function onToolboxCreated(eventId, toolbox) {
++function onToolboxCreated(toolbox) {
+   toolbox.target.makeRemote();
+   let client = toolbox.target.client;
+   let transport = client._transport;
+ 
+   transport.on("send", send1);
+   transport.on("packet", onPacket1);
+ 
+-  client.addOneTimeListener("closed", event => {
++  client.addOneTimeListener("closed", () => {
+     transport.off("send", send1);
+     transport.off("packet", onPacket1);
+   });
+ }
+ 
+ // Listen to all debugger client object protocols.
+ var sent2 = [];
+ var received2 = [];
+ 
+-function send2(eventId, packet) {
++function send2(packet) {
+   sent2.push(packet);
+ }
+ 
+-function onPacket2(eventId, packet) {
++function onPacket2(packet) {
+   received2.push(packet);
+ }
+ 
+ function onDebuggerClientConnect(client) {
+   let transport = client._transport;
+ 
+   transport.on("send", send2);
+   transport.on("packet", onPacket2);
+ 
+-  client.addOneTimeListener("closed", event => {
++  client.addOneTimeListener("closed", () => {
+     transport.off("send", send2);
+     transport.off("packet", onPacket2);
+   });
+ }
+diff --git a/devtools/client/framework/test/browser_toolbox_window_shortcuts.js b/devtools/client/framework/test/browser_toolbox_window_shortcuts.js
+--- a/devtools/client/framework/test/browser_toolbox_window_shortcuts.js
++++ b/devtools/client/framework/test/browser_toolbox_window_shortcuts.js
+@@ -63,17 +63,17 @@ function testShortcuts(aToolbox, aIndex)
+     shiftKey: toolModifiers.includes("shift"),
+   };
+   idIndex = aIndex;
+   info("Testing shortcut for tool " + aIndex + ":" + toolIDs[aIndex] +
+        " using key " + key);
+   EventUtils.synthesizeKey(key, modifiers, toolbox.win.parent);
+ }
+ 
+-function selectCB(event, id) {
++function selectCB(id) {
+   info("toolbox-select event from " + id);
+ 
+   is(toolIDs.indexOf(id), idIndex,
+      "Correct tool is selected on pressing the shortcut for " + id);
+ 
+   testShortcuts(toolbox, idIndex + 1);
+ }
+ 
+diff --git a/devtools/client/framework/test/head.js b/devtools/client/framework/test/head.js
+--- a/devtools/client/framework/test/head.js
++++ b/devtools/client/framework/test/head.js
+@@ -3,17 +3,17 @@
+ /* Any copyright is dedicated to the Public Domain.
+  * http://creativecommons.org/publicdomain/zero/1.0/ */
+ 
+ /* import-globals-from ../../shared/test/shared-head.js */
+ 
+ // shared-head.js handles imports, constants, and utility functions
+ Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js", this);
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ 
+ function toggleAllTools(state) {
+   for (let [, tool] of gDevTools._tools) {
+     if (!tool.visibilityswitch) {
+       continue;
+     }
+     if (state) {
+       Services.prefs.setBoolPref(tool.visibilityswitch, true);
+@@ -175,17 +175,17 @@ function createScript(url) {
+  *        the url to wait for
+  * @return {Promise} a promise that is resolved when the source is loaded
+  */
+ function waitForSourceLoad(toolbox, url) {
+   info(`Waiting for source ${url} to be available...`);
+   return new Promise(resolve => {
+     let target = toolbox.target;
+ 
+-    function sourceHandler(_, sourceEvent) {
++    function sourceHandler(sourceEvent) {
+       if (sourceEvent && sourceEvent.source && sourceEvent.source.url === url) {
+         resolve();
+         target.off("source-updated", sourceHandler);
+       }
+     }
+ 
+     target.on("source-updated", sourceHandler);
+   });
+diff --git a/devtools/client/framework/toolbox-hosts.js b/devtools/client/framework/toolbox-hosts.js
+--- a/devtools/client/framework/toolbox-hosts.js
++++ b/devtools/client/framework/toolbox-hosts.js
+@@ -1,17 +1,17 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ const {DOMHelpers} = require("resource://devtools/client/shared/DOMHelpers.jsm");
+ 
+ loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+ loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
+ 
+diff --git a/devtools/client/framework/toolbox-options.js b/devtools/client/framework/toolbox-options.js
+--- a/devtools/client/framework/toolbox-options.js
++++ b/devtools/client/framework/toolbox-options.js
+@@ -65,17 +65,17 @@ function OptionsPanel(iframeWindow, tool
+   this._themeRegistered = this._themeRegistered.bind(this);
+   this._themeUnregistered = this._themeUnregistered.bind(this);
+   this._disableJSClicked = this._disableJSClicked.bind(this);
+ 
+   this.disableJSNode = this.panelDoc.getElementById("devtools-disable-javascript");
+ 
+   this._addListeners();
+ 
+-  const EventEmitter = require("devtools/shared/old-event-emitter");
++  const EventEmitter = require("devtools/shared/event-emitter");
+   EventEmitter.decorate(this);
+ }
+ 
+ OptionsPanel.prototype = {
+ 
+   get target() {
+     return this.toolbox.target;
+   },
+@@ -121,21 +121,21 @@ OptionsPanel.prototype = {
+       cbx.checked = cacheDisabled;
+     } else if (prefName === "devtools.theme") {
+       this.updateCurrentTheme();
+     } else if (prefName === "devtools.source-map.client-service.enabled") {
+       this.updateSourceMapPref();
+     }
+   },
+ 
+-  _themeRegistered: function(event, themeId) {
++  _themeRegistered: function(themeId) {
+     this.setupThemeList();
+   },
+ 
+-  _themeUnregistered: function(event, theme) {
++  _themeUnregistered: function(theme) {
+     let themeBox = this.panelDoc.getElementById("devtools-theme-box");
+     let themeInput = themeBox.querySelector(`[value=${theme.id}]`);
+ 
+     if (themeInput) {
+       themeInput.parentNode.remove();
+     }
+   },
+ 
+diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js
+--- a/devtools/client/framework/toolbox.js
++++ b/devtools/client/framework/toolbox.js
+@@ -16,17 +16,17 @@ const CURRENT_THEME_SCALAR = "devtools.c
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ 
+ var {Ci, Cc} = require("chrome");
+ var promise = require("promise");
+ var defer = require("devtools/shared/defer");
+ var Services = require("Services");
+ var ChromeUtils = require("ChromeUtils");
+ var {gDevTools} = require("devtools/client/framework/devtools");
+-var EventEmitter = require("devtools/shared/old-event-emitter");
++var EventEmitter = require("devtools/shared/event-emitter");
+ var Telemetry = require("devtools/client/shared/telemetry");
+ var { attachThread, detachThread } = require("./attach-thread");
+ var Menu = require("devtools/client/framework/menu");
+ var MenuItem = require("devtools/client/framework/menu-item");
+ var { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
+ const { KeyCodes } = require("devtools/client/shared/keycodes");
+ var Startup = Cc["@mozilla.org/devtools/startup-clh;1"].getService(Ci.nsISupports)
+   .wrappedJSObject;
+@@ -303,17 +303,17 @@ Toolbox.prototype = {
+    *          A promise that resolves once the panel is ready.
+    */
+   getPanelWhenReady: function(id) {
+     let deferred = defer();
+     let panel = this.getPanel(id);
+     if (panel) {
+       deferred.resolve(panel);
+     } else {
+-      this.on(id + "-ready", (e, initializedPanel) => {
++      this.on(id + "-ready", initializedPanel => {
+         deferred.resolve(initializedPanel);
+       });
+     }
+ 
+     return deferred.promise;
+   },
+ 
+   /**
+@@ -2165,17 +2165,17 @@ Toolbox.prototype = {
+       // it can be either an addon or browser toolbox actor
+       return promise.resolve();
+     }
+     let packet = {
+       to: this._target.form.actor,
+       type: "listFrames"
+     };
+     return this._target.client.request(packet, resp => {
+-      this._updateFrames(null, { frames: resp.frames });
++      this._updateFrames({ frames: resp.frames });
+     });
+   },
+ 
+   /**
+    * Show a drop down menu that allows the user to switch frames.
+    */
+   showFramesMenu: async function(event) {
+     let menu = new Menu();
+@@ -2290,17 +2290,17 @@ Toolbox.prototype = {
+    * frames {Array}: list of frames. Every frame can have:
+    *                 id {Number}: frame ID
+    *                 url {String}: frame URL
+    *                 title {String}: frame title
+    *                 destroy {Boolean}: Set to true if destroyed
+    *                 parentID {Number}: ID of the parent frame (not set
+    *                                    for top level window)
+    */
+-  _updateFrames: function(event, data) {
++  _updateFrames: function(data) {
+     if (!Services.prefs.getBoolPref("devtools.command-button-frames.enabled")) {
+       return;
+     }
+ 
+     // We may receive this event before the toolbox is ready.
+     if (!this.isReady) {
+       return;
+     }
+@@ -2484,22 +2484,20 @@ Toolbox.prototype = {
+       if (key) {
+         key.remove();
+       }
+     }
+   },
+ 
+   /**
+    * Handler for the tool-registered event.
+-   * @param  {string} event
+-   *         Name of the event ("tool-registered")
+    * @param  {string} toolId
+    *         Id of the tool that was registered
+    */
+-  _toolRegistered: function(event, toolId) {
++  _toolRegistered: function(toolId) {
+     // Tools can either be in the global devtools, or added to this specific toolbox
+     // as an additional tool.
+     let definition = gDevTools.getToolDefinition(toolId);
+     let isAdditionalTool = false;
+     if (!definition) {
+       definition = this.additionalToolDefinitions.get(toolId);
+       isAdditionalTool = true;
+     }
+@@ -2516,22 +2514,20 @@ Toolbox.prototype = {
+       // Emit the event so tools can listen to it from the toolbox level
+       // instead of gDevTools.
+       this.emit("tool-registered", toolId);
+     }
+   },
+ 
+   /**
+    * Handler for the tool-unregistered event.
+-   * @param  {string} event
+-   *         Name of the event ("tool-unregistered")
+    * @param  {string} toolId
+    *         id of the tool that was unregistered
+    */
+-  _toolUnregistered: function(event, toolId) {
++  _toolUnregistered: function(toolId) {
+     this.unloadTool(toolId);
+     // Emit the event so tools can listen to it from the toolbox level
+     // instead of gDevTools
+     this.emit("tool-unregistered", toolId);
+   },
+ 
+   /**
+    * Initialize the inspector/walker/selection/highlighter fronts.
+@@ -2554,24 +2550,24 @@ Toolbox.prototype = {
+           let autohide = !flags.testing;
+           this._highlighter = await this._inspector.getHighlighter(autohide);
+         }
+       }.bind(this))();
+     }
+     return this._initInspector;
+   },
+ 
+-  _onNewSelectedNodeFront: function(evt) {
++  _onNewSelectedNodeFront: function() {
+     // Emit a "selection-changed" event when the toolbox.selection has been set
+     // to a new node (or cleared). Currently used in the WebExtensions APIs (to
+     // provide the `devtools.panels.elements.onSelectionChanged` event).
+     this.emit("selection-changed");
+   },
+ 
+-  _onInspectObject: function(evt, packet) {
++  _onInspectObject: function(packet) {
+     this.inspectObjectActor(packet.objectActor, packet.inspectFromAnnotation);
+   },
+ 
+   inspectObjectActor: async function(objectActor, inspectFromAnnotation) {
+     if (objectActor.preview &&
+         objectActor.preview.nodeType === domNodeConstants.ELEMENT_NODE) {
+       // Open the inspector and select the DOM Element.
+       await this.loadTool("inspector");
+diff --git a/devtools/client/shared/test/shared-head.js b/devtools/client/shared/test/shared-head.js
+--- a/devtools/client/shared/test/shared-head.js
++++ b/devtools/client/shared/test/shared-head.js
+@@ -258,17 +258,20 @@ function waitForNEvents(target, eventNam
+ 
+   for (let [add, remove] of [
+     ["on", "off"],
+     ["addEventListener", "removeEventListener"],
+     ["addListener", "removeListener"],
+   ]) {
+     if ((add in target) && (remove in target)) {
+       target[add](eventName, function onEvent(...aArgs) {
+-        info("Got event: '" + eventName + "' on " + target + ".");
++        if (typeof info === "function") {
++          info("Got event: '" + eventName + "' on " + target + ".");
++        }
++
+         if (++count == numTimes) {
+           target[remove](eventName, onEvent, useCapture);
+           deferred.resolve.apply(deferred, aArgs);
+         }
+       }, useCapture);
+       break;
+     }
+   }

+ 272 - 0
frg/work-js/mozilla-release/patches/1382581-2-61a1.patch

@@ -0,0 +1,272 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047537 -3600
+# Node ID 2d11a39440388b95eb59a28795b37a22c44e49ed
+# Parent  f1190deb6d09b62302f0120db3842fcd7ab32fcb
+Bug 1382581 - Adapt extension code to the EventEmitter change in devtools/client/framework; r=rpl.
+
+MozReview-Commit-ID: 3I7kmXiPOqd
+
+diff --git a/browser/components/extensions/ext-devtools-network.js b/browser/components/extensions/ext-devtools-network.js
+--- a/browser/components/extensions/ext-devtools-network.js
++++ b/browser/components/extensions/ext-devtools-network.js
+@@ -10,17 +10,17 @@ var {
+ } = ExtensionCommon;
+ 
+ this.devtools_network = class extends ExtensionAPI {
+   getAPI(context) {
+     return {
+       devtools: {
+         network: {
+           onNavigated: new EventManager(context, "devtools.onNavigated", fire => {
+-            let listener = (event, data) => {
++            let listener = data => {
+               fire.async(data.url);
+             };
+ 
+             let targetPromise = getDevToolsTargetForContext(context);
+             targetPromise.then(target => {
+               target.on("navigate", listener);
+             });
+             return () => {
+diff --git a/browser/components/extensions/ext-devtools-panels.js b/browser/components/extensions/ext-devtools-panels.js
+--- a/browser/components/extensions/ext-devtools-panels.js
++++ b/browser/components/extensions/ext-devtools-panels.js
+@@ -165,17 +165,17 @@ class ParentDevToolsPanel {
+ 
+         this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsPanelShown", {
+           toolboxPanelId: this.id,
+         });
+       }
+     }
+   }
+ 
+-  async onToolboxPanelSelect(what, id) {
++  async onToolboxPanelSelect(id) {
+     if (!this.waitTopLevelContext || !this.panelAdded) {
+       return;
+     }
+ 
+     // Wait that the panel is fully loaded and emit show.
+     await this.waitTopLevelContext;
+ 
+     if (!this.visible && id === this.id) {
+@@ -330,17 +330,17 @@ class DevToolsSelectionObserver extends 
+     if (this.initialized) {
+       this.toolbox.off("selection-changed", this.onSelected);
+     }
+ 
+     this.toolbox = null;
+     this.destroyed = true;
+   }
+ 
+-  onSelected(event) {
++  onSelected() {
+     this.emit("selectionChanged");
+   }
+ }
+ 
+ /**
+  * Represents an addon devtools inspector sidebar in the main process.
+  *
+  * @param {ExtensionChildProxyContext} context
+@@ -395,26 +395,26 @@ class ParentDevToolsInspectorSidebar {
+ 
+     this.toolbox.unregisterInspectorExtensionSidebar(this.id);
+     this.extensionSidebar = null;
+     this._initializeSidebar = null;
+ 
+     this.destroyed = true;
+   }
+ 
+-  onSidebarCreated(evt, sidebar) {
++  onSidebarCreated(sidebar) {
+     this.extensionSidebar = sidebar;
+ 
+     if (typeof this._initializeSidebar === "function") {
+       this._initializeSidebar();
+       this._initializeSidebar = null;
+     }
+   }
+ 
+-  onSidebarSelect(what, id) {
++  onSidebarSelect(id) {
+     if (!this.extensionSidebar) {
+       return;
+     }
+ 
+     if (!this.visible && id === this.id) {
+       // TODO: Wait for top level context if extension page
+       this.visible = true;
+       this.context.parentMessageManager.sendAsyncMessage("Extension:DevToolsInspectorSidebarShown", {
+diff --git a/browser/components/extensions/ext-devtools.js b/browser/components/extensions/ext-devtools.js
+--- a/browser/components/extensions/ext-devtools.js
++++ b/browser/components/extensions/ext-devtools.js
+@@ -230,17 +230,17 @@ class DevToolsPageDefinition {
+ 
+     this.url = url;
+     this.extension = extension;
+ 
+     // Map[TabTarget -> DevToolsPage]
+     this.devtoolsPageForTarget = new Map();
+   }
+ 
+-  onThemeChanged(evt, themeName) {
++  onThemeChanged(themeName) {
+     Services.ppmm.broadcastAsyncMessage("Extension:DevToolsThemeChanged", {themeName});
+   }
+ 
+   buildForToolbox(toolbox) {
+     if (this.devtoolsPageForTarget.has(toolbox.target)) {
+       return Promise.reject(new Error("DevtoolsPage has been already created for this toolbox"));
+     }
+ 
+@@ -297,17 +297,17 @@ let devToolsInitialized = false;
+ initDevTools = function() {
+   if (devToolsInitialized) {
+     return;
+   }
+ 
+   /* eslint-disable mozilla/balanced-listeners */
+   // Create a devtools page context for a new opened toolbox,
+   // based on the registered devtools_page definitions.
+-  DevToolsShim.on("toolbox-created", (evt, toolbox) => {
++  DevToolsShim.on("toolbox-created", toolbox => {
+     if (!toolbox.target.isLocalTab) {
+       // Only local tabs are currently supported (See Bug 1304378 for additional details
+       // related to remote targets support).
+       let msg = `Ignoring DevTools Toolbox for target "${toolbox.target.toString()}": ` +
+                 `"${toolbox.target.name}" ("${toolbox.target.url}"). ` +
+                 "Only local tab are currently supported by the WebExtensions DevTools API.";
+       let scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(Ci.nsIScriptError);
+       scriptError.init(msg, null, null, null, null, Ci.nsIScriptError.warningFlag, "content javascript");
+@@ -318,17 +318,17 @@ initDevTools = function() {
+ 
+     for (let devtoolsPage of devtoolsPageDefinitionMap.values()) {
+       devtoolsPage.buildForToolbox(toolbox);
+     }
+   });
+ 
+   // Destroy a devtools page context for a destroyed toolbox,
+   // based on the registered devtools_page definitions.
+-  DevToolsShim.on("toolbox-destroy", (evt, target) => {
++  DevToolsShim.on("toolbox-destroy", target => {
+     if (!target.isLocalTab) {
+       // Only local tabs are currently supported (See Bug 1304378 for additional details
+       // related to remote targets support).
+       return;
+     }
+ 
+     for (let devtoolsPageDefinition of devtoolsPageDefinitionMap.values()) {
+       devtoolsPageDefinition.shutdownForTarget(target);
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+@@ -88,19 +88,17 @@ add_task(async function test_devtools_in
+ 
+   // Test that inspect($0) switch the developer toolbox to the inspector.
+ 
+   await gDevTools.showToolbox(target, "styleeditor");
+ 
+   info("Toolbox switched back to the styleeditor panel");
+ 
+   const inspectorPanelSelectedPromise = (async () => {
+-    const toolId = await new Promise(resolve => {
+-      toolbox.once("select", (evt, toolId) => resolve(toolId));
+-    });
++    const toolId = await toolbox.once("select");
+ 
+     if (toolId === "inspector") {
+       const selectedNodeName = toolbox.selection.nodeFront &&
+                                toolbox.selection.nodeFront._form.nodeName;
+       is(selectedNodeName, "HTML", "The expected DOM node has been selected in the inspector");
+     } else {
+       throw new Error(`inspector panel expected, ${toolId} has been selected instead`);
+     }
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+@@ -15,20 +15,20 @@ const DEVTOOLS_THEME_PREF = "devtools.th
+  *
+  * - devtools.panels.themeName returns the correct value,
+  *   both from a page and a panel.
+  * - devtools.panels.onThemeChanged fires for theme changes,
+  *   both from a page and a panel.
+  * - devtools.panels.create is able to create a devtools panel.
+  */
+ 
+-async function switchTheme(theme) {
+-  const waitforThemeChanged = new Promise(resolve => gDevTools.once("theme-changed", resolve));
++function switchTheme(theme) {
++  const waitforThemeChanged = gDevTools.once("theme-changed");
+   Preferences.set(DEVTOOLS_THEME_PREF, theme);
+-  await waitforThemeChanged;
++  return waitforThemeChanged;
+ }
+ 
+ async function testThemeSwitching(extension, locations = ["page"]) {
+   for (let newTheme of ["dark", "light"]) {
+     await switchTheme(newTheme);
+     for (let location of locations) {
+       is(await extension.awaitMessage(`devtools_theme_changed_${location}`),
+         newTheme,
+@@ -245,35 +245,31 @@ add_task(async function test_devtools_pa
+   const secondCycleResults = await extension.awaitMessage("devtools_panel_hidden");
+   info("Addon Devtools Panel hidden - second cycle");
+ 
+   is(secondCycleResults.panelCreated, 1, "devtools.panel.create callback has been called once");
+   is(secondCycleResults.panelShown, 2, "panel.onShown listener has been called twice");
+   is(secondCycleResults.panelHidden, 2, "panel.onHidden listener has been called twice");
+ 
+   // Turn off the addon devtools panel using the visibilityswitch.
+-  const waitToolVisibilityOff = new Promise(resolve => {
+-    toolbox.once("tool-unregistered", resolve);
+-  });
++  const waitToolVisibilityOff = toolbox.once("tool-unregistered");
+ 
+   Services.prefs.setBoolPref(`devtools.webext-${panelId}.enabled`, false);
+   gDevTools.emit("tool-unregistered", panelId);
+ 
+   await waitToolVisibilityOff;
+ 
+   ok(toolbox.hasAdditionalTool(panelId),
+      "The tool has not been removed on visibilityswitch set to false");
+ 
+   is(toolbox.visibleAdditionalTools.filter(tool => tool.id == panelId).length, 0,
+      "The tool is not visible on visibilityswitch set to false");
+ 
+   // Turn on the addon devtools panel using the visibilityswitch.
+-  const waitToolVisibilityOn = new Promise(resolve => {
+-    toolbox.once("tool-registered", resolve);
+-  });
++  const waitToolVisibilityOn = toolbox.once("tool-registered");
+ 
+   Services.prefs.setBoolPref(`devtools.webext-${panelId}.enabled`, true);
+   gDevTools.emit("tool-registered", panelId);
+ 
+   await waitToolVisibilityOn;
+ 
+   ok(toolbox.hasAdditionalTool(panelId),
+      "The tool has been added on visibilityswitch set to true");
+diff --git a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+--- a/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
++++ b/devtools/client/aboutdebugging/test/browser_addons_debug_webextension_popup.js
+@@ -92,17 +92,17 @@ add_task(async function testWebExtension
+ 
+     toolbox.selectTool("webconsole")
+       .then(async (console) => {
+         dump(`Clicking the noautohide button\n`);
+         toolbox.doc.getElementById("command-button-noautohide").click();
+         dump(`Clicked the noautohide button\n`);
+ 
+         popupFramePromise = new Promise(resolve => {
+-          let listener = (event, data) => {
++          let listener = data => {
+             if (data.frames.some(({url}) => url && url.endsWith("popup.html"))) {
+               toolbox.target.off("frame-update", listener);
+               resolve();
+             }
+           };
+           toolbox.target.on("frame-update", listener);
+         });
+ 

+ 54 - 0
frg/work-js/mozilla-release/patches/1382581-3-61a1.patch

@@ -0,0 +1,54 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047662 -3600
+# Node ID 072a665b018115ceabfb56dc54d9b277be888f7d
+# Parent  3ebad67adf95db2709387009df9ecb7846fe0684
+Bug 1382581 - Adapt canvasdebugger code to the EventEmitter change in devtools/client/framework; r=sole.
+
+MozReview-Commit-ID: 3DDGWvQ3MLX
+
+diff --git a/devtools/client/canvasdebugger/canvasdebugger.js b/devtools/client/canvasdebugger/canvasdebugger.js
+--- a/devtools/client/canvasdebugger/canvasdebugger.js
++++ b/devtools/client/canvasdebugger/canvasdebugger.js
+@@ -129,37 +129,31 @@ var EventsHandler = {
+    */
+   initialize: function () {
+     // Make sure the backend is prepared to handle <canvas> contexts.
+     // Since actors are created lazily on the first request to them, we need to send an
+     // early request to ensure the CallWatcherActor is running and watching for new window
+     // globals.
+     gFront.setup({ reload: false });
+ 
+-    this._onTabNavigated = this._onTabNavigated.bind(this);
+-    gTarget.on("will-navigate", this._onTabNavigated);
+-    gTarget.on("navigate", this._onTabNavigated);
++    this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
++    gTarget.on("will-navigate", this._onTabWillNavigate);
+   },
+ 
+   /**
+    * Remove events emitted by the current tab target.
+    */
+   destroy: function () {
+-    gTarget.off("will-navigate", this._onTabNavigated);
+-    gTarget.off("navigate", this._onTabNavigated);
++    gTarget.off("will-navigate", this._onTabWillNavigate);
+   },
+ 
+   /**
+    * Called for each location change in the debugged tab.
+    */
+-  _onTabNavigated: function (event) {
+-    if (event != "will-navigate") {
+-      return;
+-    }
+-
++  _onTabWillNavigate: function () {
+     // Reset UI.
+     SnapshotsListView.empty();
+     CallsListView.empty();
+ 
+     $("#record-snapshot").removeAttribute("checked");
+     $("#record-snapshot").removeAttribute("disabled");
+     $("#record-snapshot").hidden = false;
+ 

+ 75 - 0
frg/work-js/mozilla-release/patches/1382581-4-61a1.patch

@@ -0,0 +1,75 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047704 -3600
+# Node ID 4815e9bd7de0389439509b80ffa662c76c53255e
+# Parent  e272a2109eb8feedf426717305a1218b5e3b741d
+Bug 1382581 - Adapt debugger code to the EventEmitter change in devtools/client/framework; r=yulia.
+
+MozReview-Commit-ID: GGbylfsYM6x
+
+diff --git a/devtools/client/debugger/new/debugger.js b/devtools/client/debugger/new/debugger.js
+--- a/devtools/client/debugger/new/debugger.js
++++ b/devtools/client/debugger/new/debugger.js
+@@ -23950,17 +23950,17 @@ function _interopRequireDefault(obj) { r
+ /**
+  * @memberof actions/navigation
+  * @static
+  */
+ /* 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/>. */
+ 
+-function willNavigate(_, event) {
++function willNavigate(event) {
+   return async function ({ dispatch, getState, client, sourceMaps }) {
+     await sourceMaps.clearSourceMaps();
+     (0, _wasm.clearWasmStates)();
+     (0, _editor.clearDocuments)();
+     (0, _parser.clearSymbols)();
+     (0, _parser.clearASTs)();
+     (0, _parser.clearScopes)();
+     (0, _parser.clearSources)();
+diff --git a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-create.js b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-create.js
+--- a/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-create.js
++++ b/devtools/client/debugger/new/test/mochitest/browser_dbg-chrome-create.js
+@@ -17,17 +17,17 @@ const { BrowserToolboxProcess } = Chrome
+   "resource://devtools/client/framework/ToolboxProcess.jsm",
+   {}
+ );
+ let gProcess = undefined;
+ 
+ function initChromeDebugger() {
+   info("Initializing a chrome debugger process.");
+   return new Promise(resolve => {
+-    BrowserToolboxProcess.init(onClose, (event, _process) => {
++    BrowserToolboxProcess.init(onClose, _process => {
+       info("Browser toolbox process started successfully.");
+       resolve(_process);
+     });
+   });
+ }
+ 
+ function onClose() {
+   is(
+diff --git a/devtools/client/debugger/test/mochitest/head.js b/devtools/client/debugger/test/mochitest/head.js
+--- a/devtools/client/debugger/test/mochitest/head.js
++++ b/devtools/client/debugger/test/mochitest/head.js
+@@ -769,17 +769,17 @@ AddonDebugger.prototype = {
+ };
+ 
+ function initChromeDebugger(aOnClose) {
+   info("Initializing a chrome debugger process.");
+ 
+   let deferred = promise.defer();
+ 
+   // Wait for the toolbox process to start...
+-  BrowserToolboxProcess.init(aOnClose, (aEvent, aProcess) => {
++  BrowserToolboxProcess.init(aOnClose, aProcess => {
+     info("Browser toolbox process started successfully.");
+ 
+     prepareDebugger(aProcess);
+     deferred.resolve(aProcess);
+   });
+ 
+   return deferred.promise;
+ }

+ 286 - 0
frg/work-js/mozilla-release/patches/1382581-5-61a1.patch

@@ -0,0 +1,286 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047742 -3600
+# Node ID 3ccfccd21cc7d8dd0e8b7443efc15157497b548d
+# Parent  8243091037874e37bacb90560050f4a28ee6c38d
+Bug 1382581 - Adapt inspector code to the EventEmitter change in devtools/client/framework; r=pbro.
+
+MozReview-Commit-ID: 7BssAmdv2S0
+
+diff --git a/devtools/client/inspector/animation/animation.js b/devtools/client/inspector/animation/animation.js
+--- a/devtools/client/inspector/animation/animation.js
++++ b/devtools/client/inspector/animation/animation.js
+@@ -276,20 +276,20 @@ class AnimationInspector {
+   }
+ 
+   onElementPickerStopped() {
+     this.inspector.store.dispatch(updateElementPickerEnabled(false));
+   }
+ 
+   onSidebarSelect() {
+     this.update();
+-    this.onSidebarResized(null, this.inspector.getSidebarSize());
++    this.onSidebarResized(this.inspector.getSidebarSize());
+   }
+ 
+-  onSidebarResized(type, size) {
++  onSidebarResized(size) {
+     if (!this.isPanelVisible()) {
+       return;
+     }
+ 
+     this.inspector.store.dispatch(updateSidebarSize(size));
+   }
+ 
+   removeAnimationsCurrentTimeListener(listener) {
+diff --git a/devtools/client/inspector/computed/computed.js b/devtools/client/inspector/computed/computed.js
+--- a/devtools/client/inspector/computed/computed.js
++++ b/devtools/client/inspector/computed/computed.js
+@@ -1412,17 +1412,17 @@ function ComputedViewTool(inspector, win
+     this.inspector.pageStyle);
+ 
+   this.onSelected = this.onSelected.bind(this);
+   this.refresh = this.refresh.bind(this);
+   this.onPanelSelected = this.onPanelSelected.bind(this);
+   this.onMutations = this.onMutations.bind(this);
+   this.onResized = this.onResized.bind(this);
+ 
+-  this.inspector.selection.on("detached-front", this.onSelected);
++  this.inspector.selection.on("detached-front", this.onDetachedFront);
+   this.inspector.selection.on("new-node-front", this.onSelected);
+   this.inspector.selection.on("pseudoclass", this.refresh);
+   this.inspector.sidebar.on("computedview-selected", this.onPanelSelected);
+   this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
+   this.inspector.walker.on("mutations", this.onMutations);
+   this.inspector.walker.on("resize", this.onResized);
+ 
+   this.computedView.selectElement(null);
+@@ -1433,17 +1433,21 @@ function ComputedViewTool(inspector, win
+ ComputedViewTool.prototype = {
+   isSidebarActive: function() {
+     if (!this.computedView) {
+       return false;
+     }
+     return this.inspector.sidebar.getCurrentTabID() == "computedview";
+   },
+ 
+-  onSelected: function(event) {
++  onDetachedFront: function() {
++    this.onSelected(false);
++  },
++
++  onSelected: function(selectElement = true) {
+     // Ignore the event if the view has been destroyed, or if it's inactive.
+     // But only if the current selection isn't null. If it's been set to null,
+     // let the update go through as this is needed to empty the view on
+     // navigation.
+     if (!this.computedView) {
+       return;
+     }
+ 
+@@ -1456,17 +1460,17 @@ ComputedViewTool.prototype = {
+     this.computedView.setPageStyle(this.inspector.pageStyle);
+ 
+     if (!this.inspector.selection.isConnected() ||
+         !this.inspector.selection.isElementNode()) {
+       this.computedView.selectElement(null);
+       return;
+     }
+ 
+-    if (!event || event == "new-node-front") {
++    if (selectElement) {
+       let done = this.inspector.updating("computed-view");
+       this.computedView.selectElement(this.inspector.selection.nodeFront).then(() => {
+         done();
+       });
+     }
+   },
+ 
+   refresh: function() {
+@@ -1506,17 +1510,17 @@ ComputedViewTool.prototype = {
+   },
+ 
+   destroy: function() {
+     this.inspector.walker.off("mutations", this.onMutations);
+     this.inspector.walker.off("resize", this.onResized);
+     this.inspector.sidebar.off("computedview-selected", this.refresh);
+     this.inspector.selection.off("pseudoclass", this.refresh);
+     this.inspector.selection.off("new-node-front", this.onSelected);
+-    this.inspector.selection.off("detached-front", this.onSelected);
++    this.inspector.selection.off("detached-front", this.onDetachedFront);
+     this.inspector.sidebar.off("computedview-selected", this.onPanelSelected);
+     if (this.inspector.pageStyle) {
+       this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
+     }
+ 
+     this.computedView.destroy();
+ 
+     this.computedView = this.document = this.inspector = null;
+diff --git a/devtools/client/inspector/fonts/fonts.js b/devtools/client/inspector/fonts/fonts.js
+--- a/devtools/client/inspector/fonts/fonts.js
++++ b/devtools/client/inspector/fonts/fonts.js
+@@ -148,17 +148,17 @@ class FontInspector {
+   onPreviewFonts(value) {
+     this.store.dispatch(updatePreviewText(value));
+     this.update();
+   }
+ 
+   /**
+    * Handler for the "theme-switched" event.
+    */
+-  onThemeChanged(event, frame) {
++  onThemeChanged(frame) {
+     if (frame === this.document.defaultView) {
+       this.update();
+     }
+   }
+ 
+   async update() {
+     // Stop refreshing if the inspector or store is already destroyed.
+     if (!this.inspector || !this.store) {
+diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js
+--- a/devtools/client/inspector/inspector.js
++++ b/devtools/client/inspector/inspector.js
+@@ -1138,17 +1138,17 @@ Inspector.prototype = {
+            !selection.isAnonymousNode() &&
+            !invalidTagNames.includes(
+             selection.nodeFront.nodeName.toLowerCase());
+   },
+ 
+   /**
+    * When a new node is selected.
+    */
+-  onNewSelection: function(event, value, reason) {
++  onNewSelection: function(value, reason) {
+     if (reason === "selection-destroy") {
+       return;
+     }
+ 
+     // Wait for all the known tools to finish updating and then let the
+     // client know.
+     let selection = this.selection.nodeFront;
+ 
+@@ -1233,17 +1233,17 @@ Inspector.prototype = {
+     this._updateProgress = null;
+   },
+ 
+   /**
+    * When a node is deleted, select its parent node or the defaultNode if no
+    * parent is found (may happen when deleting an iframe inside which the
+    * node was selected).
+    */
+-  onDetached: function(event, parentNode) {
++  onDetached: function(parentNode) {
+     this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
+     this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
+   },
+ 
+   /**
+    * Destroy the inspector.
+    */
+   destroy: function() {
+diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js
+--- a/devtools/client/inspector/markup/markup.js
++++ b/devtools/client/inspector/markup/markup.js
+@@ -176,17 +176,17 @@ MarkupView.prototype = {
+     this.imagePreviewTooltip.startTogglingOnHover(this._elt,
+       this._isImagePreviewTarget);
+   },
+ 
+   _disableImagePreviewTooltip: function() {
+     this.imagePreviewTooltip.stopTogglingOnHover();
+   },
+ 
+-  _onToolboxPickerHover: function(event, nodeFront) {
++  _onToolboxPickerHover: function(nodeFront) {
+     this.showNode(nodeFront).then(() => {
+       this._showContainerAsHovered(nodeFront);
+     }, console.error);
+   },
+ 
+   /**
+    * If the element picker gets canceled, make sure and re-center the view on the
+    * current selected element.
+diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js
+--- a/devtools/client/inspector/rules/rules.js
++++ b/devtools/client/inspector/rules/rules.js
+@@ -1630,17 +1630,17 @@ function RuleViewTool(inspector, window)
+   this.refresh = this.refresh.bind(this);
+   this.onMutations = this.onMutations.bind(this);
+   this.onPanelSelected = this.onPanelSelected.bind(this);
+   this.onResized = this.onResized.bind(this);
+   this.onSelected = this.onSelected.bind(this);
+   this.onViewRefreshed = this.onViewRefreshed.bind(this);
+ 
+   this.view.on("ruleview-refreshed", this.onViewRefreshed);
+-  this.inspector.selection.on("detached-front", this.onSelected);
++  this.inspector.selection.on("detached-front", this.onDetachedFront);
+   this.inspector.selection.on("new-node-front", this.onSelected);
+   this.inspector.selection.on("pseudoclass", this.refresh);
+   this.inspector.target.on("navigate", this.clearUserProperties);
+   this.inspector.ruleViewSideBar.on("ruleview-selected", this.onPanelSelected);
+   this.inspector.sidebar.on("ruleview-selected", this.onPanelSelected);
+   this.inspector.pageStyle.on("stylesheet-updated", this.refresh);
+   this.inspector.walker.on("mutations", this.onMutations);
+   this.inspector.walker.on("resize", this.onResized);
+@@ -1653,17 +1653,21 @@ RuleViewTool.prototype = {
+     if (!this.view) {
+       return false;
+     }
+ 
+     return this.inspector.isSplitRuleViewEnabled ?
+       true : this.inspector.sidebar.getCurrentTabID() == "ruleview";
+   },
+ 
+-  onSelected: function(event) {
++  onDetachedFront: function() {
++    this.onSelected(false);
++  },
++
++  onSelected: function(selectElement = true) {
+     // Ignore the event if the view has been destroyed, or if it's inactive.
+     // But only if the current selection isn't null. If it's been set to null,
+     // let the update go through as this is needed to empty the view on
+     // navigation.
+     if (!this.view) {
+       return;
+     }
+ 
+@@ -1676,17 +1680,17 @@ RuleViewTool.prototype = {
+     this.view.setPageStyle(this.inspector.pageStyle);
+ 
+     if (!this.inspector.selection.isConnected() ||
+         !this.inspector.selection.isElementNode()) {
+       this.view.selectElement(null);
+       return;
+     }
+ 
+-    if (!event || event == "new-node-front") {
++    if (selectElement) {
+       let done = this.inspector.updating("rule-view");
+       this.view.selectElement(this.inspector.selection.nodeFront)
+         .then(done, done);
+     }
+   },
+ 
+   refresh: function() {
+     if (this.isSidebarActive()) {
+@@ -1732,17 +1736,17 @@ RuleViewTool.prototype = {
+    */
+   onResized: function() {
+     this.refresh();
+   },
+ 
+   destroy: function() {
+     this.inspector.walker.off("mutations", this.onMutations);
+     this.inspector.walker.off("resize", this.onResized);
+-    this.inspector.selection.off("detached-front", this.onSelected);
++    this.inspector.selection.off("detached-front", this.onDetachedFront);
+     this.inspector.selection.off("pseudoclass", this.refresh);
+     this.inspector.selection.off("new-node-front", this.onSelected);
+     this.inspector.target.off("navigate", this.clearUserProperties);
+     this.inspector.sidebar.off("ruleview-selected", this.onPanelSelected);
+     if (this.inspector.pageStyle) {
+       this.inspector.pageStyle.off("stylesheet-updated", this.refresh);
+     }
+ 

+ 122 - 0
frg/work-js/mozilla-release/patches/1382581-6-61a1.patch

@@ -0,0 +1,122 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047788 -3600
+# Node ID 79e040625d0d9c2f74e97d3b75980072f7b6bb24
+# Parent  4cbeaeb7467f2db575c09ea1086916995de7c465
+Bug 1382581 - Adapt webaudioeditor code to the EventEmitter change in devtools/client/framework; r=sole.
+
+MozReview-Commit-ID: 1lmzZZSyO7c
+
+diff --git a/devtools/client/webaudioeditor/controller.js b/devtools/client/webaudioeditor/controller.js
+--- a/devtools/client/webaudioeditor/controller.js
++++ b/devtools/client/webaudioeditor/controller.js
+@@ -39,21 +39,20 @@ function shutdownWebAudioEditor() {
+ /**
+  * Functions handling target-related lifetime events.
+  */
+ var WebAudioEditorController = {
+   /**
+    * Listen for events emitted by the current tab target.
+    */
+   async initialize() {
+-    this._onTabNavigated = this._onTabNavigated.bind(this);
++    this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
+     this._onThemeChange = this._onThemeChange.bind(this);
+ 
+-    gTarget.on("will-navigate", this._onTabNavigated);
+-    gTarget.on("navigate", this._onTabNavigated);
++    gTarget.on("will-navigate", this._onTabWillNavigate);
+     gFront.on("start-context", this._onStartContext);
+     gFront.on("create-node", this._onCreateNode);
+     gFront.on("connect-node", this._onConnectNode);
+     gFront.on("connect-param", this._onConnectParam);
+     gFront.on("disconnect-node", this._onDisconnectNode);
+     gFront.on("change-param", this._onChangeParam);
+     gFront.on("destroy-node", this._onDestroyNode);
+ 
+@@ -80,18 +79,17 @@ var WebAudioEditorController = {
+     // globals.
+     gFront.setup({ reload: false });
+   },
+ 
+   /**
+    * Remove events emitted by the current tab target.
+    */
+   destroy: function () {
+-    gTarget.off("will-navigate", this._onTabNavigated);
+-    gTarget.off("navigate", this._onTabNavigated);
++    gTarget.off("will-navigate", this._onTabWillNavigate);
+     gFront.off("start-context", this._onStartContext);
+     gFront.off("create-node", this._onCreateNode);
+     gFront.off("connect-node", this._onConnectNode);
+     gFront.off("connect-param", this._onConnectParam);
+     gFront.off("disconnect-node", this._onDisconnectNode);
+     gFront.off("change-param", this._onChangeParam);
+     gFront.off("destroy-node", this._onDestroyNode);
+     this._prefObserver.off("devtools.theme", this._onThemeChange);
+@@ -137,47 +135,37 @@ var WebAudioEditorController = {
+   _onThemeChange: function () {
+     let newValue = Services.prefs.getCharPref("devtools.theme");
+     window.emit(EVENTS.THEME_CHANGE, newValue);
+   },
+ 
+   /**
+    * Called for each location change in the debugged tab.
+    */
+-  async _onTabNavigated(event, {isFrameSwitching}) {
+-    switch (event) {
+-      case "will-navigate": {
+-        // Clear out current UI.
+-        this.reset();
++  _onTabWillNavigate: function({isFrameSwitching}) {
++    // Clear out current UI.
++    this.reset();
+ 
+-        // When switching to an iframe, ensure displaying the reload button.
+-        // As the document has already been loaded without being hooked.
+-        if (isFrameSwitching) {
+-          $("#reload-notice").hidden = false;
+-          $("#waiting-notice").hidden = true;
+-        } else {
+-          // Otherwise, we are loading a new top level document,
+-          // so we don't need to reload anymore and should receive
+-          // new node events.
+-          $("#reload-notice").hidden = true;
+-          $("#waiting-notice").hidden = false;
+-        }
++    // When switching to an iframe, ensure displaying the reload button.
++    // As the document has already been loaded without being hooked.
++    if (isFrameSwitching) {
++      $("#reload-notice").hidden = false;
++      $("#waiting-notice").hidden = true;
++    } else {
++      // Otherwise, we are loading a new top level document,
++      // so we don't need to reload anymore and should receive
++      // new node events.
++      $("#reload-notice").hidden = true;
++      $("#waiting-notice").hidden = false;
++    }
+ 
+-        // Clear out stored audio nodes
+-        gAudioNodes.reset();
++    // Clear out stored audio nodes
++    gAudioNodes.reset();
+ 
+-        window.emit(EVENTS.UI_RESET);
+-        break;
+-      }
+-      case "navigate": {
+-        // TODO Case of bfcache, needs investigating
+-        // bug 994250
+-        break;
+-      }
+-    }
++    window.emit(EVENTS.UI_RESET);
+   },
+ 
+   /**
+    * Called after the first audio node is created in an audio context,
+    * signaling that the audio context is being used.
+    */
+   _onStartContext: function () {
+     $("#reload-notice").hidden = true;

+ 343 - 0
frg/work-js/mozilla-release/patches/1382581-7-61a1.patch

@@ -0,0 +1,343 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521047827 -3600
+# Node ID 0027f4cb4994178a80ec0a06412896a7d6bcd09d
+# Parent  7d01a999e966e268060fe8f24e39614fb9a449e0
+Bug 1382581 - Adapt webconsole code to the EventEmitter change in devtools/client/framework; r=bgrins.
+
+MozReview-Commit-ID: 4GELwA2Nf4q
+
+diff --git a/devtools/client/webconsole/console-commands.js b/devtools/client/webconsole/console-commands.js
+--- a/devtools/client/webconsole/console-commands.js
++++ b/devtools/client/webconsole/console-commands.js
+@@ -24,22 +24,21 @@ exports.items = [
+       isChecked: function(target) {
+         let toolbox = gDevTools.getToolbox(target);
+         return !!(toolbox && toolbox.splitConsole);
+       },
+       onChange: function(target, changeHandler) {
+         // Register handlers for when a change event should be fired
+         // (which resets the checked state of the button).
+         let toolbox = gDevTools.getToolbox(target);
+-        let callback = changeHandler.bind(null, "changed", { target: target });
+-
+         if (!toolbox) {
+           return;
+         }
+ 
++        let callback = changeHandler.bind(null, { target });
+         toolbox.on("split-console", callback);
+         toolbox.once("destroyed", () => {
+           toolbox.off("split-console", callback);
+         });
+       }
+     },
+     exec: function(args, context) {
+       let target = context.environment.target;
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_styleeditor_link.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_styleeditor_link.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_styleeditor_link.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_location_styleeditor_link.js
+@@ -23,21 +23,17 @@ add_task(async function() {
+ });
+ 
+ async function testViewSource(hud, toolbox, text) {
+   info(`Testing message with text "${text}"`);
+   let messageNode = await waitFor(() => findMessage(hud, text));
+   let frameLinkNode = messageNode.querySelector(".message-location .frame-link");
+   ok(frameLinkNode, "The message does have a location link");
+ 
+-  let onStyleEditorSelected = new Promise((resolve) => {
+-    toolbox.once("styleeditor-selected", (event, panel) => {
+-      resolve(panel);
+-    });
+-  });
++  let onStyleEditorSelected = toolbox.once("styleeditor-selected");
+ 
+   EventUtils.sendMouseEvent({ type: "click" },
+     messageNode.querySelector(".frame-link-filename"));
+ 
+   let panel = await onStyleEditorSelected;
+   ok(true, "The style editor is selected when clicking on the location element");
+ 
+   await onStyleEditorReady(panel);
+diff --git a/devtools/client/webconsole/new-webconsole.js b/devtools/client/webconsole/new-webconsole.js
+--- a/devtools/client/webconsole/new-webconsole.js
++++ b/devtools/client/webconsole/new-webconsole.js
+@@ -310,37 +310,39 @@ NewWebConsoleFrame.prototype = {
+   /**
+    * Handler for the tabNavigated notification.
+    *
+    * @param string event
+    *        Event name.
+    * @param object packet
+    *        Notification packet received from the server.
+    */
+-  handleTabNavigated: async function(event, packet) {
+-    if (event == "will-navigate") {
+-      if (this.persistLog) {
+-        // Add a _type to hit convertCachedPacket.
+-        packet._type = true;
+-        this.newConsoleOutput.dispatchMessageAdd(packet);
+-      } else {
+-        this.jsterm.clearOutput(false);
+-      }
++  handleTabNavigated: async function(packet) {
++    if (packet.url) {
++      this.onLocationChange(packet.url, packet.title);
++    }
++
++    if (!packet.nativeConsoleAPI) {
++      this.logWarningAboutReplacedAPI();
++    }
++
++    // Wait for completion of any async dispatch before notifying that the console
++    // is fully updated after a page reload
++    await this.newConsoleOutput.waitAsyncDispatches();
++    this.emit("reloaded");
++  },
++
++  handleTabWillNavigate: function(packet) {
++    if (this.persistLog) {
++      // Add a _type to hit convertCachedPacket.
++      packet._type = true;
++      this.newConsoleOutput.dispatchMessageAdd(packet);
++    } else {
++      this.jsterm.clearOutput(false);
+     }
+ 
+     if (packet.url) {
+       this.onLocationChange(packet.url, packet.title);
+     }
+-
+-    if (event == "navigate" && !packet.nativeConsoleAPI) {
+-      this.logWarningAboutReplacedAPI();
+-    }
+-
+-    if (event == "navigate") {
+-      // Wait for completion of any async dispatch before notifying that the console
+-      // is fully updated after a page reload
+-      await this.newConsoleOutput.waitAsyncDispatches();
+-      this.emit("reloaded");
+-    }
+-  },
++  }
+ };
+ 
+ exports.NewWebConsoleFrame = NewWebConsoleFrame;
+diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
+--- a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
++++ b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
+@@ -41,17 +41,17 @@ function testViewSource() {
+     let error2Msg = [...error2Rule.matched][0];
+     nodes = [error1Msg.querySelector(".message-location .frame-link"),
+              error2Msg.querySelector(".message-location .frame-link")];
+     ok(nodes[0], ".frame-link node for the first error");
+     ok(nodes[1], ".frame-link node for the second error");
+ 
+     let target = TargetFactory.forTab(gBrowser.selectedTab);
+     let toolbox = gDevTools.getToolbox(target);
+-    toolbox.once("styleeditor-selected", (event, panel) => {
++    toolbox.once("styleeditor-selected", panel => {
+       StyleEditorUI = panel.UI;
+       deferred.resolve(panel);
+     });
+ 
+     EventUtils.sendMouseEvent({ type: "click" }, nodes[0].querySelector(".frame-link-filename"));
+   });
+ 
+   return deferred.promise;
+@@ -60,19 +60,17 @@ function testViewSource() {
+ function onStyleEditorReady(panel) {
+   let deferred = defer();
+ 
+   let win = panel.panelWindow;
+   ok(win, "Style Editor Window is defined");
+   ok(StyleEditorUI, "Style Editor UI is defined");
+ 
+   function fireEvent(toolbox, href, line) {
+-    toolbox.once("styleeditor-selected", function (evt) {
+-      info(evt + " event fired");
+-
++    toolbox.once("styleeditor-selected", function () {
+       checkStyleEditorForSheetAndLine(href, line - 1).then(deferred.resolve);
+     });
+ 
+     EventUtils.sendMouseEvent({ type: "click" }, nodes[1].querySelector(".frame-link-filename"));
+   }
+ 
+   waitForFocus(function () {
+     info("style editor window focused");
+diff --git a/devtools/client/webconsole/webconsole-connection-proxy.js b/devtools/client/webconsole/webconsole-connection-proxy.js
+--- a/devtools/client/webconsole/webconsole-connection-proxy.js
++++ b/devtools/client/webconsole/webconsole-connection-proxy.js
+@@ -30,16 +30,17 @@ function WebConsoleConnectionProxy(webCo
+   this._onLogMessage = this._onLogMessage.bind(this);
+   this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
+   this._onNetworkEvent = this._onNetworkEvent.bind(this);
+   this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
+   this._onFileActivity = this._onFileActivity.bind(this);
+   this._onReflowActivity = this._onReflowActivity.bind(this);
+   this._onServerLogCall = this._onServerLogCall.bind(this);
+   this._onTabNavigated = this._onTabNavigated.bind(this);
++  this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
+   this._onAttachConsole = this._onAttachConsole.bind(this);
+   this._onCachedMessages = this._onCachedMessages.bind(this);
+   this._connectionTimeout = this._connectionTimeout.bind(this);
+   this._onLastPrivateContextExited =
+     this._onLastPrivateContextExited.bind(this);
+ }
+ 
+ WebConsoleConnectionProxy.prototype = {
+@@ -137,17 +138,17 @@ WebConsoleConnectionProxy.prototype = {
+     client.addListener("pageError", this._onPageError);
+     client.addListener("consoleAPICall", this._onConsoleAPICall);
+     client.addListener("fileActivity", this._onFileActivity);
+     client.addListener("reflowActivity", this._onReflowActivity);
+     client.addListener("serverLogCall", this._onServerLogCall);
+     client.addListener("lastPrivateContextExited",
+                        this._onLastPrivateContextExited);
+ 
+-    this.target.on("will-navigate", this._onTabNavigated);
++    this.target.on("will-navigate", this._onTabWillNavigate);
+     this.target.on("navigate", this._onTabNavigated);
+ 
+     this._consoleActor = this.target.form.consoleActor;
+     if (this.target.isTabActor) {
+       let tab = this.target.form;
+       this.webConsoleFrame.onLocationChange(tab.url, tab.title);
+     }
+     this._attachConsole();
+@@ -445,31 +446,43 @@ WebConsoleConnectionProxy.prototype = {
+    */
+   _onLastPrivateContextExited: function(type, packet) {
+     if (this.webConsoleFrame && packet.from == this._consoleActor) {
+       this.webConsoleFrame.jsterm.clearPrivateMessages();
+     }
+   },
+ 
+   /**
+-   * The "will-navigate" and "navigate" event handlers. We redirect any message
+-   * to the UI for displaying.
++   * The "navigate" event handlers. We redirect any message to the UI for displaying.
+    *
+    * @private
+-   * @param string event
+-   *        Event type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onTabNavigated: function(event, packet) {
++  _onTabNavigated: function(packet) {
+     if (!this.webConsoleFrame) {
+       return;
+     }
+ 
+-    this.webConsoleFrame.handleTabNavigated(event, packet);
++    this.webConsoleFrame.handleTabNavigated(packet);
++  },
++
++  /**
++   * The "will-navigate" event handlers. We redirect any message to the UI for displaying.
++   *
++   * @private
++   * @param object packet
++   *        The message received from the server.
++   */
++  _onTabWillNavigate: function(packet) {
++    if (!this.webConsoleFrame) {
++      return;
++    }
++
++    this.webConsoleFrame.handleTabWillNavigate(packet);
+   },
+ 
+   /**
+    * Release an object actor.
+    *
+    * @param string actor
+    *        The actor ID to send the request to.
+    */
+@@ -502,17 +515,17 @@ WebConsoleConnectionProxy.prototype = {
+     this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
+     this.client.removeListener("fileActivity", this._onFileActivity);
+     this.client.removeListener("reflowActivity", this._onReflowActivity);
+     this.client.removeListener("serverLogCall", this._onServerLogCall);
+     this.client.removeListener("lastPrivateContextExited",
+                                this._onLastPrivateContextExited);
+     this.webConsoleClient.off("networkEvent", this._onNetworkEvent);
+     this.webConsoleClient.off("networkEventUpdate", this._onNetworkEventUpdate);
+-    this.target.off("will-navigate", this._onTabNavigated);
++    this.target.off("will-navigate", this._onTabWillNavigate);
+     this.target.off("navigate", this._onTabNavigated);
+ 
+     this.client = null;
+     this.webConsoleClient = null;
+     this.target = null;
+     this.connected = false;
+     this.webConsoleFrame = null;
+     this._disconnecter.resolve(null);
+diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js
+--- a/devtools/client/webconsole/webconsole.js
++++ b/devtools/client/webconsole/webconsole.js
+@@ -1871,40 +1871,49 @@ WebConsoleFrame.prototype = {
+     if (this.owner.onLocationChange) {
+       this.owner.onLocationChange(uri, title);
+     }
+   },
+ 
+   /**
+    * Handler for the tabNavigated notification.
+    *
+-   * @param string event
+-   *        Event name.
+    * @param object packet
+    *        Notification packet received from the server.
+    */
+-  handleTabNavigated: function (event, packet) {
+-    if (event == "will-navigate") {
+-      if (this.persistLog) {
+-        let marker = new Messages.NavigationMarker(packet, Date.now());
+-        this.output.addMessage(marker);
+-      } else {
+-        this.jsterm.clearOutput();
+-      }
+-    }
++  handleTabNavigated: function (packet) {
+     if (packet.url) {
+       this.onLocationChange(packet.url, packet.title);
+     }
+ 
+-    if (event == "navigate" && !packet.nativeConsoleAPI) {
++    if (!packet.nativeConsoleAPI) {
+       this.logWarningAboutReplacedAPI();
+     }
+   },
+ 
+   /**
++   * Handler for the tabNavigated notification.
++   *
++   * @param object packet
++   *        Notification packet received from the server.
++   */
++  handleTabWillNavigate: function (packet) {
++    if (this.persistLog) {
++      let marker = new Messages.NavigationMarker(packet, Date.now());
++      this.output.addMessage(marker);
++    } else {
++      this.jsterm.clearOutput();
++    }
++
++    if (packet.url) {
++      this.onLocationChange(packet.url, packet.title);
++    }
++  },
++
++  /**
+    * Output a message node. This filters a node appropriately, then sends it to
+    * the output, regrouping and pruning output as necessary.
+    *
+    * Note: this call is async - the given message node may not be displayed when
+    * you call this method.
+    *
+    * @param integer category
+    *        The category of the message you want to output. See the CATEGORY_*

+ 130 - 0
frg/work-js/mozilla-release/patches/1382581-8-61a1.patch

@@ -0,0 +1,130 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521223140 -3600
+# Node ID 3214b1a3f2355410e940991a4435aef2cbc38025
+# Parent  7b9a1c4f283b3fb75ca6bcf0c2a1d191af53454b
+Bug 1382581 - Adapt shadereditor code to the EventEmitter change in devtools/client/framework; r=bgrins.
+
+MozReview-Commit-ID: 9Z9uVMuTqKE
+
+diff --git a/devtools/client/shadereditor/shadereditor.js b/devtools/client/shadereditor/shadereditor.js
+--- a/devtools/client/shadereditor/shadereditor.js
++++ b/devtools/client/shadereditor/shadereditor.js
+@@ -86,32 +86,33 @@ function shutdownShaderEditor() {
+  */
+ var EventsHandler = {
+   /**
+    * Listen for events emitted by the current tab target.
+    */
+   initialize: function () {
+     this._onHostChanged = this._onHostChanged.bind(this);
+     this._onTabNavigated = this._onTabNavigated.bind(this);
++    this._onTabWillNavigate = this._onTabWillNavigate.bind(this);
+     this._onProgramLinked = this._onProgramLinked.bind(this);
+     this._onProgramsAdded = this._onProgramsAdded.bind(this);
+     gToolbox.on("host-changed", this._onHostChanged);
+-    gTarget.on("will-navigate", this._onTabNavigated);
++    gTarget.on("will-navigate", this._onTabWillNavigate);
+     gTarget.on("navigate", this._onTabNavigated);
+     gFront.on("program-linked", this._onProgramLinked);
+     this.reloadButton = $("#requests-menu-reload-notice-button");
+     this.reloadButton.addEventListener("command", this._onReloadCommand);
+   },
+ 
+   /**
+    * Remove events emitted by the current tab target.
+    */
+   destroy: function () {
+     gToolbox.off("host-changed", this._onHostChanged);
+-    gTarget.off("will-navigate", this._onTabNavigated);
++    gTarget.off("will-navigate", this._onTabWillNavigate);
+     gTarget.off("navigate", this._onTabNavigated);
+     gFront.off("program-linked", this._onProgramLinked);
+     this.reloadButton.removeEventListener("command", this._onReloadCommand);
+   },
+ 
+   /**
+    * Handles a command event on reload button
+    */
+@@ -123,53 +124,47 @@ var EventsHandler = {
+    * Handles a host change event on the parent toolbox.
+    */
+   _onHostChanged: function () {
+     if (gToolbox.hostType == "side") {
+       $("#shaders-pane").removeAttribute("height");
+     }
+   },
+ 
++  _onTabWillNavigate: function({isFrameSwitching}) {
++    // Make sure the backend is prepared to handle WebGL contexts.
++    if (!isFrameSwitching) {
++      gFront.setup({ reload: false });
++    }
++
++    // Reset UI.
++    ShadersListView.empty();
++    // When switching to an iframe, ensure displaying the reload button.
++    // As the document has already been loaded without being hooked.
++    if (isFrameSwitching) {
++      $("#reload-notice").hidden = false;
++      $("#waiting-notice").hidden = true;
++    } else {
++      $("#reload-notice").hidden = true;
++      $("#waiting-notice").hidden = false;
++    }
++
++    $("#content").hidden = true;
++    window.emit(EVENTS.UI_RESET);
++  },
++
+   /**
+    * Called for each location change in the debugged tab.
+    */
+-  _onTabNavigated: function (event, {isFrameSwitching}) {
+-    switch (event) {
+-      case "will-navigate": {
+-        // Make sure the backend is prepared to handle WebGL contexts.
+-        if (!isFrameSwitching) {
+-          gFront.setup({ reload: false });
+-        }
+-
+-        // Reset UI.
+-        ShadersListView.empty();
+-        // When switching to an iframe, ensure displaying the reload button.
+-        // As the document has already been loaded without being hooked.
+-        if (isFrameSwitching) {
+-          $("#reload-notice").hidden = false;
+-          $("#waiting-notice").hidden = true;
+-        } else {
+-          $("#reload-notice").hidden = true;
+-          $("#waiting-notice").hidden = false;
+-        }
+-
+-        $("#content").hidden = true;
+-        window.emit(EVENTS.UI_RESET);
+-
+-        break;
+-      }
+-      case "navigate": {
+-        // Manually retrieve the list of program actors known to the server,
+-        // because the backend won't emit "program-linked" notifications
+-        // in the case of a bfcache navigation (since no new programs are
+-        // actually linked).
+-        gFront.getPrograms().then(this._onProgramsAdded);
+-        break;
+-      }
+-    }
++  _onTabNavigated: function () {
++    // Manually retrieve the list of program actors known to the server,
++    // because the backend won't emit "program-linked" notifications
++    // in the case of a bfcache navigation (since no new programs are
++    // actually linked).
++    gFront.getPrograms().then(this._onProgramsAdded);
+   },
+ 
+   /**
+    * Called every time a program was linked in the debugged tab.
+    */
+   _onProgramLinked: function (programActor) {
+     this._addProgram(programActor);
+     window.emit(EVENTS.NEW_PROGRAM);

+ 72 - 0
frg/work-js/mozilla-release/patches/1382585-61a1.patch

@@ -0,0 +1,72 @@
+# HG changeset patch
+# User yulia <ystartsev@mozilla.com>
+# Date 1521125462 -3600
+# Node ID 0b77c1e6a87e0763eb0ef58fd1074efeb4e0f58d
+# Parent  a6f76b5e18dc57229dd2cb319c4e72160c21763c
+Bug 1382585 - update inspector/markup to new event emitter. r=pbro
+
+MozReview-Commit-ID: 3knSthKBL1J
+
+diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js
+--- a/devtools/client/inspector/markup/markup.js
++++ b/devtools/client/inspector/markup/markup.js
+@@ -4,17 +4,17 @@
+ 
+ "use strict";
+ 
+ const promise = require("promise");
+ const Services = require("Services");
+ const {Task} = require("devtools/shared/task");
+ const nodeConstants = require("devtools/shared/dom-node-constants");
+ const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const {PluralForm} = require("devtools/shared/plural-form");
+ const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ const {scrollIntoViewIfNeeded} = require("devtools/client/shared/scroll");
+ const {UndoStack} = require("devtools/client/shared/undo");
+ const {HTMLTooltip} = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
+ const {PrefObserver} = require("devtools/client/shared/prefs");
+@@ -1436,17 +1436,17 @@ MarkupView.prototype = {
+         return;
+       }
+       // Load load and create HTML Editor as it is rarely used and fetch complex deps
+       if (!this.htmlEditor) {
+         let HTMLEditor = require("devtools/client/inspector/markup/views/html-editor");
+         this.htmlEditor = new HTMLEditor(this.doc);
+       }
+       this.htmlEditor.show(container.tagLine, oldValue);
+-      this.htmlEditor.once("popuphidden", (e, commit, value) => {
++      this.htmlEditor.once("popuphidden", (commit, value) => {
+         // Need to focus the <html> element instead of the frame / window
+         // in order to give keyboard focus back to doc (from editor).
+         this.doc.documentElement.focus();
+ 
+         if (commit) {
+           this.updateNodeOuterHTML(node, value, oldValue);
+         }
+       });
+diff --git a/devtools/client/inspector/markup/views/html-editor.js b/devtools/client/inspector/markup/views/html-editor.js
+--- a/devtools/client/inspector/markup/views/html-editor.js
++++ b/devtools/client/inspector/markup/views/html-editor.js
+@@ -1,17 +1,17 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const Editor = require("devtools/client/sourceeditor/editor");
+ const Services = require("Services");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ 
+ /**
+  * A wrapper around the Editor component, that allows editing of HTML.
+  *
+  * The main functionality this provides around the Editor is the ability
+  * to show/hide/position an editor inplace. It only appends once to the
+  * body, and uses CSS to position the editor.  The reason it is done this
+  * way is that the editor is loaded in an iframe, and calling appendChild

+ 73 - 0
frg/work-js/mozilla-release/patches/1382599-61a1.patch

@@ -0,0 +1,73 @@
+# HG changeset patch
+# User yulia <ystartsev@mozilla.com>
+# Date 1521126633 -3600
+# Node ID 4a55a695dc756e5a91cb60acc716b0a82049ec14
+# Parent  e859310bde9fc9cb41bf427a78bb29d9171e2d92
+Bug 1382599 - update inspector/rules to the new event emitter. r=pbro
+
+MozReview-Commit-ID: EKQDuoyCLEU
+
+diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js
+--- a/devtools/client/inspector/rules/rules.js
++++ b/devtools/client/inspector/rules/rules.js
+@@ -27,17 +27,17 @@ const {
+   VIEW_NODE_SHAPE_POINT_TYPE,
+   VIEW_NODE_VARIABLE_TYPE,
+   VIEW_NODE_FONT_TYPE,
+ } = require("devtools/client/inspector/shared/node-types");
+ const StyleInspectorMenu = require("devtools/client/inspector/shared/style-inspector-menu");
+ const TooltipsOverlay = require("devtools/client/inspector/shared/tooltips-overlay");
+ const {createChild, promiseWarn} = require("devtools/client/inspector/shared/utils");
+ const {debounce} = require("devtools/shared/debounce");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ const clipboardHelper = require("devtools/shared/platform/clipboard");
+ const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
+ const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
+ const FILTER_CHANGED_TIMEOUT = 150;
+diff --git a/devtools/client/inspector/rules/views/class-list-previewer.js b/devtools/client/inspector/rules/views/class-list-previewer.js
+--- a/devtools/client/inspector/rules/views/class-list-previewer.js
++++ b/devtools/client/inspector/rules/views/class-list-previewer.js
+@@ -1,15 +1,15 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ 
+ const L10N = new LocalizationHelper("devtools/client/locales/inspector.properties");
+ 
+ // This serves as a local cache for the classes applied to each of the node we care about
+ // here.
+ // The map is indexed by NodeFront. Any time a new node is selected in the inspector, an
+ // entry is added here, indexed by the corresponding NodeFront.
+diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js
+--- a/devtools/client/inspector/rules/views/rule-editor.js
++++ b/devtools/client/inspector/rules/views/rule-editor.js
+@@ -23,17 +23,17 @@ const {
+   parseNamedDeclarations,
+   parsePseudoClassesAndAttributes,
+   SELECTOR_ATTRIBUTE,
+   SELECTOR_ELEMENT,
+   SELECTOR_PSEUDO_CLASS
+ } = require("devtools/shared/css/parsing-utils");
+ const promise = require("promise");
+ const Services = require("Services");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {Task} = require("devtools/shared/task");
+ const {Tools} = require("devtools/client/definitions");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ const CssLogic = require("devtools/shared/inspector/css-logic");
+ 
+ const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);

+ 419 - 0
frg/work-js/mozilla-release/patches/1382600-61a1.patch

@@ -0,0 +1,419 @@
+# HG changeset patch
+# User yulia <ystartsev@mozilla.com>
+# Date 1521130089 -3600
+# Node ID 189ee552e4a4a06418a876129c0552204994059a
+# Parent  977305f11d1f3eac0eb8d4f70ddc9635f27e007a
+Bug 1382600 - update inspector/shared to new event emitter. r=nchevobbe,pbro
+
+MozReview-Commit-ID: LvRT0b6rpu3
+
+diff --git a/devtools/client/inspector/flexbox/flexbox.js b/devtools/client/inspector/flexbox/flexbox.js
+--- a/devtools/client/inspector/flexbox/flexbox.js
++++ b/devtools/client/inspector/flexbox/flexbox.js
+@@ -15,17 +15,18 @@ const {
+ class FlexboxInspector {
+   constructor(inspector, window) {
+     this.document = window.document;
+     this.highlighters = inspector.highlighters;
+     this.inspector = inspector;
+     this.store = inspector.store;
+     this.walker = inspector.walker;
+ 
+-    this.onHighlighterChange = this.onHighlighterChange.bind(this);
++    this.onHighlighterShown = this.onHighlighterShown.bind(this);
++    this.onHighlighterHidden = this.onHighlighterHidden.bind(this);
+     this.onReflow = throttle(this.onReflow, 500, this);
+     this.onSidebarSelect = this.onSidebarSelect.bind(this);
+     this.onToggleFlexboxHighlighter = this.onToggleFlexboxHighlighter.bind(this);
+     this.onUpdatePanel = this.onUpdatePanel.bind(this);
+ 
+     this.init();
+   }
+ 
+@@ -38,26 +39,26 @@ class FlexboxInspector {
+         "getCurrentFlexbox");
+       this.layoutInspector = await this.walker.getLayoutInspector();
+     } catch (e) {
+       // These calls might fail if called asynchrously after the toolbox is finished
+       // closing.
+       return;
+     }
+ 
+-    this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterChange);
+-    this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterChange);
++    this.highlighters.on("flexbox-highlighter-hidden", this.onHighlighterHidden);
++    this.highlighters.on("flexbox-highlighter-shown", this.onHighlighterShown);
+     this.inspector.sidebar.on("select", this.onSidebarSelect);
+ 
+     this.onSidebarSelect();
+   }
+ 
+   destroy() {
+-    this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterChange);
+-    this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterChange);
++    this.highlighters.off("flexbox-highlighter-hidden", this.onHighlighterHidden);
++    this.highlighters.off("flexbox-highlighter-shown", this.onHighlighterShown);
+     this.inspector.selection.off("new-node-front", this.onUpdatePanel);
+     this.inspector.sidebar.off("select", this.onSidebarSelect);
+     this.inspector.off("new-root", this.onUpdatePanel);
+ 
+     this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
+ 
+     this.document = null;
+     this.hasGetCurrentFlexbox = null;
+@@ -77,31 +78,57 @@ class FlexboxInspector {
+   /**
+    * Returns true if the layout panel is visible, and false otherwise.
+    */
+   isPanelVisible() {
+     return this.inspector && this.inspector.toolbox && this.inspector.sidebar &&
+            this.inspector.toolbox.currentToolId === "inspector" &&
+            this.inspector.sidebar.getCurrentTabID() === "layoutview";
+   }
++  /**
++   * Handler for "flexbox-highlighter-shown" events emitted from the
++   * HighlightersOverlay. Passes nodefront and highlight status to
++   * handleHighlighterChange.  Required since on and off events need
++   * the same reference object.
++   *
++   * @param  {NodeFront} nodeFront
++   *         The NodeFront of the flex container element for which the flexbox
++   *         highlighter is shown for.
++   */
++  onHighlighterShown(nodeFront) {
++    return this.onHighlighterChange(true, nodeFront);
++  }
++
++  /**
++   * Handler for "flexbox-highlighter-hidden" events emitted from the
++   * HighlightersOverlay. Passes nodefront and highlight status to
++   * handleHighlighterChange.  Required since on and off events need
++   * the same reference object.
++   *
++   * @param  {NodeFront} nodeFront
++   *         The NodeFront of the flex container element for which the flexbox
++   *         highlighter is shown for.
++   */
++  onHighlighterHidden(nodeFront) {
++    return this.onHighlighterChange(false, nodeFront);
++  }
+ 
+   /**
+    * Handler for "flexbox-highlighter-shown" and "flexbox-highlighter-hidden" events
+    * emitted from the HighlightersOverlay. Updates the flex container highlighted state
+    * only if the provided NodeFront is the current selected flex container.
+    *
+-   * @param  {Event} event
+-   *         Event that was triggered.
++   * @param  {Boolean} highlighted
++   *          If the change is to highlight or hide the overlay.
+    * @param  {NodeFront} nodeFront
+    *         The NodeFront of the flex container element for which the flexbox
+    *         highlighter is shown for.
+    */
+-  onHighlighterChange(event, nodeFront) {
++  onHighlighterChange(highlighted, nodeFront) {
+     const { flexbox } = this.store.getState();
+-    const highlighted = event === "flexbox-highlighter-shown";
+ 
+     if (flexbox.nodeFront === nodeFront && flexbox.highlighted !== highlighted) {
+       this.store.dispatch(updateFlexboxHighlighted(highlighted));
+     }
+   }
+ 
+   /**
+    * Handler for the "reflow" event fired by the inspector's reflow tracker. On reflows,
+diff --git a/devtools/client/inspector/grids/grid-inspector.js b/devtools/client/inspector/grids/grid-inspector.js
+--- a/devtools/client/inspector/grids/grid-inspector.js
++++ b/devtools/client/inspector/grids/grid-inspector.js
+@@ -52,17 +52,18 @@ class GridInspector {
+     this.inspector = inspector;
+     this.store = inspector.store;
+     this.telemetry = inspector.telemetry;
+     this.walker = this.inspector.walker;
+ 
+     this.getSwatchColorPickerTooltip = this.getSwatchColorPickerTooltip.bind(this);
+     this.updateGridPanel = this.updateGridPanel.bind(this);
+ 
+-    this.onHighlighterChange = this.onHighlighterChange.bind(this);
++    this.onHighlighterShown = this.onHighlighterShown.bind(this);
++    this.onHighlighterHidden = this.onHighlighterHidden.bind(this);
+     this.onNavigate = this.onNavigate.bind(this);
+     this.onReflow = throttle(this.onReflow, 500, this);
+     this.onSetGridOverlayColor = this.onSetGridOverlayColor.bind(this);
+     this.onShowGridAreaHighlight = this.onShowGridAreaHighlight.bind(this);
+     this.onShowGridCellHighlight = this.onShowGridCellHighlight.bind(this);
+     this.onShowGridLineNamesHighlight = this.onShowGridLineNamesHighlight.bind(this);
+     this.onSidebarSelect = this.onSidebarSelect.bind(this);
+     this.onToggleGridHighlighter = this.onToggleGridHighlighter.bind(this);
+@@ -90,31 +91,31 @@ class GridInspector {
+     this.swatchColorPickerTooltip = new SwatchColorPickerTooltip(
+       this.inspector.toolbox.doc,
+       this.inspector,
+       {
+         supportsCssColor4ColorFunction: () => false
+       }
+     );
+ 
+-    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterChange);
+-    this.highlighters.on("grid-highlighter-shown", this.onHighlighterChange);
++    this.highlighters.on("grid-highlighter-hidden", this.onHighlighterHidden);
++    this.highlighters.on("grid-highlighter-shown", this.onHighlighterShown);
+     this.inspector.sidebar.on("select", this.onSidebarSelect);
+     this.inspector.on("new-root", this.onNavigate);
+ 
+     this.onSidebarSelect();
+   }
+ 
+   /**
+    * Destruction function called when the inspector is destroyed. Removes event listeners
+    * and cleans up references.
+    */
+   destroy() {
+-    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterChange);
+-    this.highlighters.off("grid-highlighter-shown", this.onHighlighterChange);
++    this.highlighters.off("grid-highlighter-hidden", this.onHighlighterHidden);
++    this.highlighters.off("grid-highlighter-shown", this.onHighlighterShown);
+     this.inspector.sidebar.off("select", this.onSidebarSelect);
+     this.inspector.off("new-root", this.onNavigate);
+ 
+     this.inspector.reflowTracker.untrackReflows(this, this.onReflow);
+ 
+     // The color picker may not be ready as `init` function is async,
+     // and we do not wait for its completion before calling destroy in tests
+     if (this.swatchColorPickerTooltip) {
+@@ -350,31 +351,59 @@ class GridInspector {
+         nodeFront,
+         writingMode: grid.writingMode,
+       });
+     }
+ 
+     this.store.dispatch(updateGrids(grids));
+     this.inspector.emit("grid-panel-updated");
+   }
++  /**
++   * Handler for "grid-highlighter-shown" events emitted from the
++   * HighlightersOverlay. Passes nodefront and event name to handleHighlighterChange.
++   * Required since on and off events need the same reference object.
++   *
++   * @param  {NodeFront} nodeFront
++   *         The NodeFront of the flex container element for which the flexbox
++   *         highlighter is shown for.
++   * @param  {Object} options
++   *         The highlighter options used for the highlighter being shown/hidden.
++   */
++  onHighlighterShown(nodeFront, options) {
++    return this.onHighlighterChange(true, nodeFront, options);
++  }
++
++  /**
++   * Handler for "grid-highlighter-hidden" events emitted from the
++   * HighlightersOverlay. Passes nodefront and event name to handleHighlighterChange.
++   * Required since on and off events need the same reference object.
++   *
++   * @param  {NodeFront} nodeFront
++   *         The NodeFront of the flex container element for which the flexbox
++   *         highlighter is shown for.
++   * @param  {Object} options
++   *         The highlighter options used for the highlighter being shown/hidden.
++   */
++  onHighlighterHidden(nodeFront, options) {
++    return this.onHighlighterChange(false, nodeFront, options);
++  }
+ 
+   /**
+    * Handler for "grid-highlighter-shown" and "grid-highlighter-hidden" events emitted
+    * from the HighlightersOverlay. Updates the NodeFront's grid highlighted state.
+    *
+-   * @param  {Event} event
+-   *         Event that was triggered.
++   * @param  {Boolean} highlighted
++   *         If the grid should be updated to highlight or hide.
+    * @param  {NodeFront} nodeFront
+    *         The NodeFront of the grid container element for which the grid highlighter
+    *         is shown for.
+    * @param  {Object} options
+    *         The highlighter options used for the highlighter being shown/hidden.
+    */
+-  onHighlighterChange(event, nodeFront, options = {}) {
+-    let highlighted = event === "grid-highlighter-shown";
++  onHighlighterChange(highlighted, nodeFront, options = {}) {
+     let { color } = options;
+ 
+     // Only tell the store that the highlighter changed if it did change.
+     // If we're still highlighting the same node, with the same color, no need to force
+     // a refresh.
+     if (this.lastHighlighterState !== highlighted ||
+         this.lastHighlighterNode !== nodeFront) {
+       this.store.dispatch(updateGridHighlighted(nodeFront, highlighted));
+diff --git a/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js b/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js
+--- a/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js
++++ b/devtools/client/inspector/grids/test/browser_grids_color-in-rules-grid-toggle.js
+@@ -57,17 +57,17 @@ add_task(function* () {
+ 
+   yield selectNode("#grid", inspector);
+ 
+   let container = getRuleViewProperty(ruleView, "#grid", "display").valueSpan;
+   let gridToggle = container.querySelector(".ruleview-grid");
+ 
+   info("Toggling ON the CSS grid highlighter from the rule-view.");
+   let onHighlighterShown = highlighters.once("grid-highlighter-shown",
+-    (event, nodeFront, options) => {
++    (nodeFront, options) => {
+       info("Checking the grid highlighter display settings.");
+       let {
+         color,
+         showGridAreasOverlay,
+         showGridLineNumbers,
+         showInfiniteLines,
+       } = options;
+ 
+diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js
+--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js
++++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-area.js
+@@ -50,17 +50,17 @@ add_task(function* () {
+   yield onCheckboxChange;
+   yield onHighlighterShown;
+   let elements = yield onGridOutlineRendered;
+ 
+   let gridCellA = elements[0];
+ 
+   info("Hovering over grid cell A in the grid outline.");
+   let onCellAHighlight = highlighters.once("grid-highlighter-shown",
+-    (event, nodeFront, options) => {
++    (nodeFront, options) => {
+       info("Checking the grid highlighter options for the show grid area" +
+       "and cell parameters.");
+       const { showGridCell, showGridArea } = options;
+       const { gridFragmentIndex, rowNumber, columnNumber } = showGridCell;
+ 
+       is(gridFragmentIndex, 0, "Should be the first grid fragment index.");
+       is(rowNumber, 1, "Should be the first grid row.");
+       is(columnNumber, 1, "Should be the first grid column.");
+diff --git a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js
+--- a/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js
++++ b/devtools/client/inspector/grids/test/browser_grids_grid-outline-highlight-cell.js
+@@ -41,17 +41,17 @@ add_task(function* () {
+   yield onCheckboxChange;
+   yield onHighlighterShown;
+   let elements = yield onGridOutlineRendered;
+ 
+   let gridCellA = elements[0];
+ 
+   info("Hovering over grid cell A in the grid outline.");
+   let onCellAHighlight = highlighters.once("grid-highlighter-shown",
+-    (event, nodeFront, options) => {
++    (nodeFront, options) => {
+       info("Checking show grid cell options are correct.");
+       const { showGridCell } = options;
+       const { gridFragmentIndex, rowNumber, columnNumber } = showGridCell;
+ 
+       is(gridFragmentIndex, 0, "Should be the first grid fragment index.");
+       is(rowNumber, 1, "Should be the first grid row.");
+       is(columnNumber, 1, "Should be the first grid column.");
+     });
+diff --git a/devtools/client/inspector/grids/test/browser_grids_highlighter-setting-rules-grid-toggle.js b/devtools/client/inspector/grids/test/browser_grids_highlighter-setting-rules-grid-toggle.js
+--- a/devtools/client/inspector/grids/test/browser_grids_highlighter-setting-rules-grid-toggle.js
++++ b/devtools/client/inspector/grids/test/browser_grids_highlighter-setting-rules-grid-toggle.js
+@@ -43,17 +43,17 @@ add_task(function* () {
+ 
+   yield selectNode("#grid", inspector);
+ 
+   let container = getRuleViewProperty(ruleView, "#grid", "display").valueSpan;
+   let gridToggle = container.querySelector(".ruleview-grid");
+ 
+   info("Toggling ON the CSS grid highlighter from the rule-view.");
+   let onHighlighterShown = highlighters.once("grid-highlighter-shown",
+-    (event, nodeFront, options) => {
++    (nodeFront, options) => {
+       info("Checking the grid highlighter display settings.");
+       let {
+         color,
+         showGridAreasOverlay,
+         showGridLineNumbers,
+         showInfiniteLines,
+       } = options;
+ 
+diff --git a/devtools/client/inspector/rules/views/text-property-editor.js b/devtools/client/inspector/rules/views/text-property-editor.js
+--- a/devtools/client/inspector/rules/views/text-property-editor.js
++++ b/devtools/client/inspector/rules/views/text-property-editor.js
+@@ -1038,17 +1038,17 @@ TextPropertyEditor.prototype = {
+    * Highlight the given shape point in the rule view. Called when "hover-shape-point"
+    * event is emitted.
+    *
+    * @param {Event} event
+    *        The "hover-shape-point" event.
+    * @param {String} point
+    *        The point to highlight.
+    */
+-  _onHoverShapePoint: function(event, point) {
++  _onHoverShapePoint: function(point) {
+     // If there is no shape toggle, or it is not active, return.
+     let shapeToggle = this.valueSpan.querySelector(".ruleview-shapeswatch.active");
+     if (!shapeToggle) {
+       return;
+     }
+ 
+     let view = this.ruleView;
+     let { highlighters } = view;
+diff --git a/devtools/client/inspector/shared/dom-node-preview.js b/devtools/client/inspector/shared/dom-node-preview.js
+--- a/devtools/client/inspector/shared/dom-node-preview.js
++++ b/devtools/client/inspector/shared/dom-node-preview.js
+@@ -1,15 +1,15 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ "use strict";
+ 
+ const {Task} = require("devtools/shared/task");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {createNode} = require("devtools/client/animationinspector/utils");
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ 
+ const STRINGS_URI = "devtools/client/locales/inspector.properties";
+ const L10N = new LocalizationHelper(STRINGS_URI);
+ 
+ /**
+  * UI component responsible for displaying a preview of a dom node.
+@@ -250,17 +250,17 @@ DomNodePreview.prototype = {
+     } else {
+       classList.add("selected");
+       HighlighterLock.highlight(this).then(() => {
+         this.emit("target-highlighter-locked");
+       }, console.error);
+     }
+   },
+ 
+-  onHighlighterLocked: function(e, domNodePreview) {
++  onHighlighterLocked: function(domNodePreview) {
+     if (domNodePreview !== this) {
+       this.highlightNodeEl.classList.remove("selected");
+     }
+   },
+ 
+   onMarkupMutations: function(e, mutations) {
+     if (!this.nodeFront) {
+       return;
+diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js
+--- a/devtools/client/inspector/shared/highlighters-overlay.js
++++ b/devtools/client/inspector/shared/highlighters-overlay.js
+@@ -2,17 +2,17 @@
+ /* vim: set ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {
+   VIEW_NODE_VALUE_TYPE,
+   VIEW_NODE_SHAPE_POINT_TYPE
+ } = require("devtools/client/inspector/shared/node-types");
+ 
+ const DEFAULT_GRID_COLOR = "#4B0082";
+ const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];
+ 

+ 441 - 0
frg/work-js/mozilla-release/patches/1382601-1-61a1.patch

@@ -0,0 +1,441 @@
+# HG changeset patch
+# User yulia <ystartsev@mozilla.com>
+# Date 1521206866 -3600
+# Node ID c37ee39efce7a318de366e9c2736ca4951e091cd
+# Parent  cb1703147b1017012a938dc676a3f06de5b0819c
+Bug 1382601 - update inspector to use new event emitter. r=pbro
+
+MozReview-Commit-ID: 8hGCpkC1eHz
+
+diff --git a/devtools/client/inspector/boxmodel/test/head.js b/devtools/client/inspector/boxmodel/test/head.js
+--- a/devtools/client/inspector/boxmodel/test/head.js
++++ b/devtools/client/inspector/boxmodel/test/head.js
+@@ -51,17 +51,17 @@ function isNodeVisible(node) {
+  * @param  {InspectorPanel} inspector
+  *         The instance of InspectorPanel currently loaded in the toolbox.
+  * @param  {Boolean} waitForSelectionUpdate
+  *         Should the boxmodel-view-updated event come from a new selection.
+  * @return {Promise} a promise
+  */
+ function waitForUpdate(inspector, waitForSelectionUpdate) {
+   return new Promise(resolve => {
+-    inspector.on("boxmodel-view-updated", function onUpdate(e, reasons) {
++    inspector.on("boxmodel-view-updated", function onUpdate(reasons) {
+       // Wait for another update event if we are waiting for a selection related event.
+       if (waitForSelectionUpdate && !reasons.includes("new-selection")) {
+         return;
+       }
+ 
+       inspector.off("boxmodel-view-updated", onUpdate);
+       resolve();
+     });
+diff --git a/devtools/client/inspector/breadcrumbs.js b/devtools/client/inspector/breadcrumbs.js
+--- a/devtools/client/inspector/breadcrumbs.js
++++ b/devtools/client/inspector/breadcrumbs.js
+@@ -10,17 +10,17 @@ const promise = require("promise");
+ 
+ const {ELLIPSIS} = require("devtools/shared/l10n");
+ 
+ const MAX_LABEL_LENGTH = 40;
+ 
+ const NS_XHTML = "http://www.w3.org/1999/xhtml";
+ const SCROLL_REPEAT_MS = 100;
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ 
+ // Some margin may be required for visible element detection.
+ const SCROLL_MARGIN = 1;
+ 
+ /**
+  * Component to replicate functionality of XUL arrowscrollbox
+  * for breadcrumbs
+@@ -396,21 +396,22 @@ HTMLBreadcrumbs.prototype = {
+ 
+     // Last selected node in nodeHierarchy.
+     this.currentIndex = -1;
+ 
+     // Used to build a unique breadcrumb button Id.
+     this.breadcrumbsWidgetItemId = 0;
+ 
+     this.update = this.update.bind(this);
++    this.updateWithMutations = this.updateWithMutations.bind(this);
+     this.updateSelectors = this.updateSelectors.bind(this);
+     this.selection.on("new-node-front", this.update);
+     this.selection.on("pseudoclass", this.updateSelectors);
+     this.selection.on("attribute-changed", this.updateSelectors);
+-    this.inspector.on("markupmutation", this.update);
++    this.inspector.on("markupmutation", this.updateWithMutations);
+     this.update();
+   },
+ 
+   /**
+ 
+    * Build a string that represents the node: tagName#id.class1.class2.
+    * @param {NodeFront} node The node to pretty-print
+    * @return {String}
+@@ -607,17 +608,17 @@ HTMLBreadcrumbs.prototype = {
+ 
+   /**
+    * Remove nodes and clean up.
+    */
+   destroy: function() {
+     this.selection.off("new-node-front", this.update);
+     this.selection.off("pseudoclass", this.updateSelectors);
+     this.selection.off("attribute-changed", this.updateSelectors);
+-    this.inspector.off("markupmutation", this.update);
++    this.inspector.off("markupmutation", this.updateWithMutations);
+ 
+     this.container.removeEventListener("click", this, true);
+     this.container.removeEventListener("mouseover", this, true);
+     this.container.removeEventListener("mouseout", this, true);
+     this.container.removeEventListener("focus", this, true);
+     this.shortcuts.destroy();
+ 
+     this.empty();
+@@ -825,16 +826,26 @@ HTMLBreadcrumbs.prototype = {
+     }
+ 
+     // Catch all return in case the mutations array was empty, or in case none
+     // of the changes iterated above were interesting.
+     return false;
+   },
+ 
+   /**
++   * Update the breadcrumbs display when a new node is selected and there are
++   * mutations.
++   * @param {Array} mutations An array of mutations in case this was called as
++   * the "markupmutation" event listener.
++   */
++  updateWithMutations(mutations) {
++    return this.update("markupmutation", mutations);
++  },
++
++  /**
+    * Update the breadcrumbs display when a new node is selected.
+    * @param {String} reason The reason for the update, if any.
+    * @param {Array} mutations An array of mutations in case this was called as
+    * the "markupmutation" event listener.
+    */
+   update: function(reason, mutations) {
+     if (this.isDestroyed) {
+       return;
+diff --git a/devtools/client/inspector/inspector-search.js b/devtools/client/inspector/inspector-search.js
+--- a/devtools/client/inspector/inspector-search.js
++++ b/devtools/client/inspector/inspector-search.js
+@@ -3,17 +3,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const promise = require("promise");
+ const {Task} = require("devtools/shared/task");
+ const {KeyCodes} = require("devtools/client/shared/keycodes");
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+ const Services = require("Services");
+ 
+ // Maximum number of selector suggestions shown in the panel.
+ const MAX_SUGGESTIONS = 15;
+ 
+ /**
+  * Converts any input field into a document search box.
+diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js
+--- a/devtools/client/inspector/inspector.js
++++ b/devtools/client/inspector/inspector.js
+@@ -5,17 +5,17 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ /* global window, BrowserLoader */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+ const promise = require("promise");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {executeSoon} = require("devtools/shared/DevToolsUtils");
+ const {Task} = require("devtools/shared/task");
+ const {PrefObserver} = require("devtools/client/shared/prefs");
+ const Telemetry = require("devtools/client/shared/telemetry");
+ const HighlightersOverlay = require("devtools/client/inspector/shared/highlighters-overlay");
+ const ReflowTracker = require("devtools/client/inspector/shared/reflow-tracker");
+ const Store = require("devtools/client/inspector/store");
+ 
+@@ -118,16 +118,17 @@ function Inspector(toolbox) {
+ 
+   this.nodeMenuTriggerInfo = null;
+ 
+   this._handleRejectionIfNotDestroyed = this._handleRejectionIfNotDestroyed.bind(this);
+   this._onContextMenu = this._onContextMenu.bind(this);
+   this._onBeforeNavigate = this._onBeforeNavigate.bind(this);
+   this._onMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
+   this._updateSearchResultsLabel = this._updateSearchResultsLabel.bind(this);
++  this._clearSearchResultsLabel = this._clearSearchResultsLabel.bind(this);
+ 
+   this.onDetached = this.onDetached.bind(this);
+   this.onMarkupLoaded = this.onMarkupLoaded.bind(this);
+   this.onNewSelection = this.onNewSelection.bind(this);
+   this.onNewRoot = this.onNewRoot.bind(this);
+   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
+   this.onShowBoxModelHighlighterForNode =
+     this.onShowBoxModelHighlighterForNode.bind(this);
+@@ -378,17 +379,17 @@ 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.searchResultsLabel = this.panelDoc.getElementById("inspector-searchlabel");
+ 
+     this.search = new InspectorSearch(this, this.searchBox, this.searchClearButton);
+-    this.search.on("search-cleared", this._updateSearchResultsLabel);
++    this.search.on("search-cleared", this._clearSearchResultsLabel);
+     this.search.on("search-result", this._updateSearchResultsLabel);
+ 
+     let shortcuts = new KeyShortcuts({
+       window: this.panelDoc.defaultView,
+     });
+     let key = INSPECTOR_L10N.getStr("inspector.searchHTML.key");
+     shortcuts.on(key, event => {
+       // Prevent overriding same shortcut from the computed/rule views
+@@ -400,19 +401,23 @@ Inspector.prototype = {
+       this.searchBox.focus();
+     });
+   },
+ 
+   get searchSuggestions() {
+     return this.search.autocompleter;
+   },
+ 
+-  _updateSearchResultsLabel: function(event, result) {
++  _clearSearchResultsLabel: function(result) {
++    return this._updateSearchResultsLabel(result, true);
++  },
++
++  _updateSearchResultsLabel: function(result, clear = false) {
+     let str = "";
+-    if (event !== "search-cleared") {
++    if (!clear) {
+       if (result) {
+         str = INSPECTOR_L10N.getFormatStr(
+           "inspector.searchResultsCount2", result.resultsIndex + 1, result.resultsLength);
+       } else {
+         str = INSPECTOR_L10N.getStr("inspector.searchResultsNone");
+       }
+     }
+ 
+@@ -554,17 +559,17 @@ Inspector.prototype = {
+     Services.prefs.setIntPref("devtools.toolsidebar-width.inspector.splitsidebar",
+       this.sidebarSplitBox.state.width);
+   },
+ 
+   onSidebarResized: function(width, height) {
+     this.toolbox.emit("inspector-sidebar-resized", { width, height });
+   },
+ 
+-  onSidebarSelect: function(event, toolId) {
++  onSidebarSelect: function(toolId) {
+     // Save the currently selected sidebar panel
+     Services.prefs.setCharPref("devtools.inspector.activeSidebar", toolId);
+ 
+     // Then forces the panel creation by calling getPanel
+     // (This allows lazy loading the panels only once we select them)
+     this.getPanel(toolId);
+ 
+     this.toolbox.emit("inspector-sidebar-select", toolId);
+diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js
+--- a/devtools/client/inspector/markup/markup.js
++++ b/devtools/client/inspector/markup/markup.js
+@@ -1278,17 +1278,17 @@ MarkupView.prototype = {
+     this.cancelReselectOnRemoved();
+ 
+     // Get the removedNode index in its parent node to reselect the right node.
+     let isHTMLTag = removedNode.tagName.toLowerCase() === "html";
+     let oldContainer = this.getContainer(removedNode);
+     let parentContainer = this.getContainer(removedNode.parentNode());
+     let childIndex = parentContainer.getChildContainers().indexOf(oldContainer);
+ 
+-    let onMutations = this._removedNodeObserver = (e, mutations) => {
++    let onMutations = this._removedNodeObserver = mutations => {
+       let isNodeRemovalMutation = false;
+       for (let mutation of mutations) {
+         let containsRemovedNode = mutation.removed &&
+                                   mutation.removed.some(n => n === removedNode);
+         if (mutation.type === "childList" &&
+             (containsRemovedNode || isHTMLTag)) {
+           isNodeRemovalMutation = true;
+           break;
+diff --git a/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
+--- a/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
++++ b/devtools/client/inspector/markup/test/browser_markup_accessibility_navigation.js
+@@ -267,11 +267,11 @@ add_task(function* () {
+   for (let testData of TESTS) {
+     yield runAccessibilityNavigationTest(inspector, elms, testData);
+   }
+ 
+   elms = null;
+ });
+ 
+ // Record all containers that are created dynamically into elms object.
+-function memorizeContainer(event, container) {
++function memorizeContainer(container) {
+   elms[`container-${containerID++}`] = container;
+ }
+diff --git a/devtools/client/inspector/markup/test/browser_markup_mutation_01.js b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
+--- a/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
++++ b/devtools/client/inspector/markup/test/browser_markup_mutation_01.js
+@@ -314,17 +314,17 @@ add_task(function* () {
+     numMutations = numMutations || 1;
+ 
+     info("Executing the test markup mutation");
+ 
+     // If a test expects more than one mutation it may come through in a single
+     // event or possibly in multiples.
+     let seenMutations = 0;
+     let promise = new Promise(resolve => {
+-      inspector.on("markupmutation", function onmutation(e, mutations) {
++      inspector.on("markupmutation", function onmutation(mutations) {
+         seenMutations += mutations.length;
+         info("Receieved " + seenMutations +
+              " mutations, expecting at least " + numMutations);
+         if (seenMutations >= numMutations) {
+           inspector.off("markupmutation", onmutation);
+           resolve();
+         }
+       });
+diff --git a/devtools/client/inspector/markup/views/element-editor.js b/devtools/client/inspector/markup/views/element-editor.js
+--- a/devtools/client/inspector/markup/views/element-editor.js
++++ b/devtools/client/inspector/markup/views/element-editor.js
+@@ -542,17 +542,17 @@ ElementEditor.prototype = {
+     }
+ 
+     let container = this.markup.getContainer(this.node);
+ 
+     let activeAttrs = [...this.attrList.childNodes]
+       .filter(el => el.style.display != "none");
+     let attributeIndex = activeAttrs.indexOf(attrNode);
+ 
+-    let onMutations = this._editedAttributeObserver = (e, mutations) => {
++    let onMutations = this._editedAttributeObserver = mutations => {
+       let isDeletedAttribute = false;
+       let isNewAttribute = false;
+ 
+       for (let mutation of mutations) {
+         let inContainer =
+           this.markup.getContainer(mutation.target) === container;
+         if (!inContainer) {
+           continue;
+diff --git a/devtools/client/inspector/rules/test/head.js b/devtools/client/inspector/rules/test/head.js
+--- a/devtools/client/inspector/rules/test/head.js
++++ b/devtools/client/inspector/rules/test/head.js
+@@ -473,17 +473,17 @@ function* sendKeysAndWaitForFocus(view, 
+ 
+ /**
+  * Wait for a markupmutation event on the inspector that is for a style modification.
+  * @param {InspectorPanel} inspector
+  * @return {Promise}
+  */
+ function waitForStyleModification(inspector) {
+   return new Promise(function(resolve) {
+-    function checkForStyleModification(name, mutations) {
++    function checkForStyleModification(mutations) {
+       for (let mutation of mutations) {
+         if (mutation.type === "attributes" && mutation.attributeName === "style") {
+           inspector.off("markupmutation", checkForStyleModification);
+           resolve();
+           return;
+         }
+       }
+     }
+diff --git a/devtools/client/inspector/rules/views/class-list-previewer.js b/devtools/client/inspector/rules/views/class-list-previewer.js
+--- a/devtools/client/inspector/rules/views/class-list-previewer.js
++++ b/devtools/client/inspector/rules/views/class-list-previewer.js
+@@ -164,17 +164,17 @@ ClassListPreviewerModel.prototype = {
+     };
+ 
+     // Apply the change to the node.
+     let mod = this.currentNode.startModifyingAttributes();
+     mod.setAttribute("class", this.currentClassesPreview);
+     return mod.apply();
+   },
+ 
+-  onMutations(e, mutations) {
++  onMutations(mutations) {
+     for (let {type, target, attributeName} of mutations) {
+       // Only care if this mutation is for the class attribute.
+       if (type !== "attributes" || attributeName !== "class") {
+         continue;
+       }
+ 
+       let isMutationForOurChange = this.lastStateChange &&
+                                    target === this.lastStateChange.node &&
+diff --git a/devtools/client/inspector/shared/dom-node-preview.js b/devtools/client/inspector/shared/dom-node-preview.js
+--- a/devtools/client/inspector/shared/dom-node-preview.js
++++ b/devtools/client/inspector/shared/dom-node-preview.js
+@@ -256,17 +256,17 @@ DomNodePreview.prototype = {
+   },
+ 
+   onHighlighterLocked: function(domNodePreview) {
+     if (domNodePreview !== this) {
+       this.highlightNodeEl.classList.remove("selected");
+     }
+   },
+ 
+-  onMarkupMutations: function(e, mutations) {
++  onMarkupMutations: function(mutations) {
+     if (!this.nodeFront) {
+       return;
+     }
+ 
+     for (let {target} of mutations) {
+       if (target === this.nodeFront) {
+         // Re-render with the same nodeFront to update the output.
+         this.render(this.nodeFront);
+diff --git a/devtools/client/inspector/shared/highlighters-overlay.js b/devtools/client/inspector/shared/highlighters-overlay.js
+--- a/devtools/client/inspector/shared/highlighters-overlay.js
++++ b/devtools/client/inspector/shared/highlighters-overlay.js
+@@ -856,17 +856,17 @@ class HighlightersOverlay {
+     this._lastHovered = null;
+     this._hideHoveredHighlighter();
+   }
+ 
+   /**
+    * Handler function for "markupmutation" events. Hides the flexbox/grid/shapes
+    * highlighter if the flexbox/grid/shapes container is no longer in the DOM tree.
+    */
+-  async onMarkupMutation(evt, mutations) {
++  async onMarkupMutation(mutations) {
+     let hasInterestingMutation = mutations.some(mut => mut.type === "childList");
+     if (!hasInterestingMutation) {
+       // Bail out if the mutations did not remove nodes, or if no grid highlighter is
+       // displayed.
+       return;
+     }
+ 
+     this._hideHighlighterIfDeadNode(this.flexboxHighlighterShown,
+diff --git a/devtools/client/inspector/toolsidebar.js b/devtools/client/inspector/toolsidebar.js
+--- a/devtools/client/inspector/toolsidebar.js
++++ b/devtools/client/inspector/toolsidebar.js
+@@ -1,17 +1,17 @@
+ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-var EventEmitter = require("devtools/shared/old-event-emitter");
++var EventEmitter = require("devtools/shared/event-emitter");
+ var Telemetry = require("devtools/client/shared/telemetry");
+ var { Task } = require("devtools/shared/task");
+ 
+ /**
+  * This object represents replacement for ToolSidebar
+  * implemented in devtools/client/framework/sidebar.js module
+  *
+  * This new component is part of devtools.html aimed at

+ 31 - 0
frg/work-js/mozilla-release/patches/1382601-2-61a1.patch

@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521537979 -3600
+# Node ID b0b5718ec0708b8c843b6d444f9857d2a0fb7621
+# Parent  75044e5bc9d10cb418b51fd0585a77fbfe085eb1
+Bug 1382601 - Fix test failure in damp due to EventEmitter change; r=pbro.
+
+MozReview-Commit-ID: 6eYKhqSZxpV
+
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
+--- a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
+@@ -39,17 +39,17 @@ module.exports = async function() {
+       });
+     }`
+   ) + ")()", false);
+ 
+   let test = runTest("inspector.mutations");
+ 
+   await new Promise(resolve => {
+     let childListMutationsCounter = 0;
+-    inspector.on("markupmutation", (evt, mutations) => {
++    inspector.on("markupmutation", mutations => {
+       let childListMutations = mutations.filter(m => m.type === "childList");
+       childListMutationsCounter += childListMutations.length;
+       if (childListMutationsCounter === LIMIT) {
+         // Wait until we received exactly n=LIMIT mutations in the markup view.
+         resolve();
+       }
+     });
+ 

+ 243 - 0
frg/work-js/mozilla-release/patches/1382602-61a1.patch

@@ -0,0 +1,243 @@
+# HG changeset patch
+# User yulia <ystartsev@mozilla.com>
+# Date 1521035904 -3600
+# Node ID e059b4fc0521a26584652b65726a92e3df009e73
+# Parent  981719292f44a287eb9fc3410706d16825fa2ee9
+Bug 1382602 - update devtools/client/netmonitor to use new event emitter. r=Honza,nchevobbe
+
+MozReview-Commit-ID: 6cEqHUGaMlM
+
+diff --git a/devtools/client/netmonitor/initializer.js b/devtools/client/netmonitor/initializer.js
+--- a/devtools/client/netmonitor/initializer.js
++++ b/devtools/client/netmonitor/initializer.js
+@@ -11,17 +11,17 @@
+ const { BrowserLoader } = ChromeUtils.import(
+   "resource://devtools/client/shared/browser-loader.js", {});
+ 
+ const require = window.windowRequire = BrowserLoader({
+   baseURI: "resource://devtools/client/netmonitor/",
+   window,
+ }).require;
+ 
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const { createFactory } = require("devtools/client/shared/vendor/react");
+ const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
+ const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
+ const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
+ const { Connector } = require("./src/connector/index");
+ const { configureStore } = require("./src/create-store");
+ const App = createFactory(require("./src/components/App"));
+ const { EVENTS } = require("./src/constants");
+@@ -114,17 +114,17 @@ window.Netmonitor = {
+ 
+     return HarExporter.getHar(options);
+   },
+ 
+   /**
+    * Support for `devtools.network.onRequestFinished`. A hook for
+    * every finished HTTP request used by WebExtensions API.
+    */
+-  onRequestAdded(event, requestId) {
++  onRequestAdded(requestId) {
+     let listeners = this.toolbox.getRequestFinishedListeners();
+     if (!listeners.size) {
+       return;
+     }
+ 
+     let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
+     let options = {
+       connector,
+diff --git a/devtools/client/netmonitor/src/har/toolbox-overlay.js b/devtools/client/netmonitor/src/har/toolbox-overlay.js
+--- a/devtools/client/netmonitor/src/har/toolbox-overlay.js
++++ b/devtools/client/netmonitor/src/har/toolbox-overlay.js
+@@ -44,17 +44,17 @@ ToolboxOverlay.prototype = {
+     }
+ 
+     this.initAutomation();
+   },
+ 
+   /**
+    * Executed when the toolbox is destroyed.
+    */
+-  onDestroy: function(eventId, toolbox) {
++  onDestroy: function(toolbox) {
+     this.destroyAutomation();
+   },
+ 
+   // Automation
+ 
+   initAutomation: function() {
+     this.automation = new HarAutomation(this.toolbox);
+   },
+diff --git a/devtools/client/netmonitor/test/browser_net_pause.js b/devtools/client/netmonitor/test/browser_net_pause.js
+--- a/devtools/client/netmonitor/test/browser_net_pause.js
++++ b/devtools/client/netmonitor/test/browser_net_pause.js
+@@ -72,26 +72,14 @@ async function performRequestAndWait(tab
+   });
+   await wait;
+ }
+ 
+ /**
+  * Execute simple GET request
+  */
+ async function performPausedRequest(connector, tab, monitor) {
+-  let wait = waitForWebConsoleNetworkEvent(connector);
++  let wait = connector.connector.webConsoleClient.once("networkEvent");
+   await ContentTask.spawn(tab.linkedBrowser, SIMPLE_SJS, async function(url) {
+     await content.wrappedJSObject.performRequests(url);
+   });
+   await wait;
+ }
+-
+-/**
+- * Listen for events fired by the console client since the Firefox
+- * connector (data provider) is paused.
+- */
+-function waitForWebConsoleNetworkEvent(connector) {
+-  return new Promise(resolve => {
+-    connector.connector.webConsoleClient.once("networkEvent", (type, networkInfo) => {
+-      resolve();
+-    });
+-  });
+-}
+diff --git a/devtools/client/netmonitor/test/head.js b/devtools/client/netmonitor/test/head.js
+--- a/devtools/client/netmonitor/test/head.js
++++ b/devtools/client/netmonitor/test/head.js
+@@ -141,17 +141,17 @@ function toggleCache(target, disabled) {
+ 
+ /**
+  * Wait for 2 markers during document load.
+  */
+ function waitForTimelineMarkers(monitor) {
+   return new Promise(resolve => {
+     let markers = [];
+ 
+-    function handleTimelineEvent(_, marker) {
++    function handleTimelineEvent(marker) {
+       info(`Got marker: ${marker.name}`);
+       markers.push(marker);
+       if (markers.length == 2) {
+         monitor.panelWin.off(EVENTS.TIMELINE_EVENT, handleTimelineEvent);
+         info("Got two timeline markers, done waiting");
+         resolve(markers);
+       }
+     }
+@@ -180,24 +180,24 @@ function waitForAllRequestsFinished(moni
+   let window = monitor.panelWin;
+   let { connector } = window;
+   let { getNetworkRequest } = connector;
+ 
+   return new Promise(resolve => {
+     // Key is the request id, value is a boolean - is request finished or not?
+     let requests = new Map();
+ 
+-    function onRequest(_, id) {
++    function onRequest(id) {
+       let networkInfo = getNetworkRequest(id);
+       let { url } = networkInfo.request;
+       info(`Request ${id} for ${url} not yet done, keep waiting...`);
+       requests.set(id, false);
+     }
+ 
+-    function onTimings(_, id) {
++    function onTimings(id) {
+       let networkInfo = getNetworkRequest(id);
+       let { url } = networkInfo.request;
+       info(`Request ${id} for ${url} done`);
+       requests.set(id, true);
+       maybeResolve();
+     }
+ 
+     function maybeResolve() {
+@@ -238,23 +238,23 @@ let updatedTypes = [
+   "NetMonitor:NetworkEventUpdated:ResponseContent",
+   "NetMonitor:NetworkEventUpdated:SecurityInfo",
+   "NetMonitor:NetworkEventUpdated:EventTimings",
+ ];
+ 
+ // Start collecting all networkEventUpdate event when panel is opened.
+ // removeTab() should be called once all corresponded RECEIVED_* events finished.
+ function startNetworkEventUpdateObserver(panelWin) {
+-  updatingTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+-    let key = actor + "-" + updatedTypes[updatingTypes.indexOf(event)];
++  updatingTypes.forEach((type) => panelWin.on(type, actor => {
++    let key = actor + "-" + updatedTypes[updatingTypes.indexOf(type)];
+     finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] + 1 : 1;
+   }));
+ 
+-  updatedTypes.forEach((type) => panelWin.on(type, (event, actor) => {
+-    let key = actor + "-" + event;
++  updatedTypes.forEach((type) => panelWin.on(type, actor => {
++    let key = actor + "-" + type;
+     finishedQueue[key] = finishedQueue[key] ? finishedQueue[key] - 1 : -1;
+   }));
+ }
+ 
+ async function waitForAllNetworkUpdateEvents() {
+   function checkNetworkEventUpdateState() {
+     for (let key in finishedQueue) {
+       if (finishedQueue[key] > 0) {
+@@ -349,36 +349,36 @@ function teardown(monitor) {
+ 
+ function waitForNetworkEvents(monitor, getRequests) {
+   return new Promise((resolve) => {
+     let panel = monitor.panelWin;
+     let { getNetworkRequest } = panel.connector;
+     let networkEvent = 0;
+     let payloadReady = 0;
+ 
+-    function onNetworkEvent(event, actor) {
++    function onNetworkEvent(actor) {
+       let networkInfo = getNetworkRequest(actor);
+       if (!networkInfo) {
+         // Must have been related to reloading document to disable cache.
+         // Ignore the event.
+         return;
+       }
+       networkEvent++;
+-      maybeResolve(event, actor, networkInfo);
++      maybeResolve(EVENTS.NETWORK_EVENT, actor, networkInfo);
+     }
+ 
+-    function onPayloadReady(event, actor) {
++    function onPayloadReady(actor) {
+       let networkInfo = getNetworkRequest(actor);
+       if (!networkInfo) {
+         // Must have been related to reloading document to disable cache.
+         // Ignore the event.
+         return;
+       }
+       payloadReady++;
+-      maybeResolve(event, actor, networkInfo);
++      maybeResolve(EVENTS.PAYLOAD_READY, actor, networkInfo);
+     }
+ 
+     function maybeResolve(event, actor, networkInfo) {
+       info("> Network event progress: " +
+         "NetworkEvent: " + networkEvent + "/" + getRequests + ", " +
+         "PayloadReady: " + payloadReady + "/" + getRequests + ", " +
+         "got " + event + " for " + actor);
+ 
+diff --git a/devtools/client/netmonitor/webpack.config.js b/devtools/client/netmonitor/webpack.config.js
+--- a/devtools/client/netmonitor/webpack.config.js
++++ b/devtools/client/netmonitor/webpack.config.js
+@@ -79,17 +79,17 @@ let webpackConfig = {
+       "devtools/client/shared/vendor/react-dom": "react-dom",
+       "devtools/client/shared/vendor/react-redux": "react-redux",
+       "devtools/client/shared/vendor/redux": "redux",
+       "devtools/client/shared/vendor/reselect": "reselect",
+       "devtools/client/shared/vendor/jszip": "jszip",
+ 
+       "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
+ 
+-      "devtools/shared/old-event-emitter": "devtools-modules/src/utils/event-emitter",
++      "devtools/shared/event-emitter": "devtools-modules/src/utils/event-emitter",
+       "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
+       "devtools/shared/platform/clipboard": path.join(__dirname, "../../client/shared/webpack/shims/platform-clipboard-stub"),
+       "devtools/client/netmonitor/src/utils/firefox/open-request-in-tab": path.join(__dirname, "src/utils/open-request-in-tab"),
+ 
+       // Locales need to be explicitly mapped to the en-US subfolder
+       "devtools/client/locales": path.join(__dirname, "../../client/locales/en-US"),
+       "devtools/shared/locales": path.join(__dirname, "../../shared/locales/en-US"),
+       "devtools/startup/locales": path.join(__dirname, "../../shared/locales/en-US"),

+ 1 - 1
frg/work-js/mozilla-release/patches/1394235-57a1.patch

@@ -1,5 +1,5 @@
 # HG changeset patch
-# User Florian Quèze <florian@queze.net>
+# User Florian Queze <florian@queze.net>
 # Date 1504219364 -7200
 # Node ID 9e59ca9dd30d73c57dad6a7e6220b159dd1e6db5
 # Parent  45b6d0d482c25dbb9b8afb0b0a0e7a980e20e96e

+ 229 - 0
frg/work-js/mozilla-release/patches/1394554-1-57a1.patch

@@ -0,0 +1,229 @@
+# HG changeset patch
+# User Christoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
+# Date 1504683190 -7200
+# Node ID bb932a1656cd4f8850457d85f4916030afbdcdc8
+# Parent  4c5c84dad50a67a32b58396bdd03b8975e43223f
+Bug 1394554: Block toplevel data: URI navigations after redirect. r=smaug
+
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -39,16 +39,17 @@
+ #include "nsIContent.h"
+ #include "nsIContentInlines.h"
+ #include "nsIDocument.h"
+ #include "nsIDOMDocument.h"
+ #include "nsIDOMElement.h"
+ 
+ #include "nsArray.h"
+ #include "nsArrayUtils.h"
++#include "nsContentSecurityManager.h"
+ #include "nsICaptivePortalService.h"
+ #include "nsIDOMStorage.h"
+ #include "nsIContentViewer.h"
+ #include "nsIDocumentLoaderFactory.h"
+ #include "nsCURILoader.h"
+ #include "nsContentDLF.h"
+ #include "nsDocShellCID.h"
+ #include "nsDOMCID.h"
+@@ -9825,46 +9826,23 @@ nsDocShell::InternalLoad(nsIURI* aURI,
+       // an iframe since that's more common.
+       contentType = nsIContentPolicy::TYPE_INTERNAL_IFRAME;
+     }
+   } else {
+     contentType = nsIContentPolicy::TYPE_DOCUMENT;
+     isTargetTopLevelDocShell = true;
+   }
+ 
+-  if (contentType == nsIContentPolicy::TYPE_DOCUMENT &&
+-      nsIOService::BlockToplevelDataUriNavigations()) {
+-    bool isDataURI =
+-      (NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI);
+-    // Let's block all toplevel document navigations to a data: URI.
+-    // In all cases where the toplevel document is navigated to a
+-    // data: URI the triggeringPrincipal is a codeBasePrincipal, or
+-    // a NullPrincipal. In other cases, e.g. typing a data: URL into
+-    // the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
+-    // we don't want to block those loads. Only exception, loads coming
+-    // from an external applicaton (e.g. Thunderbird) don't load
+-    // using a codeBasePrincipal, but we want to block those loads.
+-    bool loadFromExternal = (aLoadType == LOAD_NORMAL_EXTERNAL);
+-    if (isDataURI && (loadFromExternal || 
+-        !nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal))) {
+-      NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
+-      if (specUTF16.Length() > 50) {
+-        specUTF16.Truncate(50);
+-        specUTF16.AppendLiteral("...");
+-      }
+-      const char16_t* params[] = { specUTF16.get() };
+-      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+-                                      NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
+-                                      // no doc available, log to browser console
+-                                      nullptr,
+-                                      nsContentUtils::eSECURITY_PROPERTIES,
+-                                      "BlockTopLevelDataURINavigation",
+-                                      params, ArrayLength(params));
+-      return NS_OK;
+-    }
++  if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
++        aURI,
++        contentType,
++        aTriggeringPrincipal,
++        (aLoadType == LOAD_NORMAL_EXTERNAL))) {
++    // logging to console happens within AllowTopLevelNavigationToDataURI
++    return NS_OK;
+   }
+ 
+   // If there's no targetDocShell, that means we are about to create a new
+   // window (or aWindowTarget is empty). Perform a content policy check before
+   // creating the window. Please note for all other docshell loads
+   // content policy checks are performed within the contentSecurityManager
+   // when the channel is about to be openend.
+   if (!targetDocShell && !aWindowTarget.IsEmpty()) {
+diff --git a/dom/security/nsContentSecurityManager.cpp b/dom/security/nsContentSecurityManager.cpp
+--- a/dom/security/nsContentSecurityManager.cpp
++++ b/dom/security/nsContentSecurityManager.cpp
+@@ -4,29 +4,80 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "nsContentSecurityManager.h"
+ #include "nsIChannel.h"
+ #include "nsIHttpChannelInternal.h"
+ #include "nsIStreamListener.h"
+ #include "nsILoadInfo.h"
++#include "nsIOService.h"
+ #include "nsContentUtils.h"
+ #include "nsCORSListenerProxy.h"
+ #include "nsIStreamListener.h"
+ #include "nsCDefaultURIFixup.h"
+ #include "nsIURIFixup.h"
+ #include "nsIImageLoadingContent.h"
++#include "NullPrincipal.h"
+ 
+ #include "mozilla/dom/Element.h"
+ 
+ NS_IMPL_ISUPPORTS(nsContentSecurityManager,
+                   nsIContentSecurityManager,
+                   nsIChannelEventSink)
+ 
++/* static */ bool
++nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
++  nsIURI* aURI,
++  nsContentPolicyType aContentPolicyType,
++  nsIPrincipal* aTriggeringPrincipal,
++  bool aLoadFromExternal)
++{
++  // Let's block all toplevel document navigations to a data: URI.
++  // In all cases where the toplevel document is navigated to a
++  // data: URI the triggeringPrincipal is a codeBasePrincipal, or
++  // a NullPrincipal. In other cases, e.g. typing a data: URL into
++  // the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
++  // we don't want to block those loads. Only exception, loads coming
++  // from an external applicaton (e.g. Thunderbird) don't load
++  // using a codeBasePrincipal, but we want to block those loads.
++  if (!mozilla::net::nsIOService::BlockToplevelDataUriNavigations()) {
++    return true;
++  }
++  if (aContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT) {
++    return true;
++  }
++  bool isDataURI =
++    (NS_SUCCEEDED(aURI->SchemeIs("data", &isDataURI)) && isDataURI);
++  if (!isDataURI) {
++    return true;
++  }
++  if (!aLoadFromExternal &&
++      nsContentUtils::IsSystemPrincipal(aTriggeringPrincipal)) {
++    return true;
++  }
++
++  nsAutoCString spec;
++  aURI->GetSpec(spec);
++  NS_ConvertUTF8toUTF16 specUTF16(aURI->GetSpecOrDefault());
++  if (specUTF16.Length() > 50) {
++    specUTF16.Truncate(50);
++    specUTF16.AppendLiteral("...");
++  }
++  const char16_t* params[] = { specUTF16.get() };
++  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
++                                  NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
++                                  // no doc available, log to browser console
++                                  nullptr,
++                                  nsContentUtils::eSECURITY_PROPERTIES,
++                                  "BlockTopLevelDataURINavigation",
++                                  params, ArrayLength(params));
++  return false;
++}
++
+ static nsresult
+ ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
+ {
+   nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
+ 
+   if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
+       securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
+       securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
+@@ -499,16 +550,37 @@ nsContentSecurityManager::AsyncOnChannel
+   if (loadInfo && loadInfo->GetEnforceSecurity()) {
+     nsresult rv = CheckChannel(aNewChannel);
+     if (NS_FAILED(rv)) {
+       aOldChannel->Cancel(rv);
+       return rv;
+     }
+   }
+ 
++  // Redirecting to a toplevel data: URI is not allowed, hence we pass
++  // a NullPrincipal as the TriggeringPrincipal to
++  // AllowTopLevelNavigationToDataURI() which definitely blocks any
++  // data: URI load.
++  nsCOMPtr<nsILoadInfo> newLoadInfo = aNewChannel->GetLoadInfo();
++  if (newLoadInfo) {
++    nsCOMPtr<nsIURI> uri;
++    nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(uri));
++    NS_ENSURE_SUCCESS(rv, rv);
++    nsCOMPtr<nsIPrincipal> nullTriggeringPrincipal = NullPrincipal::Create();
++    if (!nsContentSecurityManager::AllowTopLevelNavigationToDataURI(
++          uri,
++          newLoadInfo->GetExternalContentPolicyType(),
++          nullTriggeringPrincipal,
++          false)) {
++        // logging to console happens within AllowTopLevelNavigationToDataURI
++      aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
++      return NS_ERROR_DOM_BAD_URI;
++    }
++  }
++
+   // Also verify that the redirecting server is allowed to redirect to the
+   // given URI
+   nsCOMPtr<nsIPrincipal> oldPrincipal;
+   nsContentUtils::GetSecurityManager()->
+     GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
+ 
+   nsCOMPtr<nsIURI> newURI;
+   Unused << NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
+diff --git a/dom/security/nsContentSecurityManager.h b/dom/security/nsContentSecurityManager.h
+--- a/dom/security/nsContentSecurityManager.h
++++ b/dom/security/nsContentSecurityManager.h
+@@ -27,16 +27,21 @@ public:
+   NS_DECL_NSICONTENTSECURITYMANAGER
+   NS_DECL_NSICHANNELEVENTSINK
+ 
+   nsContentSecurityManager() {}
+ 
+   static nsresult doContentSecurityCheck(nsIChannel* aChannel,
+                                          nsCOMPtr<nsIStreamListener>& aInAndOutListener);
+ 
++  static bool AllowTopLevelNavigationToDataURI(nsIURI* aURI,
++                                               nsContentPolicyType aContentPolicyType,
++                                               nsIPrincipal* aTriggeringPrincipal,
++                                               bool aLoadFromExternal);
++
+ private:
+   static nsresult CheckChannel(nsIChannel* aChannel);
+ 
+   virtual ~nsContentSecurityManager() {}
+ 
+ };
+ 
+ #endif /* nsContentSecurityManager_h___ */

+ 81 - 0
frg/work-js/mozilla-release/patches/1394554-2-57a1.patch

@@ -0,0 +1,81 @@
+# HG changeset patch
+# User Christoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
+# Date 1504683278 -7200
+# Node ID 5fb3040f8887f30b7f86c7d0afe474282c6abaa2
+# Parent  8a0090c8ae0aad275dfca6a9809fa026f5abdd48
+Bug 1394554: Test block data: URI toplevel navigations after redirect. r=smaug
+
+diff --git a/dom/security/test/general/file_block_toplevel_data_redirect.sjs b/dom/security/test/general/file_block_toplevel_data_redirect.sjs
+new file mode 100644
+--- /dev/null
++++ b/dom/security/test/general/file_block_toplevel_data_redirect.sjs
+@@ -0,0 +1,14 @@
++// Custom *.sjs file specifically for the needs of Bug:
++// Bug 1394554 - Block toplevel data: URI navigations after redirect
++
++var DATA_URI =
++  "<body>toplevel data: URI navigations after redirect should be blocked</body>";
++
++function handleRequest(request, response)
++{
++  // avoid confusing cache behaviors
++  response.setHeader("Cache-Control", "no-cache", false);
++
++  response.setStatusLine("1.1", 302, "Found");
++  response.setHeader("Location", "data:text/html," + escape(DATA_URI), false);
++}
+diff --git a/dom/security/test/general/mochitest.ini b/dom/security/test/general/mochitest.ini
+--- a/dom/security/test/general/mochitest.ini
++++ b/dom/security/test/general/mochitest.ini
+@@ -1,16 +1,17 @@
+ [DEFAULT]
+ support-files =
+   file_contentpolicytype_targeted_link_iframe.sjs
+   file_nosniff_testserver.sjs
+   file_block_script_wrong_mime_server.sjs
+   file_block_toplevel_data_navigation.html
+   file_block_toplevel_data_navigation2.html
+   file_block_toplevel_data_navigation3.html
++  file_block_toplevel_data_redirect.sjs
+   file_same_site_cookies_subrequest.sjs
+   file_same_site_cookies_toplevel_nav.sjs
+   file_same_site_cookies_cross_origin_context.sjs
+   file_same_site_cookies_from_script.sjs
+   file_same_site_cookies_redirect.sjs
+   file_same_site_cookies_toplevel_set_cookie.sjs
+   file_same_site_cookies_blob_iframe_navigation.html
+   file_same_site_cookies_blob_iframe_inclusion.html
+diff --git a/dom/security/test/general/test_block_toplevel_data_navigation.html b/dom/security/test/general/test_block_toplevel_data_navigation.html
+--- a/dom/security/test/general/test_block_toplevel_data_navigation.html
++++ b/dom/security/test/general/test_block_toplevel_data_navigation.html
+@@ -60,17 +60,29 @@ function test3() {
+ function test4() {
+   // navigating to a data: URI using window.open() should be blocked
+   let win4 = window.open("data:text/html,<body>toplevel data: URI navigations should be blocked</body>");
+   setTimeout(function () {
+     // Please note that the data: URI will be displayed in the URL-Bar but not
+     // loaded, hence we rather rely on document.body than document.location
+     is(win4.document.body.innerHTML, "",
+       "navigating to a data: URI using window.open() should be blocked");
+-    win4.close();
++    test5();
++  }, 1000);
++}
++
++function test5() {
++  // navigating to a URI which redirects to a data: URI using window.open() should be blocked
++  let win5 = window.open("file_block_toplevel_data_redirect.sjs");
++  setTimeout(function () {
++    // Please note that the data: URI will be displayed in the URL-Bar but not
++    // loaded, hence we rather rely on document.body than document.location
++    is(SpecialPowers.wrap(win5).document.body.innerHTML, "",
++      "navigating to URI which redirects to a data: URI using window.open() should be blocked");
++    win5.close();
+     SimpleTest.finish();
+   }, 1000);
+ }
+ 
+ // fire up the tests
+ test1();
+ 
+ </script>

+ 109 - 0
frg/work-js/mozilla-release/patches/1394554-3-57a1.patch

@@ -0,0 +1,109 @@
+# HG changeset patch
+# User Christoph Kerschbaumer <ckerschb@christophkerschbaumer.com>
+# Date 1504683299 -7200
+# Node ID d80fa4d123620fd099db9b0e790e5d5daed24590
+# Parent  d6c69f7a3471b84e296132d28727248af9e66167
+Bug 1394554: Test block data: URI toplevel navigations after redirect. r=smaug
+
+diff --git a/dom/security/test/general/browser.ini b/dom/security/test/general/browser.ini
+--- a/dom/security/test/general/browser.ini
++++ b/dom/security/test/general/browser.ini
+@@ -1,2 +1,5 @@
+ [DEFAULT]
+ [browser_test_toplevel_data_navigations.js]
++support-files =
++  file_toplevel_data_navigations.sjs
++  file_toplevel_data_meta_redirect.html
+diff --git a/dom/security/test/general/browser_test_toplevel_data_navigations.js b/dom/security/test/general/browser_test_toplevel_data_navigations.js
+--- a/dom/security/test/general/browser_test_toplevel_data_navigations.js
++++ b/dom/security/test/general/browser_test_toplevel_data_navigations.js
+@@ -1,16 +1,54 @@
++/* eslint-disable mozilla/no-arbitrary-setTimeout */
++
+ "use strict";
+ 
+ const kDataBody = "toplevel navigation to data: URI allowed";
+ const kDataURI = "data:text/html,<body>" + kDataBody + "</body>";
++const kTestPath = getRootDirectory(gTestPath)
++                  .replace("chrome://mochitests/content", "http://example.com")
++const kRedirectURI = kTestPath + "file_toplevel_data_navigations.sjs";
++const kMetaRedirectURI = kTestPath + "file_toplevel_data_meta_redirect.html";
+ 
+-add_task(async function test_nav_data_uri_click() {
++add_task(async function test_nav_data_uri() {
+   await SpecialPowers.pushPrefEnv({
+     "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]],
+   });
+   await BrowserTestUtils.withNewTab(kDataURI, async function(browser) {
+     await ContentTask.spawn(gBrowser.selectedBrowser, {kDataBody}, async function({kDataBody}) { // eslint-disable-line
+      is(content.document.body.innerHTML, kDataBody,
+         "data: URI navigation from system should be allowed");
+     });
+   });
+ });
++
++add_task(async function test_nav_data_uri_redirect() {
++  await SpecialPowers.pushPrefEnv({
++    "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]],
++  });
++  let tab = BrowserTestUtils.addTab(gBrowser, kRedirectURI);
++  registerCleanupFunction(async function() {
++    await BrowserTestUtils.removeTab(tab);
++  });
++  // wait to make sure data: URI did not load before checking that it got blocked
++  await new Promise(resolve => setTimeout(resolve, 500));
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
++    is(content.document.body.innerHTML, "",
++       "data: URI navigation after server redirect should be blocked");
++  });
++});
++
++add_task(async function test_nav_data_uri_meta_redirect() {
++  await SpecialPowers.pushPrefEnv({
++    "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]],
++  });
++  let tab = BrowserTestUtils.addTab(gBrowser, kMetaRedirectURI);
++  registerCleanupFunction(async function() {
++    await BrowserTestUtils.removeTab(tab);
++  });
++  // wait to make sure data: URI did not load before checking that it got blocked
++  await new Promise(resolve => setTimeout(resolve, 500));
++  await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
++    is(content.document.body.innerHTML, "",
++       "data: URI navigation after meta redirect should be blocked");
++  });
++});
+diff --git a/dom/security/test/general/file_toplevel_data_meta_redirect.html b/dom/security/test/general/file_toplevel_data_meta_redirect.html
+new file mode 100644
+--- /dev/null
++++ b/dom/security/test/general/file_toplevel_data_meta_redirect.html
+@@ -0,0 +1,10 @@
++<html>
++<body>
++<head>
++  <meta http-equiv="refresh"
++        content="0; url='data:text/html,<body>toplevel meta redirect to data: URI should be blocked</body>'">
++</head>
++<body>
++Meta Redirect to data: URI
++</body>
++</html>
+diff --git a/dom/security/test/general/file_toplevel_data_navigations.sjs b/dom/security/test/general/file_toplevel_data_navigations.sjs
+new file mode 100644
+--- /dev/null
++++ b/dom/security/test/general/file_toplevel_data_navigations.sjs
+@@ -0,0 +1,14 @@
++// Custom *.sjs file specifically for the needs of Bug:
++// Bug 1394554 - Block toplevel data: URI navigations after redirect
++
++var DATA_URI =
++  "data:text/html,<body>toplevel data: URI navigations after redirect should be blocked</body>";
++
++function handleRequest(request, response)
++{
++  // avoid confusing cache behaviors
++  response.setHeader("Cache-Control", "no-cache", false);
++
++  response.setStatusLine("1.1", 302, "Found");
++  response.setHeader("Location", DATA_URI, false);
++}

+ 2951 - 0
frg/work-js/mozilla-release/patches/1398981-59a1.patch

@@ -0,0 +1,2951 @@
+# HG changeset patch
+# User Jessica Jong <jjong@mozilla.com>
+# Date 1515140258 -28800
+# Node ID b647cd8f54368a02c67c14025d8e0a21ae01d18c
+# Parent  8f1d9214610e691d004e165ff08c01241361f60d
+Bug 1398981 - Turn off webcomponents pref by default when running tests. r=smaug
+
+diff --git a/accessible/tests/mochitest/elm/a11y.ini b/accessible/tests/mochitest/elm/a11y.ini
+--- a/accessible/tests/mochitest/elm/a11y.ini
++++ b/accessible/tests/mochitest/elm/a11y.ini
+@@ -8,8 +8,9 @@ support-files =
+ skip-if = buildapp == 'mulet'
+ [test_figure.html]
+ [test_listbox.xul]
+ [test_MathMLSpec.html]
+ [test_nsApplicationAcc.html]
+ [test_canvas.html]
+ [test_shadowroot.html]
+ skip-if = stylo # bug 1293844
++support-files = test_shadowroot_subframe.html
+diff --git a/accessible/tests/mochitest/elm/test_shadowroot.html b/accessible/tests/mochitest/elm/test_shadowroot.html
+--- a/accessible/tests/mochitest/elm/test_shadowroot.html
++++ b/accessible/tests/mochitest/elm/test_shadowroot.html
+@@ -3,57 +3,38 @@
+ <head>
+   <title>ShadowRoot tests</title>
+   <link rel="stylesheet" type="text/css"
+         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ 
+   <script type="application/javascript"
+           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ 
+-  <script type="application/javascript"
+-          src="../common.js"></script>
+-  <script type="application/javascript"
+-          src="../role.js"></script>
+-
+-  <script type="application/javascript">
+-    function doTest() {
+-      testElm("component", {
+-        role: ROLE_GROUPING,
+-        children: [
+-        {
+-          role: ROLE_PUSHBUTTON,
+-        },
+-        {
+-          role: ROLE_LINK,
+-        },
+-        ]
+-      });
+-
+-      SimpleTest.finish();
+-    }
+-
+-    SimpleTest.waitForExplicitFinish();
+-    addA11yLoadEvent(doTest);
+-  </script>
+ </head>
+ <body>
+ 
+   <a target="_blank"
+     title="Ensure accessible objects are created for shadow root"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=1026125">
+      Mozilla Bug 1026125
+   </a><br/>
+   <p id="display"></p>
+   <div id="content" style="display: none"></div>
+   <pre id="test">
+   </pre>
+ 
+-  <div role="group" id="component"></div>
+   <script>
+-    var component = document.getElementById("component");
+-    var shadow = component.createShadowRoot();
+-
+-    shadow.innerHTML = "<button>Hello</button>" +
+-      '<a href="#"> World</a>';
++    SimpleTest.waitForExplicitFinish();
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, function() {
++      // This test loads in an iframe, to ensure that the element instance is
++      // loaded with the correct value of the preference.
++      var iframe = document.createElement("iframe");
++      iframe.src = "test_shadowroot_subframe.html";
++      document.body.appendChild(iframe);
++    });
+   </script>
+ 
+ </body>
+ </html>
+diff --git a/accessible/tests/mochitest/elm/test_shadowroot_subframe.html b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html
+new file mode 100644
+--- /dev/null
++++ b/accessible/tests/mochitest/elm/test_shadowroot_subframe.html
+@@ -0,0 +1,43 @@
++<!DOCTYPE HTML>
++<html>
++<head>
++  <title>ShadowRoot tests</title>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
++  <script type="application/javascript" src="../common.js"></script>
++  <script type="application/javascript" src="../role.js"></script>
++
++  <script type="application/javascript">
++    let SimpleTest = window.parent.SimpleTest;
++    let ok = window.parent.ok;
++    let is = window.parent.is;
++
++    function doTest() {
++      testElm("component", {
++        role: ROLE_GROUPING,
++        children: [
++        {
++          role: ROLE_PUSHBUTTON,
++        },
++        {
++          role: ROLE_LINK,
++        },
++        ]
++      });
++
++      SimpleTest.finish();
++    }
++
++    addA11yLoadEvent(doTest);
++  </script>
++
++</head>
++<body>
++  <div role="group" id="component"></div>
++  <script>
++    var component = document.getElementById("component");
++    var shadow = component.attachShadow({mode: "open"});
++
++    shadow.innerHTML = "<button>Hello</button>" +
++      '<a href="#"> World</a>';
++  </script>
++</body>
+diff --git a/accessible/tests/mochitest/hittest/a11y.ini b/accessible/tests/mochitest/hittest/a11y.ini
+--- a/accessible/tests/mochitest/hittest/a11y.ini
++++ b/accessible/tests/mochitest/hittest/a11y.ini
+@@ -4,11 +4,12 @@ support-files = zoom_tree.xul
+   !/accessible/tests/mochitest/letters.gif
+ 
+ [test_browser.html]
+ [test_canvas_hitregion.html]
+ skip-if = (os == "android" || appname == "b2g")
+ [test_general.html]
+ [test_menu.xul]
+ [test_shadowroot.html]
++support-files = test_shadowroot_subframe.html
+ [test_zoom.html]
+ [test_zoom_text.html]
+ [test_zoom_tree.xul]
+diff --git a/accessible/tests/mochitest/hittest/test_shadowroot.html b/accessible/tests/mochitest/hittest/test_shadowroot.html
+--- a/accessible/tests/mochitest/hittest/test_shadowroot.html
++++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
+@@ -3,70 +3,38 @@
+ <head>
+   <title>ShadowRoot hit tests</title>
+   <link rel="stylesheet" type="text/css"
+         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+ 
+   <script type="application/javascript"
+           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ 
+-  <script type="application/javascript"
+-          src="../common.js"></script>
+-  <script type="application/javascript"
+-          src="../layout.js"></script>
+-
+-  <script type="application/javascript">
+-    function doTest() {
+-      var componentAcc = getAccessible("component1");
+-      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+-                       componentAcc.firstChild);
+-
+-      componentAcc = getAccessible("component2");
+-      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
+-                       componentAcc.firstChild);
+-      SimpleTest.finish();
+-    }
+-
+-    SimpleTest.waitForExplicitFinish();
+-    addA11yLoadEvent(doTest);
+-  </script>
+ </head>
+ <body>
+ 
+   <a target="_blank"
+      title="Test getChildAtPoint works for shadow DOM content"
+      href="https://bugzilla.mozilla.org/show_bug.cgi?id=1027315">
+     Mozilla Bug 1027315
+   </a><br/>
+   <p id="display"></p>
+   <div id="content" style="display: none"></div>
+   <pre id="test">
+   </pre>
+ 
+-  <div role="group" class="components" id="component1" style="display: inline-block;">
+-  <!--
+-    <div role="button" id="component-child"
+-         style="width: 100px; height: 100px; background-color: pink;">
+-    </div>
+-  -->
+-  </div>
+-  <div role="group" class="components"  id="component2" style="display: inline-block;">
+-  <!--
+-    <button>Hello world</button>
+-  -->
+-  </div>
+   <script>
+-    // This routine adds the comment children of each 'component' to its
+-    // shadow root.
+-    var components = document.querySelectorAll(".components");
+-    for (var i = 0; i < components.length; i++) {
+-      var component = components[i];
+-      var shadow = component.createShadowRoot();
+-      for (var child = component.firstChild; child; child = child.nextSibling) {
+-        if (child.nodeType === 8)
+-          // eslint-disable-next-line no-unsanitized/property
+-          shadow.innerHTML = child.data;
+-      }
+-    }
++    SimpleTest.waitForExplicitFinish();
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, function() {
++      // This test loads in an iframe, to ensure that the element instance is
++      // loaded with the correct value of the preference.
++      var iframe = document.createElement("iframe");
++      iframe.src = "test_shadowroot_subframe.html";
++      document.body.appendChild(iframe);
++    });
+   </script>
+ 
+ </body>
+ </html>
+diff --git a/accessible/tests/mochitest/hittest/test_shadowroot_subframe.html b/accessible/tests/mochitest/hittest/test_shadowroot_subframe.html
+new file mode 100644
+--- /dev/null
++++ b/accessible/tests/mochitest/hittest/test_shadowroot_subframe.html
+@@ -0,0 +1,58 @@
++<!DOCTYPE HTML>
++<html>
++<head>
++  <title>ShadowRoot hit tests</title>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
++  <script type="application/javascript" src="../common.js"></script>
++  <script type="application/javascript" src="../layout.js"></script>
++
++  <script type="application/javascript">
++    let SimpleTest = window.parent.SimpleTest;
++    let ok = window.parent.ok;
++    let is = window.parent.is;
++
++    function doTest() {
++      var componentAcc = getAccessible("component1");
++      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
++                       componentAcc.firstChild);
++
++      componentAcc = getAccessible("component2");
++      testChildAtPoint(componentAcc, 1, 1, componentAcc.firstChild,
++                       componentAcc.firstChild);
++      SimpleTest.finish();
++    }
++
++    SimpleTest.waitForExplicitFinish();
++    addA11yLoadEvent(doTest);
++  </script>
++
++</head>
++<body>
++  <div role="group" class="components" id="component1" style="display: inline-block;">
++  <!--
++    <div role="button" id="component-child"
++         style="width: 100px; height: 100px; background-color: pink;">
++    </div>
++  -->
++  </div>
++  <div role="group" class="components"  id="component2" style="display: inline-block;">
++  <!--
++    <button>Hello world</button>
++  -->
++  </div>
++  <script>
++    // This routine adds the comment children of each 'component' to its
++    // shadow root.
++    var components = document.querySelectorAll(".components");
++    for (var i = 0; i < components.length; i++) {
++      var component = components[i];
++      var shadow = component.attachShadow({mode: "open"});
++      for (var child = component.firstChild; child; child = child.nextSibling) {
++        if (child.nodeType === 8)
++          // eslint-disable-next-line no-unsanitized/property
++          shadow.innerHTML = child.data;
++      }
++    }
++  </script>
++</body>
++</html>
+diff --git a/accessible/tests/mochitest/treeupdate/a11y.ini b/accessible/tests/mochitest/treeupdate/a11y.ini
+--- a/accessible/tests/mochitest/treeupdate/a11y.ini
++++ b/accessible/tests/mochitest/treeupdate/a11y.ini
+@@ -10,16 +10,17 @@ support-files =
+ [test_bug883708.xhtml]
+ [test_bug884251.xhtml]
+ [test_bug895082.html]
+ [test_bug1040735.html]
+ [test_bug1100602.html]
+ [test_bug1175913.html]
+ [test_bug1189277.html]
+ [test_bug1276857.html]
++support-files = test_bug1276857_subframe.html
+ [test_canvas.html]
+ [test_colorpicker.xul]
+ [test_contextmenu.xul]
+ [test_cssoverflow.html]
+ [test_deck.xul]
+ [test_doc.html]
+ [test_gencontent.html]
+ [test_general.html]
+diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857.html b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+--- a/accessible/tests/mochitest/treeupdate/test_bug1276857.html
++++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+@@ -13,80 +13,89 @@
+           src="../common.js"></script>
+   <script type="application/javascript"
+           src="../role.js"></script>
+   <script type="application/javascript"
+           src="../events.js"></script>
+ 
+   <script type="application/javascript">
+     function runTest() {
++      let iframe = document.getElementById("iframe");
++
+       // children change will recreate the table
+       this.eventSeq = [
+-        new invokerChecker(EVENT_REORDER, getNode("c1"))
++        new invokerChecker(EVENT_REORDER, () => {
++          let doc = getNode("iframe").contentDocument;
++          return doc && doc.getElementById("c1");
++        })
+       ];
+ 
+       this.invoke = function runTest_invoke() {
+         var tree = {
+           SECTION: [ // c1
+             { TEXT_LEAF: [] }, // Some text
+             { TEXT_CONTAINER: [
+               { TEXT_LEAF: [] } // something with ..
+             ] },
+             { TEXT_LEAF: [] } // More text
+           ]
+         };
+-        testAccessibleTree("c1", tree);
++        testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree);
+ 
+-        getNode("c1_t").querySelector("span").remove();
++        iframe.contentDocument.getElementById("c1_t").querySelector("span").remove();
+       };
+ 
+       this.finalCheck = function runTest_finalCheck() {
+         var tree = {
+           SECTION: [ // c1
+             { TEXT_LEAF: [] }, // Some text
+             { TEXT_LEAF: [] } // More text
+           ]
+         };
+-        testAccessibleTree("c1", tree);
++        testAccessibleTree(iframe.contentDocument.getElementById("c1"), tree);
+       };
+ 
+       this.getID = function runTest_getID() {
+         return "child DOM node is removed before the layout notifies the a11y about parent removal/show";
+       };
+     }
+ 
+     function runShadowTest() {
+       // children change will recreate the table
+       this.eventSeq = [
+-        new invokerChecker(EVENT_REORDER, "c2")
++        new invokerChecker(EVENT_REORDER, () => {
++          let doc = getNode("iframe").contentDocument;
++          return doc && doc.getElementById("c2");
++        })
+       ];
+ 
+       this.invoke = function runShadowTest_invoke() {
+         var tree = {
+           SECTION: [ // c2
+             { TEXT_LEAF: [] }, // Some text
+             { TEXT_CONTAINER: [
+               { TEXT_LEAF: [] } // something with ..
+             ] },
+             { TEXT_LEAF: [] } // More text
+           ]
+         };
+-        testAccessibleTree("c2", tree);
++        testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree);
+ 
+-        gShadowRoot.firstElementChild.querySelector("span").remove();
++        var shadowRoot = iframe.contentDocument.getElementById("c2_c").shadowRoot;
++        shadowRoot.firstElementChild.querySelector("span").remove();
+       };
+ 
+       this.finalCheck = function runShadowTest_finalCheck() {
+         var tree = {
+           SECTION: [ // c2
+             { TEXT_LEAF: [] }, // Some text
+             { TEXT_LEAF: [] } // More text
+           ]
+         };
+-        testAccessibleTree("c2", tree);
++        testAccessibleTree(iframe.contentDocument.getElementById("c2"), tree);
+       };
+ 
+       this.getID = function runShadowTest_getID() {
+         return "child DOM node is removed before the layout notifies the a11y about parent removal/show in shadow DOM";
+       };
+     }
+ 
+     // enableLogging("tree");
+@@ -96,44 +105,27 @@
+     function doTest() {
+       gQueue = new eventQueue();
+       gQueue.push(new runTest());
+       gQueue.push(new runShadowTest());
+       gQueue.invoke(); // will call SimpleTest.finish();
+     }
+ 
+     SimpleTest.waitForExplicitFinish();
+-    addA11yLoadEvent(doTest);
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, function() {
++      // This test loads in an iframe, to ensure that the element instance is
++      // loaded with the correct value of the preference.
++      let iframe = document.createElement("iframe");
++      iframe.id = "iframe";
++      iframe.src = "test_bug1276857_subframe.html";
++      addA11yLoadEvent(doTest, iframe.contentWindow);
++      document.body.appendChild(iframe);
++    });
+   </script>
+ 
+ </head>
+-
+ <body>
+-  <p id="display"></p>
+-  <div id="content" style="display: none"></div>
+-  <pre id="test">
+-  </pre>
+-
+-  <div id="c1">
+-    <div id="c1_t" style="display: table" role="presentation">
+-    Some text
+-    <span style="display: table-cell">something with accessibles goes here</span>
+-    More text
+-    </div>
+-  </div>
+-
+-  <template id="tmpl">
+-    <div style="display: table" role="presentation">
+-    Some text
+-    <span style="display: table-cell">something with accessibles goes here</span>
+-    More text
+-    </div>
+-  </template>
+-
+-  <div id="c2"><div id="c2_c" role="presentation"></div></div>
+-
+-  <script>
+-    var gShadowRoot = document.getElementById("c2_c").createShadowRoot();
+-    var tmpl = document.getElementById("tmpl");
+-    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
+-  </script>
+ </body>
+ </html>
+diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html b/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html
+new file mode 100644
+--- /dev/null
++++ b/accessible/tests/mochitest/treeupdate/test_bug1276857_subframe.html
+@@ -0,0 +1,33 @@
++<!DOCTYPE HTML>
++<html>
++<head>
++  <title>DOM mutations test</title>
++  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
++  <script type="application/javascript" src="../role.js"></script>
++</head>
++<body>
++  <div id="c1">
++    <div id="c1_t" style="display: table" role="presentation">
++    Some text
++    <span style="display: table-cell">something with accessibles goes here</span>
++    More text
++    </div>
++  </div>
++
++  <template id="tmpl">
++    <div style="display: table" role="presentation">
++    Some text
++    <span style="display: table-cell">something with accessibles goes here</span>
++    More text
++    </div>
++  </template>
++
++  <div id="c2"><div id="c2_c" role="presentation"></div></div>
++
++  <script>
++    var gShadowRoot = document.getElementById("c2_c").attachShadow({mode: "open"});
++    var tmpl = document.getElementById("tmpl");
++    gShadowRoot.appendChild(document.importNode(tmpl.content, true));
++  </script>
++</body>
++</html>
+diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini
+--- a/dom/base/test/mochitest.ini
++++ b/dom/base/test/mochitest.ini
+@@ -575,16 +575,17 @@ skip-if = toolkit == 'android' #bug 6870
+ [test_bug976673.html]
+ [test_bug982153.html]
+ [test_bug999456.html]
+ [test_bug1022229.html]
+ [test_bug1025933.html]
+ skip-if = stylo # bug 1293844
+ [test_bug1037687.html]
+ skip-if = stylo # bug 1293844
++support-files = test_bug1037687_subframe.html
+ [test_bug1043106.html]
+ [test_bug1057176.html]
+ [test_bug1060938.html]
+ [test_bug1064481.html]
+ [test_bug1070015.html]
+ [test_bug1075702.html]
+ [test_bug1091883.html]
+ [test_bug1101364.html]
+diff --git a/dom/base/test/test_bug1025933.html b/dom/base/test/test_bug1025933.html
+--- a/dom/base/test/test_bug1025933.html
++++ b/dom/base/test/test_bug1025933.html
+@@ -10,28 +10,38 @@ https://bugzilla.mozilla.org/show_bug.cg
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+   <script type="application/javascript">
+ 
+   /** Test for Bug 1025933 **/
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   function test() {
+-    var s = document.getElementById("host").createShadowRoot();
+-    s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
+-    var el = s.firstElementChild;
+-    is(el.clientWidth, 100);
+-    is(el.clientHeight, 100);
+-    SimpleTest.finish();
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, function() {
++      var iframe = document.createElement('iframe');
++      iframe.srcdoc = '<div id="content"> <div id="host"></div </div>';
++
++      iframe.onload = function() {
++        var s = iframe.contentDocument.getElementById("host").attachShadow({mode: 'open'});
++        s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
++        var el = s.firstElementChild;
++        is(el.clientWidth, 100);
++        is(el.clientHeight, 100);
++        SimpleTest.finish();
++      }
++
++      document.body.appendChild(iframe);
++    });
+   }
+ 
+   </script>
+ </head>
+ <body onload="test()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1025933">Mozilla Bug 1025933</a>
+ <p id="display"></p>
+-<div id="content">
+-  <div id="host"></div>
+-</div>
+ <pre id="test">
+ </pre>
+ </body>
+ </html>
+diff --git a/dom/base/test/test_bug1037687.html b/dom/base/test/test_bug1037687.html
+--- a/dom/base/test/test_bug1037687.html
++++ b/dom/base/test/test_bug1037687.html
+@@ -3,61 +3,34 @@
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1037687
+ -->
+ <head>
+   <meta charset="utf-8">
+   <title>Test for Bug 1037687</title>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+-  <script type="application/javascript">
+-
+-  /** Test for Bug 1037687 **/
+-
+-  SimpleTest.waitForExplicitFinish();
+-
+-  var host;
+-  var sr;
+-  var embed;
+-  var object;
+-  var iframe;
+-  var resourceLoadCount = 0;
+-
+-  function resourceLoaded(event) {
+-    ++resourceLoadCount;
+-    ok(true, event.target + " got " + event.load);
+-    if (resourceLoadCount == 3) {
+-      SimpleTest.finish();
+-    }
+-  }
+-
+-  function createResource(sr, type) {
+-    var el = document.createElement(type);
+-    var attrName = type == "object" ? "data" : "src";
+-    el.setAttribute(attrName, "file_mozfiledataurl_img.jpg");
+-    el.onload = resourceLoaded;
+-    var info = document.createElement("div");
+-    info.textContent = type;
+-    sr.appendChild(info);
+-    sr.appendChild(el);
+-  }
+-
+-  function test() {
+-    host = document.getElementById("host");
+-    sr = host.createShadowRoot();
+-    embed = createResource(sr, "embed");
+-    object = createResource(sr, "object");
+-    iframe = createResource(sr, "iframe");
+-  }
+-
+-  </script>
+ </head>
+-<body onload="test()">
++<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1037687">Mozilla Bug 1037687</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ </pre>
+-<div id="host"></div>
++<script type="application/javascript">
++  /** Test for Bug 1037687 **/
++  SimpleTest.waitForExplicitFinish();
++  SpecialPowers.pushPrefEnv({
++    set: [
++      ["dom.webcomponents.enabled", true]
++    ]
++  }, function() {
++    // This test loads in an iframe, to ensure that the element instance is
++    // loaded with the correct value of the preference.
++    let iframe = document.createElement("iframe");
++    iframe.src = "test_bug1037687_subframe.html";
++    document.body.appendChild(iframe);
++  });
++</script>
+ </body>
+ </html>
+diff --git a/dom/base/test/test_bug1037687_subframe.html b/dom/base/test/test_bug1037687_subframe.html
+new file mode 100644
+--- /dev/null
++++ b/dom/base/test/test_bug1037687_subframe.html
+@@ -0,0 +1,47 @@
++<!DOCTYPE html>
++<html>
++<head>
++<script type="application/javascript">
++  var SimpleTest = window.parent.SimpleTest;
++  var ok = window.parent.ok;
++  var is = window.parent.is;
++
++  var host;
++  var sr;
++  var embed;
++  var object;
++  var iframe;
++  var resourceLoadCount = 0;
++
++  function resourceLoaded(event) {
++    ++resourceLoadCount;
++    ok(true, event.target + " got " + event.load);
++    if (resourceLoadCount == 3) {
++      SimpleTest.finish();
++    }
++  }
++
++  function createResource(sr, type) {
++    var el = document.createElement(type);
++    var attrName = type == "object" ? "data" : "src";
++    el.setAttribute(attrName, "file_mozfiledataurl_img.jpg");
++    el.onload = resourceLoaded;
++    var info = document.createElement("div");
++    info.textContent = type;
++    sr.appendChild(info);
++    sr.appendChild(el);
++  }
++
++  function test() {
++    host = document.getElementById("host");
++    sr = host.attachShadow({mode: 'open'});
++    embed = createResource(sr, "embed");
++    object = createResource(sr, "object");
++    iframe = createResource(sr, "iframe");
++  }
++</script>
++</head>
++<body onload="test()">
++<div id="host"></div>
++</body>
++</html>
+diff --git a/dom/events/test/test_bug1079236.html b/dom/events/test/test_bug1079236.html
+--- a/dom/events/test/test_bug1079236.html
++++ b/dom/events/test/test_bug1079236.html
+@@ -8,48 +8,58 @@ https://bugzilla.mozilla.org/show_bug.cg
+   <title>Test for Bug 1079236</title>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+   <script type="application/javascript">
+ 
+   /** Test for Bug 1079236 **/
+ 
+-SimpleTest.waitForExplicitFinish();
+-SimpleTest.waitForFocus(runTests);
++  function runTests() {
++    var iframe = document.createElement('iframe');
++    document.body.appendChild(iframe);
++    iframe.contentDocument.body.innerHTML = '<div id="content"></div>';
+ 
+-function runTests() {
+-  var c = document.getElementById("content");
+-  var sr = c.createShadowRoot();
+-  sr.innerHTML = "<input type='file'" + ">";
+-  var file = sr.firstChild;
+-  is(file.type, "file");
+-  file.offsetLeft; // Flush layout because dispatching mouse events.
+-  document.body.onmousemove = function(e) {
+-    is(e.target, c, "Event target should be the element in non-Shadow DOM");
+-    if (e.originalTarget == file) {
+-      is(e.originalTarget, file,
+-         "type='file' implementation doesn't seem to have native anonymous content");
+-    } else {
+-      var wrapped = SpecialPowers.wrap(e.originalTarget);
+-      isnot(wrapped, file, "Shouldn't have the same event.target and event.originalTarget");
++    var c = iframe.contentDocument.getElementById("content");
++    var sr = c.attachShadow({mode: 'open'});
++    sr.innerHTML = "<input type='file'" + ">";
++    var file = sr.firstChild;
++    is(file.type, "file");
++    file.offsetLeft; // Flush layout because dispatching mouse events.
++    iframe.contentDocument.body.onmousemove = function(e) {
++      is(e.target, c, "Event target should be the element in non-Shadow DOM");
++      if (e.originalTarget == file) {
++        is(e.originalTarget, file,
++           "type='file' implementation doesn't seem to have native anonymous content");
++      } else {
++        var wrapped = SpecialPowers.wrap(e.originalTarget);
++        isnot(wrapped, file, "Shouldn't have the same event.target and event.originalTarget");
++      }
++
++      ok(!("composedTarget" in e), "Events shouldn't have composedTarget in non-chrome context!");
++      e = SpecialPowers.wrap(e);
++      var composedTarget = SpecialPowers.unwrap(e.composedTarget);
++      ok(composedTarget, file, "composedTarget should be the file object.");
++
++      SimpleTest.finish();
+     }
+ 
+-    ok(!("composedTarget" in e), "Events shouldn't have composedTarget in non-chrome context!");
+-    e = SpecialPowers.wrap(e);
+-    var composedTarget = SpecialPowers.unwrap(e.composedTarget);
+-    ok(composedTarget, file, "composedTarget should be the file object.");
+-
+-    SimpleTest.finish();
++    var r = file.getBoundingClientRect();
++    synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"}, iframe.contentWindow);
++    iframe.contentDocument.body.onmousemove = null;
+   }
+ 
+-  var r = file.getBoundingClientRect();
+-  synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"} );
+-  document.body.onmousemove = null;
+-}
++  SimpleTest.waitForExplicitFinish();
++  SimpleTest.waitForFocus(() => {
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, runTests);
++  });
+ 
+   </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079236">Mozilla Bug 1079236</a>
+ <p id="display"></p>
+ <div id="content">
+ 
+diff --git a/dom/events/test/test_bug1145910.html b/dom/events/test/test_bug1145910.html
+--- a/dom/events/test/test_bug1145910.html
++++ b/dom/events/test/test_bug1145910.html
+@@ -5,44 +5,54 @@ https://bugzilla.mozilla.org/show_bug.cg
+ -->
+ <head>
+   <title>Test for Bug 1145910</title>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+-<style>
+-div:active {
+-  color: rgb(0, 255, 0);
+-}
+-</style>
+-<div id="host">Foo</div>
++
+ <script type="application/javascript">
+ 
+ /** Test for Bug 1145910 **/
+-SimpleTest.waitForExplicitFinish();
++
++function runTests() {
+ 
+-SimpleTest.waitForFocus(function() {
+-  var host = document.getElementById("host");
+-  var shadow = host.createShadowRoot();
++  var iframe = document.createElement('iframe');
++  document.body.appendChild(iframe);
++  iframe.contentDocument.body.innerHTML =
++    '<style> div:active { color: rgb(0, 255, 0); } </style> <div id="host">Foo</div>';
++
++  var host = iframe.contentDocument.getElementById("host");
++  var shadow = host.attachShadow({mode: 'open'});
+   shadow.innerHTML = '<style>div:active { color: rgb(0, 255, 0); }</style><div id="inner">Bar</div>';
+   var inner = shadow.getElementById("inner");
++  var iframeWin = iframe.contentWindow;
+ 
+-  is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
+-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
++  is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+ 
+-  synthesizeMouseAtCenter(host, { type: "mousedown" });
++  synthesizeMouseAtCenter(host, { type: "mousedown" }, iframeWin);
+ 
+-  is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+-  is(window.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
++  is(iframeWin.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
+ 
+-  synthesizeMouseAtCenter(host, { type: "mouseup" });
++  synthesizeMouseAtCenter(host, { type: "mouseup" }, iframeWin);
+ 
+-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+-  is(window.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
++  is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
+ 
+   SimpleTest.finish();
++};
++
++SimpleTest.waitForExplicitFinish();
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({
++    set: [
++      ["dom.webcomponents.enabled", true]
++    ]
++  }, runTests);
+ });
+ 
+ </script>
+ </body>
+ </html>
+diff --git a/dom/events/test/test_bug1150308.html b/dom/events/test/test_bug1150308.html
+--- a/dom/events/test/test_bug1150308.html
++++ b/dom/events/test/test_bug1150308.html
+@@ -5,37 +5,49 @@ https://bugzilla.mozilla.org/show_bug.cg
+ -->
+ <head>
+   <title>Test for Bug 1150308</title>
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body>
+-<div id="host"><span id="distributeme">Foo</span></div>
+ <script type="application/javascript">
+ 
+ /** Test for Bug 1150308 **/
+-SimpleTest.waitForExplicitFinish();
+ 
+-SimpleTest.waitForFocus(function() {
+-  var host = document.getElementById("host");
+-  var shadow = host.createShadowRoot();
+-  shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><content></content></div>';
+-  var inner = shadow.getElementById("inner");
+-  var distributed = document.getElementById("distributeme");
++function runTests() {
++  var iframe = document.createElement('iframe');
++  document.body.appendChild(iframe);
++  iframe.contentDocument.body.innerHTML =
++    '<div id="host"><span id="distributeme">Foo</span></div>';
+ 
+-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+-
+-  synthesizeMouseAtCenter(distributed, { type: "mousedown" });
++  var host = iframe.contentDocument.getElementById("host");
++  var shadow = host.attachShadow({mode: 'open'});
++  shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><slot></slot></div>';
++  var inner = shadow.getElementById("inner");
++  var distributed = iframe.contentDocument.getElementById("distributeme");
++  var iframeWin = iframe.contentWindow;
+ 
+-  is(window.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
++
++  synthesizeMouseAtCenter(distributed, { type: "mousedown" }, iframeWin);
+ 
+-  synthesizeMouseAtCenter(distributed, { type: "mouseup" });
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+ 
+-  is(window.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
++  synthesizeMouseAtCenter(distributed, { type: "mouseup" }, iframeWin);
++
++  is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+ 
+   SimpleTest.finish();
++};
++
++SimpleTest.waitForExplicitFinish();
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({
++    set: [
++      ["dom.webcomponents.enabled", true]
++    ]
++  }, runTests);
+ });
+-
+ </script>
+ </body>
+ </html>
+diff --git a/dom/events/test/test_bug1264380.html b/dom/events/test/test_bug1264380.html
+--- a/dom/events/test/test_bug1264380.html
++++ b/dom/events/test/test_bug1264380.html
+@@ -1,54 +1,64 @@
+ <html>
+ <head>
+   <title>Test the dragstart event on the anchor in side shadow DOM</title>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ 
+-SimpleTest.waitForExplicitFinish();
+-
+-SpecialPowers.pushPrefEnv({"set": [
+-  ["dom.webcomponents.enabled", true]
+-]});
+-
+ function runTests()
+ {
+   let dragService = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
+     getService(SpecialPowers.Ci.nsIDragService);
+ 
+-  let shadow = document.querySelector('#outter').createShadowRoot();
+-  let target = document.createElement('a');
+-  let linkText = document.createTextNode("Drag me if you can!");
++  let iframe = document.createElement('iframe');
++  document.body.appendChild(iframe);
++  iframe.contentDocument.body.innerHTML = '<div id="outter"/>';
++
++  let iframeDoc = iframe.contentDocument;
++  let iframeWin = iframe.contentWindow;
++
++  let shadow = iframeDoc.querySelector('#outter').attachShadow({mode: 'open'});
++  let target = iframeDoc.createElement('a');
++  let linkText = iframeDoc.createTextNode("Drag me if you can!");
+   target.appendChild(linkText);
+   target.href = "http://www.mozilla.org/";
+   shadow.appendChild(target);
+ 
+   let dataTransfer;
+   let trapDrag = function(event) {
+     ok(true, "Got dragstart event");
+     dataTransfer = event.dataTransfer;
+     ok(dataTransfer, "DataTransfer object is available.");
+     is(dataTransfer.mozItemCount, 1, "initial link item count");
+     is(dataTransfer.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
+     is(dataTransfer.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
+   }
+ 
+   ok(!dragService.getCurrentSession(), "There shouldn't be a drag session!");
+-  window.addEventListener("dragstart", trapDrag, true);
+-  synthesizeMouse(target, 2, 2, { type: "mousedown" });
+-  synthesizeMouse(target, 11, 11, { type: "mousemove" });
+-  synthesizeMouse(target, 20, 20, { type: "mousemove" });
+-  window.removeEventListener("dragstart", trapDrag, true);
++  iframeWin.addEventListener("dragstart", trapDrag, true);
++  synthesizeMouse(target, 2, 2, { type: "mousedown" }, iframeWin);
++  synthesizeMouse(target, 11, 11, { type: "mousemove" }, iframeWin);
++  synthesizeMouse(target, 20, 20, { type: "mousemove" }, iframeWin);
++  iframeWin.removeEventListener("dragstart", trapDrag, true);
+   ok(dragService.getCurrentSession(), "Drag session is available.");
+   dragService.endDragSession(false);
+   ok(!dragService.getCurrentSession(), "There shouldn't be a drag session anymore!");
+   SimpleTest.finish();
+ }
+ 
++
++SimpleTest.waitForExplicitFinish();
++SimpleTest.waitForFocus(() => {
++  SpecialPowers.pushPrefEnv({
++    set: [
++      ["dom.webcomponents.enabled", true]
++    ]
++  }, runTests);
++});
++
+ </script>
+ 
+-<body onload="window.setTimeout(runTests, 0);">
+-<div id="outter"/>
++<body>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/general/test_interfaces.js b/dom/tests/mochitest/general/test_interfaces.js
+--- a/dom/tests/mochitest/general/test_interfaces.js
++++ b/dom/tests/mochitest/general/test_interfaces.js
+@@ -498,17 +498,17 @@ var interfaceNamesInGlobalScope = [
+     "HTMLProgressElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLQuoteElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLScriptElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLSelectElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+-    "HTMLSlotElement",
++    {name: "HTMLSlotElement", disabled: true},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLSourceElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLSpanElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLStyleElement",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "HTMLTableCaptionElement",
+@@ -822,17 +822,17 @@ var interfaceNamesInGlobalScope = [
+     "ServiceWorkerContainer",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "ServiceWorkerRegistration",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     {name: "ScopedCredential", disabled: true},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     {name: "ScopedCredentialInfo", disabled: true},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+-    "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
++    {name: "ShadowRoot", disabled: true},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "SharedWorker",
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     {name: "SimpleGestureEvent", xbl: true},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     {name: "SimpleTest", xbl: false},
+ // IMPORTANT: Do not change this list without review from a DOM peer!
+     "SourceBuffer",
+diff --git a/dom/tests/mochitest/webcomponents/head.js b/dom/tests/mochitest/webcomponents/head.js
+new file mode 100644
+--- /dev/null
++++ b/dom/tests/mochitest/webcomponents/head.js
+@@ -0,0 +1,30 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++/**
++ * Set dom.webcomponents.enabled pref to true and loads an iframe, to ensure
++ * that the Element instance is loaded with the correct value of the
++ * preference.
++ *
++ * @return {Promise} promise that resolves when iframe is loaded.
++ */
++function setWebComponentsPrefAndCreateIframe(aSrcDoc) {
++  return new Promise(function (aResolve, aReject) {
++    SpecialPowers.pushPrefEnv({
++      set: [
++        ["dom.webcomponents.enabled", true]
++      ]
++    }, () => {
++      let iframe = document.createElement("iframe");
++      iframe.onload = function () { aResolve(iframe.contentDocument); }
++      iframe.onerror = function () { aReject('Failed to load iframe'); }
++      if (aSrcDoc) {
++        iframe.srcdoc = aSrcDoc;
++      }
++      document.body.appendChild(iframe);
++    });
++  });
++}
+diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini
+--- a/dom/tests/mochitest/webcomponents/mochitest.ini
++++ b/dom/tests/mochitest/webcomponents/mochitest.ini
+@@ -1,19 +1,18 @@
+ [DEFAULT]
+ support-files =
+   inert_style.css
+   dummy_page.html
++  head.js
+ 
+ [test_bug900724.html]
+ [test_bug1017896.html]
+ [test_bug1176757.html]
+ [test_bug1276240.html]
+-[test_content_element.html]
+-skip-if = true # Triggers assertions about flattened tree inconsistencies and it's going away in bug 1418002.
+ [test_custom_element_callback_innerhtml.html]
+ [test_custom_element_htmlconstructor.html]
+ skip-if = os == 'android' # bug 1323645
+ support-files =
+   htmlconstructor_autonomous_tests.js
+   htmlconstructor_builtin_tests.js
+ [test_custom_element_in_shadow.html]
+ [test_custom_element_throw_on_dynamic_markup_insertion.html]
+diff --git a/dom/tests/mochitest/webcomponents/test_bug1176757.html b/dom/tests/mochitest/webcomponents/test_bug1176757.html
+--- a/dom/tests/mochitest/webcomponents/test_bug1176757.html
++++ b/dom/tests/mochitest/webcomponents/test_bug1176757.html
+@@ -1,40 +1,46 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1176757
+ -->
+ <head>
+   <title>Test for Bug 1176757</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+-	href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176757">Mozilla Bug 1176757</a>
++  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176757">Mozilla Bug 1176757</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ /** Test for Bug 1176757 **/
+-var element = document.createElement("a");
+-var shadowRoot = element.createShadowRoot();
+-var thrownException = false;
++SimpleTest.waitForExplicitFinish();
++setWebComponentsPrefAndCreateIframe()
++  .then((aDocument) => {
++    var element = aDocument.createElement("div");
++    var shadowRoot = element.attachShadow({mode: "open"});
++    var thrownException = false;
+ 
+-try {
+-  shadowRoot.cloneNode();
+-} catch(err) {
+-  thrownException = err;
+-}
++    try {
++      shadowRoot.cloneNode();
++    } catch(err) {
++      thrownException = err;
++    }
+ 
+-ok(thrownException !== false, "An exception should've been thrown");
+-is(thrownException.name, "DataCloneError", "A DataCloneError exception should've been thrown");
++    ok(thrownException !== false, "An exception should've been thrown");
++    is(thrownException.name, "DataCloneError", "A DataCloneError exception should've been thrown");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </pre>
+ </body>
+ </html>
+ 
+diff --git a/dom/tests/mochitest/webcomponents/test_bug1269155.html b/dom/tests/mochitest/webcomponents/test_bug1269155.html
+--- a/dom/tests/mochitest/webcomponents/test_bug1269155.html
++++ b/dom/tests/mochitest/webcomponents/test_bug1269155.html
+@@ -1,89 +1,95 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1269155
+ -->
+ <head>
+   <title>Test for Bug 1269155</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+-	href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269155">Mozilla Bug 1269155</a>
++  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1269155">Mozilla Bug 1269155</a>
+ <p id="display"></p>
+-<div id="content" style="display: none">
+ 
+-</div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ /** Test for Bug 1269155 **/
+-var host = document.querySelector('#content');
+- var root = host.createShadowRoot();
++SimpleTest.waitForExplicitFinish();
+ 
+- var header1 = document.createElement('h1');
+- header1.textContent = 'Shadow Header1';
++var content = '<div id="content" style="display: none"> </div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    var host = aDocument.querySelector('#content');
++    var root = host.attachShadow({mode: "open"});
+ 
+- var paragraph1 = document.createElement('p');
+- paragraph1.textContent = 'shadow text paragraph1';
++    var header1 = aDocument.createElement('h1');
++    header1.textContent = 'Shadow Header1';
+ 
+- root.appendChild(header1);
+- root.appendChild(paragraph1);
++    var paragraph1 = aDocument.createElement('p');
++    paragraph1.textContent = 'shadow text paragraph1';
++
++    root.appendChild(header1);
++    root.appendChild(paragraph1);
+ 
+-var root2 = paragraph1.createShadowRoot();
+-var header2 = document.createElement('h2');
+-header2.textContent = 'Shadow Header2';
++    var root2 = paragraph1.attachShadow({mode: "open"});
++    var header2 = aDocument.createElement('h2');
++    header2.textContent = 'Shadow Header2';
+ 
+-var paragraph2 = document.createElement('p');
+-paragraph2.textContent = 'shadow text paragraph2';
+-root2.appendChild(header2);
+-root2.appendChild(paragraph2);
++    var paragraph2 = aDocument.createElement('p');
++    paragraph2.textContent = 'shadow text paragraph2';
++    root2.appendChild(header2);
++    root2.appendChild(paragraph2);
+ 
+ 
+-var frag = document.createDocumentFragment();
+-var paragraph3 = document.createElement('p');
+-paragraph3.textContent = 'fragment paragraph3';
+-frag.appendChild(paragraph3);
++    var frag = aDocument.createDocumentFragment();
++    var paragraph3 = aDocument.createElement('p');
++    paragraph3.textContent = 'fragment paragraph3';
++    frag.appendChild(paragraph3);
+ 
+-var root3 = paragraph3.createShadowRoot();
+-var header4 = document.createElement('h2');
+-header4.textContent = 'Shadow Header3';
++    var root3 = paragraph3.attachShadow({mode: "open"});
++    var header4 = aDocument.createElement('h2');
++    header4.textContent = 'Shadow Header3';
+ 
+-var paragraph4 = document.createElement('p');
+-paragraph4.textContent = 'shadow text paragraph4';
++    var paragraph4 = aDocument.createElement('p');
++    paragraph4.textContent = 'shadow text paragraph4';
+ 
+-root3.appendChild(header4);
+-root3.appendChild(paragraph4);
++    root3.appendChild(header4);
++    root3.appendChild(paragraph4);
+ 
+-//shadow dom without compose
+-is(root.getRootNode(), root, "root.getRootNode() should be root.");
+-is(root2.getRootNode(), root2, "root2.getRootNode() should be root.");
+-is(root3.getRootNode(), root3, "root3.getRootNode() should be root.");
+-is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+-is(header2.getRootNode(), root2, "header1.getRootNode() should be root2.");
+-is(header4.getRootNode(), root3, "header1.getRootNode() should be root3.");
+-//shadow dom with compose
+-is(root.getRootNode({ composed: true }), document, "root.getRootNode() with composed flag should be document.");
+-is(root2.getRootNode({ composed: true }), document, "root2.getRootNode() with composed flag should be document.");
+-is(root3.getRootNode({ composed: true }), frag, "root3.getRootNode() with composed flag should be frag.");
+-is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
+-is(header2.getRootNode({ composed: true }) , document, "header2.getRootNode() with composed flag should be document.");
+-is(header4.getRootNode({ composed: true }) , frag, "head4.getRootNode() with composed flag should be frag.");
+-//dom without compose
+-is(host.getRootNode(), document, "host.getRootNode() should be document.");
+-is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
+-is(paragraph1.getRootNode(), root, "paragraph1.getRootNode() should be root.");
+-is(frag.getRootNode(), frag, "frag.getRootNode() should be frag.");
+-//dom with compose
+-is(host.getRootNode({ composed: true }) , document, "host.getRootNode() with composed flag should be document.");
+-is(header1.getRootNode({ composed: true }) , document, "header1.getRootNode() with composed flag should be document.");
+-is(paragraph1.getRootNode({ composed: true }) , document, "paragraph1.getRootNode() with composed flag should be document.");
+-is(frag.getRootNode({ composed: true }) , frag, "frag.getRootNode() with composed flag should be frag.");
++    //shadow dom without compose
++    is(root.getRootNode(), root, "root.getRootNode() should be root.");
++    is(root2.getRootNode(), root2, "root2.getRootNode() should be root.");
++    is(root3.getRootNode(), root3, "root3.getRootNode() should be root.");
++    is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
++    is(header2.getRootNode(), root2, "header1.getRootNode() should be root2.");
++    is(header4.getRootNode(), root3, "header1.getRootNode() should be root3.");
++    //shadow dom with compose
++    is(root.getRootNode({ composed: true }), aDocument, "root.getRootNode() with composed flag should be document.");
++    is(root2.getRootNode({ composed: true }), aDocument, "root2.getRootNode() with composed flag should be document.");
++    is(root3.getRootNode({ composed: true }), frag, "root3.getRootNode() with composed flag should be frag.");
++    is(header1.getRootNode({ composed: true }) , aDocument, "header1.getRootNode() with composed flag should be document.");
++    is(header2.getRootNode({ composed: true }) , aDocument, "header2.getRootNode() with composed flag should be document.");
++    is(header4.getRootNode({ composed: true }) , frag, "head4.getRootNode() with composed flag should be frag.");
++    //dom without compose
++    is(host.getRootNode(), aDocument, "host.getRootNode() should be document.");
++    is(header1.getRootNode(), root, "header1.getRootNode() should be root.");
++    is(paragraph1.getRootNode(), root, "paragraph1.getRootNode() should be root.");
++    is(frag.getRootNode(), frag, "frag.getRootNode() should be frag.");
++    //dom with compose
++    is(host.getRootNode({ composed: true }) , aDocument, "host.getRootNode() with composed flag should be document.");
++    is(header1.getRootNode({ composed: true }) , aDocument, "header1.getRootNode() with composed flag should be document.");
++    is(paragraph1.getRootNode({ composed: true }) , aDocument, "paragraph1.getRootNode() with composed flag should be document.");
++    is(frag.getRootNode({ composed: true }) , frag, "frag.getRootNode() with composed flag should be frag.");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </pre>
+ </body>
+ </html>
+ 
+diff --git a/dom/tests/mochitest/webcomponents/test_content_element.html b/dom/tests/mochitest/webcomponents/test_content_element.html
+deleted file mode 100644
+--- a/dom/tests/mochitest/webcomponents/test_content_element.html
++++ /dev/null
+@@ -1,131 +0,0 @@
+-<!DOCTYPE HTML>
+-<html>
+-<!--
+-https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+--->
+-<head>
+-  <title>Test for HTMLContent element</title>
+-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+-</head>
+-<body>
+-<div id="grabme"></div>
+-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+-<script>
+-// Create a ShadowRoot and append some nodes, containing an insertion point with a universal selector.
+-var shadow = $("grabme").createShadowRoot();
+-shadow.innerHTML = '<span><content id="point"></content></span>';
+-
+-// Get the insertion point from the ShadowRoot and check that child of host is distributed.
+-// Insertion point should match everything because the selector set is empty.
+-var insertionPoint = shadow.getElementById("point");
+-$("grabme").innerHTML = '<div id="distme"></div>';
+-var distNodes = insertionPoint.getDistributedNodes();
+-is(distNodes[0], $("distme"), "Child of bound content should be distributed into insertion point with universal selector.");
+-is(distNodes.length, 1, "Should only have one child distributed into insertion point.");
+-
+-// Add another node to bound content and make sure that the node list is static and does not change.
+-var someSpan = document.createElement("span");
+-$("grabme").appendChild(someSpan);
+-is(distNodes.length, 1, "NodeList from getDistributedNodes should be static.");
+-
+-// Test content select.
+-$("grabme").innerHTML = '<div id="first" class="tall"></div><div id="second" class="skinny"></div>';
+-shadow.innerHTML = '<span><content select=".tall" id="point"></content></span>';
+-var insertionPoint = shadow.getElementById("point");
+-distNodes = insertionPoint.getDistributedNodes();
+-is(distNodes.length, 1, "Insertion point should only match element with the 'tall' class.");
+-is(distNodes[0], $("first"), "Insertion point should only match element with the 'tall' class.");
+-
+-// Get rid of the select attribute and check that the insertion point matches everything.
+-insertionPoint.removeAttribute("select");
+-is(insertionPoint.getDistributedNodes().length, 2, "After removing the 'select' attribute, the insertion point should match everything.");
+-
+-// Set an invalid selector and make sure that nothing is matched.
+-insertionPoint.setAttribute("select", "div:first-child");
+-is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
+-
+-// all compound selectors must only be permitted simple selectors.
+-insertionPoint.setAttribute("select", "div:first-child, span");
+-is(insertionPoint.getDistributedNodes().length, 0, "Invalid selectors should match nothing.");
+-
+-// Test multiple compound selectors.
+-$("grabme").innerHTML = '<div id="first"></div><span id="second"></span><span data-match-me="pickme" id="third"></span>';
+-insertionPoint.setAttribute("select", "span[data-match-me=pickme], div");
+-distNodes = insertionPoint.getDistributedNodes();
+-is(distNodes.length, 2, "Insertion point selector should only match two nodes.");
+-is(distNodes[0], $("first"), "First child node should match selector.");
+-is(distNodes[1], $("third"), "Third child node should match selector.");
+-
+-// Test select property
+-insertionPoint.select = "#second, #third";
+-distNodes = insertionPoint.getDistributedNodes();
+-is(distNodes.length, 2, "Insertion point selector (set using property) should only match two nodes.");
+-is(distNodes[0], $("second"), "First child node should match selector.");
+-is(distNodes[1], $("third"), "Third child node should match selector.");
+-is(insertionPoint.select, "#second, #third", "select property should be transparent.");
+-
+-// Empty set of selectors should match everything.
+-insertionPoint.select = "";
+-is(insertionPoint.getDistributedNodes().length, 3, "Empty set of selectors (set using property) should match everything.");
+-
+-// Remove insertion point and make sure that the point does not have any nodes distributed.
+-$("grabme").innerHTML = '<div></div><span></span>';
+-insertionPoint.removeAttribute("select");
+-is(insertionPoint.getDistributedNodes().length, 2, "Insertion point with univeral selector should match two nodes.");
+-var insertionParent = insertionPoint.parentNode;
+-insertionParent.removeChild(insertionPoint);
+-is(insertionPoint.getDistributedNodes().length, 0, "Insertion point should match no nodes after removal.");
+-insertionParent.appendChild(insertionPoint);
+-is(insertionPoint.getDistributedNodes().length, 2, "Insertion point should match two nodes after appending.");
+-
+-// Test multiple insertion points and check tree order distribution of points.
+-// Append two divs and three spans into host.
+-$("grabme").innerHTML = '<div></div><span></span><div></div><span></span><span></span>';
+-shadow.innerHTML = '<content select="div" id="divpoint"></content><content select="div, span" id="allpoint"></content>';
+-// Insertion point matching div
+-var divPoint = shadow.getElementById("divpoint");
+-// Insertion point matching span and div
+-var allPoint = shadow.getElementById("allpoint");
+-
+-is(divPoint.getDistributedNodes().length, 2, "Two div nodes should be distributed into divPoint.");
+-is(allPoint.getDistributedNodes().length, 3, "Remaining nodes should be distributed into allPoint.");
+-
+-shadow.removeChild(allPoint);
+-is(divPoint.getDistributedNodes().length, 2, "Number of div distributed into insertion point should not change.");
+-is(allPoint.getDistributedNodes().length, 0, "Removed insertion point should not have any nodes.");
+-
+-shadow.insertBefore(allPoint, divPoint);
+-is(allPoint.getDistributedNodes().length, 5, "allPoint should have nodes distributed before divPoint.");
+-is(divPoint.getDistributedNodes().length, 0, "divPoint should have no distributed nodes because they are all distributed to allPoint.");
+-
+-// Make sure that fallback content are in the distributed nodes.
+-$("grabme").innerHTML = '<div id="one"></div><div id="two"></div>';
+-shadow.innerHTML = '<content select="#nothing" id="point"><span id="fallback"></span></content>';
+-insertionPoint = shadow.getElementById("point");
+-is(insertionPoint.getDistributedNodes().length, 1, "There should be one distributed node from fallback content.");
+-is(insertionPoint.getDistributedNodes()[0].id, "fallback", "Distributed node should be fallback content.");
+-
+-$("grabme").innerHTML = '';
+-shadow.innerHTML = '<content select="div" id="point"><span id="one"></span><span id="two"></span></content>';
+-insertionPoint = shadow.getElementById("point");
+-// Make sure that two fallback nodes are distributed into the insertion point.
+-is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
+-is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
+-is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
+-
+-// Append a node that gets matched by the insertion point, thus causing the fallback content to be removed.
+-var matchingDiv = document.createElement("div");
+-matchingDiv.id = "three";
+-$("grabme").appendChild(matchingDiv);
+-is(insertionPoint.getDistributedNodes().length, 1, "There should be one node distributed from the host.");
+-is(insertionPoint.getDistributedNodes()[0].id, "three", "Node distriubted from host should have id of three.");
+-
+-// Remove the matching node from the host and make sure that the fallback content gets distributed.
+-$("grabme").removeChild(matchingDiv);
+-is(insertionPoint.getDistributedNodes().length, 2, "There should be two distributed nodes from fallback content.");
+-is(insertionPoint.getDistributedNodes()[0].id, "one", "First distributed node should have an ID of one.");
+-is(insertionPoint.getDistributedNodes()[1].id, "two", "Second distributed node should have an ID of two.");
+-</script>
+-</body>
+-</html>
+diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+--- a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
++++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+@@ -1,120 +1,129 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1087460
+ -->
+ <head>
+   <title>Test for custom element callbacks in shadow DOM.</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
+-<div id="container"></div>
+ 
+ <script>
+ 
+-// Test callback for custom element when used after registration.
++SimpleTest.waitForExplicitFinish();
+ 
+-var connectedCallbackCount = 0;
+-var disconnectedCallbackCount = 0;
+-var attributeChangedCallbackCount = 0;
++var content = '<div id="container"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++
++    // Test callback for custom element when used after registration.
+ 
+-class Foo extends HTMLElement
+-{
+-  connectedCallback() {
+-    connectedCallbackCount++;
+-  }
++    var iframeWin = aDocument.defaultView;
++    var connectedCallbackCount = 0;
++    var disconnectedCallbackCount = 0;
++    var attributeChangedCallbackCount = 0;
+ 
+-  disconnectedCallback() {
+-    disconnectedCallbackCount++;
+-  }
++    class Foo extends iframeWin.HTMLElement
++    {
++      connectedCallback() {
++        connectedCallbackCount++;
++      }
+ 
+-  attributeChangedCallback(aName, aOldValue, aNewValue) {
+-    attributeChangedCallbackCount++;
+-  }
++      disconnectedCallback() {
++        disconnectedCallbackCount++;
++      }
+ 
+-  static get observedAttributes() {
+-    return ["data-foo"];
+-  }
+-}
++      attributeChangedCallback(aName, aOldValue, aNewValue) {
++        attributeChangedCallbackCount++;
++      }
+ 
+-customElements.define("x-foo", Foo);
++      static get observedAttributes() {
++        return ["data-foo"];
++      }
++    }
+ 
+-var container = document.getElementById("container");
+-var shadow = container.createShadowRoot();
+-var customElem = document.createElement("x-foo");
++    iframeWin.customElements.define("x-foo", Foo);
+ 
+-is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
+-customElem.setAttribute("data-foo", "bar");
+-is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
++    var container = aDocument.getElementById("container");
++    var shadow = container.attachShadow({mode: "open"});
++    var customElem = aDocument.createElement("x-foo");
+ 
+-is(connectedCallbackCount, 0, "connectedCallback should not be called on an element that is not in a document/composed document.");
+-shadow.appendChild(customElem);
+-is(connectedCallbackCount, 1, "connectedCallback should be called after attaching custom element to the composed document.");
++    is(attributeChangedCallbackCount, 0, "attributeChangedCallback should not be called after just creating an element.");
++    customElem.setAttribute("data-foo", "bar");
++    is(attributeChangedCallbackCount, 1, "attributeChangedCallback should be called after setting an attribute.");
+ 
+-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called without detaching custom element.");
+-shadow.removeChild(customElem);
+-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
++    is(connectedCallbackCount, 0, "connectedCallback should not be called on an element that is not in a document/composed document.");
++    shadow.appendChild(customElem);
++    is(connectedCallbackCount, 1, "connectedCallback should be called after attaching custom element to the composed document.");
+ 
+-// Test callback for custom element already in the composed doc when created.
++    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called without detaching custom element.");
++    shadow.removeChild(customElem);
++    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
+ 
+-connectedCallbackCount = 0;
+-disconnectedCallbackCount = 0;
+-attributeChangedCallbackCount = 0;
++    // Test callback for custom element already in the composed doc when created.
+ 
+-shadow.innerHTML = "<x-foo></x-foo>";
+-is(connectedCallbackCount, 1, "connectedCallback should be called after creating an element in the composed document.");
+-
+-shadow.innerHTML = "";
+-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
++    connectedCallbackCount = 0;
++    disconnectedCallbackCount = 0;
++    attributeChangedCallbackCount = 0;
+ 
+-// Test callback for custom element in shadow DOM when host attached/detached to/from document.
++    shadow.innerHTML = "<x-foo></x-foo>";
++    is(connectedCallbackCount, 1, "connectedCallback should be called after creating an element in the composed document.");
+ 
+-connectedCallbackCount = 0;
+-disconnectedCallbackCount = 0;
+-attributeChangedCallbackCount = 0;
++    shadow.innerHTML = "";
++    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after detaching custom element from the composed document.");
++
++    // Test callback for custom element in shadow DOM when host attached/detached to/from document.
+ 
+-var host = document.createElement("div");
+-shadow = host.createShadowRoot();
+-customElem = document.createElement("x-foo");
++    connectedCallbackCount = 0;
++    disconnectedCallbackCount = 0;
++    attributeChangedCallbackCount = 0;
++
++    var host = aDocument.createElement("div");
++    shadow = host.attachShadow({mode: "open"});
++    customElem = aDocument.createElement("x-foo");
+ 
+-is(connectedCallbackCount, 0, "connectedCallback should not be called on newly created element.");
+-shadow.appendChild(customElem);
+-is(connectedCallbackCount, 0, "connectedCallback should not be called on attaching to a tree that is not in the composed document.");
++    is(connectedCallbackCount, 0, "connectedCallback should not be called on newly created element.");
++    shadow.appendChild(customElem);
++    is(connectedCallbackCount, 0, "connectedCallback should not be called on attaching to a tree that is not in the composed document.");
+ 
+-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called.");
+-shadow.removeChild(customElem);
+-is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called when detaching from a tree that is not in the composed document.");
++    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called.");
++    shadow.removeChild(customElem);
++    is(disconnectedCallbackCount, 0, "disconnectedCallback should not be called when detaching from a tree that is not in the composed document.");
+ 
+-shadow.appendChild(customElem);
+-is(connectedCallbackCount, 0, "connectedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
++    shadow.appendChild(customElem);
++    is(connectedCallbackCount, 0, "connectedCallback should still not be called after reattaching to a shadow tree that is not in the composed document.");
++
++    container.appendChild(host);
++    is(connectedCallbackCount, 1, "connectedCallback should be called after host is inserted into document.");
+ 
+-container.appendChild(host);
+-is(connectedCallbackCount, 1, "connectedCallback should be called after host is inserted into document.");
++    container.removeChild(host);
++    is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after host is removed from document.");
+ 
+-container.removeChild(host);
+-is(disconnectedCallbackCount, 1, "disconnectedCallback should be called after host is removed from document.");
++    // Test callback for custom element for upgraded element.
+ 
+-// Test callback for custom element for upgraded element.
++    connectedCallbackCount = 0;
++    disconnectedCallbackCount = 0;
++    attributeChangedCallbackCount = 0;
+ 
+-connectedCallbackCount = 0;
+-disconnectedCallbackCount = 0;
+-attributeChangedCallbackCount = 0;
+-
+-shadow = container.shadowRoot;
+-shadow.innerHTML = "<x-bar></x-bar>";
++    shadow = container.shadowRoot;
++    shadow.innerHTML = "<x-bar></x-bar>";
+ 
+-class Bar extends HTMLElement {
+-  connectedCallback() {
+-    connectedCallbackCount++;
+-  }
+-};
++    class Bar extends iframeWin.HTMLElement {
++      connectedCallback() {
++        connectedCallbackCount++;
++      }
++    };
+ 
+-customElements.define("x-bar", Bar);
+-is(connectedCallbackCount, 1, "connectedCallback should be called after upgrading element in composed document.");
++    iframeWin.customElements.define("x-bar", Bar);
++    is(connectedCallbackCount, 1, "connectedCallback should be called after upgrading element in composed document.");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ 
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_detached_style.html b/dom/tests/mochitest/webcomponents/test_detached_style.html
+--- a/dom/tests/mochitest/webcomponents/test_detached_style.html
++++ b/dom/tests/mochitest/webcomponents/test_detached_style.html
+@@ -1,25 +1,35 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1062578
+ -->
+ <head>
+   <title>Test for creating style in shadow root of host not in document.</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+-<div id="grabme"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1062578">Bug 1062578</a>
+ <script>
+-var host = document.createElement("div");
+-var shadow = host.createShadowRoot();
+-shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
++
++SimpleTest.waitForExplicitFinish();
+ 
+-grabme.appendChild(host);
++var content = '<div id="grabme"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    var host = aDocument.createElement("div");
++    var shadow = host.attachShadow({mode: "open"});
++    shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
+ 
+-var inner = shadow.getElementById("inner");
+-is(getComputedStyle(inner, null).getPropertyValue("height"), "200px", "Style in shadow root should take effect.");
++    var iframeWin = aDocument.defaultView;
++    iframeWin.grabme.appendChild(host);
++
++    var inner = shadow.getElementById("inner");
++    is(iframeWin.getComputedStyle(inner, null).getPropertyValue("height"), "200px", "Style in shadow root should take effect.");
++
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+--- a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
++++ b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+@@ -1,36 +1,42 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1177991
+ -->
+ <head>
+   <title>Test for Bug 1177991</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1177991">Mozilla Bug 1177991</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+-var thrownException = false;
+-var shadowRoot = document.createElement('a').createShadowRoot();
++SimpleTest.waitForExplicitFinish();
++setWebComponentsPrefAndCreateIframe()
++  .then((aDocument) => {
++    var thrownException = false;
++    var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
+ 
+-try {
+-  document.adoptNode(shadowRoot);
+-} catch(err) {
+-  thrownException = err;
+-}
++    try {
++      aDocument.adoptNode(shadowRoot);
++    } catch(err) {
++      thrownException = err;
++    }
+ 
+-ok(thrownException !== false, "A HierarchyRequestError");
+-is(thrownException.name, "HierarchyRequestError", "A HierarchyRequestError should've been thrown");
++    ok(thrownException !== false, "A HierarchyRequestError");
++    is(thrownException.name, "HierarchyRequestError", "A HierarchyRequestError should've been thrown");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_document_importnode.html b/dom/tests/mochitest/webcomponents/test_document_importnode.html
+--- a/dom/tests/mochitest/webcomponents/test_document_importnode.html
++++ b/dom/tests/mochitest/webcomponents/test_document_importnode.html
+@@ -1,37 +1,42 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1177914
+ -->
+ <head>
+   <title>Test for Bug 1177914</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1177914">Mozilla Bug 1177914</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+-var thrownException = false;
+-var shadowRoot = document.createElement('a').createShadowRoot();
++SimpleTest.waitForExplicitFinish();
++setWebComponentsPrefAndCreateIframe()
++  .then((aDocument) => {
++    var thrownException = false;
++    var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
+ 
+-try {
+-  document.importNode(shadowRoot);
+-} catch(err) {
+-  thrownException = err;
+-}
++    try {
++      aDocument.importNode(shadowRoot);
++    } catch(err) {
++      thrownException = err;
++    }
+ 
++    ok(thrownException !== false, "A HierarchyRequestError");
++    is(thrownException.name, "NotSupportedError", "A NotSupportedError exception should've been thrown");
+ 
+-ok(thrownException !== false, "An exception should've been thrown");
+-is(thrownException.name, "NotSupportedError", "A NotSupportedError exception should've been thrown");
+-
++    SimpleTest.finish();
++  });
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_event_retarget.html b/dom/tests/mochitest/webcomponents/test_event_retarget.html
+--- a/dom/tests/mochitest/webcomponents/test_event_retarget.html
++++ b/dom/tests/mochitest/webcomponents/test_event_retarget.html
+@@ -1,147 +1,153 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=887541
+ -->
+ <head>
+   <title>Test for event retargeting in web components</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+ <script>
+ 
+-/*
+- * Creates an event listener with an expected event target.
+- */
+-function createEventListener(expectedTarget, msg) {
+-  return function(e) {
+-    is(e.target, expectedTarget, msg);
+-  };
+-}
++SimpleTest.waitForExplicitFinish();
++setWebComponentsPrefAndCreateIframe()
++  .then((aDocument) => {
++    /*
++     * Creates an event listener with an expected event target.
++     */
++    function createEventListener(expectedTarget, msg) {
++      return function(e) {
++        is(e.target, expectedTarget, msg);
++      };
++    }
+ 
+-/*
+- * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+- *
+- * <div elemThree> ---- <shadow-root shadowOne>
+- *        |                        |
+- * <div elemOne>            <content elemTwo>
+- *
+- * Dispatch event on elemOne
+- */
++    /*
++     * Test of event retargeting through a basic ShadowRoot with a content insertion point.
++     *
++     * <div elemThree> ---- <shadow-root shadowOne>
++     *        |                        |
++     * <div elemOne>            <content elemTwo>
++     *
++     * Dispatch event on elemOne
++     */
+ 
+-var elemOne = document.createElement("div");
+-var elemTwo = document.createElement("content");
+-var elemThree = document.createElement("div");
+-var shadowOne = elemThree.createShadowRoot();
++    var elemOne = aDocument.createElement("div");
++    var elemTwo = aDocument.createElement("content");
++    var elemThree = aDocument.createElement("div");
++    var shadowOne = elemThree.attachShadow({mode: "open"});
+ 
+-elemThree.appendChild(elemOne);
+-shadowOne.appendChild(elemTwo);
++    elemThree.appendChild(elemOne);
++    shadowOne.appendChild(elemTwo);
+ 
+-elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+-elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+-elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+-shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
++    elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
++    elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
++    elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
++    shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+ 
+-var customEvent = new CustomEvent("custom", { "bubbles" : true });
+-elemOne.dispatchEvent(customEvent);
++    var customEvent = new CustomEvent("custom", { "bubbles" : true });
++    elemOne.dispatchEvent(customEvent);
+ 
+-/*
+- * Test of event retargeting through a basic ShadowRoot with a content insertion point.
+- *
+- * <div elemThree> ---- <shadow-root shadowOne>
+- *        |                        |
+- * <div elemOne>            <content elemTwo>
+- *
+- * Dispatch event on elemTwo
+- */
++    /*
++     * Test of event retargeting through a basic ShadowRoot with a content insertion point.
++     *
++     * <div elemThree> ---- <shadow-root shadowOne>
++     *        |                        |
++     * <div elemOne>            <content elemTwo>
++     *
++     * Dispatch event on elemTwo
++     */
+ 
+-elemOne = document.createElement("div");
+-elemTwo = document.createElement("content");
+-elemThree = document.createElement("div");
+-shadowOne = elemThree.createShadowRoot();
++    elemOne = aDocument.createElement("div");
++    elemTwo = aDocument.createElement("content");
++    elemThree = aDocument.createElement("div");
++    shadowOne = elemThree.attachShadow({mode: "open"});
+ 
+-elemThree.appendChild(elemOne);
+-shadowOne.appendChild(elemTwo);
++    elemThree.appendChild(elemOne);
++    shadowOne.appendChild(elemTwo);
+ 
+-elemTwo.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of elemTwo."));
+-elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+-shadowOne.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of shadowOne."));
++    elemTwo.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of elemTwo."));
++    elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
++    shadowOne.addEventListener("custom", createEventListener(elemTwo, "elemTwo is in common ancestor tree of shadowOne."));
+ 
+-customEvent = new CustomEvent("custom", { "bubbles" : true });
+-elemTwo.dispatchEvent(customEvent);
++    customEvent = new CustomEvent("custom", { "bubbles" : true });
++    elemTwo.dispatchEvent(customEvent);
+ 
+-/*
+- * Test of event retargeting through a nested ShadowRoots with content insertion points.
+- *
+- * <div elemFive> --- <shadow-root shadowTwo>
+- *       |                       |
+- * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+- *                               |                        |
+- *                       <content elemTwo>       <content elemThree>
+- *
+- * Dispatch custom event on elemOne.
+- */
++    /*
++     * Test of event retargeting through a nested ShadowRoots with content insertion points.
++     *
++     * <div elemFive> --- <shadow-root shadowTwo>
++     *       |                       |
++     * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
++     *                               |                        |
++     *                       <content elemTwo>       <content elemThree>
++     *
++     * Dispatch custom event on elemOne.
++     */
+ 
+-elemOne = document.createElement("div");
+-elemTwo = document.createElement("content");
+-elemThree = document.createElement("content");
+-var elemFour = document.createElement("div");
+-var elemFive = document.createElement("div");
+-var shadowTwo = elemFive.createShadowRoot();
+-shadowOne = elemFour.createShadowRoot();
++    elemOne = aDocument.createElement("div");
++    elemTwo = aDocument.createElement("content");
++    elemThree = aDocument.createElement("content");
++    var elemFour = aDocument.createElement("div");
++    var elemFive = aDocument.createElement("div");
++    var shadowTwo = elemFive.attachShadow({mode: "open"});
++    shadowOne = elemFour.attachShadow({mode: "open"});
+ 
+-elemFive.appendChild(elemOne);
+-shadowTwo.appendChild(elemFour);
+-elemFour.appendChild(elemTwo);
+-shadowOne.appendChild(elemThree);
++    elemFive.appendChild(elemOne);
++    shadowTwo.appendChild(elemFour);
++    elemFour.appendChild(elemTwo);
++    shadowOne.appendChild(elemThree);
+ 
+-elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
+-elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
+-elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
+-elemFour.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFour."));
+-elemFive.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFive."));
+-shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
+-shadowTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowTwo."));
++    elemOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemOne."));
++    elemTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemTwo."));
++    elemThree.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemThree."));
++    elemFour.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFour."));
++    elemFive.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of elemFive."));
++    shadowOne.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowOne."));
++    shadowTwo.addEventListener("custom", createEventListener(elemOne, "elemOne is in common ancestor tree of shadowTwo."));
++
++    customEvent = new CustomEvent("custom", { "bubbles" : true });
++    elemOne.dispatchEvent(customEvent);
+ 
+-customEvent = new CustomEvent("custom", { "bubbles" : true });
+-elemOne.dispatchEvent(customEvent);
+-
+-/*
+- * Test of event retargeting through a nested ShadowRoots with content insertion points.
+- *
+- * <div elemFive> --- <shadow-root shadowTwo>
+- *       |                       |
+- * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
+- *                               |                        |
+- *                       <content elemTwo>       <content elemThree>
+- *
+- * Dispatch custom event on elemThree.
+- */
++    /*
++     * Test of event retargeting through a nested ShadowRoots with content insertion points.
++     *
++     * <div elemFive> --- <shadow-root shadowTwo>
++     *       |                       |
++     * <div elemOne>          <div elemFour> ----- <shadow-root shadowOne>
++     *                               |                        |
++     *                       <content elemTwo>       <content elemThree>
++     *
++     * Dispatch custom event on elemThree.
++     */
+ 
+-elemOne = document.createElement("div");
+-elemTwo = document.createElement("content");
+-elemThree = document.createElement("content");
+-elemFour = document.createElement("div");
+-elemFive = document.createElement("div");
+-shadowTwo = elemFive.createShadowRoot();
+-shadowOne = elemFour.createShadowRoot();
++    elemOne = aDocument.createElement("div");
++    elemTwo = aDocument.createElement("content");
++    elemThree = aDocument.createElement("content");
++    elemFour = aDocument.createElement("div");
++    elemFive = aDocument.createElement("div");
++    shadowTwo = elemFive.attachShadow({mode: "open"});
++    shadowOne = elemFour.attachShadow({mode: "open"});
+ 
+-elemFive.appendChild(elemOne);
+-shadowTwo.appendChild(elemFour);
+-elemFour.appendChild(elemTwo);
+-shadowOne.appendChild(elemThree);
++    elemFive.appendChild(elemOne);
++    shadowTwo.appendChild(elemFour);
++    elemFour.appendChild(elemTwo);
++    shadowOne.appendChild(elemThree);
+ 
+-elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
+-elemFour.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of elemFour."));
+-elemFive.addEventListener("custom", createEventListener(elemFive, "elemFive is in common ancestor tree of elemFive."));
+-shadowOne.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of shadowOne."));
+-shadowTwo.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of shadowTwo."));
++    elemThree.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of elemThree."));
++    elemFour.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of elemFour."));
++    elemFive.addEventListener("custom", createEventListener(elemFive, "elemFive is in common ancestor tree of elemFive."));
++    shadowOne.addEventListener("custom", createEventListener(elemThree, "elemThree is in common ancestor tree of shadowOne."));
++    shadowTwo.addEventListener("custom", createEventListener(elemFour, "elemFour is in common ancestor tree of shadowTwo."));
+ 
+-customEvent = new CustomEvent("custom", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
++    customEvent = new CustomEvent("custom", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_event_stopping.html b/dom/tests/mochitest/webcomponents/test_event_stopping.html
+--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
++++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
+@@ -1,168 +1,174 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=887541
+ -->
+ <head>
+   <title>Test for event model in web components</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+ <script>
+ 
+ var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
+             .getService(SpecialPowers.Ci.nsIEventListenerService);
+ 
+-function eventListener(e) {
+-  eventChain.push(this);
+-}
++SimpleTest.waitForExplicitFinish();
++setWebComponentsPrefAndCreateIframe()
++  .then((aDocument) => {
++    function eventListener(e) {
++      eventChain.push(this);
++    }
+ 
+-function isEventChain(actual, expected, msg) {
+-  is(actual.length, expected.length, msg);
+-  for (var i = 0; i < expected.length; i++) {
+-    is(actual[i], expected[i], msg + " at " + i);
+-  }
+-
+-  if (0 < actual.length) {
+-    var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
+-    ok(expected.length < chain.length, "There should be additional chrome event targets.");
+-  }
+-}
++    function isEventChain(actual, expected, msg) {
++      is(actual.length, expected.length, msg);
++      for (var i = 0; i < expected.length; i++) {
++        is(actual[i], expected[i], msg + " at " + i);
++      }
+ 
+-/*
+- * <div elemOne> ------ <shadow-root shadowOne>
+- *                                 |
+- *                          <span elemTwo>
+- *                                 |
+- *                         <span elemThree>
+- */
++      if (0 < actual.length) {
++        var chain = els.getEventTargetChainFor(actual[0], false); // Events should be dispatched on actual[0].
++        ok(expected.length < chain.length, "There should be additional chrome event targets.");
++      }
++    }
+ 
+-var elemOne = document.createElement("div");
+-var elemTwo = document.createElement("span");
+-var elemThree = document.createElement("span");
+-var shadowOne = elemOne.createShadowRoot();
++    /*
++     * <div elemOne> ------ <shadow-root shadowOne>
++     *                                 |
++     *                          <span elemTwo>
++     *                                 |
++     *                         <span elemThree>
++     */
+ 
+-shadowOne.appendChild(elemTwo);
+-elemTwo.appendChild(elemThree);
++    var elemOne = aDocument.createElement("div");
++    var elemTwo = aDocument.createElement("span");
++    var elemThree = aDocument.createElement("span");
++    var shadowOne = elemOne.attachShadow({mode: "open"});
+ 
+-// Test stopping "abort" event.
++    shadowOne.appendChild(elemTwo);
++    elemTwo.appendChild(elemThree);
++
++    // Test stopping "abort" event.
+ 
+-elemOne.addEventListener("abort", eventListener);
+-elemTwo.addEventListener("abort", eventListener);
+-elemThree.addEventListener("abort", eventListener);
+-shadowOne.addEventListener("abort", eventListener);
++    elemOne.addEventListener("abort", eventListener);
++    elemTwo.addEventListener("abort", eventListener);
++    elemThree.addEventListener("abort", eventListener);
++    shadowOne.addEventListener("abort", eventListener);
+ 
+-var eventChain = [];
++    var eventChain = [];
+ 
+-var customEvent = new CustomEvent("abort", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that abort event is stopped at shadow root.");
++    var customEvent = new CustomEvent("abort", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that abort event is stopped at shadow root.");
+ 
+-// Test stopping "error" event.
++    // Test stopping "error" event.
+ 
+-elemOne.addEventListener("error", eventListener);
+-elemTwo.addEventListener("error", eventListener);
+-elemThree.addEventListener("error", eventListener);
+-shadowOne.addEventListener("error", eventListener);
++    elemOne.addEventListener("error", eventListener);
++    elemTwo.addEventListener("error", eventListener);
++    elemThree.addEventListener("error", eventListener);
++    shadowOne.addEventListener("error", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("error", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that error event is stopped at shadow root.");
++    customEvent = new CustomEvent("error", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that error event is stopped at shadow root.");
+ 
+-// Test stopping "select" event.
++    // Test stopping "select" event.
+ 
+-elemOne.addEventListener("select", eventListener);
+-elemTwo.addEventListener("select", eventListener);
+-elemThree.addEventListener("select", eventListener);
+-shadowOne.addEventListener("select", eventListener);
++    elemOne.addEventListener("select", eventListener);
++    elemTwo.addEventListener("select", eventListener);
++    elemThree.addEventListener("select", eventListener);
++    shadowOne.addEventListener("select", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("select", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that select event is stopped at shadow root.");
++    customEvent = new CustomEvent("select", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that select event is stopped at shadow root.");
+ 
+-// Test stopping "change" event.
++    // Test stopping "change" event.
+ 
+-elemOne.addEventListener("change", eventListener);
+-elemTwo.addEventListener("change", eventListener);
+-elemThree.addEventListener("change", eventListener);
+-shadowOne.addEventListener("change", eventListener);
++    elemOne.addEventListener("change", eventListener);
++    elemTwo.addEventListener("change", eventListener);
++    elemThree.addEventListener("change", eventListener);
++    shadowOne.addEventListener("change", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("change", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
++    customEvent = new CustomEvent("change", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
+ 
+-// Test stopping "reset" event.
++    // Test stopping "reset" event.
+ 
+-elemOne.addEventListener("reset", eventListener);
+-elemTwo.addEventListener("reset", eventListener);
+-elemThree.addEventListener("reset", eventListener);
+-shadowOne.addEventListener("reset", eventListener);
++    elemOne.addEventListener("reset", eventListener);
++    elemTwo.addEventListener("reset", eventListener);
++    elemThree.addEventListener("reset", eventListener);
++    shadowOne.addEventListener("reset", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("reset", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that reset event is stopped at shadow root.");
++    customEvent = new CustomEvent("reset", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that reset event is stopped at shadow root.");
+ 
+-// Test stopping "load" event.
++    // Test stopping "load" event.
+ 
+-elemOne.addEventListener("load", eventListener);
+-elemTwo.addEventListener("load", eventListener);
+-elemThree.addEventListener("load", eventListener);
+-shadowOne.addEventListener("load", eventListener);
++    elemOne.addEventListener("load", eventListener);
++    elemTwo.addEventListener("load", eventListener);
++    elemThree.addEventListener("load", eventListener);
++    shadowOne.addEventListener("load", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("load", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that load event is stopped at shadow root.");
++    customEvent = new CustomEvent("load", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that load event is stopped at shadow root.");
+ 
+-// Test stopping "resize" event.
++    // Test stopping "resize" event.
+ 
+-elemOne.addEventListener("resize", eventListener);
+-elemTwo.addEventListener("resize", eventListener);
+-elemThree.addEventListener("resize", eventListener);
+-shadowOne.addEventListener("resize", eventListener);
++    elemOne.addEventListener("resize", eventListener);
++    elemTwo.addEventListener("resize", eventListener);
++    elemThree.addEventListener("resize", eventListener);
++    shadowOne.addEventListener("resize", eventListener);
+ 
+-eventChain = [];
++    eventChain = [];
+ 
+-customEvent = new CustomEvent("resize", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that resize event is stopped at shadow root.");
++    customEvent = new CustomEvent("resize", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that resize event is stopped at shadow root.");
++
++    // Test stopping "scroll" event.
+ 
+-// Test stopping "scroll" event.
++    elemOne.addEventListener("scroll", eventListener);
++    elemTwo.addEventListener("scroll", eventListener);
++    elemThree.addEventListener("scroll", eventListener);
++    shadowOne.addEventListener("scroll", eventListener);
+ 
+-elemOne.addEventListener("scroll", eventListener);
+-elemTwo.addEventListener("scroll", eventListener);
+-elemThree.addEventListener("scroll", eventListener);
+-shadowOne.addEventListener("scroll", eventListener);
++    eventChain = [];
+ 
+-eventChain = [];
++    customEvent = new CustomEvent("scroll", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that scroll event is stopped at shadow root.");
+ 
+-customEvent = new CustomEvent("scroll", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that scroll event is stopped at shadow root.");
+-
+-// Test stopping "selectstart" event.
++    // Test stopping "selectstart" event.
+ 
+-elemOne.addEventListener("selectstart", eventListener);
+-elemTwo.addEventListener("selectstart", eventListener);
+-elemThree.addEventListener("selectstart", eventListener);
+-shadowOne.addEventListener("selectstart", eventListener);
++    elemOne.addEventListener("selectstart", eventListener);
++    elemTwo.addEventListener("selectstart", eventListener);
++    elemThree.addEventListener("selectstart", eventListener);
++    shadowOne.addEventListener("selectstart", eventListener);
++
++    eventChain = [];
+ 
+-eventChain = [];
++    customEvent = new CustomEvent("selectstart", { "bubbles" : true });
++    elemThree.dispatchEvent(customEvent);
++    isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that selectstart event is stopped at shadow root.");
+ 
+-customEvent = new CustomEvent("selectstart", { "bubbles" : true });
+-elemThree.dispatchEvent(customEvent);
+-isEventChain(eventChain, [elemThree, elemTwo, shadowOne], "Test that selectstart event is stopped at shadow root.");
+-
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot.html b/dom/tests/mochitest/webcomponents/test_shadowroot.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
+@@ -1,83 +1,91 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+ -->
+ <head>
+   <title>Test for ShadowRoot</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+-<div id="movedtoshadow" class="testclass"></div>
+-<svg id="svgmovedtoshadow"></svg>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+-// Create ShadowRoot.
+-var element = document.createElement("div");
+-ok(!element.shadowRoot, "div element should not have a shadow root.");
+-var shadow = element.createShadowRoot();
+-is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
++
++SimpleTest.waitForExplicitFinish();
+ 
+-// Move an element from the document to the ShadowRoot.
+-var inShadowEl = document.getElementById("movedtoshadow");
+-var inShadowSVGEl = document.getElementById("svgmovedtoshadow");
++var content = '<div id="movedtoshadow" class="testclass"></div>' +
++              '<svg id="svgmovedtoshadow"></svg>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    // Create ShadowRoot.
++    var element = aDocument.createElement("div");
++    ok(!element.shadowRoot, "div element should not have a shadow root.");
++    var shadow = element.attachShadow({mode: "open"});
++    is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
+ 
+-// Test getElementById
+-ok(!shadow.getElementById("movedtoshadow"), "Element not in ShadowRoot should not be accessible from ShadowRoot API.");
+-ok(!shadow.getElementById("svgmovedtoshadow"), "SVG element not in ShadowRoot should not be accessible from ShadowRoot API.");
+-shadow.appendChild(inShadowEl);
+-shadow.appendChild(inShadowSVGEl);
+-is(shadow.getElementById("movedtoshadow"), inShadowEl, "Element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+-ok(!document.getElementById("movedtoshadow"), "Element appended to a ShadowRoot should not be accessible from document.");
+-is(shadow.getElementById("svgmovedtoshadow"), inShadowSVGEl, "SVG element appended to a ShadowRoot should be accessible from ShadowRoot API.");
+-ok(!document.getElementById("svgmovedtoshadow"), "SVG element appended to a ShadowRoot should not be accessible from document.");
++    // Move an element from the document to the ShadowRoot.
++    var inShadowEl = aDocument.getElementById("movedtoshadow");
++    var inShadowSVGEl = aDocument.getElementById("svgmovedtoshadow");
+ 
+-// Test getElementsByClassName
+-is(document.getElementsByClassName("testclass").length, 0, "Element removed from DOM should not be accessible by DOM accessors.");
+-is(shadow.getElementsByClassName("testclass").length, 1, "Element added to ShadowRoot should be accessible by ShadowRoot API.");
++    // Test getElementById
++    ok(!shadow.getElementById("movedtoshadow"), "Element not in ShadowRoot should not be accessible from ShadowRoot API.");
++    ok(!shadow.getElementById("svgmovedtoshadow"), "SVG element not in ShadowRoot should not be accessible from ShadowRoot API.");
++    shadow.appendChild(inShadowEl);
++    shadow.appendChild(inShadowSVGEl);
++    is(shadow.getElementById("movedtoshadow"), inShadowEl, "Element appended to a ShadowRoot should be accessible from ShadowRoot API.");
++    ok(!aDocument.getElementById("movedtoshadow"), "Element appended to a ShadowRoot should not be accessible from document.");
++    is(shadow.getElementById("svgmovedtoshadow"), inShadowSVGEl, "SVG element appended to a ShadowRoot should be accessible from ShadowRoot API.");
++    ok(!aDocument.getElementById("svgmovedtoshadow"), "SVG element appended to a ShadowRoot should not be accessible from document.");
+ 
+-// Test getElementsByTagName{NS}
+-is(document.getElementsByTagName("div").length, 0, "Element removed from DOM should not be accessible from DOM accessors.");
+-is(shadow.getElementsByTagName("div").length, 1, "Elements in the ShadowRoot should be accessible from the ShadowRoot API.");
+-is(document.getElementsByTagName("svg").length, 0, "SVG elements removed from DOM should not be accessible from DOM accessors.");
+-is(shadow.getElementsByTagName("svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
+-is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
++    // Test getElementsByClassName
++    is(aDocument.getElementsByClassName("testclass").length, 0, "Element removed from DOM should not be accessible by DOM accessors.");
++    is(shadow.getElementsByClassName("testclass").length, 1, "Element added to ShadowRoot should be accessible by ShadowRoot API.");
+ 
+-// Remove elements from ShadowRoot and make sure that they are no longer accessible via the ShadowRoot API.
+-shadow.removeChild(inShadowEl);
+-shadow.removeChild(inShadowSVGEl);
+-ok(!shadow.getElementById("movedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+-ok(!shadow.getElementById("svgmovedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
+-is(shadow.getElementsByClassName("testclass").length, 0, "ShadowRoot getElementsByClassName should not be able to access elements removed from ShadowRoot.");
+-is(shadow.getElementsByTagName("svg").length, 0, "ShadowRoot getElementsByTagName should not be able to access elements removed from ShadowRoot.");
+-is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 0, "ShadowRoot getElementsByTagNameNS should not be able to access elements removed from ShadowRoot.");
++    // Test getElementsByTagName{NS}
++    is(aDocument.getElementsByTagName("div").length, 0, "Element removed from DOM should not be accessible from DOM accessors.");
++    is(shadow.getElementsByTagName("div").length, 1, "Elements in the ShadowRoot should be accessible from the ShadowRoot API.");
++    is(aDocument.getElementsByTagName("svg").length, 0, "SVG elements removed from DOM should not be accessible from DOM accessors.");
++    is(shadow.getElementsByTagName("svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
++    is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 1, "SVG element in the ShadowRoot should be accessible from the ShadowRoot API.");
++
++    // Remove elements from ShadowRoot and make sure that they are no longer accessible via the ShadowRoot API.
++    shadow.removeChild(inShadowEl);
++    shadow.removeChild(inShadowSVGEl);
++    ok(!shadow.getElementById("movedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
++    ok(!shadow.getElementById("svgmovedtoshadow"), "ShadowRoot API should not be able to access elements removed from ShadowRoot.");
++    is(shadow.getElementsByClassName("testclass").length, 0, "ShadowRoot getElementsByClassName should not be able to access elements removed from ShadowRoot.");
++    is(shadow.getElementsByTagName("svg").length, 0, "ShadowRoot getElementsByTagName should not be able to access elements removed from ShadowRoot.");
++    is(shadow.getElementsByTagNameNS("http://www.w3.org/2000/svg", "svg").length, 0, "ShadowRoot getElementsByTagNameNS should not be able to access elements removed from ShadowRoot.");
+ 
+-// Test querySelector on element in a ShadowRoot.
+-element = document.createElement("div");
+-shadow = element.createShadowRoot();
+-var parentDiv = document.createElement("div");
+-var childSpan = document.createElement("span");
+-childSpan.id = "innerdiv";
+-parentDiv.appendChild(childSpan);
+-is(parentDiv.querySelector("#innerdiv"), childSpan, "ID query selector should work on element in ShadowRoot.");
+-is(parentDiv.querySelector("span"), childSpan, "Tag query selector should work on element in ShadowRoot.");
++    // Test querySelector on element in a ShadowRoot.
++    element = aDocument.createElement("div");
++    shadow = element.attachShadow({mode: "open"});
++    var parentDiv = aDocument.createElement("div");
++    var childSpan = aDocument.createElement("span");
++    childSpan.id = "innerdiv";
++    parentDiv.appendChild(childSpan);
++    is(parentDiv.querySelector("#innerdiv"), childSpan, "ID query selector should work on element in ShadowRoot.");
++    is(parentDiv.querySelector("span"), childSpan, "Tag query selector should work on element in ShadowRoot.");
+ 
+-// Test that exception is thrown when trying to create a cycle with host node.
+-element = document.createElement("div");
+-shadow = element.createShadowRoot();
+-try {
+-  shadow.appendChild(element);
+-  ok(false, "Excpetion should be thrown when creating a cycle with host content.");
+-} catch (ex) {
+-  ok(true, "Excpetion should be thrown when creating a cycle with host content.");
+-}
++    // Test that exception is thrown when trying to create a cycle with host node.
++    element = aDocument.createElement("div");
++    shadow = element.attachShadow({mode: "open"});
++    try {
++      shadow.appendChild(element);
++      ok(false, "Excpetion should be thrown when creating a cycle with host content.");
++    } catch (ex) {
++      ok(true, "Excpetion should be thrown when creating a cycle with host content.");
++    }
+ 
+-// Basic innerHTML tests.
+-shadow.innerHTML = '<span id="first"></span><div id="second"></div>';
+-is(shadow.childNodes.length, 2, "There should be two children in the ShadowRoot.");
+-is(shadow.getElementById("second").tagName, "DIV", "Elements created by innerHTML should be accessible by ShadowRoot API.");
++    // Basic innerHTML tests.
++    shadow.innerHTML = '<span id="first"></span><div id="second"></div>';
++    is(shadow.childNodes.length, 2, "There should be two children in the ShadowRoot.");
++    is(shadow.getElementById("second").tagName, "DIV", "Elements created by innerHTML should be accessible by ShadowRoot API.");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+@@ -1,44 +1,49 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+ -->
+ <head>
+   <title>Test for inert elements in ShadowRoot</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+-<body onload="runChecks();">
+-<div id="grabme"></div>
++<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+-var element = document.getElementById("grabme");
+-var shadow = element.createShadowRoot();
+-
+-// Check that <base> is inert.
+-shadow.innerHTML = '<base href="http://www.example.org/" />';
+-isnot(document.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
+-
+ SimpleTest.waitForExplicitFinish();
+ 
+-// Check that <link> is inert.
+-var numStyleBeforeLoad = document.styleSheets.length;
++var content = '<div id="grabme"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    function runChecks() {
++      isnot(aDocument.defaultView.getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
++      is(aDocument.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
++      is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
++      // Remove link to make sure we don't get assertions.
++      shadow.removeChild(shadowStyle);
++      SimpleTest.finish();
++    };
+ 
+-shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
+-shadow.applyAuthorStyles = true;
+-var shadowSpan = shadow.getElementById("shadowspan");
+-var shadowStyle = shadow.getElementById("shadowlink");
++    var element = aDocument.getElementById("grabme");
++    var shadow = element.attachShadow({mode: "open"});
+ 
+-function runChecks() {
+-  isnot(getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
+-  is(document.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
+-  is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
+-  // Remove link to make sure we don't get assertions.
+-  shadow.removeChild(shadowStyle);
+-  SimpleTest.finish();
+-};
++    // Check that <base> is inert.
++    shadow.innerHTML = '<base href="http://www.example.org/" />';
++    isnot(aDocument.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
++
++    // Check that <link> is inert.
++    var numStyleBeforeLoad = aDocument.styleSheets.length;
+ 
++    shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
++    shadow.applyAuthorStyles = true;
++    var shadowSpan = shadow.getElementById("shadowspan");
++    var shadowStyle = shadow.getElementById("shadowlink");
++
++    runChecks();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+@@ -1,88 +1,97 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+ -->
+ <head>
+   <title>Test for ShadowRoot styling</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+-<div class="tall" id="bodydiv"></div>
+-<div id="container"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+ if (!SpecialPowers.DOMWindowUtils.isStyledByServo) {
+   SimpleTest.expectAssertions(3, 3); // GeckoRestyleManager stuff.
+ }
+ 
+-// Create ShadowRoot.
+-var container = document.getElementById("container");
+-var elem = document.createElement("div");
+-container.appendChild(elem); // Put ShadowRoot host in document.
+-var root = elem.createShadowRoot();
++SimpleTest.waitForExplicitFinish();
++
++var content = '<div class="tall" id="bodydiv"></div>' +
++              '<div id="container"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    var iframeWin = aDocument.defaultView;
+ 
+-// A style element that will be appended into the ShadowRoot.
+-var shadowStyle = document.createElement("style");
+-shadowStyle.innerHTML = ".tall { height: 100px; } .fat { padding-left: inherit; }";
++    // Create ShadowRoot.
++    var container = aDocument.getElementById("container");
++    var elem = aDocument.createElement("div");
++    container.appendChild(elem); // Put ShadowRoot host in document.
++    var root = elem.attachShadow({mode: "open"});
+ 
+-root.innerHTML = '<div id="divtostyle" class="tall fat"></div>';
+-var divToStyle = root.getElementById("divtostyle");
++    // A style element that will be appended into the ShadowRoot.
++    var shadowStyle = aDocument.createElement("style");
++    shadowStyle.innerHTML = ".tall { height: 100px; } .fat { padding-left: inherit; }";
+ 
+-// Make sure styleSheet counts are correct after appending a style to the ShadowRoot.
+-is(document.styleSheets.length, 1, "There should only be one style sheet on the document from the test style sheet.");
+-is(root.styleSheets.length, 0, "The ShadowRoot should have no style sheets.");
+-root.appendChild(shadowStyle);
+-is(document.styleSheets.length, 1, "Styles in the ShadowRoot element should not be accessible from the document.");
+-is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet from the appened style.");
+-is(root.styleSheets[0].ownerNode, shadowStyle, "First style in ShadowRoot should match the style that was just appended.");
++    root.innerHTML = '<div id="divtostyle" class="tall fat"></div>';
++    var divToStyle = root.getElementById("divtostyle");
+ 
+-var dummyStyle = document.createElement("style");
+-root.appendChild(dummyStyle);
+-is(root.styleSheets.length, 2, "ShadowRoot should have an additional style from appending dummyStyle.");
+-is(root.styleSheets[1].ownerNode, dummyStyle, "Second style in ShadowRoot should be the dummyStyle.");
+-root.removeChild(dummyStyle);
+-is(root.styleSheets.length, 1, "Removing dummyStyle should remove it from the ShadowRoot style sheets.");
+-is(root.styleSheets[0].ownerNode, shadowStyle, "The style sheet remaining in the ShadowRoot should be shadowStyle.");
++    // Make sure styleSheet counts are correct after appending a style to the ShadowRoot.
++    is(aDocument.styleSheets.length, 0, "There shouldn't be any style sheet in the test frame document.");
++    is(root.styleSheets.length, 0, "The ShadowRoot should have no style sheets.");
++    root.appendChild(shadowStyle);
++    is(aDocument.styleSheets.length, 0, "Styles in the ShadowRoot element should not be accessible from the document.");
++    is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet from the appened style.");
++    is(root.styleSheets[0].ownerNode, shadowStyle, "First style in ShadowRoot should match the style that was just appended.");
+ 
+-// Make sure that elements outside of the ShadowRoot are not affected by the ShadowRoot style.
+-isnot(getComputedStyle(document.getElementById("bodydiv"), null).getPropertyValue("height"), "100px", "Style sheets in ShadowRoot should not apply to elements no in the ShadowRoot.");
++    var dummyStyle = aDocument.createElement("style");
++    root.appendChild(dummyStyle);
++    is(root.styleSheets.length, 2, "ShadowRoot should have an additional style from appending dummyStyle.");
++    is(root.styleSheets[1].ownerNode, dummyStyle, "Second style in ShadowRoot should be the dummyStyle.");
++    root.removeChild(dummyStyle);
++    is(root.styleSheets.length, 1, "Removing dummyStyle should remove it from the ShadowRoot style sheets.");
++    is(root.styleSheets[0].ownerNode, shadowStyle, "The style sheet remaining in the ShadowRoot should be shadowStyle.");
+ 
+-// Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
+-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
++    // Make sure that elements outside of the ShadowRoot are not affected by the ShadowRoot style.
++    isnot(iframeWin.getComputedStyle(aDocument.getElementById("bodydiv"), null).getPropertyValue("height"), "100px", "Style sheets in ShadowRoot should not apply to elements no in the ShadowRoot.");
+ 
+-// Tests for applyAuthorStyles.
+-var authorStyle = document.createElement("style");
+-authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
+-document.body.appendChild(authorStyle);
++    // Make sure that elements in the ShadowRoot are styled according to the ShadowRoot style.
++    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "100px", "ShadowRoot style sheets should apply to elements in ShadowRoot.");
++
++    // Tests for applyAuthorStyles.
++    var authorStyle = aDocument.createElement("style");
++    authorStyle.innerHTML = ".fat { padding-right: 20px; padding-left: 30px; }";
++    aDocument.body.appendChild(authorStyle);
+ 
+-is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
+-isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+-root.applyAuthorStyles = true;
+-is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
+-is(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
+-root.applyAuthorStyles = false;
+-is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
+-isnot(getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
++    is(root.applyAuthorStyles, false, "applyAuthorStyles defaults to false.");
++    isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
++    root.applyAuthorStyles = true;
++    is(root.applyAuthorStyles, true, "applyAuthorStyles was set to true.");
++    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should apply to ShadowRoot when ShadowRoot.applyAuthorStyles is true.");
++    root.applyAuthorStyles = false;
++    is(root.applyAuthorStyles, false, "applyAuthorStyles was set to false.");
++    isnot(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-right"), "20px", "Author styles should not apply to ShadowRoot when ShadowRoot.applyAuthorStyles is false.");
+ 
+-// Test dynamic changes to style in ShadowRoot.
+-root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
+-divToStyle = root.getElementById("divtostyle");
+-var dummyShadowStyle = document.createElement("style");
+-dummyShadowStyle.innerHTML = ".dummy { height: 300px; }";
+-root.appendChild(dummyShadowStyle);
+-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "300px", "Dummy element in ShadowRoot should be styled by style in ShadowRoot.");
+-dummyShadowStyle.innerHTML = ".dummy { height: 200px; }";
+-is(getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Dynamic changes to styles in ShadowRoot should change style of affected elements.");
++    // Test dynamic changes to style in ShadowRoot.
++    root.innerHTML = '<div id="divtostyle" class="dummy"></div>';
++    divToStyle = root.getElementById("divtostyle");
++    var dummyShadowStyle = aDocument.createElement("style");
++    dummyShadowStyle.innerHTML = ".dummy { height: 300px; }";
++    root.appendChild(dummyShadowStyle);
++    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "300px", "Dummy element in ShadowRoot should be styled by style in ShadowRoot.");
++    dummyShadowStyle.innerHTML = ".dummy { height: 200px; }";
++    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("height"), "200px", "Dynamic changes to styles in ShadowRoot should change style of affected elements.");
+ 
+-// Test id selector in ShadowRoot style.
+-root.innerHTML = '<style>#divtostyle { padding-top: 10px; }</style><div id="divtostyle"></div>';
+-divToStyle = root.getElementById("divtostyle");
+-is(getComputedStyle(divToStyle, null).getPropertyValue("padding-top"), "10px", "ID selector in style selector should match element.");
++    // Test id selector in ShadowRoot style.
++    root.innerHTML = '<style>#divtostyle { padding-top: 10px; }</style><div id="divtostyle"></div>';
++    divToStyle = root.getElementById("divtostyle");
++    is(iframeWin.getComputedStyle(divToStyle, null).getPropertyValue("padding-top"), "10px", "ID selector in style selector should match element.");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+ 
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+@@ -1,42 +1,52 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+ -->
+ <head>
+   <title>Test for ShadowRoot style order</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+-<div id="container"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+-// Create ShadowRoot.
+-var container = document.getElementById("container");
+-var elem = document.createElement("div");
+-container.appendChild(elem); // Put ShadowRoot host in document.
+-var root = elem.createShadowRoot();
++
++SimpleTest.waitForExplicitFinish();
++
++var content = '<div id="container"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    var iframeWin = aDocument.defaultView;
+ 
+-// Style elements that will be appended into the ShadowRoot.
+-var tallShadowStyle = document.createElement("style");
+-tallShadowStyle.innerHTML = ".tall { height: 100px; }";
++    // Create ShadowRoot.
++    var container = aDocument.getElementById("container");
++    var elem = aDocument.createElement("div");
++    container.appendChild(elem); // Put ShadowRoot host in document.
++    var root = elem.attachShadow({mode: "open"});
+ 
+-var veryTallShadowStyle = document.createElement("style");
+-veryTallShadowStyle.innerHTML = ".tall { height: 200px; }";
++    // Style elements that will be appended into the ShadowRoot.
++    var tallShadowStyle = aDocument.createElement("style");
++    tallShadowStyle.innerHTML = ".tall { height: 100px; }";
+ 
+-var divToStyle = document.createElement("div");
+-divToStyle.setAttribute("class", "tall");
+-root.appendChild(divToStyle);
++    var veryTallShadowStyle = aDocument.createElement("style");
++    veryTallShadowStyle.innerHTML = ".tall { height: 200px; }";
++
++    var divToStyle = aDocument.createElement("div");
++    divToStyle.setAttribute("class", "tall");
++    root.appendChild(divToStyle);
+ 
+-// Make sure the styles are applied in tree order.
+-root.appendChild(tallShadowStyle);
+-is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet.");
+-is(window.getComputedStyle(divToStyle).getPropertyValue("height"), "100px", "Style in ShadowRoot should apply to elements in ShadowRoot.");
+-root.appendChild(veryTallShadowStyle);
+-is(root.styleSheets.length, 2, "ShadowRoot should have two style sheets.");
+-is(window.getComputedStyle(divToStyle).getPropertyValue("height"), "200px", "Style in ShadowRoot should apply to elements in ShadowRoot in tree order.");
++    // Make sure the styles are applied in tree order.
++    root.appendChild(tallShadowStyle);
++    is(root.styleSheets.length, 1, "ShadowRoot should have one style sheet.");
++    is(iframeWin.getComputedStyle(divToStyle).getPropertyValue("height"), "100px", "Style in ShadowRoot should apply to elements in ShadowRoot.");
++    root.appendChild(veryTallShadowStyle);
++    is(root.styleSheets.length, 2, "ShadowRoot should have two style sheets.");
++    is(iframeWin.getComputedStyle(divToStyle).getPropertyValue("height"), "200px", "Style in ShadowRoot should apply to elements in ShadowRoot in tree order.");
+ 
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+--- a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
++++ b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+@@ -1,28 +1,39 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=806506
+ -->
+ <head>
+   <title>Test for styling fallback content</title>
++  <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+-<div id="grabme"></div>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+-var host = document.getElementById("grabme");
+-var shadow = host.createShadowRoot();
+-shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><content><span id="innerspan">Hello</span></content></span>';
+-var innerStyle = shadow.getElementById("innerstyle");
++
++SimpleTest.waitForExplicitFinish();
++
++var content = '<div id="grabme"></div>';
++setWebComponentsPrefAndCreateIframe(content)
++  .then((aDocument) => {
++    var iframeWin = aDocument.defaultView;
+ 
+-innerStyle.innerHTML = '#innerspan { margin-top: 10px; }';
+-var innerSpan = shadow.getElementById("innerspan");
+-is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "10px", "Default content should be style by id selector.");
++    var host = aDocument.getElementById("grabme");
++    var shadow = host.attachShadow({mode: "open"});
++    shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><slot><span id="innerspan">Hello</span></slot></span>';
++    var innerStyle = shadow.getElementById("innerstyle");
+ 
+-innerStyle.innerHTML = '#container > content > #innerspan { margin-top: 30px; }';
+-is(getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "30px", "Default content should be style by child combinators.");
++    innerStyle.innerHTML = '#innerspan { margin-top: 10px; }';
++    var innerSpan = shadow.getElementById("innerspan");
++    is(iframeWin.getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "10px", "Default content should be style by id selector.");
++
++    innerStyle.innerHTML = '#container > slot > #innerspan { margin-top: 30px; }';
++    is(iframeWin.getComputedStyle(innerSpan, null).getPropertyValue("margin-top"), "30px", "Default content should be style by child combinators.");
++
++    SimpleTest.finish();
++  });
+ </script>
+ </body>
+ </html>
+diff --git a/testing/profiles/common/user.js b/testing/profiles/common/user.js
+--- a/testing/profiles/common/user.js
++++ b/testing/profiles/common/user.js
+@@ -59,17 +59,17 @@ user_pref("app.update.enabled", false);
+ user_pref("app.update.staging.enabled", false);
+ user_pref("app.update.url.android", "");
+ // Make sure GMPInstallManager won't hit the network.
+ user_pref("media.gmp-manager.url.override", "http://{server}/dummy-gmp-manager.xml");
+ user_pref("media.gmp-manager.updateEnabled", false);
+ user_pref("media.hls.server.url", "http://{server}/tests/dom/media/test/hls");
+ user_pref("dom.w3c_touch_events.enabled", 1);
+ user_pref("layout.accessiblecaret.enabled_on_touch", false);
+-user_pref("dom.webcomponents.enabled", true);
++user_pref("dom.webcomponents.enabled", false);
+ user_pref("dom.webcomponents.customelements.enabled", true);
+ // Existing tests assume there is no font size inflation.
+ user_pref("font.size.inflation.emPerLine", 0);
+ user_pref("font.size.inflation.minTwips", 0);
+ // Disable the caret blinking so we get stable snapshot
+ user_pref("ui.caretBlinkTime", -1);
+ 
+ // Don't allow background tabs to be zombified, otherwise for tests that

+ 1 - 1
frg/work-js/mozilla-release/patches/1399877-64a1.patch

@@ -327,7 +327,7 @@ diff --git a/security/manager/ssl/nsNSSComponent.cpp b/security/manager/ssl/nsNS
    nsAutoCString nss3Dir;
    rv = GetNSS3Directory(nss3Dir);
    if (NS_SUCCEEDED(rv)) {
-     if (!possibleCKBILocations.append(Move(nss3Dir))) {
+     if (!possibleCKBILocations.append(std::move(nss3Dir))) {
        return NS_ERROR_OUT_OF_MEMORY;
      }
    } else {

+ 291 - 0
frg/work-js/mozilla-release/patches/1399948-2-58a1.patch

@@ -0,0 +1,291 @@
+# HG changeset patch
+# User Darren Hobin <darrenhobin@live.com>
+# Date 1506265183 14400
+# Node ID b8c11514db364abd3e1ee099d55f1106e48e3bfc
+# Parent  d0edba63bb7f42be259a9a7212b6fa1c3d5417ed
+Bug 1399948 - Part 2: Implement photon styles for breadcrumbs. r=bgrins
+
+MozReview-Commit-ID: IQX8uDsSapO
+
+diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn
+--- a/devtools/client/jar.mn
++++ b/devtools/client/jar.mn
+@@ -106,16 +106,17 @@ devtools.jar:
+     skin/devtools-browser.css (themes/devtools-browser.css)
+     skin/dark-theme.css (themes/dark-theme.css)
+     skin/light-theme.css (themes/light-theme.css)
+     skin/firebug-theme.css (themes/firebug-theme.css)
+     skin/toolbars.css (themes/toolbars.css)
+     skin/toolbox.css (themes/toolbox.css)
+     skin/tooltips.css (themes/tooltips.css)
+     skin/images/add.svg (themes/images/add.svg)
++    skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
+     skin/images/filters.svg (themes/images/filters.svg)
+     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
+     skin/images/grid.svg (themes/images/grid.svg)
+     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
+     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
+     skin/images/controls.png (themes/images/controls.png)
+     skin/images/controls@2x.png (themes/images/controls@2x.png)
+     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
+@@ -262,17 +263,16 @@ devtools.jar:
+     skin/images/firebug/twisty-open-firebug.svg (themes/images/firebug/twisty-open-firebug.svg)
+     skin/images/firebug/arrow-down.svg (themes/images/firebug/arrow-down.svg)
+     skin/images/firebug/arrow-up.svg (themes/images/firebug/arrow-up.svg)
+     skin/images/firebug/close.svg (themes/images/firebug/close.svg)
+     skin/images/firebug/pause.svg (themes/images/firebug/pause.svg)
+     skin/images/firebug/play.svg (themes/images/firebug/play.svg)
+     skin/images/firebug/rewind.svg (themes/images/firebug/rewind.svg)
+     skin/images/firebug/disable.svg (themes/images/firebug/disable.svg)
+-    skin/images/firebug/breadcrumbs-divider.svg (themes/images/firebug/breadcrumbs-divider.svg)
+     skin/images/firebug/breakpoint.svg (themes/images/firebug/breakpoint.svg)
+     skin/images/firebug/tool-options.svg (themes/images/firebug/tool-options.svg)
+     skin/images/firebug/debugger-step-in.svg (themes/images/firebug/debugger-step-in.svg)
+     skin/images/firebug/debugger-step-out.svg (themes/images/firebug/debugger-step-out.svg)
+     skin/images/firebug/debugger-step-over.svg (themes/images/firebug/debugger-step-over.svg)
+     skin/images/firebug/pane-collapse.svg (themes/images/firebug/pane-collapse.svg)
+     skin/images/firebug/pane-expand.svg (themes/images/firebug/pane-expand.svg)
+     skin/images/firebug/dock-undock.svg (themes/images/firebug/dock-undock.svg)
+diff --git a/devtools/client/themes/breadcrumbs.css b/devtools/client/themes/breadcrumbs.css
+--- a/devtools/client/themes/breadcrumbs.css
++++ b/devtools/client/themes/breadcrumbs.css
+@@ -82,151 +82,74 @@
+ .scrollbutton-up > .toolbarbutton-icon:dir(rtl),
+ .scrollbutton-down > .toolbarbutton-icon:-moz-locale-dir(ltr):not(:dir(rtl)),
+ .scrollbutton-down > .toolbarbutton-icon:dir(ltr) {
+   transform: scaleX(-1);
+ }
+ 
+ .breadcrumbs-widget-item {
+   background-color: transparent;
+-  -moz-appearance: none;
+-  min-height: 24px;
+-  min-width: 65px;
+-  margin: 0;
+-  padding: 0 8px 0 20px;
+   border: none;
+-  outline: none;
+-  color: hsl(210,30%,85%);
+-  position: relative;
++  margin-inline-start: 10px;
++  margin-inline-end: 1px;
++  padding: 0 0 2px 0;
+ }
+ 
+ .breadcrumbs-widget-item > .button-box {
+   border: none;
+   padding-top: 0;
+   padding-bottom: 0;
+ }
+ 
+ :root[platform="win"] .breadcrumbs-widget-item:-moz-focusring > .button-box {
+   border-width: 0;
+ }
+ 
+-.breadcrumbs-widget-item::before {
+-  content: "";
+-  position: absolute;
+-  top: 1px;
+-  inset-inline-start: 0;
+-  width: 12px;
+-  height: 22px;
+-  background-repeat: no-repeat;
+-  /* Given the 1/2 aspect ratio of the separator pseudo-element and the 45deg angle of
+-     the arrow shape, we need the arrow edges to be at this position from the start of
+-     the gradient line. */
+-  --position: 66.5%;
+-  /* The color of the thin line in the arrow-shaped separator between 2 unselected
+-     crumbs. There is no theme variable for this, this used to be an image. */
+-  --line-color: #ACACAC;
+-  --background-color: var(--theme-body-background);
+-}
+-
+ #debugger-toolbar .breadcrumbs-widget-item::before {
+   --background-color: var(--theme-toolbar-background);
+ }
+ 
+-.theme-dark .breadcrumbs-widget-item::before {
+-  --line-color: #6E6E6E;
+-}
+-
+ .breadcrumbs-widget-item:first-child::before {
+   /* The first crumb does not need any separator before itself */
+   content: unset;
+ }
+ 
+ .breadcrumbs-widget-item:dir(rtl)::before {
+   transform: scaleX(-1);
+ }
+ 
+-.breadcrumbs-widget-item:not([checked])::before {
+-  background-color: var(--background-color);
+-  background-image:
+-    linear-gradient(45deg,
+-                    var(--background-color) 30%,
+-                    transparent),
+-    linear-gradient(-45deg,
+-                    transparent,
+-                    var(--background-color) 70%,
+-                    var(--background-color)),
+-    linear-gradient(45deg,
+-                    transparent var(--position),
+-                    var(--line-color) var(--position),
+-                    var(--line-color) calc(var(--position) + 1px),
+-                    transparent 0),
+-    linear-gradient(-45deg,
+-                    transparent calc(100% - var(--position)),
+-                    var(--line-color) calc(100% - var(--position)),
+-                    var(--line-color) calc(calc(100% - var(--position)) + 1px),
+-                    transparent 0);
+-  background-size:
+-    100% 50%,
+-    100% 50%,
+-    100%,
+-    100%;
+-  background-position:
+-    left bottom,
+-    left top,
+-    left top,
+-    left top;
+-}
+-
+-.breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item::before {
+-  background-color: var(--theme-selection-background);
+-  background-image:
+-    linear-gradient(45deg,
+-                    transparent var(--position),
+-                    var(--background-color) 0),
+-    linear-gradient(-45deg,
+-                    var(--background-color) calc(100% - var(--position)),
+-                    transparent 0);
+-  background-size: unset;
+-}
+-
+-.breadcrumbs-widget-item[checked]::before {
+-  background-image:
+-    linear-gradient(45deg,
+-                    transparent var(--position),
+-                    var(--theme-selection-background) 0),
+-    linear-gradient(-45deg,
+-                    var(--theme-selection-background) calc(100% - var(--position)),
+-                    var(--background-color) 0);
+-}
+-
+-.breadcrumbs-widget-item[checked] {
+-  background-color: var(--theme-selection-background);
+-}
+-
+-.breadcrumbs-widget-item:first-child {
+-  background-image: none;
+-}
+-
+ /* RTL support: move the images that were on the left to the right,
+  * and move images that were on the right to the left.
+  */
+ .breadcrumbs-widget-item:dir(rtl) {
+   padding: 0 20px 0 8px;
+ }
+ 
+ .breadcrumbs-widget-item:dir(rtl),
+ .breadcrumbs-widget-item[checked] + .breadcrumbs-widget-item:dir(rtl) {
+   background-position: center right;
+ }
+ 
+-.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id,
++.breadcrumbs-widget-item:not(:first-child)::before {
++  content: url(chrome://devtools/skin/images/breadcrumbs-divider.svg);
++  background: none;
++  position: relative;
++  left: -3px;
++  margin: 0 4px 0 -1px;
++  top: -1px;
++}
++
++.breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id {
++  color: var(--theme-highlight-purple);
++}
++
+ .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
+ .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
+ .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
+-  color: var(--theme-selection-color);
++  color: var(--theme-highlight-blue);
+ }
+ 
+ .theme-dark .breadcrumbs-widget-item {
+   color: var(--theme-selection-color);
+ }
+ 
+ .theme-light .breadcrumbs-widget-item {
+   color: var(--theme-body-color);
+@@ -239,66 +162,43 @@
+ .breadcrumbs-widget-item-classes {
+   color: var(--theme-content-color1);
+ }
+ 
+ .breadcrumbs-widget-item-pseudo-classes {
+   color: var(--theme-highlight-lightorange);
+ }
+ 
+-.theme-dark .breadcrumbs-widget-item:not([checked]):hover label {
+-  color: white;
+-}
+-
+-.theme-light .breadcrumbs-widget-item:not([checked]):hover label {
+-  color: black;
+-}
+-
+ /* Firebug theme support for breadcrumbs widget. */
+ 
+ .theme-firebug .breadcrumbs-widget-item {
+-  margin-inline-start: 10px;
+-  margin-inline-end: 1px;
+-  background-image: none;
+   border: 1px solid transparent;
+-  color: #141414;
+   border-radius: 2px;
+-  min-width: 0;
+-  min-height: 0;
+   padding: 0;
+-  font-size: var(--theme-toolbar-font-size);
+ }
+ 
+ .theme-firebug .breadcrumbs-widget-item:hover {
+   border-color: rgba(0, 0, 0, 0.2);
+-  background: transparent linear-gradient(
+-              rgba(255, 255, 255, 0.4),
+-              rgba(255, 255, 255, 0.2)) no-repeat;
+   box-shadow: 1px 1px 1px rgba(255, 255, 255, 0.6) inset,
+               0 0 1px rgba(255, 255, 255, 0.6) inset,
+               0 0 2px rgba(0, 0, 0, 0.05);
+ }
+ 
+ .theme-firebug .breadcrumbs-widget-item > .button-box {
+   padding-left: 0;
+   padding-right: 0;
+ }
+ 
+ .theme-firebug .breadcrumbs-widget-item:first-child {
+   margin: 0;
+ }
+ 
+ .theme-firebug .breadcrumbs-widget-item:not(:first-child)::before {
+-  content: url(chrome://devtools/skin/images/firebug/breadcrumbs-divider.svg);
+-  background: none;
+-  position: relative;
+-  left: -3px;
+   margin: 0 0 0 -5px;
+-  padding: 0;
+-  width: 5px;
++  top: 0;
+ }
+ 
+ /* Breadcrumbs Separators (reset selection styles) */
+ .theme-firebug .breadcrumbs-widget-item[checked],
+ .theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-id,
+ .theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-tag,
+ .theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-pseudo-classes,
+ .theme-firebug .breadcrumbs-widget-item[checked] .breadcrumbs-widget-item-classes {
+diff --git a/devtools/client/themes/images/firebug/breadcrumbs-divider.svg b/devtools/client/themes/images/breadcrumbs-divider.svg
+rename from devtools/client/themes/images/firebug/breadcrumbs-divider.svg
+rename to devtools/client/themes/images/breadcrumbs-divider.svg

+ 0 - 0
frg/work-js/mozilla-release/patches/1401939-61a1-patch → frg/work-js/mozilla-release/patches/1401939-61a1.patch


+ 1 - 1
frg/work-js/mozilla-release/patches/1408790-58a1.patch

@@ -2,7 +2,7 @@
 # User Liam <canada8715@gmail.com>
 # Date 1508111112 21600
 # Node ID ac9d12e5587fe2486a2a8c95ef911768841195e2
-# Parent  26bb2873d6762fc72b18199c29d8fc9f3387b5cf
+# Parent  2ee6106fe65b6d20dcb56e5c929e43887239bf4a
 Bug 1408790 - The non-selected elements with ids should be lightened to the same color as the non-selected classes r=gl
 
 MozReview-Commit-ID: AjcnYsxxqAs

File diff suppressed because it is too large
+ 384 - 0
frg/work-js/mozilla-release/patches/1411368-2-58a1.patch


+ 57 - 0
frg/work-js/mozilla-release/patches/1414286-1-61a1.patch

@@ -0,0 +1,57 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1521726179 -3600
+# Node ID 7d7fa73a97d02754c464aad9f50bfb42ec177de5
+# Parent  7ff384a9583f2cf232777830df649ca2b476ef7f
+Bug 1414286 - return promise in markup view collapse/expandAll;r=pbro
+
+MozReview-Commit-ID: H4OuB2ggbWG
+
+diff --git a/devtools/client/inspector/markup/markup.js b/devtools/client/inspector/markup/markup.js
+--- a/devtools/client/inspector/markup/markup.js
++++ b/devtools/client/inspector/markup/markup.js
+@@ -1185,16 +1185,17 @@ MarkupView.prototype = {
+     }).catch(console.error);
+   },
+ 
+   /**
+    * Expand the entire tree beneath a node.
+    *
+    * @param  {DOMNode} node
+    *         The node to expand, or null to start from the top.
++   * @return {Promise} promise that resolves once all children are expanded.
+    */
+   expandAll: function(node) {
+     node = node || this._rootNode;
+     return this._expandAll(this.getContainer(node));
+   },
+ 
+   /**
+    * Collapse the node's children.
+@@ -1207,19 +1208,26 @@ MarkupView.prototype = {
+   _collapseAll: function(container) {
+     container.setExpanded(false);
+     let children = container.getChildContainers() || [];
+     children.forEach(child => this._collapseAll(child));
+   },
+ 
+   /**
+    * Collapse the entire tree beneath a node.
++   *
++   * @param  {DOMNode} node
++   *         The node to collapse.
++   * @return {Promise} promise that resolves once all children are collapsed.
+    */
+   collapseAll: function(node) {
+     this._collapseAll(this.getContainer(node));
++
++    // collapseAll is synchronous, return a promise for consistency with expandAll.
++    return Promise.resolve();
+   },
+ 
+   /**
+    * Returns either the innerHTML or the outerHTML for a remote node.
+    *
+    * @param  {NodeFront} node
+    *         The NodeFront to get the outerHTML / innerHTML for.
+    * @param  {Boolean} isOuter

+ 1095 - 0
frg/work-js/mozilla-release/patches/1414286-2-61a1.patch

@@ -0,0 +1,1095 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1521150529 -3600
+# Node ID 6cdea4618a5a589c1e2838f153f48842e57d68e8
+# Parent  a46b050ec5a31a0a7648475d87bdde8686210f67
+Bug 1414286 - add DAMP test for inspector expandAll/collapseAll;r=ochameau
+
+MozReview-Commit-ID: 43TwKB4THV2
+
+diff --git a/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/generate-inspector-index-html.js b/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/generate-inspector-index-html.js
+--- a/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/generate-inspector-index-html.js
++++ b/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/generate-inspector-index-html.js
+@@ -48,16 +48,32 @@ for (i = 0; i < CSS_RULES_COUNT; i++) {
+   .many-css-rules {
+     font-size: ${i}px;
+     margin: 10px;
+     padding: 10px;
+     font-family: Arial;
+     margin: 20px;
+   }`;
+ }
++let expandManyChildren = new Array(100).join("  <div attr='my-attr'>content</div>\n");
++
++let maxBalancedDepth = 8;
++function createBalancedMarkup(level = 0) {
++  let tab = new Array(level + 1).join("  ");
++  if (level < maxBalancedDepth) {
++    let child = createBalancedMarkup(level + 1);
++    return `${tab}<div>
++${child}
++${child}
++${tab}</div>`;
++  } else {
++    return tab + "<div class='leaf'>leaf</div>";
++  }
++}
++let expandBalanced = createBalancedMarkup();
+ 
+ console.log(`
+ <!DOCTYPE html>
+ <!-- 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/.  -->
+ <!-- This file is a generated file, do not edit it directly.
+    - See generate-inspector-html.js for instructions to update this file -->
+@@ -82,11 +98,21 @@ console.log(tree);
+ console.log(`
+ <!-- ${n} <div> elements without any children -->
+ `);
+ console.log(repeat);
+ console.log(`
+ <!-- Elements for custom.inspector.manyrules tests -->
+ <div class="no-css-rules"></div>
+ <div class="many-css-rules"></div>
++<div class="expand-many-children">
++`);
++console.log(expandManyChildren);
++console.log(`
++</div>
++<div class="expand-balanced">
++`);
++console.log(expandBalanced);
++console.log(`
++</div>
+ </body>
+ </html>`);
+ 
+diff --git a/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/index.html b/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/index.html
+--- a/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/index.html
++++ b/testing/talos/talos/tests/devtools/addon/content/pages/custom/inspector/index.html
+@@ -1576,10 +1576,884 @@
+ <div data-a1="1" data-a2="2" data-a3="3" data-a4="4" data-a5="5" data-a6="6" data-a7="7" data-a8="8" data-a9="9" data-a10="10" data-a11="11" data-a12="12" data-a13="13" data-a14="14" data-a15="15" data-a16="16" data-a17="17" data-a18="18" data-a19="19" data-a20="20" data-a21="21" data-a22="22" data-a23="23" data-a24="24" data-a25="25" data-a26="26" data-a27="27" data-a28="28" data-a29="29" data-a30="30" data-a31="31" data-a32="32" data-a33="33" data-a34="34" data-a35="35" data-a36="36" data-a37="37" data-a38="38" data-a39="39" data-a40="40" data-a41="41" data-a42="42" data-a43="43" data-a44="44" data-a45="45" data-a46="46" data-a47="47" data-a48="48" data-a49="49" data-a50="50"> 48 </div>
+ <div data-a1="1" data-a2="2" data-a3="3" data-a4="4" data-a5="5" data-a6="6" data-a7="7" data-a8="8" data-a9="9" data-a10="10" data-a11="11" data-a12="12" data-a13="13" data-a14="14" data-a15="15" data-a16="16" data-a17="17" data-a18="18" data-a19="19" data-a20="20" data-a21="21" data-a22="22" data-a23="23" data-a24="24" data-a25="25" data-a26="26" data-a27="27" data-a28="28" data-a29="29" data-a30="30" data-a31="31" data-a32="32" data-a33="33" data-a34="34" data-a35="35" data-a36="36" data-a37="37" data-a38="38" data-a39="39" data-a40="40" data-a41="41" data-a42="42" data-a43="43" data-a44="44" data-a45="45" data-a46="46" data-a47="47" data-a48="48" data-a49="49" data-a50="50"> 49 </div>
+ <div data-a1="1" data-a2="2" data-a3="3" data-a4="4" data-a5="5" data-a6="6" data-a7="7" data-a8="8" data-a9="9" data-a10="10" data-a11="11" data-a12="12" data-a13="13" data-a14="14" data-a15="15" data-a16="16" data-a17="17" data-a18="18" data-a19="19" data-a20="20" data-a21="21" data-a22="22" data-a23="23" data-a24="24" data-a25="25" data-a26="26" data-a27="27" data-a28="28" data-a29="29" data-a30="30" data-a31="31" data-a32="32" data-a33="33" data-a34="34" data-a35="35" data-a36="36" data-a37="37" data-a38="38" data-a39="39" data-a40="40" data-a41="41" data-a42="42" data-a43="43" data-a44="44" data-a45="45" data-a46="46" data-a47="47" data-a48="48" data-a49="49" data-a50="50"> 50 </div>
+ 
+ 
+ <!-- Elements for custom.inspector.manyrules tests -->
+ <div class="no-css-rules"></div>
+ <div class="many-css-rules"></div>
++<div class="expand-many-children">
++
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++  <div attr='my-attr'>content</div>
++
++
++</div>
++<div class="expand-balanced">
++
++<div>
++  <div>
++    <div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++    </div>
++    <div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++    </div>
++  </div>
++  <div>
++    <div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++    </div>
++    <div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++      <div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++        <div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++          <div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++            <div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++              <div>
++                <div class='leaf'>leaf</div>
++                <div class='leaf'>leaf</div>
++              </div>
++            </div>
++          </div>
++        </div>
++      </div>
++    </div>
++  </div>
++</div>
++
++</div>
+ </body>
+ </html>
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/head.js b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
+--- a/testing/talos/talos/tests/devtools/addon/content/tests/head.js
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
+@@ -148,10 +148,8 @@ exports.reloadPageAndLog = async functio
+   await damp.reloadPage(onReload);
+   test.done();
+ 
+   dump("Wait for pending paints on '" + name + "'\n");
+   test = runTest(name + ".reload.settle.DAMP");
+   await waitForPendingPaints(toolbox);
+   test.done();
+ };
+-
+-dump("Required HEAD successfully\n");
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
+--- a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
+@@ -1,56 +1,90 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
++const { reloadInspectorAndLog, selectNodeFront } = require("chrome://damp/content/tests/inspector/inspector-helpers");
+ const { openToolboxAndLog, closeToolboxAndLog, runTest, testSetup,
+         testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
+ 
+ module.exports = async function() {
+   await testSetup(PAGES_BASE_URL + "custom/inspector/index.html");
+ 
+   let toolbox = await openToolboxAndLog("custom.inspector", "inspector");
+   await reloadInspectorAndLog("custom", toolbox);
++
+   await selectNodeWithManyRulesAndLog(toolbox);
++
++  await collapseExpandAllAndLog(toolbox);
++
+   await closeToolboxAndLog("custom.inspector", toolbox);
+ 
+   await testTeardown();
+ };
+ 
+ /**
+  * Measure the time necessary to select a node and display the rule view when many rules
+  * match the element.
+  */
+ async function selectNodeWithManyRulesAndLog(toolbox) {
+   let inspector = toolbox.getPanel("inspector");
+ 
+-  // Local helper to select a node front and wait for the ruleview to be refreshed.
+-  let selectNodeFront = (nodeFront) => {
+-    let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
+-    inspector.selection.setNodeFront(nodeFront);
+-    return onRuleViewRefreshed;
+-  };
+-
+   let initialNodeFront = inspector.selection.nodeFront;
+ 
+   // Retrieve the node front for the test node.
+   let root = await inspector.walker.getRootNode();
+   let referenceNodeFront = await inspector.walker.querySelector(root, ".no-css-rules");
+   let testNodeFront = await inspector.walker.querySelector(root, ".many-css-rules");
+ 
+   // Select test node and measure the time to display the rule view with many rules.
+   dump("Selecting .many-css-rules test node front\n");
+   let test = runTest("custom.inspector.manyrules.selectnode");
+-  await selectNodeFront(testNodeFront);
++  await selectNodeFront(inspector, testNodeFront);
+   test.done();
+ 
+   // Select reference node and measure the time to empty the rule view.
+   dump("Move the selection to a node with no rules\n");
+   test = runTest("custom.inspector.manyrules.deselectnode");
+-  await selectNodeFront(referenceNodeFront);
++  await selectNodeFront(inspector, referenceNodeFront);
++  test.done();
++
++  await selectNodeFront(inspector, initialNodeFront);
++}
++
++async function collapseExpandAllAndLog(toolbox) {
++  let inspector = toolbox.getPanel("inspector");
++
++  let initialNodeFront = inspector.selection.nodeFront;
++  let root = await inspector.walker.getRootNode();
++
++  dump("Select expand-many-children node\n");
++  let many = await inspector.walker.querySelector(root, ".expand-many-children");
++  await selectNodeFront(inspector, many);
++
++  dump("Expand all children of expand-many-children\n");
++  let test = runTest("custom.inspector.expandall.manychildren");
++  await inspector.markup.expandAll(many);
+   test.done();
+ 
+-  await selectNodeFront(initialNodeFront);
++  dump("Collapse all children of expand-many-children\n");
++  test = runTest("custom.inspector.collapseall.manychildren");
++  await inspector.markup.collapseAll(many);
++  test.done();
++
++  dump("Select expand-balanced node\n");
++  let balanced = await inspector.walker.querySelector(root, ".expand-balanced");
++  await selectNodeFront(inspector, balanced);
++
++  dump("Expand all children of expand-balanced\n");
++  test = runTest("custom.inspector.expandall.balanced");
++  await inspector.markup.expandAll(balanced);
++  test.done();
++
++  dump("Collapse all children of expand-balanced\n");
++  test = runTest("custom.inspector.collapseall.balanced");
++  await inspector.markup.collapseAll(balanced);
++  test.done();
++
++  await selectNodeFront(inspector, initialNodeFront);
+ }
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
+--- a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
+@@ -12,8 +12,17 @@ exports.reloadInspectorAndLog = async fu
+     // First wait for markup view to be loaded against the new root node
+     await inspector.once("new-root");
+     // Then wait for inspector to be updated
+     await inspector.once("inspector-updated");
+   };
+ 
+   await reloadPageAndLog(label + ".inspector", toolbox, onReload);
+ };
++
++/*
++ * Helper to select a node front and wait for the ruleview to be refreshed.
++ */
++exports.selectNodeFront = function(inspector, nodeFront) {
++  let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
++  inspector.selection.setNodeFront(nodeFront);
++  return onRuleViewRefreshed;
++};

+ 79 - 0
frg/work-js/mozilla-release/patches/1415211-60a1.patch

@@ -0,0 +1,79 @@
+# HG changeset patch
+# User Tim Xie <tim.xie@mail.utoronto.ca>
+# Date 1520582359 18000
+# Node ID 6bf3a552fc5121de94558998557bdf4e78cd585d
+# Parent  0589551809086ef88caf4794a3659e99bc52fc39
+Bug 1415211 - Realigned XHR label, set property margin-inline-start of XHR to 0; r=nchevobbe
+
+MozReview-Commit-ID: 3K2jqmr5OCJ
+
+diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css
+--- a/devtools/client/themes/webconsole.css
++++ b/devtools/client/themes/webconsole.css
+@@ -285,19 +285,19 @@ a {
+ 
+ .message[category=network] .xhr,
+ .message.network .xhr {
+   background-color: var(--theme-body-color-alt);
+   color: var(--theme-body-background);
+   border-radius: 3px;
+   font-weight: bold;
+   font-size: 10px;
+-  padding: 2px;
++  padding: 1px 2px;
+   line-height: 10px;
+-  margin-inline-start: 3px;
++  margin-inline-start: 0;
+   margin-inline-end: 1ex;
+ }
+ 
+ /* CSS styles */
+ .webconsole-filter-button[category="css"] > .toolbarbutton-menubutton-button:before {
+   background-image: linear-gradient(#2DC3F3, #00B6F0);
+   border-color: #1BA2CC;
+ }
+@@ -605,20 +605,18 @@ textbox.jsterm-input-node[focused="true"
+ }
+ 
+ .message[open] .stacktrace,
+ .message.open .stacktrace {
+   display: block;
+ }
+ 
+ .message .theme-twisty {
+-  display: inline-block;
+-  vertical-align: middle;
+-  margin: 3px 0 0 0;
+-  flex-shrink: 0;
++  position: relative;
++  top: 0.1em;
+ }
+ 
+ /*Do not mirror the twisty because container force to ltr */
+ .message .theme-twisty:dir(rtl),
+ .message .theme-twisty:-moz-locale-dir(rtl) {
+   transform: none;
+ }
+ 
+diff --git a/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js b/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
+--- a/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
++++ b/devtools/client/webconsole/new-console-output/components/message-types/NetworkEventMessage.js
+@@ -107,17 +107,17 @@ function NetworkEventMessage({
+     ? dom.span({ className: "xhr" }, l10n.getStr("webConsoleXhrIndicator"))
+     : null;
+   const requestUrl = dom.a({ className: "url", title: request.url, onClick: toggle },
+     request.url);
+   const statusBody = statusInfo
+     ? dom.a({ className: "status", onClick: toggle }, statusInfo)
+     : null;
+ 
+-  const messageBody = [method, xhr, requestUrl, statusBody];
++  const messageBody = [xhr, method, requestUrl, statusBody];
+ 
+   // API consumed by Net monitor UI components. Most of the method
+   // are not needed in context of the Console panel (atm) and thus
+   // let's just provide empty implementation.
+   // Individual methods might be implemented step by step as needed.
+   let connector = {
+     viewSourceInDebugger: (url, line) => {
+       serviceContainer.onViewSourceInDebugger({url, line});

+ 0 - 0
frg/work-js/mozilla-release/patches/1419094-1-61a1-patch → frg/work-js/mozilla-release/patches/1419094-1-61a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1419094-2-61a1-patch → frg/work-js/mozilla-release/patches/1419094-2-61a1.patch


+ 2566 - 0
frg/work-js/mozilla-release/patches/1419925-59a1.patch

@@ -0,0 +1,2566 @@
+# HG changeset patch
+# User Neil Deakin <neil@mozilla.com>
+# Date 1512653990 18000
+# Node ID 8972884c1ac7e699f081c012cde67d45eb323975
+# Parent  52c0db4eb3f9ecab96d67554a331653684b08c0d
+Bug 1419925, implement a promise-oriented version of waitForClipboard, promiseClipboardChange, change a selection of tests to use this instead. Simplify some other clipboard tests that were unreliable before the fix for 1394757. r=jmaher
+
+diff --git a/browser/base/content/test/urlbar/browser_urlbarCopying.js b/browser/base/content/test/urlbar/browser_urlbarCopying.js
+--- a/browser/base/content/test/urlbar/browser_urlbarCopying.js
++++ b/browser/base/content/test/urlbar/browser_urlbarCopying.js
+@@ -230,55 +230,56 @@ function runTest(testCase, cb) {
+     if (testCase.setURL)
+       gURLBar.value = testCase.setURL;
+     doCheck();
+   }
+ }
+ 
+ function testCopy(copyVal, targetValue, cb) {
+   info("Expecting copy of: " + targetValue);
+-  waitForClipboard(targetValue, function() {
+-    gURLBar.focus();
+-    if (copyVal) {
+-      let offsets = [];
+-      while (true) {
+-        let startBracket = copyVal.indexOf("<");
+-        let endBracket = copyVal.indexOf(">");
+-        if (startBracket == -1 && endBracket == -1) {
+-          break;
+-        }
+-        if (startBracket > endBracket || startBracket == -1) {
+-          offsets = [];
+-          break;
+-        }
+-        offsets.push([startBracket, endBracket - 1]);
+-        copyVal = copyVal.replace("<", "").replace(">", "");
++
++  gURLBar.focus();
++  if (copyVal) {
++    let offsets = [];
++    while (true) {
++      let startBracket = copyVal.indexOf("<");
++      let endBracket = copyVal.indexOf(">");
++      if (startBracket == -1 && endBracket == -1) {
++        break;
++      }
++      if (startBracket > endBracket || startBracket == -1) {
++        offsets = [];
++        break;
+       }
+-      if (offsets.length == 0 ||
+-          copyVal != gURLBar.textValue) {
+-        ok(false, "invalid copyVal: " + copyVal);
+-      }
+-      gURLBar.selectionStart = offsets[0][0];
+-      gURLBar.selectionEnd = offsets[0][1];
+-      if (offsets.length > 1) {
+-        let sel = gURLBar.editor.selection;
+-        let r0 = sel.getRangeAt(0);
+-        let node0 = r0.startContainer;
+-        sel.removeAllRanges();
+-        offsets.map(function(startEnd) {
+-          let range = r0.cloneRange();
+-          range.setStart(node0, startEnd[0]);
+-          range.setEnd(node0, startEnd[1]);
+-          sel.addRange(range);
+-        });
+-      }
+-    } else {
+-      gURLBar.select();
++      offsets.push([startBracket, endBracket - 1]);
++      copyVal = copyVal.replace("<", "").replace(">", "");
++    }
++    if (offsets.length == 0 ||
++        copyVal != gURLBar.textValue) {
++      ok(false, "invalid copyVal: " + copyVal);
+     }
++    gURLBar.selectionStart = offsets[0][0];
++    gURLBar.selectionEnd = offsets[0][1];
++    if (offsets.length > 1) {
++      let sel = gURLBar.editor.selection;
++      let r0 = sel.getRangeAt(0);
++      let node0 = r0.startContainer;
++      sel.removeAllRanges();
++      offsets.map(function(startEnd) {
++        let range = r0.cloneRange();
++        range.setStart(node0, startEnd[0]);
++        range.setEnd(node0, startEnd[1]);
++        sel.addRange(range);
++      });
++    }
++  } else {
++    gURLBar.select();
++  }
+ 
++  waitForClipboard(targetValue, function() {
+     goDoCommand("cmd_copy");
+   }, cb, cb);
+ }
+ 
+ function loadURL(aURL, aCB) {
+   BrowserTestUtils.loadURI(gBrowser.selectedBrowser, aURL);
+   BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, aURL).then(aCB);
+ }
+diff --git a/browser/components/customizableui/test/browser_947914_button_copy.js b/browser/components/customizableui/test/browser_947914_button_copy.js
+--- a/browser/components/customizableui/test/browser_947914_button_copy.js
++++ b/browser/components/customizableui/test/browser_947914_button_copy.js
+@@ -27,34 +27,18 @@ add_task(async function() {
+     gURLBar.value = testText;
+     gURLBar.focus();
+     gURLBar.select();
+     await PanelUI.show();
+     info("Menu panel was opened");
+ 
+     ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
+ 
+-    copyButton.click();
+-    is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
+-
+-    // check that the text was added to the clipboard
+-    let clipboard = Services.clipboard;
+-    let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+-    globalClipboard = clipboard.kGlobalClipboard;
++    await SimpleTest.promiseClipboardChange(testText, () => {
++      copyButton.click();
++    });
+ 
+-    transferable.init(null);
+-    transferable.addDataFlavor("text/unicode");
+-    clipboard.getData(transferable, globalClipboard);
+-    let str = {}, strLength = {};
+-    transferable.getTransferData("text/unicode", str, strLength);
+-    let clipboardValue = "";
+-
+-    if (str.value) {
+-      str.value.QueryInterface(Ci.nsISupportsString);
+-      clipboardValue = str.value.data;
+-    }
+-    is(clipboardValue, testText, "Data was copied to the clipboard.");
++    is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
+   });
+ });
+ 
+ registerCleanupFunction(function cleanup() {
+-  Services.clipboard.emptyClipboard(globalClipboard);
+ });
+diff --git a/browser/components/customizableui/test/browser_947914_button_cut.js b/browser/components/customizableui/test/browser_947914_button_cut.js
+--- a/browser/components/customizableui/test/browser_947914_button_cut.js
++++ b/browser/components/customizableui/test/browser_947914_button_cut.js
+@@ -25,34 +25,17 @@ add_task(async function() {
+     // cut text from URL bar
+     gURLBar.value = testText;
+     gURLBar.focus();
+     gURLBar.select();
+     await PanelUI.show();
+     info("Menu panel was opened");
+ 
+     ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
+-    cutButton.click();
++    await SimpleTest.promiseClipboardChange(testText, () => {
++      cutButton.click();
++    });
+     is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
+-
+-    // check that the text was added to the clipboard
+-    let clipboard = Services.clipboard;
+-    let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
+-    globalClipboard = clipboard.kGlobalClipboard;
+-
+-    transferable.init(null);
+-    transferable.addDataFlavor("text/unicode");
+-    clipboard.getData(transferable, globalClipboard);
+-    let str = {}, strLength = {};
+-    transferable.getTransferData("text/unicode", str, strLength);
+-    let clipboardValue = "";
+-
+-    if (str.value) {
+-      str.value.QueryInterface(Ci.nsISupportsString);
+-      clipboardValue = str.value.data;
+-    }
+-    is(clipboardValue, testText, "Data was copied to the clipboard.");
+   });
+ });
+ 
+ registerCleanupFunction(function cleanup() {
+-  Services.clipboard.emptyClipboard(globalClipboard);
+ });
+diff --git a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js
+--- a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js
++++ b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items-svg.js
+@@ -27,16 +27,21 @@ add_task(function* () {
+   is(html, expectedHtml, "The innerHTML of the SVG node is correct");
+ 
+   // Helpers
+   function* pasteContent(menuId, clipboardData) {
+     let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+       target: markupTagLine,
+     });
+     info(`Testing ${menuId} for ${clipboardData}`);
+-    clipboard.copyString(clipboardData);
++
++    yield SimpleTest.promiseClipboardChange(clipboardData,
++      () => {
++        clipboard.copyString(clipboardData);
++      }
++    );
+ 
+     let onMutation = inspector.once("markupmutation");
+     allMenuItems.find(item => item.id === menuId).click();
+     info("Waiting for mutation to occur");
+     yield onMutation;
+   }
+ });
+diff --git a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js
+--- a/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js
++++ b/devtools/client/inspector/test/browser_inspector_menu-03-paste-items.js
+@@ -38,17 +38,22 @@ add_task(function* () {
+   let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
+ 
+   yield testPasteOuterHTMLMenu();
+   yield testPasteInnerHTMLMenu();
+   yield testPasteAdjacentHTMLMenu();
+ 
+   function* testPasteOuterHTMLMenu() {
+     info("Testing that 'Paste Outer HTML' menu item works.");
+-    clipboard.copyString("this was pasted (outerHTML)");
++
++    yield SimpleTest.promiseClipboardChange("this was pasted (outerHTML)",
++      () => {
++        clipboard.copyString("this was pasted (outerHTML)");
++      });
++
+     let outerHTMLSelector = "#paste-area h1";
+ 
+     let nodeFront = yield getNodeFront(outerHTMLSelector, inspector);
+     yield selectNode(nodeFront, inspector);
+ 
+     let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+       target: getContainerForNodeFront(nodeFront, inspector).tagLine,
+     });
+@@ -63,17 +68,21 @@ add_task(function* () {
+     ok(outerHTML.includes(clipboard.getText()),
+        "Clipboard content was pasted into the node's outer HTML.");
+     ok(!(yield testActor.hasNode(outerHTMLSelector)),
+       "The original node was removed.");
+   }
+ 
+   function* testPasteInnerHTMLMenu() {
+     info("Testing that 'Paste Inner HTML' menu item works.");
+-    clipboard.copyString("this was pasted (innerHTML)");
++
++    yield SimpleTest.promiseClipboardChange("this was pasted (innerHTML)",
++      () => {
++        clipboard.copyString("this was pasted (innerHTML)");
++      });
+     let innerHTMLSelector = "#paste-area .inner";
+     let getInnerHTML = () => testActor.getProperty(innerHTMLSelector,
+                                                    "innerHTML");
+     let origInnerHTML = yield getInnerHTML();
+ 
+     let nodeFront = yield getNodeFront(innerHTMLSelector, inspector);
+     yield selectNode(nodeFront, inspector);
+ 
+@@ -102,17 +111,21 @@ add_task(function* () {
+     yield selectNode(nodeFront, inspector);
+     let markupTagLine = getContainerForNodeFront(nodeFront, inspector).tagLine;
+ 
+     for (let { clipboardData, menuId } of PASTE_ADJACENT_HTML_DATA) {
+       let allMenuItems = openContextMenuAndGetAllItems(inspector, {
+         target: markupTagLine,
+       });
+       info(`Testing ${menuId} for ${clipboardData}`);
+-      clipboard.copyString(clipboardData);
++
++      yield SimpleTest.promiseClipboardChange(clipboardData,
++        () => {
++          clipboard.copyString(clipboardData);
++        });
+ 
+       let onMutation = inspector.once("markupmutation");
+       allMenuItems.find(item => item.id === menuId).click();
+       info("Waiting for mutation to occur");
+       yield onMutation;
+     }
+ 
+     let html = yield testActor.getProperty(adjacentNodeSelector, "innerHTML");
+diff --git a/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js
+--- a/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js
++++ b/devtools/client/webconsole/test/browser_webconsole_output_copy_newlines.js
+@@ -5,17 +5,16 @@
+ 
+ // Test that multiple messages are copied into the clipboard and that they are
+ // separated by new lines. See bug 916997.
+ 
+ "use strict";
+ 
+ add_task(function* () {
+   const TEST_URI = "data:text/html;charset=utf8,<p>hello world, bug 916997";
+-  let clipboardValue = "";
+ 
+   yield loadTab(TEST_URI);
+   let hud = yield openConsole();
+   hud.jsterm.clearOutput();
+ 
+   let controller = top.document.commandDispatcher
+                    .getControllerForCommand("cmd_copy");
+   is(controller.isCommandEnabled("cmd_copy"), false, "cmd_copy is disabled");
+@@ -42,31 +41,26 @@ add_task(function* () {
+   goUpdateCommand("cmd_copy");
+   controller = top.document.commandDispatcher
+                .getControllerForCommand("cmd_copy");
+   is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled");
+ 
+   let selection = hud.iframeWindow.getSelection() + "";
+   info("selection '" + selection + "'");
+ 
+-  waitForClipboard((str) => {
+-    clipboardValue = str;
+-    return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1;
+-  },
+-    () => {
+-      goDoCommand("cmd_copy");
++  let clipboardValue = yield SimpleTest.promiseClipboardChange(str => {
++      return str.indexOf("bug916997a") > -1 && str.indexOf("bug916997b") > -1;
+     },
+     () => {
+-      info("clipboard value '" + clipboardValue + "'");
+-      let lines = clipboardValue.trim().split("\n");
+-      is(hud.outputNode.children.length, 2, "number of messages");
+-      is(lines.length, hud.outputNode.children.length, "number of lines");
+-      isnot(lines[0].indexOf("bug916997a"), -1,
+-            "first message text includes 'bug916997a'");
+-      isnot(lines[1].indexOf("bug916997b"), -1,
+-            "second message text includes 'bug916997b'");
+-      is(lines[0].indexOf("bug916997b"), -1,
+-         "first message text does not include 'bug916997b'");
+-    },
+-    () => {
+-      info("last clipboard value: '" + clipboardValue + "'");
+-    });
++      goDoCommand("cmd_copy");
++    }
++  );
++
++  let lines = clipboardValue.trim().split("\n");
++  is(hud.outputNode.children.length, 2, "number of messages");
++  is(lines.length, hud.outputNode.children.length, "number of lines");
++  isnot(lines[0].indexOf("bug916997a"), -1,
++        "first message text includes 'bug916997a'");
++  isnot(lines[1].indexOf("bug916997b"), -1,
++        "second message text includes 'bug916997b'");
++  is(lines[0].indexOf("bug916997b"), -1,
++     "first message text does not include 'bug916997b'");
+ });
+diff --git a/dom/base/test/copypaste.js b/dom/base/test/copypaste.js
+--- a/dom/base/test/copypaste.js
++++ b/dom/base/test/copypaste.js
+@@ -24,68 +24,68 @@ function modifySelection(s) {
+ 
+ function getLoadContext() {
+   var Ci = SpecialPowers.Ci;
+   return SpecialPowers.wrap(window).QueryInterface(Ci.nsIInterfaceRequestor)
+                                    .getInterface(Ci.nsIWebNavigation)
+                                    .QueryInterface(Ci.nsILoadContext);
+ }
+ 
+-function testCopyPaste (isXHTML) {
++async function testCopyPaste (isXHTML) {
+   var suppressUnicodeCheckIfHidden = !!isXHTML;
+   var suppressHTMLCheck = !!isXHTML;
+ 
+   var webnav = SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+                      .getInterface(SpecialPowers.Ci.nsIWebNavigation)
+ 
+   var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
+ 
+   var documentViewer = docShell.contentViewer
+                                .QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
+ 
+   var clipboard = SpecialPowers.Services.clipboard;
+ 
+   var textarea = SpecialPowers.wrap(document.getElementById('input'));
+ 
+-  function copySelectionToClipboard(suppressUnicodeCheck) {
+-    documentViewer.copySelection();
++  async function copySelectionToClipboard(suppressUnicodeCheck) {
++    await SimpleTest.promiseClipboardChange(() => true,
++                                            () => { documentViewer.copySelection(); });
+     if (!suppressUnicodeCheck)
+       ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1,1), "check text/unicode");
+     if (!suppressHTMLCheck)
+       ok(clipboard.hasDataMatchingFlavors(["text/html"], 1,1), "check text/html");
+   }
+   function clear(node, suppressUnicodeCheck) {
+     textarea.blur();
+-    clipboard.emptyClipboard(1);
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+   }
+-  function copyToClipboard(node, suppressUnicodeCheck) {
++  async function copyToClipboard(node, suppressUnicodeCheck) {
+     clear();
+     var r = document.createRange();
+     r.selectNode(node);
+     window.getSelection().addRange(r);
+-    copySelectionToClipboard(suppressUnicodeCheck);
++    await copySelectionToClipboard(suppressUnicodeCheck);
+   }
+   function addRange(startNode,startIndex,endNode,endIndex) {
+     var sel = window.getSelection();
+     var r = document.createRange();
+     r.setStart(startNode,startIndex)
+     r.setEnd(endNode,endIndex)
+     sel.addRange(r);
+   }
+-  function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) {
++  async function copyRangeToClipboard(startNode,startIndex,endNode,endIndex,suppressUnicodeCheck) {
+     clear();
+     addRange(startNode,startIndex,endNode,endIndex);
+-    copySelectionToClipboard(suppressUnicodeCheck);
++    await copySelectionToClipboard(suppressUnicodeCheck);
+   }
+-  function copyChildrenToClipboard(id) {
++  async function copyChildrenToClipboard(id) {
+     clear();
+     window.getSelection().selectAllChildren(document.getElementById(id));
+-    copySelectionToClipboard();
++    await copySelectionToClipboard();
+   }
+   function getClipboardData(mime) {
+     var transferable = SpecialPowers.Cc['@mozilla.org/widget/transferable;1']
+                                     .createInstance(SpecialPowers.Ci.nsITransferable);
+     transferable.init(getLoadContext());
+     transferable.addDataFlavor(mime);
+     clipboard.getData(transferable, 1);
+     var data = SpecialPowers.createBlankObject();
+@@ -127,123 +127,116 @@ function testCopyPaste (isXHTML) {
+   }
+   function testSelectionToString(expected) {
+     is(window.getSelection().toString().replace(/\r\n/g,"\n"), expected, "Selection.toString");
+   }
+   function testInnerHTML(id, expected) {
+     var value = document.getElementById(id).innerHTML;
+     is(value, expected, id + ".innerHTML");
+   }
+-  function testEmptyChildren(id) {
+-    copyChildrenToClipboard(id);
+-    testSelectionToString("");
+-    testClipboardValue("text/unicode", null);
+-    testClipboardValue("text/html", null);
+-    testPasteText("");
+-  }
+ 
+-  copyChildrenToClipboard("draggable");
++  await copyChildrenToClipboard("draggable");
+   testSelectionToString("This is a draggable bit of text.");
+   testClipboardValue("text/unicode",
+                      "This is a draggable bit of text.");
+   testHtmlClipboardValue("text/html",
+                      "<div id=\"draggable\" title=\"title to have a long HTML line\">This is a <em>draggable</em> bit of text.</div>");
+   testPasteText("This is a draggable bit of text.");
+ 
+-  copyChildrenToClipboard("alist");
++  await copyChildrenToClipboard("alist");
+   testSelectionToString(" bla\n\n    foo\n    bar\n\n");
+   testClipboardValue("text/unicode", " bla\n\n    foo\n    bar\n\n");
+   testHtmlClipboardValue("text/html", "<div id=\"alist\">\n    bla\n    <ul>\n      <li>foo</li>\n      \n      <li>bar</li>\n    </ul>\n  </div>");
+   testPasteText(" bla\n\n    foo\n    bar\n\n");
+ 
+-  copyChildrenToClipboard("blist");
++  await copyChildrenToClipboard("blist");
+   testSelectionToString(" mozilla\n\n    foo\n    bar\n\n");
+   testClipboardValue("text/unicode", " mozilla\n\n    foo\n    bar\n\n");
+   testHtmlClipboardValue("text/html", "<div id=\"blist\">\n    mozilla\n    <ol>\n      <li>foo</li>\n      \n      <li>bar</li>\n    </ol>\n  </div>");
+   testPasteText(" mozilla\n\n    foo\n    bar\n\n");
+ 
+-  copyChildrenToClipboard("clist");
++  await copyChildrenToClipboard("clist");
+   testSelectionToString(" mzla\n\n    foo\n        bazzinga!\n    bar\n\n");
+   testClipboardValue("text/unicode", " mzla\n\n    foo\n        bazzinga!\n    bar\n\n");
+   testHtmlClipboardValue("text/html", "<div id=\"clist\">\n    mzla\n    <ul>\n      <li>foo<ul>\n        <li>bazzinga!</li>\n      </ul></li>\n      \n      <li>bar</li>\n    </ul>\n  </div>");
+   testPasteText(" mzla\n\n    foo\n        bazzinga!\n    bar\n\n");
+ 
+-  copyChildrenToClipboard("div4");
++  await copyChildrenToClipboard("div4");
+   testSelectionToString(" Tt t t ");
+   testClipboardValue("text/unicode", " Tt t t ");
+   if (isXHTML) {
+     testHtmlClipboardValue("text/html", "<div id=\"div4\">\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">t t t</textarea>\n</div>");
+     testInnerHTML("div4", "\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">t t t</textarea>\n");
+   }
+   else {
+     testHtmlClipboardValue("text/html", "<div id=\"div4\">\n  T<textarea>t t t</textarea>\n</div>");
+     testInnerHTML("div4", "\n  T<textarea>t t t</textarea>\n");
+   }
+   testPasteText(" Tt t t ");
+ 
+-  copyChildrenToClipboard("div5");
++  await copyChildrenToClipboard("div5");
+   testSelectionToString(" T     ");
+   testClipboardValue("text/unicode", " T     ");
+   if (isXHTML) {
+     testHtmlClipboardValue("text/html", "<div id=\"div5\">\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">     </textarea>\n</div>");
+     testInnerHTML("div5", "\n  T<textarea xmlns=\"http://www.w3.org/1999/xhtml\">     </textarea>\n");
+   }
+   else {
+     testHtmlClipboardValue("text/html", "<div id=\"div5\">\n  T<textarea>     </textarea>\n</div>");
+     testInnerHTML("div5", "\n  T<textarea>     </textarea>\n");
+   }
+   testPasteText(" T     ");
+ 
+-  copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div6").childNodes[0],0, $("div6").childNodes[1],1,suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ // START Disabled due to bug 564688
+ if (false) {
+   testClipboardValue("text/unicode", "");
+   testClipboardValue("text/html", "");
+ }
+ // END Disabled due to bug 564688
+   testInnerHTML("div6", "div6");
+ 
+-  copyRangeToClipboard($("div7").childNodes[0],0, $("div7").childNodes[0],4,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div7").childNodes[0],0, $("div7").childNodes[0],4,suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ // START Disabled due to bug 564688
+ if (false) {
+   testClipboardValue("text/unicode", "");
+   testClipboardValue("text/html", "");
+ }
+ // END Disabled due to bug 564688
+   testInnerHTML("div7", "div7");
+ 
+-  copyRangeToClipboard($("div8").childNodes[0],0, $("div8").childNodes[0],4,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div8").childNodes[0],0, $("div8").childNodes[0],4,suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ // START Disabled due to bug 564688
+ if (false) {
+   testClipboardValue("text/unicode", "");
+   testClipboardValue("text/html", "");
+ }
+ // END Disabled due to bug 564688
+   testInnerHTML("div8", "div8");
+ 
+-  copyRangeToClipboard($("div9").childNodes[0],0, $("div9").childNodes[0],4,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div9").childNodes[0],0, $("div9").childNodes[0],4,suppressUnicodeCheckIfHidden);
+   testSelectionToString("div9");
+   testClipboardValue("text/unicode", "div9");
+   testHtmlClipboardValue("text/html", "div9");
+   testInnerHTML("div9", "div9");
+ 
+-  copyToClipboard($("div10"), suppressUnicodeCheckIfHidden);
++  await copyToClipboard($("div10"), suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+   testInnerHTML("div10", "div10");
+ 
+-  copyToClipboard($("div10").firstChild, suppressUnicodeCheckIfHidden);
++  await copyToClipboard($("div10").firstChild, suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ 
+-  copyRangeToClipboard($("div10").childNodes[0],0, $("div10").childNodes[0],1,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div10").childNodes[0],0, $("div10").childNodes[0],1,suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ 
+-  copyRangeToClipboard($("div10").childNodes[1],0, $("div10").childNodes[1],1,suppressUnicodeCheckIfHidden);
++  await copyRangeToClipboard($("div10").childNodes[1],0, $("div10").childNodes[1],1,suppressUnicodeCheckIfHidden);
+   testSelectionToString("");
+ 
+   if (!isXHTML) {
+     // ============ copy/paste multi-range selection (bug 1123505)
+     // with text start node
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+     var r = document.createRange();
+@@ -252,85 +245,85 @@ if (false) {
+     r.setStart(parent, 0);
+     r.setEnd(parent.firstChild, 15);  // the end of "Copy..."
+     sel.addRange(r);
+ 
+     r = document.createRange();
+     r.setStart(ul, 1);  // before the space inside the UL
+     r.setEnd(parent, 2);  // after the UL
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable1', 'Copy1then Paste');
+ 
+     // with text end node
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+     var r = document.createRange();
+     var ul = $('ul2');
+     var parent = ul.parentNode;
+     r.setStart(parent, 0);
+     r.setEnd(ul, 1);  // after the space
+     sel.addRange(r);
+ 
+     r = document.createRange();
+     r.setStart(parent.childNodes[1], 0);  // the start of "Copy..."
+     r.setEnd(parent, 2);
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable2', 'Copy2then Paste');
+ 
+     // with text end node and non-empty start
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+     var r = document.createRange();
+     var ul = $('ul3');
+     var parent = ul.parentNode;
+     r.setStart(parent, 0);
+     r.setEnd(ul, 1);  // after the space
+     sel.addRange(r);
+ 
+     r = document.createRange();
+     r.setStart(parent.childNodes[1], 0);  // the start of "Copy..."
+     r.setEnd(parent, 2);
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable3', '<ul id="ul3"><li>\n<br></li></ul>Copy3then Paste');
+ 
+     // with elements of different depth
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+     var r = document.createRange();
+     var div1 = $('div1s');
+     var parent = div1.parentNode;
+     r.setStart(parent, 0);
+     r.setEnd(document.getElementById('div1se1'), 1);  // after the "inner" DIV
+     sel.addRange(r);
+ 
+     r = document.createRange();
+     r.setStart(div1.childNodes[1], 0);  // the start of "after"
+     r.setEnd(parent, 1);
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable4', '<div id="div1s"><div id="div1se1">before</div></div><div id="div1s">after</div>');
+ 
+     // with elements of different depth, and a text node at the end
+     var sel = window.getSelection();
+     sel.removeAllRanges();
+     var r = document.createRange();
+     var div1 = $('div2s');
+     var parent = div1.parentNode;
+     r.setStart(parent, 0);
+     r.setEnd(document.getElementById('div2se1'), 1);  // after the "inner" DIV
+     sel.addRange(r);
+ 
+     r = document.createRange();
+     r.setStart(div1.childNodes[1], 0);  // the start of "after"
+     r.setEnd(parent, 1);
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable5', '<div id="div2s"><div id="div2se1">before</div></div><div id="div2s">after</div>');
+ 
+     // crash test for bug 1127835
+     var e1 = document.getElementById('1127835crash1');
+     var e2 = document.getElementById('1127835crash2');
+     var e3 = document.getElementById('1127835crash3');
+     var t1 = e1.childNodes[0];
+     var t3 = e3.childNodes[0];
+@@ -342,71 +335,73 @@ if (false) {
+     r.setStart(t1, 1);
+     r.setEnd(e2, 0);
+     sel.addRange(r);
+   
+     r = document.createRange();
+     r.setStart(e2, 1);
+     r.setEnd(t3, 0);
+     sel.addRange(r);
+-    copySelectionToClipboard(true);
++    await copySelectionToClipboard(true);
+     testPasteHTML('contentEditable6', '<span id="1127835crash1"></span><div id="1127835crash2"><div>\n</div></div><br>');
+   }
+ 
+   // ============ copy/paste test from/to a textarea
+ 
+   var val = "1\n 2\n  3";
+   textarea.value=val;
+   textarea.select();
+-  textarea.editor.copy();
+-  
++  await SimpleTest.promiseClipboardChange(() => true,
++                                          () => { textarea.editor.copy(); });
+   textarea.value="";
+   textarea.editor.paste(1);
+   is(textarea.value, val);
+   textarea.value="";
+ 
+   // ============ NOSCRIPT should not be copied
+ 
+-  copyChildrenToClipboard("div13");
++  await copyChildrenToClipboard("div13");
+   testSelectionToString("__");
+   testClipboardValue("text/unicode", "__");
+   testHtmlClipboardValue("text/html", "<div id=\"div13\">__</div>");
+   testPasteText("__");
+ 
+   // ============ converting cell boundaries to tabs in tables
+ 
+-  copyToClipboard($("tr1"));
++  await copyToClipboard($("tr1"));
+   testClipboardValue("text/unicode", "foo\tbar");
+ 
+   if (!isXHTML) {
+     // ============ spanning multiple rows
+ 
+-    copyRangeToClipboard($("tr2"),0,$("tr3"),0);
++    await copyRangeToClipboard($("tr2"),0,$("tr3"),0);
+     testClipboardValue("text/unicode", "1\t2\n3\t4\n");
+     testHtmlClipboardValue("text/html", '<table><tbody><tr id="tr2"><tr id="tr2"><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr><tr id="tr3"></tr></tr></tbody></table>');
+ 
+     // ============ spanning multiple rows in multi-range selection
+ 
+     clear();
+     addRange($("tr2"),0,$("tr2"),2);
+     addRange($("tr3"),0,$("tr3"),2);
+-    copySelectionToClipboard();
++    await copySelectionToClipboard();
+     testClipboardValue("text/unicode", "1\t2\n5\t6");
+     testHtmlClipboardValue("text/html", '<table><tbody><tr id="tr2"><td>1</td><td>2</td></tr><tr id="tr3"><td>5</td><td>6</td></tr></tbody></table>');
+   }
+ 
+   // ============ manipulating Selection in oncopy
+ 
+-  copyRangeToClipboard($("div11").childNodes[0],0, $("div11").childNodes[1],2);
++  await copyRangeToClipboard($("div11").childNodes[0],0, $("div11").childNodes[1],2);
+   testClipboardValue("text/unicode", "Xdiv11");
+   testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>11</p></div>");
+-  setTimeout(function(){testSelectionToString("div11")},0);
++
++  await new Promise(resolve => { setTimeout(resolve, 0); });
++  testSelectionToString("div11");
++
++  await new Promise(resolve => { setTimeout(resolve, 0); });
++  await copyRangeToClipboard($("div12").childNodes[0],0, $("div12").childNodes[1],2);
+ 
+-  setTimeout(function(){
+-    copyRangeToClipboard($("div12").childNodes[0],0, $("div12").childNodes[1],2);
+-    testClipboardValue("text/unicode", "Xdiv12");
+-    testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>");
+-    setTimeout(function(){ 
+-      testSelectionToString("div12"); 
+-      setTimeout(SimpleTest.finish,0);
+-    },0);
+-  },0);
++  testClipboardValue("text/unicode", "Xdiv12");
++  testHtmlClipboardValue("text/html", "<div><p>X<span>div</span>12</p></div>");
++  await new Promise(resolve => { setTimeout(resolve, 0); });
++  testSelectionToString("div12"); 
++
++  await new Promise(resolve => { setTimeout(resolve, 0); });
+ }
+diff --git a/dom/base/test/test_copypaste.html b/dom/base/test/test_copypaste.html
+--- a/dom/base/test/test_copypaste.html
++++ b/dom/base/test/test_copypaste.html
+@@ -1,29 +1,34 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+ -->
+ <head>
+   <title>Test for copy/paste</title>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+   <script type="text/javascript" src="copypaste.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=524975">Mozilla Bug </a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ SimpleTest.waitForExplicitFinish();
+-addLoadEvent(() => testCopyPaste(false));
++addLoadEvent(() => {
++  add_task(async function test_copyhtml() {
++    await testCopyPaste(false);
++  });
++});
+ 
+ </script>
+ </pre>
+ <div>
+ 
+   <div id="draggable" title="title to have a long HTML line">This is a <em>draggable</em> bit of text.</div>
+   <textarea id="input" cols="40" rows="10"></textarea>
+ 
+diff --git a/dom/base/test/test_copypaste.xhtml b/dom/base/test/test_copypaste.xhtml
+--- a/dom/base/test/test_copypaste.xhtml
++++ b/dom/base/test/test_copypaste.xhtml
+@@ -11,33 +11,38 @@ This test is different from test_copypas
+      elements, and unlike HTML, neither does it produce text/_moz_htmlcontext
+      and text/_moz_htmlinfo, which the clipboard converts to text/unicode.
+ -->
+ <html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+   <title>Test for copy/paste with XHTML</title>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="text/javascript" src="copypaste.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=888839">Mozilla Bug 888839</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ <![CDATA[
+ 
+ function modifySelectionDiv12() {
+   modifySelection("X<b style='display:none'>Y</b>");
+ }
+ 
+ SimpleTest.waitForExplicitFinish();
+-addLoadEvent(() => testCopyPaste(true));
++addLoadEvent(() => {
++  add_task(async function test_copyhtml() {
++    await testCopyPaste(true);
++  });
++});
+ 
+ ]]>
+ </script>
+ </pre>
+ <div>
+ 
+   <div id="draggable" title="title to have a long HTML line">This is a <em>draggable</em> bit of text.</div>
+   <textarea id="input" cols="40" rows="10"></textarea>
+diff --git a/dom/base/test/test_copypaste.xul b/dom/base/test/test_copypaste.xul
+--- a/dom/base/test/test_copypaste.xul
++++ b/dom/base/test/test_copypaste.xul
+@@ -12,45 +12,28 @@ https://bugzilla.mozilla.org/show_bug.cg
+ 
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(runTest);
+ 
+ function runTest() {
+   let desc = document.querySelector("description");
+   window.getSelection().selectAllChildren(desc);
+ 
+-  let webnav = window.
+-               QueryInterface(Ci.nsIInterfaceRequestor).
+-               getInterface(Ci.nsIWebNavigation);
+-
+-  webnav.
+-    QueryInterface(Ci.nsIDocShell).
+-    contentViewer.
+-    QueryInterface(Ci.nsIContentViewerEdit).
+-    copySelection();
+-
+-  let mime = "text/unicode";
+-  let whichClipboard = Ci.nsIClipboard.kGlobalClipboard;
+-  let clipboard = Cc["@mozilla.org/widget/clipboard;1"].
+-                  getService(Ci.nsIClipboard);
+-  ok(clipboard.hasDataMatchingFlavors([mime], 1, whichClipboard),
+-     "Clipboard should have text/unicode");
+-
+-  let transferable = Cc["@mozilla.org/widget/transferable;1"].
+-                     createInstance(Ci.nsITransferable);
+-  transferable.init(webnav.QueryInterface(Ci.nsILoadContext));
+-  transferable.addDataFlavor(mime);
+-  clipboard.getData(transferable, whichClipboard);
+-  var data = {};
+-  transferable.getTransferData(mime, data, {});
+-  is(data.value.QueryInterface(Ci.nsISupportsString).data,
+-     "\n    hello\n    world\n  ",
+-     "Paste is not HTML, so it should not be pretty printed");
+-
+-  SimpleTest.finish();
++  let expected = "\n    hello\n    world\n  ";
++  SimpleTest.waitForClipboard(expected, function() {
++    let webnav = window.QueryInterface(Ci.nsIInterfaceRequestor)
++                       .getInterface(Ci.nsIWebNavigation);
++    webnav.QueryInterface(Ci.nsIDocShell)
++          .contentViewer
++          .QueryInterface(Ci.nsIContentViewerEdit)
++          .copySelection();
++  }, function() {
++    ok(true, "Paste is not HTML, so it should not be pretty printed");
++    SimpleTest.finish();
++  });
+ }
+ 
+   ]]></script>
+ 
+   <description style="-moz-user-focus: normal; -moz-user-select: text;"><![CDATA[
+     hello
+     world
+   ]]></description>
+diff --git a/dom/browser-element/mochitest/browserElement_CopyPaste.js b/dom/browser-element/mochitest/browserElement_CopyPaste.js
+--- a/dom/browser-element/mochitest/browserElement_CopyPaste.js
++++ b/dom/browser-element/mochitest/browserElement_CopyPaste.js
+@@ -19,20 +19,22 @@ var iframeInner;
+ var state = 0;
+ var stateMeaning;
+ var defaultData;
+ var pasteData;
+ var focusScript;
+ var createEmbededFrame = false;
+ var testSelectionChange = false;
+ 
+-function copyToClipboard(str) {
++function copyToClipboard(str, callback) {
+   gTextarea.value = str;
+   SpecialPowers.wrap(gTextarea).editor.selectAll();
+-  SpecialPowers.wrap(gTextarea).editor.copy();
++  SimpleTest.waitForClipboard(() => true, () => {
++    SpecialPowers.wrap(gTextarea).editor.copy();
++  }, callback, () => { ok(false, "clipboard copy failed"); });
+ }
+ 
+ function getScriptForGetContent() {
+   var script = 'data:,\
+     var elt = content.document.getElementById("text"); \
+     var txt = ""; \
+     if (elt) { \
+       if (elt.tagName === "DIV" || elt.tagName === "BODY") { \
+@@ -207,46 +209,47 @@ function testSelectAll(e) {
+   });
+ 
+   mm.loadFrameScript(getScriptForSetFocus(), false);
+ }
+ 
+ function testCopy1(e) {
+   // Right now we're at "selectall" state, so we can test copy commnad by
+   // calling doCommand
+-  copyToClipboard("");
+-  let setup = function() {
+-    doCommand("copy");
+-  };
++  copyToClipboard("", () => {
++    let setup = function() {
++      doCommand("copy");
++    };
+ 
+-  let nextTest = function(success) {
+-    ok(success, "copy command works" + stateMeaning);
+-    SimpleTest.executeSoon(function() { testPaste1(e); });
+-  };
++    let nextTest = function(success) {
++      ok(success, "copy command works" + stateMeaning);
++      SimpleTest.executeSoon(function() { testPaste1(e); });
++    };
+ 
+-  let success = function() {
+-    nextTest(true);
+-  }
++    let success = function() {
++      nextTest(true);
++    }
+ 
+-  let fail = function() {
+-    nextTest(false);
+-  }
++    let fail = function() {
++      nextTest(false);
++    }
+ 
+-  let compareData = defaultData;
+-  SimpleTest.waitForClipboard(compareData, setup, success, fail);
++    let compareData = defaultData;
++    SimpleTest.waitForClipboard(compareData, setup, success, fail);
++  });
+ }
+ 
+ function testPaste1(e) {
+   // Next test paste command, first we copy to global clipboard in parent side.
+   // Then paste it to child side.
+-  copyToClipboard(pasteData);
+-
+-  doCommand('selectall');
+-  doCommand("paste");
+-  SimpleTest.executeSoon(function() { testPaste2(e); });
++  copyToClipboard(pasteData, () => {
++    doCommand('selectall');
++    doCommand("paste");
++    SimpleTest.executeSoon(function() { testPaste2(e); });
++  });
+ }
+ 
+ function testPaste2(e) {
+   mm.addMessageListener('content-text', function messageforpaste(msg) {
+     mm.removeMessageListener('content-text', messageforpaste);
+     if (state == 4) {
+       // normal div cannot paste, so the content remain unchange
+       ok(SpecialPowers.wrap(msg).json === defaultData, "paste command works" + stateMeaning);
+@@ -259,50 +262,52 @@ function testPaste2(e) {
+     SimpleTest.executeSoon(function() { testCut1(e); });
+   });
+ 
+   mm.loadFrameScript(getScriptForGetContent(), false);
+ }
+ 
+ function testCut1(e) {
+   // Clean clipboard first
+-  copyToClipboard("");
+-  let setup = function() {
+-    doCommand("selectall");
+-    doCommand("cut");
+-  };
++  copyToClipboard("", () => {
++    let setup = function() {
++      doCommand("selectall");
++      doCommand("cut");
++    };
+ 
+-  let nextTest = function(success) {
+-    if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) {
+-      // Something weird when we doCommand with content editable element in OOP.
+-      todo(false, "cut function works" + stateMeaning);
+-    } else {
+-      ok(success, "cut function works" + stateMeaning);
+-    }
+-    SimpleTest.executeSoon(function() { testCut2(e); });
+-  };
++    let nextTest = function(success) {
++      if (state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) {
++        // Something weird when we doCommand with content editable element in OOP.
++        todo(false, "cut function works" + stateMeaning);
++      } else {
++        ok(success, "cut function works" + stateMeaning);
++      }
++      SimpleTest.executeSoon(function() { testCut2(e); });
++    };
+ 
+-  let success = function() {
+-    nextTest(true);
+-  }
++    let success = function() {
++      nextTest(true);
++    }
+ 
+-  let fail = function() {
+-    nextTest(false);
+-  }
++    let fail = function() {
++      nextTest(false);
++    }
++
++    let compareData = pasteData;
+ 
+-  let compareData = pasteData;
+-  // Something weird when we doCommand with content editable element in OOP.
+-  // Always true in this case
+-  // Normal div case cannot cut, always true as well.
+-  if ((state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) ||
+-      state == 4) {
+-    compareData = function() { return true; }
+-  }
++    // Something weird when we doCommand with content editable element in OOP.
++    // Always true in this case
++    // Normal div case cannot cut, always true as well.
++    if ((state == 3 && browserElementTestHelpers.getOOPByDefaultPref()) ||
++        state == 4) {
++      compareData = function() { return true; }
++    }
+ 
+-  SimpleTest.waitForClipboard(compareData, setup, success, fail);
++    SimpleTest.waitForClipboard(compareData, setup, success, fail);
++  });
+ }
+ 
+ function testCut2(e) {
+   mm.addMessageListener('content-text', function messageforcut(msg) {
+     mm.removeMessageListener('content-text', messageforcut);
+     // normal div cannot cut
+     if (state == 4) {
+       ok(SpecialPowers.wrap(msg).json !== "", "cut command works" + stateMeaning);
+diff --git a/dom/tests/mochitest/general/test_clipboard_events.html b/dom/tests/mochitest/general/test_clipboard_events.html
+--- a/dom/tests/mochitest/general/test_clipboard_events.html
++++ b/dom/tests/mochitest/general/test_clipboard_events.html
+@@ -1,107 +1,84 @@
+ <!DOCTYPE HTML>
+ <html>
+ <head>
+   <title>Test for Clipboard Events</title>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <p id="display"></p>
+ <div id="content" style="border: 3px solid black; padding: 3em;">CONTENT TEXT<input id="content-input" value="INPUT TEXT"></div>
+ <img id="image" src="image_50.png">
+ <button id="button">Button</button>
+ 
+ <div id="syntheticSpot" oncut="compareSynthetic(event, 'cut')"
+                         oncopy="compareSynthetic(event, 'copy')"
+                         onpaste="compareSynthetic(event, 'paste')">Spot</div>
+ 
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+-
+ var content = document.getElementById("content");
+ var contentInput = document.getElementById("content-input");
+ var clipboardInitialValue = "empty";
+ 
+-// Test that clearing and reading the clipboard works.  A random number
+-// is used to make sure that leftover clipboard values from a previous
+-// test run don't cause a false-positive test.
+-var cb_text = "empty_" + Math.random();
+-setClipboardText(cb_text);
+-
+-is(getClipboardText(), cb_text, "set/get clipboard text failed");
+-
+ var cachedCutData, cachedCopyData, cachedPasteData;
+ 
+-// A list of test functions to run.  Before each test function is run, the
+-// clipboard is initialized to clipboardInitialValue, and the contents of
+-// div#content are set as the window's selection.
+-var testFunctions = [
+-  test_dom_oncopy,
+-  test_dom_oncut,
+-  test_dom_onpaste,
+-  test_dom_oncopy_abort,
+-  test_input_oncopy,
+-  test_input_oncut,
+-  test_input_onpaste,
+-  test_input_oncopy_abort,
+-  test_input_oncut_abort,
+-  test_input_onpaste_abort,
+-  test_input_cut_dataTransfer,
+-  test_input_cut_abort_dataTransfer,
+-  test_input_copy_dataTransfer,
+-  test_input_paste_dataTransfer,
+-  test_input_paste_abort_dataTransfer,
+-  test_input_copypaste_dataTransfer_multiple,
+-  test_input_copy_button_dataTransfer,
+-  test_eventspref_disabled,
+-  test_input_cut_disallowed_types_dataTransfer,
+-  test_image_dataTransfer,
+-  ];
++// Before each test function is run, the clipboard is initialized
++// to clipboardInitialValue, and the contents of div#content are
++// set as the window's selection.
++
++add_task(async function initialize_for_tests() {
++  await SimpleTest.promiseFocus();
++
++  await new Promise(resolve => {
++    SpecialPowers.pushPrefEnv({
++      // NOTE: These tests operate under the assumption that the protected mode of
++      // DataTransfer is enabled.
++      "set": [["dom.events.dataTransfer.protected.enabled", true]]
++    }, resolve);
++  });
+ 
+-function doTests()
+-{
++  // Test that clearing and reading the clipboard works.  A random number
++  // is used to make sure that leftover clipboard values from a previous
++  // test run don't cause a false-positive test.
++  var cb_text = "empty_" + Math.random();
++
++  await putOnClipboard(cb_text, () => { setClipboardText(cb_text) },
++                        "initial set/get clipboard text");
++});
++
++async function reset() {
+   // Init clipboard
+-  setClipboardText(clipboardInitialValue);
++  await putOnClipboard(clipboardInitialValue,
++                       () => { setClipboardText(clipboardInitialValue) },
++                       "reset clipboard");
+ 
+   // Reset value of contentInput.
+   contentInput.value = "INPUT TEXT";
+-
+-  if (testFunctions.length) {
+-    let func = testFunctions.shift();
+-    let result = func();
+-    if (result instanceof Promise) {
+-      result.then(doTests);
+-    }
+-    else {
+-      doTests();
+-    }
+-  }
+-  else {
+-    // Check if the cached clipboard data can be accessed or modified
+-    // and whether it modifies the real clipboard
+-    checkCachedDataTransfer(cachedCutData, "cut");
+-    checkCachedDataTransfer(cachedCopyData, "copy");
+-    checkCachedDataTransfer(cachedPasteData, "paste");
+-
+-    checkSyntheticEvents();
+-
+-    SimpleTest.finish();
+-  }
+ }
+ 
+-SimpleTest.waitForExplicitFinish();
+-
+ function getClipboardText() {
+   return SpecialPowers.getClipboardData("text/unicode");
+ }
+ 
++async function putOnClipboard(expected, operationFn, desc, type) {
++  await SimpleTest.promiseClipboardChange(expected, operationFn, type);
++  ok(true, desc);
++}
++
++async function wontPutOnClipboard(expected, operationFn, desc, type) {
++  await SimpleTest.promiseClipboardChange(null, operationFn, type, 300, true);
++  ok(SpecialPowers.getClipboardData(type || "text/unicode")
++                  .includes("waitForClipboard-known-value"), desc + " data");
++}
+ 
+ function setClipboardText(text) {
+   var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"]
+     .getService(SpecialPowers.Ci.nsIClipboardHelper);
+   helper.copyString(text);
+ }
+ 
+ function selectContentDiv() {
+@@ -111,184 +88,196 @@ function selectContentDiv() {
+   selection.selectAllChildren(content);
+ }
+ 
+ function selectContentInput() {
+   contentInput.select();
+   contentInput.focus();
+ }
+ 
+-function test_dom_oncopy() {
++add_task(async function test_dom_oncopy() {
++  await reset();
++
+   // Setup an oncopy event handler, fire copy.  Ensure that the event
+   // handler was called, and the clipboard contents have set to CONTENT TEXT.
+   // Test firing oncopy event on ctrl-c:
+   selectContentDiv();
+-  
++
+   var oncopy_fired = false;
+   content.oncopy = function() { oncopy_fired = true; };
+   try {
+-    synthesizeKey("c", {accelKey: 1});
++    await putOnClipboard("CONTENT TEXT", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy on DOM element set clipboard correctly");
+     ok(oncopy_fired, "copy event firing on DOM element");
+-    is(getClipboardText(), "CONTENT TEXT",
+-      "copy on DOM element set clipboard correctly");
+   } finally {
+     content.oncopy = null;
+   }
+-}
++});
+ 
+-function test_dom_oncut() {
++add_task(async function test_dom_oncut() {
++  await reset();
++
+   // Setup an oncut event handler, fire cut.  Ensure that the event handler
+   // was called.  The <div> doesn't handle a cut, so ensure that the
+   // clipboard text is clipboardInitialValue, NOT "CONTENT TEXT".
+   selectContentDiv();
+   var oncut_fired = false;
+   content.oncut = function() { oncut_fired = true; };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
++    await wontPutOnClipboard(clipboardInitialValue, () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "cut on DOM element set clipboard correctly");
+     ok(oncut_fired, "cut event firing on DOM element")
+-    is(getClipboardText(), clipboardInitialValue,
+-      "cut on DOM element did not modify clipboard");
+   } finally {
+     content.oncut = null;
+   }
+-}
++});
+ 
++add_task(async function test_dom_onpaste() {
++  await reset();
+ 
+-function test_dom_onpaste() {
+   // Setup an onpaste event handler, fire paste.  Ensure that the event
+   // handler was called.
+   selectContentDiv();
+   var onpaste_fired = false;
+   content.onpaste = function() { onpaste_fired = true; };
+   try {
+     synthesizeKey("v", {accelKey: 1});
+     ok(onpaste_fired, "paste event firing on DOM element");
+   } finally {
+     content.onpaste = null;
+   }
+-}
++});
+ 
++add_task(async function test_dom_oncopy_abort() {
++  await reset();
+ 
+-function test_dom_oncopy_abort() {
+   // Setup an oncopy event handler that aborts the copy, and fire the copy
+   // event.  Ensure that the event handler was fired, and the clipboard
+   // contents have not been modified.
+   selectContentDiv();
+   var oncopy_fired = false;
+   content.oncopy = function() { oncopy_fired = true; return false; };
+   try {
+-    synthesizeKey("c", {accelKey: 1});
++    await wontPutOnClipboard(clipboardInitialValue, () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "aborted copy on DOM element did not modify clipboard");
+     ok(oncopy_fired, "copy event (to-be-cancelled) firing on DOM element");
+-    is(getClipboardText(), clipboardInitialValue,
+-      "aborted copy on DOM element did not modify clipboard");
+   } finally {
+     content.oncopy = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_oncopy() {
++  await reset();
+ 
+-function test_input_oncopy() {
+   // Setup an oncopy event handler, fire copy.  Ensure that the event
+   // handler was called, and the clipboard contents have been set to 'PUT TE',
+   // which is the part that is selected below.
+   selectContentInput();
+   contentInput.focus();
+   contentInput.setSelectionRange(2, 8);
+ 
+   var oncopy_fired = false;
+   contentInput.oncopy = function() { oncopy_fired = true; };
+   try {
+-    synthesizeKey("c", {accelKey: 1});
++    await putOnClipboard("PUT TE", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy on plaintext editor set clipboard correctly");
+     ok(oncopy_fired, "copy event firing on plaintext editor");
+-    is(getClipboardText(), "PUT TE",
+-      "copy on plaintext editor set clipboard correctly");
+   } finally {
+     contentInput.oncopy = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_oncut() {
++  await reset();
+ 
+-function test_input_oncut() {
+   // Setup an oncut event handler, and fire cut.  Ensure that the event
+   // handler was fired, the clipboard contains the INPUT TEXT, and
+   // that the input itself is empty.
+   selectContentInput();
+   var oncut_fired = false;
+   contentInput.oncut = function() { oncut_fired = true; };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
++    await putOnClipboard("INPUT TEXT", () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "cut on plaintext editor set clipboard correctly");
+     ok(oncut_fired, "cut event firing on plaintext editor");
+-    is(getClipboardText(), "INPUT TEXT",
+-      "cut on plaintext editor set clipboard correctly");
+     is(contentInput.value, "",
+       "cut on plaintext editor emptied editor");
+   } finally {
+     contentInput.oncut = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_onpaste() {
++  await reset();
+ 
+-function test_input_onpaste() {
+   // Setup an onpaste event handler, and fire paste.  Ensure that the event
+   // handler was fired, the clipboard contents didn't change, and that the
+   // input value did change (ie. paste succeeded).
+   selectContentInput();
+   var onpaste_fired = false;
+   contentInput.onpaste = function() { onpaste_fired = true; };
+   try {
+     synthesizeKey("v", {accelKey: 1});
+     ok(onpaste_fired, "paste event firing on plaintext editor");
+     is(getClipboardText(), clipboardInitialValue,
+       "paste on plaintext editor did not modify clipboard contents");
+     is(contentInput.value, clipboardInitialValue,
+       "paste on plaintext editor did modify editor value");
+   } finally {
+     contentInput.onpaste = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_oncopy_abort() {
++  await reset();
+ 
+-function test_input_oncopy_abort() {
+   // Setup an oncopy event handler, fire copy.  Ensure that the event
+   // handler was called, and that the clipboard value did NOT change.
+   selectContentInput();
+   var oncopy_fired = false;
+   contentInput.oncopy = function() { oncopy_fired = true; return false; };
+   try {
+-    synthesizeKey("c", {accelKey: 1});
++    await wontPutOnClipboard(clipboardInitialValue, () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "aborted copy on plaintext editor did not modify clipboard");
+     ok(oncopy_fired, "copy event (to-be-cancelled) firing on plaintext editor");
+-    is(getClipboardText(), clipboardInitialValue,
+-      "aborted copy on plaintext editor did not modify clipboard");
+   } finally {
+     contentInput.oncopy = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_oncut_abort() {
++  await reset();
+ 
+-function test_input_oncut_abort() {
+   // Setup an oncut event handler, and fire cut.  Ensure that the event
+   // handler was fired, the clipboard contains the INPUT TEXT, and
+   // that the input itself is empty.
+   selectContentInput();
+   var oncut_fired = false;
+   contentInput.oncut = function() { oncut_fired = true; return false; };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
++    await wontPutOnClipboard(clipboardInitialValue, () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "aborted cut on plaintext editor did not modify clipboard");
+     ok(oncut_fired, "cut event (to-be-cancelled) firing on plaintext editor");
+-    is(getClipboardText(), clipboardInitialValue,
+-      "aborted cut on plaintext editor did not modify clipboard.");
+     is(contentInput.value, "INPUT TEXT",
+       "aborted cut on plaintext editor did not modify editor contents");
+   } finally {
+     contentInput.oncut = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_onpaste_abort() {
++  await reset();
+ 
+-function test_input_onpaste_abort() {
+   // Setup an onpaste event handler, and fire paste.  Ensure that the event
+   // handler was fired, the clipboard contents didn't change, and that the
+   // input value did change (ie. paste succeeded).
+   selectContentInput();
+   var onpaste_fired = false;
+   contentInput.onpaste = function() { onpaste_fired = true; return false; };
+   try {
+     synthesizeKey("v", {accelKey: 1});
+@@ -296,109 +285,114 @@ function test_input_onpaste_abort() {
+       "paste event (to-be-cancelled) firing on plaintext editor");
+     is(getClipboardText(), clipboardInitialValue,
+       "aborted paste on plaintext editor did not modify clipboard");
+     is(contentInput.value, "INPUT TEXT",
+       "aborted paste on plaintext editor did not modify modified editor value");
+   } finally {
+     contentInput.onpaste = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_cut_dataTransfer() {
++  await reset();
+ 
+-function test_input_cut_dataTransfer() {
+   // Cut using event.dataTransfer. The event is not cancelled so the default
+   // cut should occur
+   selectContentInput();
+   contentInput.oncut = function(event) {
+     ok(event instanceof ClipboardEvent, "cut event is a ClipboardEvent");
+     ok(event.clipboardData instanceof DataTransfer, "cut event dataTransfer is a DataTransfer");
+     is(event.target, contentInput, "cut event target");
+     is(event.clipboardData.mozItemCount, 0, "cut event mozItemCount");
+     is(event.clipboardData.getData("text/plain"), "", "cut event getData");
+     event.clipboardData.setData("text/plain", "This is some dataTransfer text");
+     cachedCutData = event.clipboardData;
+   };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
+-    is(getClipboardText(), "INPUT TEXT",
+-      "cut using dataTransfer on plaintext editor set clipboard correctly");
++    await putOnClipboard("INPUT TEXT", () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "cut using dataTransfer on plaintext editor set clipboard correctly");
+     is(contentInput.value, "",
+       "cut using dataTransfer on plaintext editor cleared input");
+   } finally {
+     contentInput.oncut = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_cut_abort_dataTransfer() {
++  await reset();
+ 
+-function test_input_cut_abort_dataTransfer() {
+   // Cut using event.dataTransfer but cancel the event. The data should be
+   // put on the clipboard but since we don't modify the input value, the input
+   // should have the same value.
+   selectContentInput();
+   contentInput.oncut = function(event) {
+     event.clipboardData.setData("text/plain", "Cut dataTransfer text");
+     return false;
+   };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
+-    is(getClipboardText(), "Cut dataTransfer text",
+-      "aborted cut using dataTransfer on plaintext editor set clipboard correctly");
++    await putOnClipboard("Cut dataTransfer text", () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "aborted cut using dataTransfer on plaintext editor set clipboard correctly");
+     is(contentInput.value, "INPUT TEXT",
+       "aborted cut using dataTransfer on plaintext editor did not modify input");
+   } finally {
+     contentInput.oncut = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_copy_dataTransfer() {
++  await reset();
+ 
+-function test_input_copy_dataTransfer() {
+   // Copy using event.dataTransfer
+   selectContentInput();
+   contentInput.oncopy = function(event) {
+     ok(event instanceof ClipboardEvent, "copy event is a ClipboardEvent");
+     ok(event.clipboardData instanceof DataTransfer, "copy event dataTransfer is a DataTransfer");
+     is(event.target, contentInput, "copy event target");
+     is(event.clipboardData.mozItemCount, 0, "copy event mozItemCount");
+     is(event.clipboardData.getData("text/plain"), "", "copy event getData");
+     event.clipboardData.setData("text/plain", "Copied dataTransfer text");
+     cachedCopyData = event.clipboardData;
+   };
+   try {
+-    synthesizeKey("c", {accelKey: 1});
+-    is(getClipboardText(), "INPUT TEXT",
+-      "copy using dataTransfer on plaintext editor set clipboard correctly");
++    await putOnClipboard("INPUT TEXT", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy using dataTransfer on plaintext editor set clipboard correctly");
+     is(contentInput.value, "INPUT TEXT",
+       "copy using dataTransfer on plaintext editor did not modify input");
+   } finally {
+     contentInput.oncopy = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_copy_abort_dataTransfer() {
++  await reset();
+ 
+-function test_input_copy_abort_dataTransfer() {
+   // Copy using event.dataTransfer but cancel the event.
+   selectContentInput();
+   contentInput.oncopy = function(event) {
+     event.clipboardData.setData("text/plain", "Copy dataTransfer text");
+     return false;
+   };
+   try {
+-    synthesizeKey("x", {accelKey: 1});
+-    is(getClipboardText(), "Copy dataTransfer text",
+-      "aborted copy using dataTransfer on plaintext editor set clipboard correctly");
++    await putOnClipboard("Copy dataTransfer text", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "aborted copy using dataTransfer on plaintext editor set clipboard correctly");
+     is(contentInput.value, "INPUT TEXT",
+       "aborted copy using dataTransfer on plaintext editor did not modify input");
+   } finally {
+     contentInput.oncopy = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_paste_dataTransfer() {
++  await reset();
+ 
+-function test_input_paste_dataTransfer() {
+   // Paste using event.dataTransfer
+   selectContentInput();
+   contentInput.onpaste = function(event) {
+     ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent");
+     ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer");
+     is(event.target, contentInput, "paste event target");
+     is(event.clipboardData.mozItemCount, 1, "paste event mozItemCount");
+     is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "paste event getData");
+@@ -408,39 +402,42 @@ function test_input_paste_dataTransfer()
+     synthesizeKey("v", {accelKey: 1});
+     is(getClipboardText(), clipboardInitialValue,
+       "paste using dataTransfer on plaintext editor did not modify clipboard contents");
+     is(contentInput.value, clipboardInitialValue,
+       "paste using dataTransfer on plaintext editor modified input");
+   } finally {
+     contentInput.onpaste = null;
+   }
+-}
++});
+ 
++add_task(async function test_input_paste_abort_dataTransfer() {
++  await reset();
+ 
+-function test_input_paste_abort_dataTransfer() {
+   // Paste using event.dataTransfer but cancel the event
+   selectContentInput();
+   contentInput.onpaste = function(event) {
+     is(event.clipboardData.getData("text/plain"), clipboardInitialValue, "get data on aborted paste");
+     contentInput.value = "Alternate Paste";
+     return false;
+   };
+   try {
+     synthesizeKey("v", {accelKey: 1});
+     is(getClipboardText(), clipboardInitialValue,
+       "aborted paste using dataTransfer on plaintext editor did not modify clipboard contents");
+     is(contentInput.value, "Alternate Paste",
+       "aborted paste using dataTransfer on plaintext editor modified input");
+   } finally {
+     contentInput.onpaste = null;
+   }
+-}
++});
+ 
+-function test_input_copypaste_dataTransfer_multiple() {
++add_task(async function test_input_copypaste_dataTransfer_multiple() {
++  await reset();
++
+   // Cut several types of data and paste it again
+   contentInput.value = "This is a line of text";
+   contentInput.oncopy = function(event) {
+     var cd = event.clipboardData;
+     cd.setData("text/plain", "would be a phrase");
+ 
+     var exh = false;
+     try { cd.mozSetDataAt("text/plain", "Text", 1); } catch (ex) { exh = true; }
+@@ -458,24 +455,25 @@ function test_input_copypaste_dataTransf
+     cd.setData("text/x-moz-url", "http://www.mozilla.org");
+     cd.mozSetDataAt("text/x-custom", "Custom Text with \u0000 null", 0);
+     is(cd.mozItemCount, 1, "mozItemCount after set multiple types");
+     return false;
+   };
+ 
+   try {
+     selectContentInput();
+-    synthesizeKey("c", {accelKey: 1});
++
++    await putOnClipboard("would be a phrase", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy multiple types text");
+   }
+   finally {
+     contentInput.oncopy = null;
+   }
+ 
+-  is(getClipboardText(), "would be a phrase", "copy multiple types text");
+-
+   contentInput.setSelectionRange(5, 14);
+ 
+   contentInput.onpaste = function(event) {
+     var cd = event.clipboardData;
+     is(cd.mozItemCount, 1, "paste after copy multiple types mozItemCount");
+     is(cd.getData("text/plain"), "would be a phrase", "paste text/plain multiple types");
+ 
+     // Firefox for Android's clipboard code doesn't handle x-moz-url. Therefore
+@@ -501,90 +499,97 @@ function test_input_copypaste_dataTransf
+   };
+   try {
+     synthesizeKey("v", {accelKey: 1});
+     is(contentInput.value, "This would be a phrase of text",
+       "default paste after copy multiple types");
+   } finally {
+     contentInput.onpaste = null;
+   }
+-}
++});
+ 
+-function test_input_copy_button_dataTransfer() {
++add_task(async function test_input_copy_button_dataTransfer() {
++  await reset();
++
+   // Copy using event.dataTransfer when a button is focused.
+   var button = document.getElementById("button");
+   button.focus();
+   button.oncopy = function(event) {
+     ok(false, "should not be firing copy event on button");
+     return false;
+   };
+   try {
+     // copy should not occur here because buttons don't have any controller
+     // for the copy command
+-    synthesizeKey("c", {accelKey: 1});
+-    is(getClipboardText(), clipboardInitialValue,
+-      "copy using dataTransfer on plaintext editor set clipboard correctly for button");
++    await wontPutOnClipboard(clipboardInitialValue, () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy using dataTransfer on plaintext editor set clipboard correctly for button");
+ 
+     selectContentDiv();
+-    synthesizeKey("c", {accelKey: 1});
+-    is(getClipboardText(), "CONTENT TEXT",
+-      "copy using dataTransfer with selection on plaintext editor set clipboard correctly for button");
+ 
++    await putOnClipboard("CONTENT TEXT", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy using dataTransfer with selection on plaintext editor set clipboard correctly for button");
+   } finally {
+     document.documentElement.oncopy = null;
+   }
+-}
++});
+ 
+-function test_eventspref_disabled() {
+-  // Disable clipboard events
+-  return new Promise(resolve => {
+-    SpecialPowers.pushPrefEnv({"set": [['dom.event.clipboardevents.enabled', false]]}, doPrefDisabledTest);
++add_task(async function test_eventspref_disabled() {
++  await reset();
+ 
+-    function doPrefDisabledTest() {
+-      var event_fired = false;
+-      contentInput.oncut = function() { event_fired = true; };
+-      contentInput.oncopy = function() { event_fired = true; };
+-      contentInput.onpaste = function() { event_fired = true; };
+-      try {
+-        selectContentInput();
+-        contentInput.setSelectionRange(1, 4);
+-        synthesizeKey("x", {accelKey: 1});
+-        is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
+-        is(getClipboardText(), "NPU", "cut changed clipboard when preference is disabled");
+-        ok(!event_fired, "cut event did not fire when preference is disabled")
++  // Disable clipboard events
++  await new Promise(resolve => {
++    SpecialPowers.pushPrefEnv({"set": [['dom.event.clipboardevents.enabled', false]]}, resolve);
++  });
++
++  var event_fired = false;
++  contentInput.oncut = function() { event_fired = true; };
++  contentInput.oncopy = function() { event_fired = true; };
++  contentInput.onpaste = function() { event_fired = true; };
++  try {
++    selectContentInput();
++    contentInput.setSelectionRange(1, 4);
+ 
+-        event_fired = false;
+-        contentInput.setSelectionRange(3, 6);
+-        synthesizeKey("c", {accelKey: 1});
+-        is(getClipboardText(), "TEX", "copy changed clipboard when preference is disabled");
+-        ok(!event_fired, "copy event did not fire when preference is disabled")
++    await putOnClipboard("NPU", () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "cut changed clipboard when preference is disabled");
++    is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
++    ok(!event_fired, "cut event did not fire when preference is disabled")
++
++    event_fired = false;
++    contentInput.setSelectionRange(3, 6);
++    await putOnClipboard("TEX", () => {
++      synthesizeKey("c", {accelKey: 1});
++    }, "copy changed clipboard when preference is disabled");
++    ok(!event_fired, "copy event did not fire when preference is disabled")
+ 
+-        event_fired = false;
+-        contentInput.setSelectionRange(0, 2);
+-        synthesizeKey("v", {accelKey: 1});
+-        is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
+-        ok(!event_fired, "paste event did not fire when preference is disabled")
+-      } finally {
+-        contentInput.oncut = null;
+-        contentInput.oncopy = null;
+-        contentInput.onpaste = null;
+-      }
++    event_fired = false;
++    contentInput.setSelectionRange(0, 2);
++    synthesizeKey("v", {accelKey: 1});
++    is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
++    ok(!event_fired, "paste event did not fire when preference is disabled")
++  } finally {
++    contentInput.oncut = null;
++    contentInput.oncopy = null;
++    contentInput.onpaste = null;
++  }
+ 
+-      SpecialPowers.popPrefEnv(resolve);
+-    }
++  await new Promise(resolve => {
++    SpecialPowers.popPrefEnv(resolve);
+   });
+-}
++});
+ 
+ let expectedData = [];
+ 
+ // Check to make that synthetic events do not change the clipboard
+-function checkSyntheticEvents()
+-{
++add_task(async function test_synthetic_events() {
++  await reset();
++
+   let syntheticSpot = document.getElementById("syntheticSpot");
+-  setClipboardText(clipboardInitialValue);
+ 
+   // No dataType specified
+   let event = new ClipboardEvent("cut", { data: "something" });
+   expectedData = { type: "cut", data: null }
+   compareSynthetic(event, "before");
+   syntheticSpot.dispatchEvent(event);
+   ok(expectedData.eventFired, "cut event fired");
+   compareSynthetic(event, "after");
+@@ -618,20 +623,19 @@ function checkSyntheticEvents()
+   compareSynthetic(event, "after");
+ 
+   event = new ClipboardEvent("paste", { dataType: "application/unknown", data: "unknown" });
+   expectedData = { type: "paste", dataType: "application/unknown", data: "unknown" }
+   compareSynthetic(event, "before");
+   syntheticSpot.dispatchEvent(event);
+   ok(expectedData.eventFired, "paste event fired");
+   compareSynthetic(event, "after");
+-}
++});
+ 
+-function compareSynthetic(event, eventtype)
+-{
++function compareSynthetic(event, eventtype) {
+   let step = (eventtype == "cut" || eventtype == "copy" || eventtype == "paste") ? "during" : eventtype;
+   if (step == "during") {
+     is(eventtype, expectedData.type, "synthetic " + eventtype + " event fired");
+   }
+ 
+   ok(event.clipboardData instanceof DataTransfer, "clipboardData is assigned");
+ 
+   is(event.type, expectedData.type, "synthetic " + eventtype + " event type");
+@@ -647,21 +651,21 @@ function compareSynthetic(event, eventty
+ 
+   is(getClipboardText(), "empty", "event does not change the clipboard " + step + " dispatch");
+ 
+   if (step == "during") {
+     expectedData.eventFired = true;
+   }
+ }
+ 
+-function checkCachedDataTransfer(cd, eventtype)
+-{
++async function checkCachedDataTransfer(cd, eventtype) {
+   var testprefix = "cached " + eventtype + " dataTransfer";
+ 
+-  setClipboardText("Some Clipboard Text");
++  await putOnClipboard("Some Clipboard Text", () => { setClipboardText("Some Clipboard Text") },
++                       "change clipboard outside of event");
+ 
+   var oldtext = cd.getData("text/plain");
+   ok(!oldtext, "clipboard get using " + testprefix);
+ 
+   try {
+     cd.mozSetDataAt("text/plain", "Test Cache Data", 0);
+   } catch (ex) {}
+   ok(!cd.getData("text/plain"), "clipboard set using " + testprefix);
+@@ -671,17 +675,29 @@ function checkCachedDataTransfer(cd, eve
+   try {
+     cd.mozClearDataAt("text/plain", 0);
+   } catch (ex) {}
+   ok(!cd.getData("text/plain"), "clipboard clear using " + testprefix);
+ 
+   is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);
+ }
+ 
+-function test_input_cut_disallowed_types_dataTransfer() {
++add_task(async function test_modify_datatransfer_outofevent() {
++  await reset();
++
++  // Check if the cached clipboard data can be accessed or modified
++  // and whether it modifies the real clipboard
++  await checkCachedDataTransfer(cachedCutData, "cut");
++  await checkCachedDataTransfer(cachedCopyData, "copy");
++  await checkCachedDataTransfer(cachedPasteData, "paste");
++});
++
++add_task(async function test_input_cut_disallowed_types_dataTransfer() {
++  await reset();
++
+   selectContentInput();
+   let oncutExecuted = false;
+   contentInput.oncut = function(event) {
+     // Setting an arbitrary type should be OK
+     try {
+       event.clipboardData.setData("apple/cider", "Anything your heart desires");
+       ok(true, "We should have successfully executed the setData call");
+     } catch(e) {
+@@ -695,33 +711,39 @@ function test_input_cut_disallowed_types
+     } catch(e) {
+       is(e.name, "NotSupportedError",
+          "We should have gotten an NotSupportedError exception for trying to set that data");
+     }
+     oncutExecuted = true;
+   };
+ 
+   try {
+-    synthesizeKey("x", {accelKey: 1});
++    await putOnClipboard("INPUT TEXT", () => {
++      synthesizeKey("x", {accelKey: 1});
++    }, "The oncut handler should have been executed data");
+     ok(oncutExecuted, "The oncut handler should have been executed");
+   } finally {
+     contentInput.oncut = null;
+   }
+-}
++});
+ 
+ // Try copying an image to the clipboard and make sure that it looks correct when pasting it.
+-function test_image_dataTransfer() {
++add_task(async function test_image_dataTransfer() {
++  await reset();
++
+   // cmd_copyImageContents errors on Android (bug 1299578).
+   if (navigator.userAgent.includes("Android")) {
+     return;
+   }
+ 
+   // Copy the image's data to the clipboard
+-  SpecialPowers.setCommandNode(window, document.getElementById("image"));
+-  SpecialPowers.doCommand(window, "cmd_copyImageContents");
++  await putOnClipboard("", () => {
++    SpecialPowers.setCommandNode(window, document.getElementById("image"));
++    SpecialPowers.doCommand(window, "cmd_copyImageContents");
++  }, "copy changed clipboard when preference is disabled");
+ 
+   let onpasteCalled = false;
+   document.onpaste = function(event) {
+     ok(event instanceof ClipboardEvent, "paste event is an ClipboardEvent");
+     ok(event.clipboardData instanceof DataTransfer, "paste event dataTransfer is a DataTransfer");
+     let items = event.clipboardData.items;
+     let foundData = false;
+     for (let i = 0; i < items.length; ++i)  {
+@@ -739,22 +761,14 @@ function test_image_dataTransfer() {
+   }
+ 
+   try {
+     synthesizeKey("v", {accelKey: 1});
+     ok(onpasteCalled, "The paste event listener must have been called");
+   } finally {
+     document.onpaste = null;
+   }
+-}
+-
+-SimpleTest.waitForFocus(() => {
+-  SpecialPowers.pushPrefEnv({
+-    // NOTE: These tests operate under the assumption that the protected mode of
+-    // DataTransfer is enabled.
+-    "set": [["dom.events.dataTransfer.protected.enabled", true]]
+-  }, doTests);
+ });
+ 
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/editor/libeditor/tests/test_bug525389.html b/editor/libeditor/tests/test_bug525389.html
+--- a/editor/libeditor/tests/test_bug525389.html
++++ b/editor/libeditor/tests/test_bug525389.html
+@@ -1,30 +1,31 @@
+ <!DOCTYPE HTML>
+ <html><head>
+ <title>Test for bug 525389</title>
+ <style src="/tests/SimpleTest/test.css" type="text/css"></style>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
++<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ 
+ <script class="testbody" type="application/javascript">
+ 
+   var utils = SpecialPowers.wrap(window)
+                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
+   var Cc = SpecialPowers.Cc;
+   var Ci = SpecialPowers.Ci;
+ 
+ function getLoadContext() {
+   return SpecialPowers.wrap(window)
+                .QueryInterface(Ci.nsIInterfaceRequestor)
+                .getInterface(Ci.nsIWebNavigation)
+                .QueryInterface(Ci.nsILoadContext);
+ }
+ 
+-function runTest() {
++async function runTest() {
+   var pasteCount = 0;
+   var pasteFunc = function (event) { pasteCount++; };
+ 
+   function verifyContent(s) {
+     var e = document.getElementById('i1');
+     var doc = e.contentDocument;
+     if (navigator.platform.includes("Win")) {
+       // On Windows ignore \n which got left over from the removal of the fragment tags
+@@ -109,17 +110,17 @@ function runTest() {
+       trans.addDataFlavor("text/unicode");
+       ssData.data = doc.body.innerHTML;
+       trans.setTransferData("text/unicode", ssData, ssData.length * 2);
+     }
+ 
+     return trans;
+   }
+ 
+-  function copyToClipBoard(s,asHTML,target_id) {
++  async function copyToClipBoard(s,asHTML,target_id) {
+     var e = document.getElementById('i2');
+     var doc = e.contentDocument;
+     if (asHTML) {
+       doc.body.innerHTML = s;
+     } else {
+       var text = doc.createTextNode(s);
+       doc.body.appendChild(text);
+     }
+@@ -129,64 +130,70 @@ function runTest() {
+     selection.removeAllRanges();
+     if (!target_id) {
+       selection.selectAllChildren(doc.body);
+     } else {
+       var range = document.createRange();
+       range.selectNode(doc.getElementById(target_id));
+       selection.addRange(range);
+     }
+-    SpecialPowers.wrap(doc).execCommand("copy", false, null);
++
++    await SimpleTest.promiseClipboardChange(() => true,
++      () => { SpecialPowers.wrap(doc).execCommand("copy", false, null); });
++
+     return e;
+   }
+ 
+-  copyToClipBoard('<span>Hello</span><span>Kitty</span>', true);
++  await copyToClipBoard('<span>Hello</span><span>Kitty</span>', true);
+   var trans = getTransferableFromClipboard(true);
+   pasteInto(trans, '');
+   verifyContent('<span>Hello</span><span>Kitty</span>');
+   is(pasteCount, 1, "paste event was not triggered");
+ 
+   // this test is not working out exactly like the clipboard test
+   // has to do with generating the nsITransferable above
+   //trans = makeTransferable('<span>Hello</span><span>Kitty</span>', true);
+   //pasteInto(trans, '');
+   //verifyContent('<span>Hello</span><span>Kitty</span>');
+ 
+-  copyToClipBoard("<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span>", true);
++  await copyToClipBoard("<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span>", true);
+   trans = getTransferableFromClipboard(true);
+   pasteInto(trans, '<ol><li id="paste_here">X</li></ol>',"paste_here");
+   verifyContent('<ol><li id="paste_here">X<dl><dd>Hello Kitty</dd></dl><span>Hello</span><span>Kitty</span></li></ol>');
+   is(pasteCount, 1, "paste event was not triggered");
+ 
+ // The following test doesn't do what I expected, because the special handling
+ // of IsList nodes in nsHTMLEditor::InsertHTMLWithContext simply removes
+ // non-list/item children.  See bug 481177.
+-//  copyToClipBoard("<ol><li>Hello Kitty</li><span>Hello</span></ol>", true);
++//  await copyToClipBoard("<ol><li>Hello Kitty</li><span>Hello</span></ol>", true);
+ //  pasteInto('<ol><li id="paste_here">X</li></ol>',"paste_here");
+ //  verifyContent('<ol><li id="paste_here">X</li><li>Hello Kitty</li><span>Hello</span></ol>');
+ 
+-  copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
++  await copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
+   trans = getTransferableFromClipboard(true);
+   pasteInto(trans, '<pre id="paste_here">Hello </pre>',"paste_here");
+   verifyContent('<pre id="paste_here">Hello Kitty<span>Hello</span></pre>');
+   is(pasteCount, 1, "paste event was not triggered");
+ 
+   // test that we can preventDefault pastes
+   pasteFunc = function (event) { event.preventDefault(); return false; };
+-  copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
++  await copyToClipBoard("<pre>Kitty</pre><span>Hello</span>", true);
+   trans = getTransferableFromClipboard(true);
+   pasteInto(trans, '<pre id="paste_here">Hello </pre>',"paste_here");
+   verifyContent('<pre id="paste_here">Hello </pre>');
+   is(pasteCount, 0, "paste event was triggered");
+-
+-  SimpleTest.finish();
+ }
+ 
+ SimpleTest.waitForExplicitFinish();
+-addLoadEvent(runTest);
++addLoadEvent(() => {
++  add_task(async function test_copy() {
++    await runTest();
++  });
++});
++
+ </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525389">Mozilla Bug 525389</a>
+ <p id="display"></p>
+ 
+ <pre id="test">
+ </pre>
+diff --git a/editor/libeditor/tests/test_bug596001.html b/editor/libeditor/tests/test_bug596001.html
+--- a/editor/libeditor/tests/test_bug596001.html
++++ b/editor/libeditor/tests/test_bug596001.html
+@@ -39,17 +39,19 @@ function testTab(prefix, callback) {
+       ok(inputReceived, "An input event should be raised");
+       is(dst.value, prefix + src.value, "The value should be pasted verbatim");
+       callback();
+     },
+     callback
+   );
+ }
+ 
++SimpleTest.waitForExplicitFinish();
+ testTab("", function() {
+   testTab("foo", function() {
++    SimpleTest.finish();
+   });
+ });
+ 
+ </script>
+ </pre>
+ </body>
+ </html>
+diff --git a/editor/libeditor/tests/test_bug795418-4.html b/editor/libeditor/tests/test_bug795418-4.html
+--- a/editor/libeditor/tests/test_bug795418-4.html
++++ b/editor/libeditor/tests/test_bug795418-4.html
+@@ -31,31 +31,29 @@ SimpleTest.waitForFocus(function() {
+   sel.removeAllRanges();
+ 
+   // Select the text from the text node in div.
+   var r = document.createRange();
+   r.setStart(div.firstChild, 0);
+   r.setEnd(div.firstChild, 9);
+   sel.addRange(r);
+ 
+-  SimpleTest.waitForClipboard(
+-    function compare(value) {
++  SimpleTest.waitForClipboard(() => true,
++    function setup() {
++      synthesizeKey("C", {accelKey: true});
++    },
++    function onSuccess() {
+       var theEdit = document.getElementById("editable");
+       sel.collapse(theEdit.firstChild, 2);
+ 
+       SpecialPowers.doCommand(window, "cmd_paste");
+       is(theEdit.innerHTML,
+         "ABCopy this",
+         "unexpected HTML for test");
+-      return true;
+-    },
+-    function setup() {
+-      synthesizeKey("C", {accelKey: true});
+-    },
+-    function onSuccess() {
++
+       SimpleTest.finish();
+     },
+     function onFailure() {
+       SimpleTest.finish();
+     },
+     "text/html"
+   );
+ });
+diff --git a/editor/libeditor/tests/test_bug795418-5.html b/editor/libeditor/tests/test_bug795418-5.html
+--- a/editor/libeditor/tests/test_bug795418-5.html
++++ b/editor/libeditor/tests/test_bug795418-5.html
+@@ -31,31 +31,28 @@ SimpleTest.waitForFocus(function() {
+   sel.removeAllRanges();
+ 
+   // Select the text from the text node in div.
+   var r = document.createRange();
+   r.setStart(div.firstChild, 0);
+   r.setEnd(div.firstChild, 9);
+   sel.addRange(r);
+ 
+-  SimpleTest.waitForClipboard(
+-    function compare(value) {
++  SimpleTest.waitForClipboard(() => true,
++    function setup() {
++      synthesizeKey("C", {accelKey: true});
++    },
++    function onSuccess() {
+       var theEdit = document.getElementById("editable");
+       sel.collapse(theEdit.firstChild, 2);
+ 
+       SpecialPowers.doCommand(window, "cmd_paste");
+       is(theEdit.innerHTML,
+         "ABCopy this",
+         "unexpected HTML for test");
+-      return true;
+-    },
+-    function setup() {
+-      synthesizeKey("C", {accelKey: true});
+-    },
+-    function onSuccess() {
+       SimpleTest.finish();
+     },
+     function onFailure() {
+       SimpleTest.finish();
+     },
+     "text/html"
+   );
+ });
+diff --git a/editor/libeditor/tests/test_bug795418-6.html b/editor/libeditor/tests/test_bug795418-6.html
+--- a/editor/libeditor/tests/test_bug795418-6.html
++++ b/editor/libeditor/tests/test_bug795418-6.html
+@@ -31,31 +31,28 @@ SimpleTest.waitForFocus(function() {
+   sel.removeAllRanges();
+ 
+   // Select the text from the text node in div.
+   var r = document.createRange();
+   r.setStart(div.firstChild, 0);
+   r.setEnd(div.firstChild, 9);
+   sel.addRange(r);
+ 
+-  SimpleTest.waitForClipboard(
+-    function compare(value) {
++  SimpleTest.waitForClipboard(() => true,
++    function setup() {
++      synthesizeKey("C", {accelKey: true});
++    },
++    function onSuccess() {
+       var theEdit = document.getElementById("editable");
+       sel.collapse(theEdit.firstChild, 2);
+ 
+       SpecialPowers.doCommand(window, "cmd_pasteQuote");
+       is(theEdit.innerHTML,
+         "AB<blockquote type=\"cite\">Copy this</blockquote>",
+         "unexpected HTML for test");
+-      return true;
+-    },
+-    function setup() {
+-      synthesizeKey("C", {accelKey: true});
+-    },
+-    function onSuccess() {
+       SimpleTest.finish();
+     },
+     function onFailure() {
+       SimpleTest.finish();
+     },
+     "text/html"
+   );
+ });
+diff --git a/editor/libeditor/tests/test_bug795418.html b/editor/libeditor/tests/test_bug795418.html
+--- a/editor/libeditor/tests/test_bug795418.html
++++ b/editor/libeditor/tests/test_bug795418.html
+@@ -31,31 +31,29 @@ SimpleTest.waitForFocus(function() {
+   sel.removeAllRanges();
+ 
+   // Select the text from the text node in div.
+   var r = document.createRange();
+   r.setStart(div.firstChild, 0);
+   r.setEnd(div.firstChild, 9);
+   sel.addRange(r);
+ 
+-  SimpleTest.waitForClipboard(
+-    function compare(value) {
++  SimpleTest.waitForClipboard(() => true,
++    function setup() {
++      synthesizeKey("C", {accelKey: true});
++    },
++    function onSuccess() {
+       var theEdit = document.getElementById("editable");
+       sel.collapse(theEdit.firstChild, 1);
+ 
+       SpecialPowers.doCommand(window, "cmd_pasteQuote");
+       is(theEdit.innerHTML,
+         "<span>AB<blockquote type=\"cite\">Copy this</blockquote></span>",
+         "unexpected HTML for test");
+-      return true;
+-    },
+-    function setup() {
+-      synthesizeKey("C", {accelKey: true});
+-    },
+-    function onSuccess() {
++
+       SimpleTest.finish();
+     },
+     function onFailure() {
+       SimpleTest.finish();
+     },
+     "text/html"
+   );
+ });
+diff --git a/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html b/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html
+--- a/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html
++++ b/testing/mochitest/tests/Harness_sanity/test_sanitySimpletest.html
+@@ -41,51 +41,57 @@ function starttest() {
+       var check = false;
+       $('textB').focus();
+       SimpleTest.waitForClipboard("a",
+         function () {
+           SpecialPowers.clipboardCopyString("a");
+         },
+         function () {
+           check = true;
++          is(check, true, "waitForClipboard should work");
++          manipulateElements();
+         },
+         function () {
+           check = false;
++          is(check, false, "waitForClipboard should work");
++          manipulateElements();
+         }
+       );
+-      is(check, true, "waitForClipboard should work");
+     
+       //use helper functions
+-      var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
+-      document.body.appendChild(div1);
+-      var divObj = this.getElement('somediv');
+-      is(divObj, div1, 'createEl did not create element as expected');
+-      is($('somediv'), divObj, '$ helper did not get element as expected');
+-      is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
+-      document.body.removeChild(div1);
+-    
+-      /* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
+-       * run times. It is currently being tested in: 
+-       *  dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
+-       */
++      function manipulateElements()
++      {
++        var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
++        document.body.appendChild(div1);
++        var divObj = this.getElement('somediv');
++        is(divObj, div1, 'createEl did not create element as expected');
++        is($('somediv'), divObj, '$ helper did not get element as expected');
++        is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
++        document.body.removeChild(div1);
++
++        /* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
++         * run times. It is currently being tested in:
++         *  dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
++         */
+ 
+-      //note: this also adds a short wait period
+-      SimpleTest.executeSoon(
+-        function () {
+-          //finish() calls a slew of SimpleTest functions
+-          SimpleTest.finish();
+-          //call this after finish so we can make sure it works and doesn't hang our process
+-          var endTime = new Date();
+-          info("Profile::SimpleTestRunTime: " + (endTime-startTime));
+-          //expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
+-          SimpleTest.expectUncaughtException();
+-          //make sure we catch this error
+-          throw "i am an uncaught exception"
+-        }
+-      );
++        //note: this also adds a short wait period
++        SimpleTest.executeSoon(
++          function () {
++            //finish() calls a slew of SimpleTest functions
++            SimpleTest.finish();
++            //call this after finish so we can make sure it works and doesn't hang our process
++            var endTime = new Date();
++            info("Profile::SimpleTestRunTime: " + (endTime-startTime));
++            //expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
++            SimpleTest.expectUncaughtException();
++            //make sure we catch this error
++            throw "i am an uncaught exception"
++          }
++        );
++      }
+     }
+   );
+ };
+ //use addLoadEvent
+ addLoadEvent(
+   function() {
+     starttest();
+   }
+diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js
+--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
++++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
+@@ -921,28 +921,26 @@ SimpleTest.waitForFocus = function (call
+         if (browser) {
+           targetWindow = browser.contentWindow;
+         }
+ 
+         waitForFocusInner(targetWindow, false, expectBlankPage);
+     }
+ };
+ 
+-SimpleTest.waitForClipboard_polls = 0;
+-
+ /*
+  * Polls the clipboard waiting for the expected value. A known value different than
+  * the expected value is put on the clipboard first (and also polled for) so we
+  * can be sure the value we get isn't just the expected value because it was already
+  * on the clipboard. This only uses the global clipboard and only for text/unicode
+  * values.
+  *
+  * @param aExpectedStringOrValidatorFn
+  *        The string value that is expected to be on the clipboard or a
+- *        validator function getting cripboard data and returning a bool.
++ *        validator function getting expected clipboard data and returning a bool.
+  * @param aSetupFn
+  *        A function responsible for setting the clipboard to the expected value,
+  *        called after the known value setting succeeds.
+  * @param aSuccessFn
+  *        A function called when the expected value is found on the clipboard.
+  * @param aFailureFn
+  *        A function called if the expected value isn't found on the clipboard
+  *        within 5s. It can also be called if the known value can't be found.
+@@ -951,94 +949,89 @@ SimpleTest.waitForClipboard_polls = 0;
+  *        The timeout (in milliseconds) to wait for a clipboard change.
+  *        Defaults to 5000.
+  * @param aExpectFailure [optional]
+  *        If true, fail if the clipboard contents are modified within the timeout
+  *        interval defined by aTimeout.  When aExpectFailure is true, the argument
+  *        aExpectedStringOrValidatorFn must be null, as it won't be used.
+  *        Defaults to false.
+  */
+-SimpleTest.__waitForClipboardMonotonicCounter = 0;
+-SimpleTest.__defineGetter__("_waitForClipboardMonotonicCounter", function () {
+-  return SimpleTest.__waitForClipboardMonotonicCounter++;
+-});
+ SimpleTest.waitForClipboard = function(aExpectedStringOrValidatorFn, aSetupFn,
+                                        aSuccessFn, aFailureFn, aFlavor, aTimeout, aExpectFailure) {
+-    var requestedFlavor = aFlavor || "text/unicode";
++    let promise = SimpleTest.promiseClipboardChange(aExpectedStringOrValidatorFn, aSetupFn,
++                                                    aFlavor, aTimeout, aExpectFailure);
++    promise.then(aSuccessFn).catch(aFailureFn);
++}
++
++/**
++ * Promise-oriented version of waitForClipboard.
++ */
++SimpleTest.promiseClipboardChange = async function(aExpectedStringOrValidatorFn, aSetupFn,
++                                                   aFlavor, aTimeout, aExpectFailure) {
++    let requestedFlavor = aFlavor || "text/unicode";
+ 
+     // The known value we put on the clipboard before running aSetupFn
+-    var initialVal = SimpleTest._waitForClipboardMonotonicCounter +
+-                     "-waitForClipboard-known-value";
++    let initialVal = "waitForClipboard-known-value-" + Math.random();
++    let preExpectedVal = initialVal;
+ 
+-    var inputValidatorFn;
++    let inputValidatorFn;
+     if (aExpectFailure) {
+         // If we expect failure, the aExpectedStringOrValidatorFn should be null
+         if (aExpectedStringOrValidatorFn !== null) {
+             SimpleTest.ok(false, "When expecting failure, aExpectedStringOrValidatorFn must be null");
+         }
+ 
+         inputValidatorFn = function(aData) {
+             return aData != initialVal;
+         };
+     } else {
+         // Build a default validator function for common string input.
+         inputValidatorFn = typeof(aExpectedStringOrValidatorFn) == "string"
+             ? function(aData) { return aData == aExpectedStringOrValidatorFn; }
+             : aExpectedStringOrValidatorFn;
+     }
+ 
+-    var maxPolls = aTimeout ? aTimeout / 100 : 50;
++    let maxPolls = aTimeout ? aTimeout / 100 : 50;
++
++    async function putAndVerify(operationFn, validatorFn, flavor) {
++        operationFn();
+ 
+-    // reset for the next use
+-    function reset() {
+-        SimpleTest.waitForClipboard_polls = 0;
+-    }
++        let data;
++        for (let i = 0; i < maxPolls; i++) {
++            data = SpecialPowers.getClipboardData(flavor);
++            if (validatorFn(data)) {
++                // Don't show the success message when waiting for preExpectedVal
++                if (preExpectedVal) {
++                    preExpectedVal = null;
++                } else {
++                    SimpleTest.ok(!aExpectFailure, "Clipboard has the given value: '" + data + "'");
++                }
+ 
+-    var lastValue;
+-    function wait(validatorFn, successFn, failureFn, flavor) {
+-        if (SimpleTest.waitForClipboard_polls == 0) {
+-          lastValue = undefined;
++                return data;
++            }
++
++            // Wait 100ms and check again.
++            await new Promise(resolve => { SimpleTest._originalSetTimeout.apply(window, [resolve, 100]); });
+         }
+ 
+-        if (++SimpleTest.waitForClipboard_polls > maxPolls) {
+-            // Log the failure.
+-            SimpleTest.ok(aExpectFailure, "Timed out while polling clipboard for pasted data");
+-            dump("Got this value: " + lastValue);
+-            reset();
+-            failureFn();
+-            return;
+-        }
+-
+-        var data = SpecialPowers.getClipboardData(flavor);
+-
+-        if (validatorFn(data)) {
+-            // Don't show the success message when waiting for preExpectedVal
+-            if (preExpectedVal)
+-                preExpectedVal = null;
+-            else
+-                SimpleTest.ok(!aExpectFailure, "Clipboard has the given value");
+-            reset();
+-            successFn();
+-        } else {
+-            lastValue = data;
+-            SimpleTest._originalSetTimeout.apply(window, [function() { return wait(validatorFn, successFn, failureFn, flavor); }, 100]);
++        SimpleTest.ok(aExpectFailure, "Timed out while polling clipboard for pasted data, got: " + data);
++        if (!aExpectFailure) {
++          throw "failed";
+         }
+     }
+ 
+     // First we wait for a known value different from the expected one.
+-    var preExpectedVal = initialVal;
+-    SpecialPowers.clipboardCopyString(preExpectedVal);
+-    wait(function(aData) { return aData  == preExpectedVal; },
+-         function() {
+-           // Call the original setup fn
+-           aSetupFn();
+-           wait(inputValidatorFn, aSuccessFn, aFailureFn, requestedFlavor);
+-         }, aFailureFn, "text/unicode");
++    await putAndVerify(function() { SpecialPowers.clipboardCopyString(preExpectedVal); },
++                       function(aData) { return aData  == preExpectedVal; },
++                       "text/unicode");
++
++    return await putAndVerify(aSetupFn, inputValidatorFn, requestedFlavor);
+ }
+ 
++
+ /**
+  * Wait for a condition for a while (actually up to 3s here).
+  *
+  * @param aCond
+  *        A function returns the result of the condition
+  * @param aCallback
+  *        A function called after the condition is passed or timeout.
+  * @param aErrorMsg
+@@ -1077,17 +1070,17 @@ SimpleTest.promiseWaitForCondition = fun
+  * Executes a function shortly after the call, but lets the caller continue
+  * working (or finish).
+  */
+ SimpleTest.executeSoon = function(aFunc) {
+     if ("SpecialPowers" in window) {
+         return SpecialPowers.executeSoon(aFunc, window);
+     }
+     setTimeout(aFunc, 0);
+-    return null;		// Avoid warning.
++    return null;   // Avoid warning.
+ };
+ 
+ SimpleTest.registerCleanupFunction = function(aFunc) {
+     SimpleTest._cleanupFunctions.push(aFunc);
+ };
+ 
+ SimpleTest.registerTimeoutFunction = function(aFunc) {
+     SimpleTest._timeoutFunctions.push(aFunc);

+ 107 - 0
frg/work-js/mozilla-release/patches/1421582-59a1.patch

@@ -0,0 +1,107 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1512090348 -32400
+# Node ID d7fe31eaf4f38dec638d3ce322dde91763e7fb0f
+# Parent  0ee4facc0976a7eb96dc27a6489963ac10241d84
+Bug 1421582 - Fix dom/base/test/browser_bug902350.js to wait for events in proper order. r=Gijs
+
+diff --git a/dom/base/test/browser_bug902350.js b/dom/base/test/browser_bug902350.js
+--- a/dom/base/test/browser_bug902350.js
++++ b/dom/base/test/browser_bug902350.js
+@@ -1,68 +1,44 @@
+ /*
+  * Mixed Content Block frame navigates for target="_top" - Test for Bug 902350
+  */
+ 
+-
+-const PREF_ACTIVE = "security.mixed_content.block_active_content";
+-const gHttpTestRoot = "https://example.com/browser/dom/base/test/";
+-var origBlockActive;
+-var gTestBrowser = null;
+-
+-registerCleanupFunction(function() {
+-  // Set preferences back to their original values
+-  Services.prefs.setBoolPref(PREF_ACTIVE, origBlockActive);
+-});
++add_task(async function mixed_content_block_for_target_top_test() {
++  const PREF_ACTIVE = "security.mixed_content.block_active_content";
++  const httpsTestRoot = getRootDirectory(gTestPath)
++    .replace("chrome://mochitests/content", "https://example.com");
+ 
+-function MixedTestsCompleted() {
+-  gBrowser.removeCurrentTab();
+-  window.focus();
+-  finish();
+-}
++  await SpecialPowers.pushPrefEnv({ set: [[ PREF_ACTIVE, true ]] });
+ 
+-function test() {
+-  waitForExplicitFinish();
+-
+-  origBlockActive = Services.prefs.getBoolPref(PREF_ACTIVE);
++  let newTab = await BrowserTestUtils.openNewForegroundTab({ gBrowser,
++                                                             waitForLoad: true });
++  let testBrowser = newTab.linkedBrowser;
+ 
+-  Services.prefs.setBoolPref(PREF_ACTIVE, true);
+-
+-  var newTab = BrowserTestUtils.addTab(gBrowser);
+-  gBrowser.selectedTab = newTab;
+-  gTestBrowser = gBrowser.selectedBrowser;
++  var url = httpsTestRoot + "file_bug902350.html";
++  var frameUrl = httpsTestRoot + "file_bug902350_frame.html";
++  let loadPromise = BrowserTestUtils.browserLoaded(testBrowser, false, url);
++  let frameLoadPromise = BrowserTestUtils.browserLoaded(testBrowser, true,
++                                                        frameUrl);
++  testBrowser.loadURI(url);
++  await loadPromise;
++  await frameLoadPromise;
+ 
+-  BrowserTestUtils.browserLoaded(gTestBrowser).then(() => {
+-    // about:blank is expected to be loaded here.
+-    var url = gHttpTestRoot + "file_bug902350.html";
+-    BrowserTestUtils.browserLoaded(gTestBrowser, true /*includeSubFrames*/).then(MixedTest1A);
+-    gTestBrowser.loadURI(url);
+-  });
+-}
+-
+-// Need to capture 2 loads, one for the main page and one for the iframe
+-function MixedTest1A() {
+-  BrowserTestUtils.browserLoaded(gTestBrowser, true /*includeSubFrames*/).then(MixedTest1B);
+-}
+-
+-// Find the iframe and click the link in it
+-function MixedTest1B() {
+-  BrowserTestUtils.browserLoaded(gTestBrowser).then(MixedTest1C);
+-
+-  ContentTask.spawn(gTestBrowser, null, function() {
++  // Find the iframe and click the link in it.
++  let insecureUrl = "http://example.com/";
++  let insecureLoadPromise = BrowserTestUtils.browserLoaded(testBrowser, false,
++                                                           insecureUrl);
++  ContentTask.spawn(testBrowser, null, function() {
+     var frame = content.document.getElementById("testing_frame");
+     var topTarget = frame.contentWindow.document.getElementById("topTarget");
+     topTarget.click();
+   });
+ 
+-  // The link click should have caused a load and should not invoke the Mixed Content Blocker
+-  let {gIdentityHandler} = gTestBrowser.ownerGlobal;
++  // Navigating to insecure domain through target='_top' should succeed.
++  await insecureLoadPromise;
++
++  // The link click should not invoke the Mixed Content Blocker.
++  let {gIdentityHandler} = testBrowser.ownerGlobal;
+   ok (!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+       "Mixed Content Doorhanger did not appear when trying to navigate top");
+-}
+ 
+-function MixedTest1C() {
+-  ContentTask.spawn(gTestBrowser, null, function() {
+-    Assert.equal(content.location.href, "http://example.com/",
+-      "Navigating to insecure domain through target='_top' failed.")
+-  }).then(MixedTestsCompleted);
+-}
+-
++  await BrowserTestUtils.removeTab(newTab);
++});

+ 1 - 1
frg/work-js/mozilla-release/patches/1421992-4c-59a1.patch

@@ -1,5 +1,5 @@
 # HG changeset patch
-# User Florian Quèze <florian@queze.net>
+# User Florian Queze <florian@queze.net>
 # Date 1513851023 -3600
 # Node ID f418166c70b806963aa3aacdf17bb35b4a5d8d83
 # Parent  77a1b5501a18944fd28f6c8a19c04486343f6d62

+ 0 - 0
frg/work-js/mozilla-release/patches/1422043-1-60a1 → frg/work-js/mozilla-release/patches/1422043-2-60a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1422043-2-60a1 → frg/work-js/mozilla-release/patches/1422043-3-60a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1422043-3-60a1 → frg/work-js/mozilla-release/patches/1422043-4-60a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1422043-4-60a1 → frg/work-js/mozilla-release/patches/1422043-5-60a1.patch


+ 29 - 0
frg/work-js/mozilla-release/patches/1424025-59a1.patch

@@ -0,0 +1,29 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1512757194 18000
+# Node ID efdfb9487b818ff2a60074633a1c84b14c31754c
+# Parent  7e832e52fbef8fffe972336b798169251aae86ec
+Bug 1424025 - Replace widgets.css link with only breadcrumbs.css. r=jdescottes
+
+diff --git a/devtools/client/inspector/inspector.xhtml b/devtools/client/inspector/inspector.xhtml
+--- a/devtools/client/inspector/inspector.xhtml
++++ b/devtools/client/inspector/inspector.xhtml
+@@ -3,17 +3,17 @@
+    - License, v. 2.0. If a copy of the MPL was not distributed with this
+    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <!DOCTYPE html>
+ 
+ <html xmlns="http://www.w3.org/1999/xhtml" dir="">
+ <head>
+   <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ 
+-  <link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
++  <link rel="stylesheet" href="chrome://devtools/skin/breadcrumbs.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>
+   <link rel="stylesheet" href="chrome://devtools/skin/animation.css"/>
+   <link rel="stylesheet" href="resource://devtools/client/shared/components/tabs/Tabs.css"/>

+ 618 - 0
frg/work-js/mozilla-release/patches/1427677-1-59a1.patch

@@ -0,0 +1,618 @@
+# HG changeset patch
+# User Emilio Cobos Alvarez <emilio@crisal.io>
+# Date 1514901878 -3600
+# Node ID 0a4fe34f8d6c636b17bb56eddfd7ad8f6ce4931b
+# Parent  1450aee5d1183a335996473c9c64dc3324050cbc
+Bug 1427677: Get rid of nsContentUtils::HasDistributedChildren. r=bz
+
+The whole function doesn't have much sense.
+
+I killed its only DOM use in bug 1427511.
+
+Now it only has two callers in nsCSSFrameConstructor, which basically only want
+to know whether the children of the same node can have different flattened tree
+parents.
+
+So let's check that directly instead (checking whether the element has a binding
+or a shadow root), and simplify a bit other surrounding code while at it.
+
+Leave the XUL popup / menubar code doing the broken thing they were doing
+beforehand, because it doesn't look to me like it's trivial to fix... They're
+effectively assuming that the children of the menupopup end up in a single
+insertion point, which is true, but doesn't need to be. Maybe they should walk
+the DOM tree? Don't want to dig into that right now, since XUL insertion points
+can be filtered and all that... Not fun.
+
+Also, this removes the broken optimization that used to check
+mParentFrame->GetContent()->HasChildren(), because it's pretty broken. It used
+to be relevant before bug 653881, because <children> element used to not exist,
+but now the insertion point at least needs to contain the <children> element all
+the time.
+
+There even used to be a XXX comment saying that the optimization didn't work,
+which was removed in:
+
+  https://hg.mozilla.org/mozilla-central/rev/2d8585ec74b3
+
+We could still check for "no insertion points", and optimize that, but it
+doesn't seem worth it.
+
+MozReview-Commit-ID: L4lspkxKENr
+
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -7679,40 +7679,16 @@ nsContentUtils::GetHTMLEditor(nsPresCont
+       NS_FAILED(docShell->GetEditable(&isEditable)) || !isEditable)
+     return nullptr;
+ 
+   return docShell->GetHTMLEditor();
+ }
+ 
+ // static
+ bool
+-nsContentUtils::HasDistributedChildren(nsIContent* aContent)
+-{
+-  if (!aContent || !nsDocument::IsWebComponentsEnabled(aContent)) {
+-    return false;
+-  }
+-
+-  if (aContent->GetShadowRoot()) {
+-    // Children of a shadow root host are distributed
+-    // to content insertion points in the shadow root.
+-    return true;
+-  }
+-
+-  HTMLSlotElement* slotEl = HTMLSlotElement::FromContent(aContent);
+-  if (slotEl && slotEl->GetContainingShadow()) {
+-    // Children of a slot are rendered if the slot does not have any assigned
+-    // nodes (fallback content).
+-    return slotEl->AssignedNodes().IsEmpty();
+-  }
+-
+-  return false;
+-}
+-
+-// static
+-bool
+ nsContentUtils::IsForbiddenRequestHeader(const nsACString& aHeader)
+ {
+   if (IsForbiddenSystemRequestHeader(aHeader)) {
+     return true;
+   }
+ 
+   return StringBeginsWith(aHeader, NS_LITERAL_CSTRING("proxy-"),
+                           nsCaseInsensitiveCStringComparator()) ||
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -2743,22 +2743,16 @@ public:
+   /**
+    * Returns a LogModule that dump calls from content script are logged to.
+    * This can be enabled with the 'Dump' module, and is useful for synchronizing
+    * content JS to other logging modules.
+    */
+   static mozilla::LogModule* DOMDumpLog();
+ 
+   /**
+-   * Returns whether the children of the provided content are
+-   * nodes that are distributed to Shadow DOM insertion points.
+-   */
+-  static bool HasDistributedChildren(nsIContent* aContent);
+-
+-  /**
+    * Returns whether a given header is forbidden for an XHR or fetch
+    * request.
+    */
+   static bool IsForbiddenRequestHeader(const nsACString& aHeader);
+ 
+   /**
+    * Returns whether a given header is forbidden for a system XHR
+    * request.
+diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
+--- a/layout/base/nsCSSFrameConstructor.cpp
++++ b/layout/base/nsCSSFrameConstructor.cpp
+@@ -7404,44 +7404,73 @@ nsCSSFrameConstructor::IssueSingleInsert
+                 !GetDisplayContentsStyleFor(child)));
+ 
+     // Call ContentRangeInserted with this node.
+     ContentRangeInserted(aContainer, child, child->GetNextSibling(),
+                          mTempFrameTreeState, InsertionKind::Sync, nullptr);
+   }
+ }
+ 
++bool
++nsCSSFrameConstructor::InsertionPoint::IsMultiple() const
++{
++  if (!mParentFrame) {
++    return false;
++  }
++
++  // Fieldset frames have multiple normal flow child frame lists so handle it
++  // the same as if it had multiple content insertion points.
++  if (mParentFrame->IsFieldSetFrame()) {
++    return true;
++  }
++
++  // A details frame moves the first summary frame to be its first child, so we
++  // treat it as if it has multiple content insertion points.
++  if (mParentFrame->IsDetailsFrame()) {
++    return true;
++  }
++
++  return false;
++}
++
+ nsCSSFrameConstructor::InsertionPoint
+ nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
+                                               nsIContent* aStartChild,
+                                               nsIContent* aEndChild)
+ {
+-  // See if we have an XBL insertion point. If so, then that's our
+-  // real parent frame; if not, then the frame hasn't been built yet
+-  // and we just bail.
+-  InsertionPoint insertionPoint = GetInsertionPoint(aContainer, nullptr);
+-  if (!insertionPoint.mParentFrame && !insertionPoint.mMultiple) {
+-    return insertionPoint; // Don't build the frames.
+-  }
+-
+-  if (insertionPoint.mMultiple || aStartChild->GetXBLInsertionPoint()) {
+-    // If we have multiple insertion points or if we have an insertion point
+-    // and the operation is not a true append or if the insertion point already
+-    // has explicit children, then we must fall back.
+-    if (insertionPoint.mMultiple || aEndChild ||
+-        insertionPoint.mParentFrame->GetContent()->HasChildren()) {
+-      // Now comes the fun part.  For each inserted child, make a
+-      // ContentInserted call as if it had just gotten inserted and
+-      // let ContentInserted handle the mess.
+-      IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
+-      insertionPoint.mParentFrame = nullptr;
+-    }
+-  }
+-
+-  return insertionPoint;
++  MOZ_ASSERT(aStartChild);
++
++  // If the children of the container may be distributed to different insertion
++  // points, insert them separately and bail out, letting ContentInserted handle
++  // the mess.
++  if (aContainer->GetShadowRoot() || aContainer->GetXBLBinding()) {
++    IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
++    return { };
++  }
++
++#ifdef DEBUG
++  {
++    nsIContent* expectedParent = aStartChild->GetFlattenedTreeParent();
++    for (nsIContent* child = aStartChild->GetNextSibling(); child;
++         child = child->GetNextSibling()) {
++      MOZ_ASSERT(child->GetFlattenedTreeParent() == expectedParent);
++    }
++  }
++#endif
++
++  // Now the flattened tree parent of all the siblings is the same, just use the
++  // same insertion point and take the fast path, unless it's a multiple
++  // insertion point.
++  InsertionPoint ip = GetInsertionPoint(aStartChild);
++  if (ip.IsMultiple()) {
++    IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
++    return { };
++  }
++
++  return ip;
+ }
+ 
+ bool
+ nsCSSFrameConstructor::MaybeRecreateForFrameset(nsIFrame* aParentFrame,
+                                                 nsIContent* aStartChild,
+                                                 nsIContent* aEndChild)
+ {
+   if (aParentFrame->IsFrameSetFrame()) {
+@@ -8070,17 +8099,17 @@ nsCSSFrameConstructor::ContentRangeInser
+   // if needed.
+   styleNewChildRangeEagerly();
+ 
+   InsertionPoint insertion;
+   if (isSingleInsert) {
+     // See if we have an XBL insertion point. If so, then that's our
+     // real parent frame; if not, then the frame hasn't been built yet
+     // and we just bail.
+-    insertion = GetInsertionPoint(aContainer, aStartChild);
++    insertion = GetInsertionPoint(aStartChild);
+   } else {
+     // Get our insertion point. If we need to issue single ContentInserted's
+     // GetRangeInsertionPoint will take care of that for us.
+     LAYOUT_PHASE_TEMP_EXIT();
+     insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild);
+     LAYOUT_PHASE_TEMP_REENTER();
+   }
+ 
+@@ -9319,88 +9348,27 @@ nsCSSFrameConstructor::ReplicateFixedFra
+   // broken auto-positioning. Oh, well.
+   NS_ASSERTION(!canvasFrame->PrincipalChildList().FirstChild(),
+                "leaking frames; doc root continuation must be empty");
+   canvasFrame->SetInitialChildList(kPrincipalList, fixedPlaceholders);
+   return NS_OK;
+ }
+ 
+ nsCSSFrameConstructor::InsertionPoint
+-nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aContainer,
+-                                         nsIContent* aChild)
+-{
+-  nsBindingManager* bindingManager = mDocument->BindingManager();
+-
+-  nsIContent* insertionElement;
+-  if (aChild) {
+-    // We've got an explicit insertion child. Check to see if it's
+-    // anonymous.
+-    if (aChild->GetBindingParent() == aContainer) {
+-      // This child content is anonymous. Don't use the insertion
+-      // point, since that's only for the explicit kids.
+-      return InsertionPoint(GetContentInsertionFrameFor(aContainer), aContainer);
+-    }
+-
+-    if (nsContentUtils::HasDistributedChildren(aContainer) ||
+-        aContainer->IsShadowRoot()) {
+-      // The container distributes nodes or is a shadow root, use the frame of
+-      // the flattened tree parent.
+-      //
+-      // It may be the case that the node is distributed but not matched to any
+-      // insertion points, so there is no flattened parent.
+-      //
+-      // FIXME(emilio): We should be able to use this path all the time.
+-      nsIContent* flattenedParent = aChild->GetFlattenedTreeParent();
+-      if (flattenedParent) {
+-        return InsertionPoint(GetContentInsertionFrameFor(flattenedParent),
+-                              flattenedParent);
+-      }
+-      return InsertionPoint();
+-    }
+-
+-    insertionElement = bindingManager->FindNestedInsertionPoint(aContainer, aChild);
+-  } else {
+-    if (nsContentUtils::HasDistributedChildren(aContainer)) {
+-      // The container distributes nodes to shadow DOM insertion points.
+-      // Return with aMultiple set to true to induce callers to insert children
+-      // individually into the node's flattened tree parent.
+-      return InsertionPoint(nullptr, nullptr, true);
+-    }
+-
+-    bool multiple;
+-    insertionElement = bindingManager->FindNestedSingleInsertionPoint(aContainer, &multiple);
+-    if (multiple) {
+-      return InsertionPoint(nullptr, nullptr, true);
+-    }
+-  }
+-
++nsCSSFrameConstructor::GetInsertionPoint(nsIContent* aChild)
++{
++  MOZ_ASSERT(aChild);
++  nsIContent* insertionElement = aChild->GetFlattenedTreeParent();
+   if (!insertionElement) {
+-    // The FindNested{,Single}InsertionPoint methods return null in the case
+-    // that there is a binding with anonymous content but no insertion point.
+-    // In that case the element doesn't belong in the flattened tree, and we
+-    // don't want to render it.
+-    return InsertionPoint();
+-  }
+-
+-  InsertionPoint insertion(GetContentInsertionFrameFor(insertionElement),
+-                           insertionElement);
+-
+-  // Fieldset frames have multiple normal flow child frame lists so handle it
+-  // the same as if it had multiple content insertion points.
+-  if (insertion.mParentFrame && insertion.mParentFrame->IsFieldSetFrame()) {
+-    insertion.mMultiple = true;
+-  }
+-
+-  // A details frame moves the first summary frame to be its first child, so we
+-  // treat it as if it has multiple content insertion points.
+-  if (insertion.mParentFrame && insertion.mParentFrame->IsDetailsFrame()) {
+-    insertion.mMultiple = true;
+-  }
+-
+-  return insertion;
++    // The element doesn't belong in the flattened tree, and thus we don't want
++    // to render it.
++    return { };
++  }
++
++  return { GetContentInsertionFrameFor(insertionElement), insertionElement };
+ }
+ 
+ // Capture state for the frame tree rooted at the frame associated with the
+ // content object, aContent
+ void
+ nsCSSFrameConstructor::CaptureStateForFramesOf(nsIContent* aContent,
+                                                nsILayoutHistoryState* aHistoryState)
+ {
+@@ -11098,17 +11066,17 @@ nsCSSFrameConstructor::ProcessChildren(n
+       // for default content). Push the children element as an ancestor here because
+       // it does not have a frame and would not otherwise be pushed as an ancestor.
+       insertion.mContainer = aContent;
+       nsIContent* parent = child->GetParent();
+       MOZ_ASSERT(parent, "Parent must be non-null because we are iterating children.");
+       TreeMatchContext::AutoAncestorPusher ancestorPusher(aState.mTreeMatchContext);
+       if (parent != aContent && parent->IsElement()) {
+         insertion.mContainer = child->GetFlattenedTreeParent();
+-        MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(parent, child).mContainer);
++        MOZ_ASSERT(insertion.mContainer == GetInsertionPoint(child).mContainer);
+         if (aState.HasAncestorFilter()) {
+           ancestorPusher.PushAncestor(parent->AsElement());
+         }
+       }
+ 
+       // Frame construction item construction should not post
+       // restyles, so removing restyle flags here is safe.
+       child->UnsetRestyleFlagsIfGecko();
+diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h
+--- a/layout/base/nsCSSFrameConstructor.h
++++ b/layout/base/nsCSSFrameConstructor.h
+@@ -140,41 +140,47 @@ private:
+                                     nsIContent* aEndChild);
+ 
+   /**
+    * Data that represents an insertion point for some child content.
+    */
+   struct InsertionPoint
+   {
+     InsertionPoint()
+-      : mParentFrame(nullptr), mContainer(nullptr), mMultiple(false) {}
+-    InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer,
+-                   bool aMultiple = false)
+-      : mParentFrame(aParentFrame), mContainer(aContainer),
+-        mMultiple(aMultiple) {}
++      : mParentFrame(nullptr)
++      , mContainer(nullptr)
++    {}
++
++    InsertionPoint(nsContainerFrame* aParentFrame, nsIContent* aContainer)
++      : mParentFrame(aParentFrame)
++      , mContainer(aContainer)
++    {}
++
+     /**
+      * The parent frame to use if the inserted children needs to create
+      * frame(s).  May be null, which signals that  we shouldn't try to
+      * create frames for the inserted children; either because there are
+      * no parent frame or because there are multiple insertion points and
+      * we will call IssueSingleInsertNofications for each child instead.
+      * mContainer should not be used when mParentFrame is null.
+      */
+     nsContainerFrame* mParentFrame;
+     /**
+      * The flattened tree parent for the inserted children.
+      * It's undefined if mParentFrame is null.
+      */
+     nsIContent* mContainer;
++
+     /**
+-     * If true then there are multiple insertion points, which means consumers
+-     * should insert children individually into the node's flattened tree parent.
++     * Whether it is required to insert children one-by-one instead of as a
++     * range.
+      */
+-    bool mMultiple;
++    bool IsMultiple() const;
+   };
++
+   /**
+    * Checks if the children of aContainer in the range [aStartChild, aEndChild)
+    * can be inserted/appended to one insertion point together. If so, returns
+    * that insertion point. If not, returns with InsertionPoint.mFrame == nullptr
+    * and issues single ContentInserted calls for each child.
+    * aEndChild = nullptr indicates that we are dealing with an append.
+    */
+   InsertionPoint GetRangeInsertionPoint(nsIContent* aContainer,
+@@ -354,19 +360,25 @@ public:
+                                   nsIFrame*         aFrame,
+                                   nsContainerFrame* aParentFrame,
+                                   bool              aIsFluid = true);
+ 
+   // Copy over fixed frames from aParentFrame's prev-in-flow
+   nsresult ReplicateFixedFrames(nsPageContentFrame* aParentFrame);
+ 
+   /**
+-   * Get the XBL insertion point for aChild in aContainer.
++   * Get the insertion point for aChild.
+    */
+-  InsertionPoint GetInsertionPoint(nsIContent* aContainer, nsIContent* aChild);
++  InsertionPoint GetInsertionPoint(nsIContent* aChild);
++
++  /**
++   * Return the insertion frame of the primary frame of aContent, or its nearest
++   * ancestor that isn't display:contents.
++   */
++  nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent);
+ 
+   void CreateListBoxContent(nsContainerFrame* aParentFrame,
+                             nsIFrame*         aPrevFrame,
+                             nsIContent*       aChild,
+                             nsIFrame**        aResult,
+                             bool              aIsAppend);
+ 
+   // GetInitialContainingBlock() is deprecated in favor of GetRootElementFrame();
+@@ -2181,22 +2193,16 @@ private:
+   // insertion points.
+   nsIFrame* GetInsertionPrevSibling(InsertionPoint* aInsertion, // inout
+                                     nsIContent* aChild,
+                                     bool* aIsAppend,
+                                     bool* aIsRangeInsertSafe,
+                                     nsIContent* aStartSkipChild = nullptr,
+                                     nsIContent *aEndSkipChild = nullptr);
+ 
+-  /**
+-   * Return the insertion frame of the primary frame of aContent, or its nearest
+-   * ancestor that isn't display:contents.
+-   */
+-  nsContainerFrame* GetContentInsertionFrameFor(nsIContent* aContent);
+-
+   // see if aContent and aSibling are legitimate siblings due to restrictions
+   // imposed by table columns
+   // XXXbz this code is generally wrong, since the frame for aContent
+   // may be constructed based on tag, not based on aDisplay!
+   bool IsValidSibling(nsIFrame*              aSibling,
+                       nsIContent*            aContent,
+                       mozilla::StyleDisplay& aDisplay);
+ 
+diff --git a/layout/xul/nsMenuBarFrame.cpp b/layout/xul/nsMenuBarFrame.cpp
+--- a/layout/xul/nsMenuBarFrame.cpp
++++ b/layout/xul/nsMenuBarFrame.cpp
+@@ -157,21 +157,18 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
+   }
+   if (accessKeys.IsEmpty() && charCode)
+     accessKeys.AppendElement(charCode);
+ 
+   if (accessKeys.IsEmpty())
+     return nullptr; // no character was pressed so just return
+ 
+   // Enumerate over our list of frames.
+-  auto insertion = PresShell()->FrameConstructor()->
+-    GetInsertionPoint(GetContent(), nullptr);
+-  nsContainerFrame* immediateParent = insertion.mParentFrame;
+-  if (!immediateParent)
+-    immediateParent = this;
++  nsContainerFrame* immediateParent =
++    nsXULPopupManager::ImmediateParentFrame(this);
+ 
+   // Find a most preferred accesskey which should be returned.
+   nsIFrame* foundMenu = nullptr;
+   size_t foundIndex = accessKeys.NoIndex;
+   nsIFrame* currFrame = immediateParent->PrincipalChildList().FirstChild();
+ 
+   while (currFrame) {
+     nsIContent* current = currFrame->GetContent();
+diff --git a/layout/xul/nsMenuPopupFrame.cpp b/layout/xul/nsMenuPopupFrame.cpp
+--- a/layout/xul/nsMenuPopupFrame.cpp
++++ b/layout/xul/nsMenuPopupFrame.cpp
+@@ -2085,22 +2085,18 @@ nsMenuPopupFrame::FindMenuWithShortcut(n
+ {
+   uint32_t charCode, keyCode;
+   aKeyEvent->GetCharCode(&charCode);
+   aKeyEvent->GetKeyCode(&keyCode);
+ 
+   doAction = false;
+ 
+   // Enumerate over our list of frames.
+-  auto insertion = PresShell()->
+-    FrameConstructor()->GetInsertionPoint(GetContent(), nullptr);
+-  nsContainerFrame* immediateParent = insertion.mParentFrame;
+-  if (!immediateParent)
+-    immediateParent = this;
+-
++  nsContainerFrame* immediateParent =
++    nsXULPopupManager::ImmediateParentFrame(this);
+   uint32_t matchCount = 0, matchShortcutCount = 0;
+   bool foundActive = false;
+   nsMenuFrame* frameBefore = nullptr;
+   nsMenuFrame* frameAfter = nullptr;
+   nsMenuFrame* frameShortcut = nullptr;
+ 
+   nsIContent* parentContent = mContent->GetParent();
+ 
+diff --git a/layout/xul/nsXULPopupManager.cpp b/layout/xul/nsXULPopupManager.cpp
+--- a/layout/xul/nsXULPopupManager.cpp
++++ b/layout/xul/nsXULPopupManager.cpp
+@@ -2432,29 +2432,42 @@ nsXULPopupManager::HandleKeyboardEventWi
+   if (consume) {
+     aKeyEvent->AsEvent()->StopPropagation();
+     aKeyEvent->AsEvent()->StopCrossProcessForwarding();
+     aKeyEvent->AsEvent()->PreventDefault();
+   }
+   return true;
+ }
+ 
++nsContainerFrame*
++nsXULPopupManager::ImmediateParentFrame(nsContainerFrame* aFrame)
++{
++  MOZ_ASSERT(aFrame && aFrame->GetContent());
++
++  bool multiple = false; // Unused
++  nsIContent* insertionPoint =
++    aFrame->GetContent()->OwnerDoc()->BindingManager()->
++      FindNestedSingleInsertionPoint(aFrame->GetContent(), &multiple);
++
++  nsCSSFrameConstructor* fc = aFrame->PresContext()->FrameConstructor();
++  nsContainerFrame* insertionFrame =
++    insertionPoint
++      ? fc->GetContentInsertionFrameFor(insertionPoint)
++      : nullptr;
++
++  return insertionFrame ? insertionFrame : aFrame;
++}
++
+ nsMenuFrame*
+ nsXULPopupManager::GetNextMenuItem(nsContainerFrame* aParent,
+                                    nsMenuFrame* aStart,
+                                    bool aIsPopup,
+                                    bool aWrap)
+ {
+-  nsPresContext* presContext = aParent->PresContext();
+-  auto insertion = presContext->PresShell()->
+-    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
+-  nsContainerFrame* immediateParent = insertion.mParentFrame;
+-  if (!immediateParent)
+-    immediateParent = aParent;
+-
++  nsContainerFrame* immediateParent = ImmediateParentFrame(aParent);
+   nsIFrame* currFrame = nullptr;
+   if (aStart) {
+     if (aStart->GetNextSibling())
+       currFrame = aStart->GetNextSibling();
+     else if (aStart->GetParent()->GetContent()->IsXULElement(nsGkAtoms::menugroup))
+       currFrame = aStart->GetParent()->GetNextSibling();
+   }
+   else
+@@ -2504,23 +2517,17 @@ nsXULPopupManager::GetNextMenuItem(nsCon
+ }
+ 
+ nsMenuFrame*
+ nsXULPopupManager::GetPreviousMenuItem(nsContainerFrame* aParent,
+                                        nsMenuFrame* aStart,
+                                        bool aIsPopup,
+                                        bool aWrap)
+ {
+-  nsPresContext* presContext = aParent->PresContext();
+-  auto insertion = presContext->PresShell()->
+-    FrameConstructor()->GetInsertionPoint(aParent->GetContent(), nullptr);
+-  nsContainerFrame* immediateParent = insertion.mParentFrame;
+-  if (!immediateParent)
+-    immediateParent = aParent;
+-
++  nsContainerFrame* immediateParent = ImmediateParentFrame(aParent);
+   const nsFrameList& frames(immediateParent->PrincipalChildList());
+ 
+   nsIFrame* currFrame = nullptr;
+   if (aStart) {
+     if (aStart->GetPrevSibling())
+       currFrame = aStart->GetPrevSibling();
+     else if (aStart->GetParent()->GetContent()->IsXULElement(nsGkAtoms::menugroup))
+       currFrame = aStart->GetParent()->GetPrevSibling();
+diff --git a/layout/xul/nsXULPopupManager.h b/layout/xul/nsXULPopupManager.h
+--- a/layout/xul/nsXULPopupManager.h
++++ b/layout/xul/nsXULPopupManager.h
+@@ -358,16 +358,23 @@ public:
+   // initialize and shutdown methods called by nsLayoutStatics
+   static nsresult Init();
+   static void Shutdown();
+ 
+   // returns a weak reference to the popup manager instance, could return null
+   // if a popup manager could not be allocated
+   static nsXULPopupManager* GetInstance();
+ 
++  // Returns the immediate parent frame of inserted children of aFrame's
++  // content.
++  //
++  // FIXME(emilio): Or something like that, because this is kind of broken in a
++  // variety of situations like multiple insertion points.
++  static nsContainerFrame* ImmediateParentFrame(nsContainerFrame* aFrame);
++
+   // This should be called when a window is moved or resized to adjust the
+   // popups accordingly.
+   void AdjustPopupsOnWindowChange(nsPIDOMWindowOuter* aWindow);
+   void AdjustPopupsOnWindowChange(nsIPresShell* aPresShell);
+ 
+   // given a menu frame, find the prevous or next menu frame. If aPopup is
+   // true then navigate a menupopup, from one item on the menu to the previous
+   // or next one. This is used for cursor navigation between items in a popup

+ 92 - 0
frg/work-js/mozilla-release/patches/1427677-2-59a1.patch

@@ -0,0 +1,92 @@
+# HG changeset patch
+# User Emilio Cobos Alvarez <emilio@crisal.io>
+# Date 1514957901 -3600
+# Node ID d5dc9bcdd84bb3f8beb97725f65c357ca61d7223
+# Parent  a2c5cd3f905dc3aea0df96a013d9ed9c0e5e6910
+Bug 1427677: Remove nsBindingManager::FindNestedInsertionPoint. r=bz
+
+No more callers!
+
+MozReview-Commit-ID: 1mxi4IjViuC
+
+diff --git a/dom/xbl/nsBindingManager.cpp b/dom/xbl/nsBindingManager.cpp
+--- a/dom/xbl/nsBindingManager.cpp
++++ b/dom/xbl/nsBindingManager.cpp
+@@ -1080,54 +1080,16 @@ nsBindingManager::HandleChildInsertion(n
+     if (newParent == parent) {
+       break;
+     }
+ 
+     parent = newParent;
+   }
+ }
+ 
+-
+-nsIContent*
+-nsBindingManager::FindNestedInsertionPoint(nsIContent* aContainer,
+-                                           nsIContent* aChild)
+-{
+-  NS_PRECONDITION(aChild->GetParent() == aContainer,
+-                  "Wrong container");
+-
+-  nsIContent* parent = aContainer;
+-  if (aContainer->IsActiveChildrenElement()) {
+-    if (static_cast<XBLChildrenElement*>(aContainer)->
+-          HasInsertedChildren()) {
+-      return nullptr;
+-    }
+-    parent = aContainer->GetParent();
+-  }
+-
+-  while (parent) {
+-    nsXBLBinding* binding = GetBindingWithContent(parent);
+-    if (!binding) {
+-      break;
+-    }
+-
+-    XBLChildrenElement* point = binding->FindInsertionPointFor(aChild);
+-    if (!point) {
+-      return nullptr;
+-    }
+-
+-    nsIContent* newParent = point->GetParent();
+-    if (newParent == parent) {
+-      break;
+-    }
+-    parent = newParent;
+-  }
+-
+-  return parent;
+-}
+-
+ nsIContent*
+ nsBindingManager::FindNestedSingleInsertionPoint(nsIContent* aContainer,
+                                                  bool* aMulti)
+ {
+   *aMulti = false;
+ 
+   nsIContent* parent = aContainer;
+   if (aContainer->IsActiveChildrenElement()) {
+diff --git a/dom/xbl/nsBindingManager.h b/dom/xbl/nsBindingManager.h
+--- a/dom/xbl/nsBindingManager.h
++++ b/dom/xbl/nsBindingManager.h
+@@ -170,19 +170,16 @@ public:
+ 
+   // When removing an insertion point or a parent of one, clear the insertion
+   // points and their insertion parents.
+   void ClearInsertionPointsRecursively(nsIContent* aContent);
+ 
+   // Called when the document is going away
+   void DropDocumentReference();
+ 
+-  nsIContent* FindNestedInsertionPoint(nsIContent* aContainer,
+-                                       nsIContent* aChild);
+-
+   nsIContent* FindNestedSingleInsertionPoint(nsIContent* aContainer, bool* aMulti);
+ 
+   // Enumerate each bound content's bindings (including its base bindings)
+   // in mBoundContentSet. Return false from the callback to stop enumeration.
+   using BoundContentBindingCallback = std::function<bool (nsXBLBinding*)>;
+   bool EnumerateBoundContentBindings(
+     const BoundContentBindingCallback& aCallback) const;
+ 

+ 310 - 0
frg/work-js/mozilla-release/patches/1427908-59a1.patch

@@ -0,0 +1,310 @@
+# HG changeset patch
+# User Emilio Cobos Alvarez <emilio@crisal.io>
+# Date 1514955072 -3600
+# Node ID 97d9e1823553fec9684c6937d0d82cf288d037c3
+# Parent  9ab52b94025914882c00394b864de9f9714cf4be
+Bug 1427908: Never reenter synchronously into frame construction. r=bz
+
+We remove async from the DOM all the time now since bug 1389743.
+
+We could, before this patch, recurse into frame construction in a sync way, due
+to the way we handle the weird insertion cases for <fieldset>, <details>, and
+<mathml>.
+
+This patch makes those also async, making the IssueSingleInsertNotifications
+condition unnecessary.
+
+MozReview-Commit-ID: LujPaYPwA4G
+
+diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
+--- a/layout/base/nsCSSFrameConstructor.cpp
++++ b/layout/base/nsCSSFrameConstructor.cpp
+@@ -7387,46 +7387,37 @@ nsCSSFrameConstructor::CreateNeededFrame
+     CreateNeededFrames(rootElement, treeMatchContext);
+   }
+ }
+ #endif
+ 
+ void
+ nsCSSFrameConstructor::IssueSingleInsertNofications(nsIContent* aContainer,
+                                                     nsIContent* aStartChild,
+-                                                    nsIContent* aEndChild,
+-                                                    InsertionKind aInsertionKind)
++                                                    nsIContent* aEndChild)
+ {
+   for (nsIContent* child = aStartChild;
+        child != aEndChild;
+        child = child->GetNextSibling()) {
+-    if ((child->GetPrimaryFrame() || GetDisplayNoneStyleFor(child) ||
+-         GetDisplayContentsStyleFor(child))
+-#ifdef MOZ_XUL
+-        //  Except listboxes suck, so do NOT skip anything here if
+-        //  we plan to notify a listbox.
+-        && !MaybeGetListBoxBodyFrame(aContainer, child)
+-#endif
+-        ) {
+-      // Already have a frame or undisplayed entry for this content; a
+-      // previous ContentRangeInserted in this loop must have reconstructed
+-      // its insertion parent.  Skip it.
+-      continue;
+-    }
++    // listboxes suck.
++    MOZ_ASSERT(MaybeGetListBoxBodyFrame(aContainer, child) ||
++               (!child->GetPrimaryFrame() &&
++                !GetDisplayNoneStyleFor(child) &&
++                !GetDisplayContentsStyleFor(child)));
++
+     // Call ContentRangeInserted with this node.
+     ContentRangeInserted(aContainer, child, child->GetNextSibling(),
+-                         mTempFrameTreeState, aInsertionKind, nullptr);
++                         mTempFrameTreeState, InsertionKind::Sync, nullptr);
+   }
+ }
+ 
+ nsCSSFrameConstructor::InsertionPoint
+ nsCSSFrameConstructor::GetRangeInsertionPoint(nsIContent* aContainer,
+                                               nsIContent* aStartChild,
+-                                              nsIContent* aEndChild,
+-                                              InsertionKind aInsertionKind)
++                                              nsIContent* aEndChild)
+ {
+   // See if we have an XBL insertion point. If so, then that's our
+   // real parent frame; if not, then the frame hasn't been built yet
+   // and we just bail.
+   InsertionPoint insertionPoint = GetInsertionPoint(aContainer, nullptr);
+   if (!insertionPoint.mParentFrame && !insertionPoint.mMultiple) {
+     return insertionPoint; // Don't build the frames.
+   }
+@@ -7435,18 +7426,17 @@ nsCSSFrameConstructor::GetRangeInsertion
+     // If we have multiple insertion points or if we have an insertion point
+     // and the operation is not a true append or if the insertion point already
+     // has explicit children, then we must fall back.
+     if (insertionPoint.mMultiple || aEndChild ||
+         insertionPoint.mParentFrame->GetContent()->HasChildren()) {
+       // Now comes the fun part.  For each inserted child, make a
+       // ContentInserted call as if it had just gotten inserted and
+       // let ContentInserted handle the mess.
+-      IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
+-                                   aInsertionKind);
++      IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
+       insertionPoint.mParentFrame = nullptr;
+     }
+   }
+ 
+   return insertionPoint;
+ }
+ 
+ bool
+@@ -7596,18 +7586,17 @@ nsCSSFrameConstructor::ContentAppended(n
+   // if needed (when aInsertionKind == InsertionKind::Sync, we know that the
+   // styles are up-to-date already).
+   if (aInsertionKind == InsertionKind::Async && aContainer->IsStyledByServo()) {
+     StyleNewChildRange(aFirstNewContent, nullptr);
+   }
+ 
+   LAYOUT_PHASE_TEMP_EXIT();
+   InsertionPoint insertion =
+-    GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr,
+-                           aInsertionKind);
++    GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr);
+   nsContainerFrame*& parentFrame = insertion.mParentFrame;
+   LAYOUT_PHASE_TEMP_REENTER();
+   if (!parentFrame) {
+     return;
+   }
+ 
+   LAYOUT_PHASE_TEMP_EXIT();
+   if (MaybeRecreateForFrameset(parentFrame, aFirstNewContent, nullptr)) {
+@@ -7620,17 +7609,17 @@ nsCSSFrameConstructor::ContentAppended(n
+     // Nothing to do here; we shouldn't be constructing kids of leaves
+     // Clear lazy bits so we don't try to construct again.
+     ClearLazyBits(aFirstNewContent, nullptr);
+     return;
+   }
+ 
+   if (parentFrame->IsFrameOfType(nsIFrame::eMathML)) {
+     LAYOUT_PHASE_TEMP_EXIT();
+-    RecreateFramesForContent(parentFrame->GetContent(), aInsertionKind);
++    RecreateFramesForContent(parentFrame->GetContent(), InsertionKind::Async);
+     LAYOUT_PHASE_TEMP_REENTER();
+     return;
+   }
+ 
+ #ifdef DEBUG
+   if (gNoisyContentUpdates && IsFramePartOfIBSplit(parentFrame)) {
+     printf("nsCSSFrameConstructor::ContentAppended: parentFrame=");
+     nsFrame::ListTag(stdout, parentFrame);
+@@ -7990,18 +7979,17 @@ nsCSSFrameConstructor::ContentRangeInser
+                             // doesn't use "old next sibling".
+                             aStartChild, nullptr, nullptr, CONTENT_INSERTED)) {
+         return;
+       }
+     } else {
+       // We don't handle a range insert to a listbox parent, issue single
+       // ContertInserted calls for each node inserted.
+       LAYOUT_PHASE_TEMP_EXIT();
+-      IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
+-                                   aInsertionKind);
++      IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
+       LAYOUT_PHASE_TEMP_REENTER();
+       return;
+     }
+   }
+ #endif // MOZ_XUL
+ 
+   // If we have a null parent, then this must be the document element being
+   // inserted, or some other child of the document in the DOM (might be a PI,
+@@ -8087,35 +8075,33 @@ nsCSSFrameConstructor::ContentRangeInser
+     // See if we have an XBL insertion point. If so, then that's our
+     // real parent frame; if not, then the frame hasn't been built yet
+     // and we just bail.
+     insertion = GetInsertionPoint(aContainer, aStartChild);
+   } else {
+     // Get our insertion point. If we need to issue single ContentInserted's
+     // GetRangeInsertionPoint will take care of that for us.
+     LAYOUT_PHASE_TEMP_EXIT();
+-    insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild,
+-                                       aInsertionKind);
++    insertion = GetRangeInsertionPoint(aContainer, aStartChild, aEndChild);
+     LAYOUT_PHASE_TEMP_REENTER();
+   }
+ 
+   if (!insertion.mParentFrame) {
+     return;
+   }
+ 
+   bool isAppend, isRangeInsertSafe;
+   nsIFrame* prevSibling = GetInsertionPrevSibling(&insertion, aStartChild,
+                                                   &isAppend, &isRangeInsertSafe);
+ 
+   // check if range insert is safe
+   if (!isSingleInsert && !isRangeInsertSafe) {
+     // must fall back to a single ContertInserted for each child in the range
+     LAYOUT_PHASE_TEMP_EXIT();
+-    IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
+-                                 aInsertionKind);
++    IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
+     LAYOUT_PHASE_TEMP_REENTER();
+     return;
+   }
+ 
+   LayoutFrameType frameType = insertion.mParentFrame->Type();
+   LAYOUT_PHASE_TEMP_EXIT();
+   if (MaybeRecreateForFrameset(insertion.mParentFrame, aStartChild, aEndChild)) {
+     LAYOUT_PHASE_TEMP_REENTER();
+@@ -8132,47 +8118,49 @@ nsCSSFrameConstructor::ContentRangeInser
+     // Just reframe the parent, since figuring out whether this
+     // should be the new legend and then handling it is too complex.
+     // We could do a little better here --- check if the fieldset already
+     // has a legend which occurs earlier in its child list than this node,
+     // and if so, proceed. But we'd have to extend nsFieldSetFrame
+     // to locate this legend in the inserted frames and extract it.
+     LAYOUT_PHASE_TEMP_EXIT();
+     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
+-                             aInsertionKind);
++                             InsertionKind::Async);
+     LAYOUT_PHASE_TEMP_REENTER();
+     return;
+   }
+ 
+   // We should only get here with details when doing a single insertion because
+   // we treat details frame as if it has multiple insertion points.
+   MOZ_ASSERT(isSingleInsert || frameType != LayoutFrameType::Details);
+   if (frameType == LayoutFrameType::Details) {
+     // When inserting an element into <details>, just reframe the details frame
+     // and let it figure out where the element should be laid out. It might seem
+     // expensive to recreate the entire details frame, but it's the simplest way
+     // to handle the insertion.
+     LAYOUT_PHASE_TEMP_EXIT();
+     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
+-                             aInsertionKind);
++                             InsertionKind::Async);
+     LAYOUT_PHASE_TEMP_REENTER();
+     return;
+   }
+ 
+   // Don't construct kids of leaves
+   if (insertion.mParentFrame->IsLeaf()) {
+     // Clear lazy bits so we don't try to construct again.
+     ClearLazyBits(aStartChild, aEndChild);
+     return;
+   }
+ 
++  // FIXME(emilio): This looks terribly inefficient if you insert elements deep
++  // in a MathML subtree.
+   if (insertion.mParentFrame->IsFrameOfType(nsIFrame::eMathML)) {
+     LAYOUT_PHASE_TEMP_EXIT();
+     RecreateFramesForContent(insertion.mParentFrame->GetContent(),
+-                             aInsertionKind);
++                             InsertionKind::Async);
+     LAYOUT_PHASE_TEMP_REENTER();
+     return;
+   }
+ 
+   Maybe<TreeMatchContext> matchContext;
+   if (!aProvidedTreeMatchContext && !aContainer->IsStyledByServo()) {
+     // We use GetParentElementCrossingShadowRoot to handle the case where
+     // aContainer is a ShadowRoot.
+@@ -8243,18 +8231,17 @@ nsCSSFrameConstructor::ContentRangeInser
+ 
+       // Need check whether a range insert is still safe.
+       if (!isSingleInsert && !isRangeInsertSafe) {
+         // Need to recover the letter frames first.
+         RecoverLetterFrames(state.mFloatedItems.containingBlock);
+ 
+         // must fall back to a single ContertInserted for each child in the range
+         LAYOUT_PHASE_TEMP_EXIT();
+-        IssueSingleInsertNofications(aContainer, aStartChild, aEndChild,
+-                                     aInsertionKind);
++        IssueSingleInsertNofications(aContainer, aStartChild, aEndChild);
+         LAYOUT_PHASE_TEMP_REENTER();
+         return;
+       }
+ 
+       frameType = insertion.mParentFrame->Type();
+     }
+   }
+ 
+diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h
+--- a/layout/base/nsCSSFrameConstructor.h
++++ b/layout/base/nsCSSFrameConstructor.h
+@@ -132,18 +132,17 @@ private:
+ #else
+   void CheckBitsForLazyFrameConstruction(nsIContent*) {}
+ #endif
+ 
+   // Issues a single ContentInserted for each child of aContainer in the range
+   // [aStartChild, aEndChild).
+   void IssueSingleInsertNofications(nsIContent* aContainer,
+                                     nsIContent* aStartChild,
+-                                    nsIContent* aEndChild,
+-                                    InsertionKind);
++                                    nsIContent* aEndChild);
+ 
+   /**
+    * Data that represents an insertion point for some child content.
+    */
+   struct InsertionPoint
+   {
+     InsertionPoint()
+       : mParentFrame(nullptr), mContainer(nullptr), mMultiple(false) {}
+@@ -175,18 +174,17 @@ private:
+    * Checks if the children of aContainer in the range [aStartChild, aEndChild)
+    * can be inserted/appended to one insertion point together. If so, returns
+    * that insertion point. If not, returns with InsertionPoint.mFrame == nullptr
+    * and issues single ContentInserted calls for each child.
+    * aEndChild = nullptr indicates that we are dealing with an append.
+    */
+   InsertionPoint GetRangeInsertionPoint(nsIContent* aContainer,
+                                         nsIContent* aStartChild,
+-                                        nsIContent* aEndChild,
+-                                        InsertionKind);
++                                        nsIContent* aEndChild);
+ 
+   // Returns true if parent was recreated due to frameset child, false otherwise.
+   bool MaybeRecreateForFrameset(nsIFrame* aParentFrame,
+                                 nsIContent* aStartChild,
+                                 nsIContent* aEndChild);
+ 
+   /**
+    * For each child in the aStartChild/aEndChild range, calls

+ 1443 - 0
frg/work-js/mozilla-release/patches/1428685-59a1.patch

@@ -0,0 +1,1443 @@
+# HG changeset patch
+# User Jessica Jong <jjong@mozilla.com>
+# Date 1516095720 -28800
+# Node ID bdd0bea249e3ded6e7e9a263ee002205ec30abf9
+# Parent  70c6f6e3a9097ef301c71b962d1f9e59feb4d02a
+Bug 1428685 - Use dom.webcomponents.shadowdom.enabled pref for Shadow DOM. r=smaug
+
+Most of the Shadow DOM related code are behind "dom.webcomponents.enabled" and
+this pref is only used by Shadow DOM right now, so we should rename it to
+"dom.webcomponents.shadowdom.enabled"
+
+MozReview-Commit-ID: er1c7AsSSW
+
+diff --git a/accessible/tests/mochitest/elm/test_shadowroot.html b/accessible/tests/mochitest/elm/test_shadowroot.html
+--- a/accessible/tests/mochitest/elm/test_shadowroot.html
++++ b/accessible/tests/mochitest/elm/test_shadowroot.html
+@@ -20,17 +20,17 @@
+   <div id="content" style="display: none"></div>
+   <pre id="test">
+   </pre>
+ 
+   <script>
+     SimpleTest.waitForExplicitFinish();
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, function() {
+       // This test loads in an iframe, to ensure that the element instance is
+       // loaded with the correct value of the preference.
+       var iframe = document.createElement("iframe");
+       iframe.src = "test_shadowroot_subframe.html";
+       document.body.appendChild(iframe);
+     });
+diff --git a/accessible/tests/mochitest/hittest/test_shadowroot.html b/accessible/tests/mochitest/hittest/test_shadowroot.html
+--- a/accessible/tests/mochitest/hittest/test_shadowroot.html
++++ b/accessible/tests/mochitest/hittest/test_shadowroot.html
+@@ -20,17 +20,17 @@
+   <div id="content" style="display: none"></div>
+   <pre id="test">
+   </pre>
+ 
+   <script>
+     SimpleTest.waitForExplicitFinish();
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, function() {
+       // This test loads in an iframe, to ensure that the element instance is
+       // loaded with the correct value of the preference.
+       var iframe = document.createElement("iframe");
+       iframe.src = "test_shadowroot_subframe.html";
+       document.body.appendChild(iframe);
+     });
+diff --git a/accessible/tests/mochitest/treeupdate/test_bug1276857.html b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+--- a/accessible/tests/mochitest/treeupdate/test_bug1276857.html
++++ b/accessible/tests/mochitest/treeupdate/test_bug1276857.html
+@@ -107,17 +107,17 @@
+       gQueue.push(new runTest());
+       gQueue.push(new runShadowTest());
+       gQueue.invoke(); // will call SimpleTest.finish();
+     }
+ 
+     SimpleTest.waitForExplicitFinish();
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, function() {
+       // This test loads in an iframe, to ensure that the element instance is
+       // loaded with the correct value of the preference.
+       let iframe = document.createElement("iframe");
+       iframe.id = "iframe";
+       iframe.src = "test_bug1276857_subframe.html";
+       addA11yLoadEvent(doTest, iframe.contentWindow);
+diff --git a/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
+--- a/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
++++ b/devtools/client/inspector/markup/test/browser_markup_anonymous_03.js
+@@ -5,17 +5,17 @@
+ "use strict";
+ 
+ // Test shadow DOM content in the markupview.
+ // Note that many features are not yet enabled, but basic listing
+ // of elements should be working.
+ const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
+ 
+ add_task(function* () {
+-  Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
++  Services.prefs.setBoolPref("dom.webcomponents.shadowdom.enabled", true);
+ 
+   let {inspector} = yield openInspectorForURL(TEST_URL);
+ 
+   let shadow = yield getNodeFront("#shadow", inspector.markup);
+   let children = yield inspector.walker.children(shadow);
+ 
+   is(shadow.numChildren, 3, "Children of the shadow root are counted");
+   is(children.nodes.length, 3, "Children returned from walker");
+diff --git a/devtools/client/inspector/markup/test/head.js b/devtools/client/inspector/markup/test/head.js
+--- a/devtools/client/inspector/markup/test/head.js
++++ b/devtools/client/inspector/markup/test/head.js
+@@ -30,17 +30,17 @@ registerCleanupFunction(() => {
+ // Services.prefs.setBoolPref("devtools.dump.emit", true);
+ 
+ // Clear preferences that may be set during the course of tests.
+ registerCleanupFunction(() => {
+   Services.prefs.clearUserPref("devtools.dump.emit");
+   Services.prefs.clearUserPref("devtools.inspector.htmlPanelOpen");
+   Services.prefs.clearUserPref("devtools.inspector.sidebarOpen");
+   Services.prefs.clearUserPref("devtools.markup.pagesize");
+-  Services.prefs.clearUserPref("dom.webcomponents.enabled");
++  Services.prefs.clearUserPref("dom.webcomponents.shadowdom.enabled");
+   Services.prefs.clearUserPref("devtools.inspector.showAllAnonymousContent");
+ });
+ 
+ /**
+  * Some tests may need to import one or more of the test helper scripts.
+  * A test helper script is simply a js file that contains common test code that
+  * is either not common-enough to be in head.js, or that is located in a
+  * separate directory.
+diff --git a/devtools/server/tests/mochitest/test_inspector-anonymous.html b/devtools/server/tests/mochitest/test_inspector-anonymous.html
+--- a/devtools/server/tests/mochitest/test_inspector-anonymous.html
++++ b/devtools/server/tests/mochitest/test_inspector-anonymous.html
+@@ -21,17 +21,17 @@ window.onload = function () {
+ 
+   const nodeFilterConstants =
+     require("devtools/shared/dom-node-filter-constants");
+   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ 
+   const isStylo = SpecialPowers.DOMWindowUtils.isStyledByServo;
+ 
+   SpecialPowers.pushPrefEnv({"set": [
+-    ["dom.webcomponents.enabled", true]
++    ["dom.webcomponents.shadowdom.enabled", true]
+   ]});
+   SimpleTest.waitForExplicitFinish();
+ 
+   let gWalker = null;
+   let gInspectee = null;
+ 
+   addTest(function setup() {
+     info("Setting up inspector and walker actors.");
+diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp
+--- a/dom/base/ChildIterator.cpp
++++ b/dom/base/ChildIterator.cpp
+@@ -20,17 +20,17 @@ namespace dom {
+ ExplicitChildIterator::ExplicitChildIterator(const nsIContent* aParent,
+                                              bool aStartAtBeginning)
+   : mParent(aParent),
+     mChild(nullptr),
+     mDefaultChild(nullptr),
+     mIsFirst(aStartAtBeginning),
+     mIndexInInserted(0)
+ {
+-  mParentAsSlot = nsDocument::IsWebComponentsEnabled(mParent) ?
++  mParentAsSlot = nsDocument::IsShadowDOMEnabled(mParent) ?
+     HTMLSlotElement::FromContent(mParent) : nullptr;
+ }
+ 
+ nsIContent*
+ ExplicitChildIterator::GetNextChild()
+ {
+   // If we're already in the inserted-children array, look there first
+   if (mIndexInInserted) {
+diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp
+--- a/dom/base/DocumentOrShadowRoot.cpp
++++ b/dom/base/DocumentOrShadowRoot.cpp
+@@ -117,17 +117,17 @@ DocumentOrShadowRoot::GetRetargetedFocus
+                                            getter_AddRefs(focusedWindow));
+     // be safe and make sure the element is from this document
+     if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
+       if (focusedContent->ChromeOnlyAccess()) {
+         focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
+       }
+ 
+       if (focusedContent) {
+-        if (!nsDocument::IsWebComponentsEnabled(focusedContent)) {
++        if (!nsDocument::IsShadowDOMEnabled(focusedContent)) {
+           return focusedContent->AsElement();
+         }
+ 
+         if (nsIContent* retarget = Retarget(focusedContent)) {
+           return retarget->AsElement();
+         }
+       }
+     }
+diff --git a/dom/base/crashtests/1324463.html b/dom/base/crashtests/1324463.html
+--- a/dom/base/crashtests/1324463.html
++++ b/dom/base/crashtests/1324463.html
+@@ -1,12 +1,12 @@
+ <!DOCTYPE html>
+ <html>
+ <script>
+-// requires: user_pref("dom.webcomponents.enabled", true);
++// requires: user_pref("dom.webcomponents.shadowdom.enabled", true);
+ addEventListener("DOMContentLoaded", function(){
+   let o_0 = document.createElement("span").createShadowRoot();
+   let o_1 = document.createElementNS("http://www.mozilla.org/xbl", "binding");
+   let o_2 = document.createElementNS("http://www.mozilla.org/xbl", "children");
+   let o_3 = document.createTextNode("");
+   o_0.appendChild(o_1);
+   o_1.appendChild(o_2);
+   o_2.appendChild(o_3);
+diff --git a/dom/base/crashtests/1422931.html b/dom/base/crashtests/1422931.html
+--- a/dom/base/crashtests/1422931.html
++++ b/dom/base/crashtests/1422931.html
+@@ -1,6 +1,6 @@
+ <!DOCTYPE html>
+ <html>
+ <body>
+-<!-- Testing slot element with "dom.webcomponents.enabled" set to false -->
++<!-- Testing slot element with "dom.webcomponents.shadowdom.enabled" set to false -->
+ <slot><div></div></slot>
+ </html>
+diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list
+--- a/dom/base/crashtests/crashtests.list
++++ b/dom/base/crashtests/crashtests.list
+@@ -189,26 +189,26 @@ load 852381.html
+ load 863950.html
+ load 864448.html
+ load 886213.html
+ load 898906.html
+ load 930250.html
+ load 942979.html
+ load 973401.html
+ load 978646.html
+-pref(dom.webcomponents.enabled,true) load 1024428-1.html
+-pref(dom.webcomponents.enabled,true) load 1027461-1.html
+-pref(dom.webcomponents.enabled,true) load 1029710.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1024428-1.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1027461-1.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1029710.html
+ load 1154598.xhtml
+ load 1157995.html
+ load 1158412.html
+ load 1181619.html
+ load 1230422.html
+ load 1251361.html
+-pref(dom.webcomponents.enabled,true) load 1281715.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1281715.html
+ load 1304437.html
+ pref(dom.IntersectionObserver.enabled,true) load 1324209.html
+ load 1324500.html
+ pref(dom.IntersectionObserver.enabled,true) load 1326194-1.html
+ pref(dom.IntersectionObserver.enabled,true) load 1326194-2.html
+ pref(dom.IntersectionObserver.enabled,true) load 1332939.html
+ pref(dom.webcomponents.customelements.enabled,true) load 1341693.html
+ load 1352453.html
+@@ -228,15 +228,15 @@ load 1383780.html
+ pref(clipboard.autocopy,true) load 1385272-1.html
+ load 1393806.html
+ load 1396466.html
+ load 1397795.html
+ load 1400701.html
+ load 1403377.html
+ load 1405771.html
+ load 1406109-1.html
+-pref(dom.webcomponents.enabled,true) load 1324463.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1324463.html
+ pref(dom.webcomponents.customelements.enabled,true) load 1413815.html
+ load 1411473.html
+-pref(dom.webcomponents.enabled,false) load 1422931.html
+-pref(dom.webcomponents.enabled,true) load 1419799.html
+-pref(dom.webcomponents.enabled,true) load 1428053.html
++pref(dom.webcomponents.shadowdom.enabled,false) load 1422931.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1419799.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1428053.html
+ pref(layout.css.resizeobserver.enabled,true) load 1555786.html
+diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp
+--- a/dom/base/nsContentUtils.cpp
++++ b/dom/base/nsContentUtils.cpp
+@@ -290,17 +290,17 @@ bool nsContentUtils::sIsUnprefixedFullsc
+ bool nsContentUtils::sTrustedFullScreenOnly = true;
+ bool nsContentUtils::sIsCutCopyAllowed = true;
+ bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
+ bool nsContentUtils::sIsPerformanceTimingEnabled = false;
+ bool nsContentUtils::sIsResourceTimingEnabled = false;
+ bool nsContentUtils::sIsPerformanceNavigationTimingEnabled = false;
+ bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
+ bool nsContentUtils::sIsFormAutofillAutocompleteEnabled = false;
+-bool nsContentUtils::sIsWebComponentsEnabled = false;
++bool nsContentUtils::sIsShadowDOMEnabled = false;
+ bool nsContentUtils::sIsCustomElementsEnabled = false;
+ bool nsContentUtils::sDevToolsEnabled = false;
+ bool nsContentUtils::sSendPerformanceTimingNotifications = false;
+ bool nsContentUtils::sUseActivityCursor = false;
+ bool nsContentUtils::sAnimationsAPICoreEnabled = false;
+ bool nsContentUtils::sAnimationsAPIElementAnimateEnabled = false;
+ bool nsContentUtils::sAnimationsAPIPendingMemberEnabled = false;
+ bool nsContentUtils::sGetBoxQuadsEnabled = false;
+@@ -732,18 +732,18 @@ nsContentUtils::Init()
+                                "dom.performance.enable_user_timing_logging", false);
+ 
+   Preferences::AddBoolVarCache(&sIsFrameTimingPrefEnabled,
+                                "dom.enable_frame_timing", false);
+ 
+   Preferences::AddBoolVarCache(&sIsFormAutofillAutocompleteEnabled,
+                                "dom.forms.autocomplete.formautofill", false);
+ 
+-  Preferences::AddBoolVarCache(&sIsWebComponentsEnabled,
+-                               "dom.webcomponents.enabled", false);
++  Preferences::AddBoolVarCache(&sIsShadowDOMEnabled,
++                               "dom.webcomponents.shadowdom.enabled", false);
+ 
+   Preferences::AddBoolVarCache(&sIsCustomElementsEnabled,
+                                "dom.webcomponents.customelements.enabled", false);
+ 
+   Preferences::AddBoolVarCache(&sDevToolsEnabled,
+                                "devtools.enabled");
+ 
+   Preferences::AddIntVarCache(&sPrivacyMaxInnerWidth,
+diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h
+--- a/dom/base/nsContentUtils.h
++++ b/dom/base/nsContentUtils.h
+@@ -3153,17 +3153,17 @@ public:
+                                         uint64_t* aRequestContextID);
+ 
+   static nsresult
+   CreateJSValueFromSequenceOfObject(JSContext* aCx,
+                                     const mozilla::dom::Sequence<JSObject*>& aTransfer,
+                                     JS::MutableHandle<JS::Value> aValue);
+ 
+   static bool
+-  IsWebComponentsEnabled() { return sIsWebComponentsEnabled; }
++  IsShadowDOMEnabled() { return sIsShadowDOMEnabled; }
+ 
+   /**
+    * Returns true if reserved key events should be prevented from being sent
+    * to their target. Instead, the key event should be handled by chrome only.
+    */
+   static bool ShouldBlockReservedKeys(mozilla::WidgetKeyboardEvent* aKeyEvent);
+ 
+   /**
+@@ -3443,17 +3443,17 @@ private:
+   static bool sIsCutCopyAllowed;
+   static uint32_t sHandlingInputTimeout;
+   static bool sIsPerformanceTimingEnabled;
+   static bool sIsResourceTimingEnabled;
+   static bool sIsPerformanceNavigationTimingEnabled;
+   static bool sIsUserTimingLoggingEnabled;
+   static bool sIsFrameTimingPrefEnabled;
+   static bool sIsFormAutofillAutocompleteEnabled;
+-  static bool sIsWebComponentsEnabled;
++  static bool sIsShadowDOMEnabled;
+   static bool sIsCustomElementsEnabled;
+   static bool sDevToolsEnabled;
+   static bool sSendPerformanceTimingNotifications;
+   static bool sUseActivityCursor;
+   static bool sAnimationsAPICoreEnabled;
+   static bool sAnimationsAPIElementAnimateEnabled;
+   static bool sAnimationsAPIPendingMemberEnabled;
+   static bool sGetBoxQuadsEnabled;
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -1499,17 +1499,17 @@ nsIDocument::nsIDocument()
+ {
+   SetIsInDocument();
+   for (auto& cnt : mIncCounters) {
+     cnt = 0;
+   }
+ 
+   // Set this when document is created and value stays the same for the lifetime
+   // of the document.
+-  mIsWebComponentsEnabled = nsContentUtils::IsWebComponentsEnabled();
++  mIsShadowDOMEnabled = nsContentUtils::IsShadowDOMEnabled();
+ }
+ 
+ nsDocument::nsDocument(const char* aContentType)
+   : nsIDocument()
+   , mSubDocuments(nullptr)
+   , mHeaderData(nullptr)
+   , mIsGoingAway(false)
+   , mInDestructor(false)
+@@ -2587,37 +2587,37 @@ nsDocument::IsSynthesized() {
+   if (internalChan) {
+     DebugOnly<nsresult> rv = internalChan->GetResponseSynthesized(&synthesized);
+     MOZ_ASSERT(NS_SUCCEEDED(rv), "GetResponseSynthesized shouldn't fail.");
+   }
+   return synthesized;
+ }
+ 
+ bool
+-nsDocument::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
++nsDocument::IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject)
+ {
+   JS::Rooted<JSObject*> obj(aCx, aObject);
+ 
+   JSAutoRealm ar(aCx, obj);
+   JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, obj));
+   nsCOMPtr<nsPIDOMWindowInner> window =
+     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(global));
+ 
+   nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
+   if (!doc) {
+     return false;
+   }
+ 
+-  return doc->IsWebComponentsEnabled();
++  return doc->IsShadowDOMEnabled();
+ }
+ 
+ bool
+-nsDocument::IsWebComponentsEnabled(const nsINode* aNode)
+-{
+-  return aNode->OwnerDoc()->IsWebComponentsEnabled();
++nsDocument::IsShadowDOMEnabled(const nsINode* aNode)
++{
++  return aNode->OwnerDoc()->IsShadowDOMEnabled();
+ }
+ 
+ nsresult
+ nsDocument::StartDocumentLoad(const char* aCommand, nsIChannel* aChannel,
+                               nsILoadGroup* aLoadGroup,
+                               nsISupports* aContainer,
+                               nsIStreamListener **aDocListener,
+                               bool aReset, nsIContentSink* aSink)
+diff --git a/dom/base/nsDocument.h b/dom/base/nsDocument.h
+--- a/dom/base/nsDocument.h
++++ b/dom/base/nsDocument.h
+@@ -625,21 +625,20 @@ public:
+   virtual void UnscheduleSVGForPresAttrEvaluation(nsSVGElement* aSVG) override;
+   virtual void ResolveScheduledSVGPresAttrs() override;
+   bool IsSynthesized();
+ 
+   virtual void AddResizeObserver(mozilla::dom::ResizeObserver&) override;
+   virtual void RemoveResizeObserver(mozilla::dom::ResizeObserver&) override;
+   virtual void ScheduleResizeObserversNotification() const override;
+ 
+-  // Check whether web components are enabled for the global of aObject.
+-  static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
+-  // Check whether web components are enabled for the document this node belongs
+-  // to.
+-  static bool IsWebComponentsEnabled(const nsINode* aNode);
++  // Check whether shadow DOM is enabled for the global of aObject.
++  static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject);
++  // Check whether shadow DOM is enabled for the document this node belongs to.
++  static bool IsShadowDOMEnabled(const nsINode* aNode);
+ private:
+   void AddOnDemandBuiltInUASheet(mozilla::StyleSheet* aSheet);
+   void SendToConsole(nsCOMArray<nsISecurityConsoleMessage>& aMessages);
+ 
+ public:
+   // nsIDOMNode
+   NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE
+ 
+diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
+--- a/dom/base/nsIDocument.h
++++ b/dom/base/nsIDocument.h
+@@ -3197,19 +3197,19 @@ public:
+ 
+   // ResizeObserver usage.
+   virtual void AddResizeObserver(mozilla::dom::ResizeObserver&) = 0;
+   virtual void RemoveResizeObserver(mozilla::dom::ResizeObserver&) = 0;
+   virtual void ScheduleResizeObserversNotification() const = 0;
+ 
+   bool ModuleScriptsEnabled();
+ 
+-  bool IsWebComponentsEnabled() const
++  bool IsShadowDOMEnabled() const
+   {
+-    return mIsWebComponentsEnabled;
++    return mIsShadowDOMEnabled;
+   }
+ 
+ protected:
+   bool GetUseCounter(mozilla::UseCounter aUseCounter)
+   {
+     return mUseCounters[aUseCounter];
+   }
+ 
+@@ -3558,18 +3558,19 @@ protected:
+ 
+   // True if the document is allowed to use PaymentRequest.
+   bool mAllowPaymentRequest : 1;
+ 
+   // True if unsafe HTML fragments should be allowed in chrome-privileged
+   // documents.
+   bool mAllowUnsafeHTML : 1;
+ 
+-  // True if dom.webcomponents.enabled pref is set when document is created.
+-  bool mIsWebComponentsEnabled : 1;
++  // True if dom.webcomponents.shadowdom.enabled pref is set when document is
++  // created.
++  bool mIsShadowDOMEnabled : 1;
+ 
+   // True if this document is for an SVG-in-OpenType font.
+   bool mIsSVGGlyphsDocument : 1;
+ 
+   // Compatibility mode
+   nsCompatibility mCompatMode;
+ 
+   // Our readyState
+diff --git a/dom/base/nsTextNode.cpp b/dom/base/nsTextNode.cpp
+--- a/dom/base/nsTextNode.cpp
++++ b/dom/base/nsTextNode.cpp
+@@ -154,19 +154,19 @@ nsTextNode::BindToTree(nsIDocument* aDoc
+ void nsTextNode::UnbindFromTree(bool aDeep, bool aNullParent)
+ {
+   ResetDirectionSetByTextNode(this);
+ 
+   nsGenericDOMDataNode::UnbindFromTree(aDeep, aNullParent);
+ }
+ 
+ bool
+-nsTextNode::IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject)
++nsTextNode::IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject)
+ {
+-  return nsDocument::IsWebComponentsEnabled(aCx, aObject);
++  return nsDocument::IsShadowDOMEnabled(aCx, aObject);
+ }
+ 
+ #ifdef DEBUG
+ void
+ nsTextNode::List(FILE* out, int32_t aIndent) const
+ {
+   int32_t index;
+   for (index = aIndent; --index >= 0; ) fputs("  ", out);
+diff --git a/dom/base/nsTextNode.h b/dom/base/nsTextNode.h
+--- a/dom/base/nsTextNode.h
++++ b/dom/base/nsTextNode.h
+@@ -72,17 +72,17 @@ public:
+ 
+   nsresult AppendTextForNormalize(const char16_t* aBuffer, uint32_t aLength,
+                                   bool aNotify, nsIContent* aNextSibling);
+ 
+   virtual nsIDOMNode* AsDOMNode() override { return this; }
+ 
+   // Need to have a copy here because including nsDocument.h in this file will
+   // fail to build on Windows.
+-  static bool IsWebComponentsEnabled(JSContext* aCx, JSObject* aObject);
++  static bool IsShadowDOMEnabled(JSContext* aCx, JSObject* aObject);
+ 
+ #ifdef DEBUG
+   virtual void List(FILE* out, int32_t aIndent) const override;
+   virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const override;
+ #endif
+ 
+ protected:
+   virtual ~nsTextNode();
+diff --git a/dom/base/test/test_bug1025933.html b/dom/base/test/test_bug1025933.html
+--- a/dom/base/test/test_bug1025933.html
++++ b/dom/base/test/test_bug1025933.html
+@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ 
+   /** Test for Bug 1025933 **/
+ 
+   SimpleTest.waitForExplicitFinish();
+ 
+   function test() {
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, function() {
+       var iframe = document.createElement('iframe');
+       iframe.srcdoc = '<div id="content"> <div id="host"></div </div>';
+ 
+       iframe.onload = function() {
+         var s = iframe.contentDocument.getElementById("host").attachShadow({mode: 'open'});
+         s.innerHTML = '<div style="width:100px;height:100px;background:red"></div>';
+diff --git a/dom/base/test/test_bug1037687.html b/dom/base/test/test_bug1037687.html
+--- a/dom/base/test/test_bug1037687.html
++++ b/dom/base/test/test_bug1037687.html
+@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ </div>
+ <pre id="test">
+ </pre>
+ <script type="application/javascript">
+   /** Test for Bug 1037687 **/
+   SimpleTest.waitForExplicitFinish();
+   SpecialPowers.pushPrefEnv({
+     set: [
+-      ["dom.webcomponents.enabled", true]
++      ["dom.webcomponents.shadowdom.enabled", true]
+     ]
+   }, function() {
+     // This test loads in an iframe, to ensure that the element instance is
+     // loaded with the correct value of the preference.
+     let iframe = document.createElement("iframe");
+     iframe.src = "test_bug1037687_subframe.html";
+     document.body.appendChild(iframe);
+   });
+diff --git a/dom/events/test/test_bug1079236.html b/dom/events/test/test_bug1079236.html
+--- a/dom/events/test/test_bug1079236.html
++++ b/dom/events/test/test_bug1079236.html
+@@ -46,17 +46,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+     synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"}, iframe.contentWindow);
+     iframe.contentDocument.body.onmousemove = null;
+   }
+ 
+   SimpleTest.waitForExplicitFinish();
+   SimpleTest.waitForFocus(() => {
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, runTests);
+   });
+ 
+   </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079236">Mozilla Bug 1079236</a>
+diff --git a/dom/events/test/test_bug1145910.html b/dom/events/test/test_bug1145910.html
+--- a/dom/events/test/test_bug1145910.html
++++ b/dom/events/test/test_bug1145910.html
+@@ -43,16 +43,16 @@ function runTests() {
+ 
+   SimpleTest.finish();
+ };
+ 
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(() => {
+   SpecialPowers.pushPrefEnv({
+     set: [
+-      ["dom.webcomponents.enabled", true]
++      ["dom.webcomponents.shadowdom.enabled", true]
+     ]
+   }, runTests);
+ });
+ 
+ </script>
+ </body>
+ </html>
+diff --git a/dom/events/test/test_bug1150308.html b/dom/events/test/test_bug1150308.html
+--- a/dom/events/test/test_bug1150308.html
++++ b/dom/events/test/test_bug1150308.html
+@@ -39,15 +39,15 @@ function runTests() {
+ 
+   SimpleTest.finish();
+ };
+ 
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(() => {
+   SpecialPowers.pushPrefEnv({
+     set: [
+-      ["dom.webcomponents.enabled", true]
++      ["dom.webcomponents.shadowdom.enabled", true]
+     ]
+   }, runTests);
+ });
+ </script>
+ </body>
+ </html>
+diff --git a/dom/events/test/test_bug1264380.html b/dom/events/test/test_bug1264380.html
+--- a/dom/events/test/test_bug1264380.html
++++ b/dom/events/test/test_bug1264380.html
+@@ -47,17 +47,17 @@ function runTests()
+   SimpleTest.finish();
+ }
+ 
+ 
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(() => {
+   SpecialPowers.pushPrefEnv({
+     set: [
+-      ["dom.webcomponents.enabled", true]
++      ["dom.webcomponents.shadowdom.enabled", true]
+     ]
+   }, runTests);
+ });
+ 
+ </script>
+ 
+ <body>
+ </body>
+diff --git a/dom/html/HTMLSlotElement.cpp b/dom/html/HTMLSlotElement.cpp
+--- a/dom/html/HTMLSlotElement.cpp
++++ b/dom/html/HTMLSlotElement.cpp
+@@ -12,17 +12,17 @@
+ #include "nsGkAtoms.h"
+ #include "nsDocument.h"
+ 
+ nsGenericHTMLElement*
+ NS_NewHTMLSlotElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+                       mozilla::dom::FromParser aFromParser)
+ {
+   RefPtr<mozilla::dom::NodeInfo> nodeInfo(aNodeInfo);
+-  if (nsDocument::IsWebComponentsEnabled(nodeInfo->GetDocument())) {
++  if (nsDocument::IsShadowDOMEnabled(nodeInfo->GetDocument())) {
+     already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+     return new mozilla::dom::HTMLSlotElement(nodeInfoArg);
+   }
+ 
+   already_AddRefed<mozilla::dom::NodeInfo> nodeInfoArg(nodeInfo.forget());
+   return new mozilla::dom::HTMLUnknownElement(nodeInfoArg);
+ }
+ 
+diff --git a/dom/ipc/ContentPrefs.cpp b/dom/ipc/ContentPrefs.cpp
+--- a/dom/ipc/ContentPrefs.cpp
++++ b/dom/ipc/ContentPrefs.cpp
+@@ -77,17 +77,17 @@ const char* mozilla::dom::ContentPrefs::
+   "dom.storage.testing",
+   "dom.url.encode_decode_hash",
+   "dom.url.getters_decode_hash",
+   "dom.use_watchdog",
+   "dom.vibrator.enabled",
+   "dom.vibrator.max_vibrate_list_len",
+   "dom.vibrator.max_vibrate_ms",
+   "dom.webcomponents.customelements.enabled",
+-  "dom.webcomponents.enabled",
++  "dom.webcomponents.shadowdom.enabled",
+   "focusmanager.testmode",
+   "font.size.inflation.disabledInMasterProcess",
+   "font.size.inflation.emPerLine",
+   "font.size.inflation.forceEnabled",
+   "font.size.inflation.lineThreshold",
+   "font.size.inflation.mappingIntercept",
+   "font.size.inflation.maxRatio",
+   "font.size.inflation.minTwips",
+diff --git a/dom/tests/mochitest/webcomponents/head.js b/dom/tests/mochitest/webcomponents/head.js
+--- a/dom/tests/mochitest/webcomponents/head.js
++++ b/dom/tests/mochitest/webcomponents/head.js
+@@ -1,26 +1,26 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ /**
+- * Set dom.webcomponents.enabled pref to true and loads an iframe, to ensure
+- * that the Element instance is loaded with the correct value of the
++ * Set dom.webcomponents.shadowdom.enabled pref to true and loads an iframe, to
++ * ensure that the Element instance is loaded with the correct value of the
+  * preference.
+  *
+  * @return {Promise} promise that resolves when iframe is loaded.
+  */
+-function setWebComponentsPrefAndCreateIframe(aSrcDoc) {
++function setShadowDOMPrefAndCreateIframe(aSrcDoc) {
+   return new Promise(function (aResolve, aReject) {
+     SpecialPowers.pushPrefEnv({
+       set: [
+-        ["dom.webcomponents.enabled", true]
++        ["dom.webcomponents.shadowdom.enabled", true]
+       ]
+     }, () => {
+       let iframe = document.createElement("iframe");
+       iframe.onload = function () { aResolve(iframe.contentDocument); }
+       iframe.onerror = function () { aReject('Failed to load iframe'); }
+       if (aSrcDoc) {
+         iframe.srcdoc = aSrcDoc;
+       }
+diff --git a/dom/tests/mochitest/webcomponents/test_bug1269155.html b/dom/tests/mochitest/webcomponents/test_bug1269155.html
+--- a/dom/tests/mochitest/webcomponents/test_bug1269155.html
++++ b/dom/tests/mochitest/webcomponents/test_bug1269155.html
+@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ 
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ /** Test for Bug 1269155 **/
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="content" style="display: none"> </div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     var host = aDocument.querySelector('#content');
+     var root = host.attachShadow({mode: "open"});
+ 
+     var header1 = aDocument.createElement('h1');
+     header1.textContent = 'Shadow Header1';
+ 
+     var paragraph1 = aDocument.createElement('p');
+diff --git a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+--- a/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
++++ b/dom/tests/mochitest/webcomponents/test_custom_element_in_shadow.html
+@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1087460">Bug 1087460</a>
+ 
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="container"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+ 
+     // Test callback for custom element when used after registration.
+ 
+     var iframeWin = aDocument.defaultView;
+     var connectedCallbackCount = 0;
+     var disconnectedCallbackCount = 0;
+     var attributeChangedCallbackCount = 0;
+diff --git a/dom/tests/mochitest/webcomponents/test_detached_style.html b/dom/tests/mochitest/webcomponents/test_detached_style.html
+--- a/dom/tests/mochitest/webcomponents/test_detached_style.html
++++ b/dom/tests/mochitest/webcomponents/test_detached_style.html
+@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1062578">Bug 1062578</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="grabme"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     var host = aDocument.createElement("div");
+     var shadow = host.attachShadow({mode: "open"});
+     shadow.innerHTML = '<style> #inner { height: 200px; } </style><div id="inner">Hello</div>';
+ 
+     var iframeWin = aDocument.defaultView;
+     iframeWin.grabme.appendChild(host);
+ 
+diff --git a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+--- a/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
++++ b/dom/tests/mochitest/webcomponents/test_document_adoptnode.html
+@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ SimpleTest.waitForExplicitFinish();
+-setWebComponentsPrefAndCreateIframe()
++setShadowDOMPrefAndCreateIframe()
+   .then((aDocument) => {
+     var thrownException = false;
+     var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
+ 
+     try {
+       aDocument.adoptNode(shadowRoot);
+     } catch(err) {
+       thrownException = err;
+diff --git a/dom/tests/mochitest/webcomponents/test_document_importnode.html b/dom/tests/mochitest/webcomponents/test_document_importnode.html
+--- a/dom/tests/mochitest/webcomponents/test_document_importnode.html
++++ b/dom/tests/mochitest/webcomponents/test_document_importnode.html
+@@ -15,17 +15,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ SimpleTest.waitForExplicitFinish();
+-setWebComponentsPrefAndCreateIframe()
++setShadowDOMPrefAndCreateIframe()
+   .then((aDocument) => {
+     var thrownException = false;
+     var shadowRoot = aDocument.createElement('div').attachShadow({mode: "open"});
+ 
+     try {
+       aDocument.importNode(shadowRoot);
+     } catch(err) {
+       thrownException = err;
+diff --git a/dom/tests/mochitest/webcomponents/test_event_retarget.html b/dom/tests/mochitest/webcomponents/test_event_retarget.html
+--- a/dom/tests/mochitest/webcomponents/test_event_retarget.html
++++ b/dom/tests/mochitest/webcomponents/test_event_retarget.html
+@@ -9,17 +9,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+-setWebComponentsPrefAndCreateIframe()
++setShadowDOMPrefAndCreateIframe()
+   .then((aDocument) => {
+     /*
+      * Creates an event listener with an expected event target.
+      */
+     function createEventListener(expectedTarget, msg) {
+       return function(e) {
+         is(e.target, expectedTarget, msg);
+       };
+diff --git a/dom/tests/mochitest/webcomponents/test_event_stopping.html b/dom/tests/mochitest/webcomponents/test_event_stopping.html
+--- a/dom/tests/mochitest/webcomponents/test_event_stopping.html
++++ b/dom/tests/mochitest/webcomponents/test_event_stopping.html
+@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=887541">Bug 887541</a>
+ <script>
+ 
+ var els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
+             .getService(SpecialPowers.Ci.nsIEventListenerService);
+ 
+ SimpleTest.waitForExplicitFinish();
+-setWebComponentsPrefAndCreateIframe()
++setShadowDOMPrefAndCreateIframe()
+   .then((aDocument) => {
+     function eventListener(e) {
+       eventChain.push(this);
+     }
+ 
+     function isEventChain(actual, expected, msg) {
+       is(actual.length, expected.length, msg);
+       for (var i = 0; i < expected.length; i++) {
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot.html b/dom/tests/mochitest/webcomponents/test_shadowroot.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot.html
+@@ -12,17 +12,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="movedtoshadow" class="testclass"></div>' +
+               '<svg id="svgmovedtoshadow"></svg>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     // Create ShadowRoot.
+     var element = aDocument.createElement("div");
+     ok(!element.shadowRoot, "div element should not have a shadow root.");
+     var shadow = element.attachShadow({mode: "open"});
+     is(element.shadowRoot, shadow, "shadowRoot property should return the same shadow root that was just created.");
+ 
+     // Move an element from the document to the ShadowRoot.
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html b/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+ /** Test for Bug 1429982 **/
+ SimpleTest.waitForExplicitFinish();
+-setWebComponentsPrefAndCreateIframe()
++setShadowDOMPrefAndCreateIframe()
+   .then((aDocument) => {
+     var element = aDocument.createElement("div");
+     var shadowRoot = element.attachShadow({mode: "open"});
+     var thrownException = false;
+ 
+     try {
+       shadowRoot.cloneNode();
+     } catch(err) {
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="grabme"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     function runChecks() {
+       isnot(aDocument.defaultView.getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
+       is(aDocument.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
+       is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
+       // Remove link to make sure we don't get assertions.
+       shadow.removeChild(shadowStyle);
+       SimpleTest.finish();
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style.html
+@@ -16,17 +16,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ if (!SpecialPowers.DOMWindowUtils.isStyledByServo) {
+   SimpleTest.expectAssertions(3, 3); // GeckoRestyleManager stuff.
+ }
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div class="tall" id="bodydiv"></div>' +
+               '<div id="container"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     var iframeWin = aDocument.defaultView;
+ 
+     // Create ShadowRoot.
+     var container = aDocument.getElementById("container");
+     var elem = aDocument.createElement("div");
+     container.appendChild(elem); // Put ShadowRoot host in document.
+     var root = elem.attachShadow({mode: "open"});
+diff --git a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+--- a/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_style_order.html
+@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="container"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     var iframeWin = aDocument.defaultView;
+ 
+     // Create ShadowRoot.
+     var container = aDocument.getElementById("container");
+     var elem = aDocument.createElement("div");
+     container.appendChild(elem); // Put ShadowRoot host in document.
+     var root = elem.attachShadow({mode: "open"});
+diff --git a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+--- a/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
++++ b/dom/tests/mochitest/webcomponents/test_style_fallback_content.html
+@@ -11,17 +11,17 @@ https://bugzilla.mozilla.org/show_bug.cg
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
+ <script>
+ 
+ SimpleTest.waitForExplicitFinish();
+ 
+ var content = '<div id="grabme"></div>';
+-setWebComponentsPrefAndCreateIframe(content)
++setShadowDOMPrefAndCreateIframe(content)
+   .then((aDocument) => {
+     var iframeWin = aDocument.defaultView;
+ 
+     var host = aDocument.getElementById("grabme");
+     var shadow = host.attachShadow({mode: "open"});
+     shadow.innerHTML = '<style id="innerstyle"></style><span id="container"><slot><span id="innerspan">Hello</span></slot></span>';
+     var innerStyle = shadow.getElementById("innerstyle");
+ 
+diff --git a/dom/webidl/Element.webidl b/dom/webidl/Element.webidl
+--- a/dom/webidl/Element.webidl
++++ b/dom/webidl/Element.webidl
+@@ -252,27 +252,27 @@ partial interface Element {
+ // https://dom.spec.whatwg.org/#dictdef-shadowrootinit
+ dictionary ShadowRootInit {
+   required ShadowRootMode mode;
+ };
+ 
+ // https://dom.spec.whatwg.org/#element
+ partial interface Element {
+   // Shadow DOM v1
+-  [Throws, Func="nsDocument::IsWebComponentsEnabled"]
++  [Throws, Func="nsDocument::IsShadowDOMEnabled"]
+   ShadowRoot attachShadow(ShadowRootInit shadowRootInitDict);
+-  [BinaryName="shadowRootByMode", Func="nsDocument::IsWebComponentsEnabled"]
++  [BinaryName="shadowRootByMode", Func="nsDocument::IsShadowDOMEnabled"]
+   readonly attribute ShadowRoot? shadowRoot;
+-  [BinaryName="assignedSlotByMode", Func="nsDocument::IsWebComponentsEnabled"]
++  [BinaryName="assignedSlotByMode", Func="nsDocument::IsShadowDOMEnabled"]
+   readonly attribute HTMLSlotElement? assignedSlot;
+-  [CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsWebComponentsEnabled"]
++  [CEReactions, Unscopable, SetterThrows, Func="nsDocument::IsShadowDOMEnabled"]
+            attribute DOMString slot;
+ 
+   // [deprecated] Shadow DOM v0
+-  [Throws, Func="nsDocument::IsWebComponentsEnabled"]
++  [Throws, Func="nsDocument::IsShadowDOMEnabled"]
+   ShadowRoot createShadowRoot();
+ };
+ 
+ Element implements ChildNode;
+ Element implements NonDocumentTypeChildNode;
+ Element implements ParentNode;
+ Element implements Animatable;
+ Element implements GeometryUtils;
+diff --git a/dom/webidl/HTMLSlotElement.webidl b/dom/webidl/HTMLSlotElement.webidl
+--- a/dom/webidl/HTMLSlotElement.webidl
++++ b/dom/webidl/HTMLSlotElement.webidl
+@@ -6,17 +6,17 @@
+  * The origin of this IDL file is
+  * https://html.spec.whatwg.org/multipage/forms.html#the-dialog-element
+  *
+  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
+  * Opera Software ASA. You are granted a license to use, reproduce
+  * and create derivative works of this document.
+  */
+ 
+-[Func="nsDocument::IsWebComponentsEnabled", Exposed=Window, HTMLConstructor]
++[Func="nsDocument::IsShadowDOMEnabled", Exposed=Window, HTMLConstructor]
+ interface HTMLSlotElement : HTMLElement {
+   [CEReactions, SetterThrows] attribute DOMString name;
+   sequence<Node> assignedNodes(optional AssignedNodesOptions options);
+ };
+ 
+ dictionary AssignedNodesOptions {
+   boolean flatten = false;
+ };
+diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl
+--- a/dom/webidl/ShadowRoot.webidl
++++ b/dom/webidl/ShadowRoot.webidl
+@@ -12,17 +12,17 @@
+ 
+ // https://dom.spec.whatwg.org/#enumdef-shadowrootmode
+ enum ShadowRootMode {
+   "open",
+   "closed"
+ };
+ 
+ // https://dom.spec.whatwg.org/#shadowroot
+-[Func="nsDocument::IsWebComponentsEnabled"]
++[Func="nsDocument::IsShadowDOMEnabled"]
+ interface ShadowRoot : DocumentFragment
+ {
+   readonly attribute Element? activeElement;
+ 
+   // Shadow DOM v1
+   readonly attribute ShadowRootMode mode;
+   readonly attribute Element host;
+ 
+diff --git a/dom/webidl/Text.webidl b/dom/webidl/Text.webidl
+--- a/dom/webidl/Text.webidl
++++ b/dom/webidl/Text.webidl
+@@ -14,13 +14,13 @@
+ interface Text : CharacterData {
+   [Throws]
+   Text splitText(unsigned long offset);
+   [Throws]
+   readonly attribute DOMString wholeText;
+ };
+ 
+ partial interface Text {
+-  [BinaryName="assignedSlotByMode", Func="nsTextNode::IsWebComponentsEnabled"]
++  [BinaryName="assignedSlotByMode", Func="nsTextNode::IsShadowDOMEnabled"]
+   readonly attribute HTMLSlotElement? assignedSlot;
+ };
+ 
+ Text implements GeometryUtils;
+diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list
+--- a/layout/base/crashtests/crashtests.list
++++ b/layout/base/crashtests/crashtests.list
+@@ -470,17 +470,17 @@ load 1127198-1.html
+ load 1140198.html
+ load 1143535.html
+ load 1153716.html
+ load 1156588.html
+ load 1162813.xul
+ load 1163583.html
+ load 1234622-1.html
+ load 1235467-1.html
+-pref(dom.webcomponents.enabled,true) load 1261351.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1261351.html
+ load 1270797-1.html
+ load 1278455-1.html
+ load 1286889.html
+ load 1288608.html
+ load 1288946-1.html
+ load 1288946-2a.html
+ load 1288946-2b.html
+ load 1297835.html
+@@ -515,15 +515,15 @@ load 1406562.html
+ load 1409088.html
+ load 1409147.html
+ load 1411138.html
+ load 1419762.html
+ load 1420533.html
+ load 1425959.html
+ load 1425893.html
+ load 1428353.html
+-pref(dom.webcomponents.enabled,true) load 1429088.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1429088.html
+ load 1429961.html
+ load 1435015.html
+ load 1429962.html
+ pref(dom.webcomponents.enabled,true) load 1439016.html
+ pref(layout.css.resizeobserver.enabled,true) load 1548057.html
+ load 1599518.html
+diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list
+--- a/layout/generic/crashtests/crashtests.list
++++ b/layout/generic/crashtests/crashtests.list
+@@ -579,17 +579,17 @@ asserts(11) asserts-if(stylo&&Android,27
+ pref(font.size.inflation.minTwips,200) load 1032450.html
+ load 1032613-1.svg
+ load 1032613-2.html
+ load 1037903.html
+ load 1039454-1.html
+ load 1042489.html
+ load 1054010-1.html
+ load 1058954-1.html
+-asserts-if(!stylo,0-2) skip-if(stylo) pref(dom.webcomponents.enabled,true) pref(dom.webcomponents.customelements.enabled,true) load 1059138-1.html # bug 1389936 for non-stylo, bug 1409136 for stylo.
++asserts-if(!stylo,0-2) skip-if(stylo) pref(dom.webcomponents.shadowdom.enabled,true) pref(dom.webcomponents.customelements.enabled,true) load 1059138-1.html # bug 1389936 for non-stylo, bug 1409136 for stylo.
+ load 1134531.html
+ load 1134667.html
+ load 1137723-1.html
+ load 1137723-2.html
+ load 1140268-1.html
+ load 1145768.html
+ load 1145931.html
+ load 1146103.html
+diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list
+--- a/layout/reftests/bugs/reftest.list
++++ b/layout/reftests/bugs/reftest.list
+@@ -1866,17 +1866,17 @@ pref(layout.css.moz-document.content.ena
+ == 1053035-1-grid.html 1053035-1-ref.html
+ == 1059167-1.html 1059167-1-ref.html
+ == 1059498-1.html 1059498-1-ref.html
+ == 1059498-2.html 1059498-1-ref.html
+ == 1059498-3.html 1059498-1-ref.html
+ == 1062108-1.html 1062108-1-ref.html
+ == 1062792-1.html 1062792-1-ref.html
+ == 1062963-floatmanager-reflow.html 1062963-floatmanager-reflow-ref.html
+-test-pref(dom.webcomponents.enabled,true) == 1066554-1.html 1066554-1-ref.html
++test-pref(dom.webcomponents.shadowdom.enabled,true) == 1066554-1.html 1066554-1-ref.html
+ == 1069716-1.html 1069716-1-ref.html
+ == 1078262-1.html about:blank
+ test-pref(layout.testing.overlay-scrollbars.always-visible,false) == 1081072-1.html 1081072-1-ref.html
+ fails-if(webrender) == 1081185-1.html 1081185-1-ref.html
+ == 1097437-1.html 1097437-1-ref.html
+ == 1103258-1.html 1103258-1-ref.html # assertion crash test with layers culling test
+ == 1105137-1.html 1105137-1-ref.html
+ fuzzy-if(d2d,36,304) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&d2d,139,701) == 1116480-1-fakeitalic-overflow.html 1116480-1-fakeitalic-overflow-ref.html
+diff --git a/layout/reftests/css-display/reftest.list b/layout/reftests/css-display/reftest.list
+--- a/layout/reftests/css-display/reftest.list
++++ b/layout/reftests/css-display/reftest.list
+@@ -12,17 +12,17 @@ fails-if(styloVsGecko||stylo) pref(layou
+ fuzzy-if(winWidget,12,100) skip-if(styloVsGecko||stylo) pref(layout.css.scoped-style.enabled,true) == display-contents-style-inheritance-1-dom-mutations.html display-contents-style-inheritance-1-ref.html
+ == display-contents-tables.xhtml display-contents-tables-ref.xhtml
+ == display-contents-tables-2.xhtml display-contents-tables-ref.xhtml
+ == display-contents-tables-3.xhtml display-contents-tables-3-ref.xhtml
+ == display-contents-visibility-hidden.html display-contents-visibility-hidden-ref.html
+ == display-contents-visibility-hidden-2.html display-contents-visibility-hidden-ref.html
+ == display-contents-495385-2d.html display-contents-495385-2d-ref.html
+ fuzzy-if(Android,7,3935) == display-contents-xbl.xhtml display-contents-xbl-ref.html
+-#bug 1421540 fuzzy-if(Android,7,1186) pref(dom.webcomponents.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
++#bug 1421540 fuzzy-if(Android,7,1186) pref(dom.webcomponents.shadowdom.enabled,true) == display-contents-shadow-dom-1.html display-contents-shadow-dom-1-ref.html
+ == display-contents-xbl-2.xul display-contents-xbl-2-ref.xul
+ == display-contents-xbl-3.xul display-contents-xbl-3-ref.xul
+ skip == display-contents-xbl-4.xul display-contents-xbl-4-ref.xul # fails (not just asserts) due to bug 1089223
+ asserts(0-1) fuzzy-if(Android,8,3216) == display-contents-fieldset.html display-contents-fieldset-ref.html # bug 1089223
+ == display-contents-xbl-5.xul display-contents-xbl-3-ref.xul
+ fails-if(!stylo) == display-contents-xbl-6.xhtml display-contents-xbl-6-ref.html # bug 1345809
+ == display-contents-xbl-7.xhtml display-contents-xbl-7-ref.html
+ == display-contents-list-item-child.html display-contents-list-item-child-ref.html
+diff --git a/layout/reftests/forms/legend/reftest.list b/layout/reftests/forms/legend/reftest.list
+--- a/layout/reftests/forms/legend/reftest.list
++++ b/layout/reftests/forms/legend/reftest.list
+@@ -1,4 +1,4 @@
+ == legend.html legend-ref.html
+-#bug 1418002 fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.enabled,true) == shadow-dom.html shadow-dom-ref.html
++#bug 1418002 fuzzy-if(skiaContent,1,7) pref(dom.webcomponents.shadowdom.enabled,true) == shadow-dom.html shadow-dom-ref.html
+ == 1273433.html 1273433-ref.html
+ fails-if(styloVsGecko||stylo) == 1339287.html 1339287-ref.html
+diff --git a/layout/reftests/mathml/reftest.list b/layout/reftests/mathml/reftest.list
+--- a/layout/reftests/mathml/reftest.list
++++ b/layout/reftests/mathml/reftest.list
+@@ -362,17 +362,17 @@ fuzzy-if(OSX,1,100) fuzzy-if(skiaContent
+ == mfrac-C-2.html mfrac-C-2-ref.html
+ == mfrac-C-3.html mfrac-C-3-ref.html
+ == mfrac-C-4.html mfrac-C-4-ref.html
+ fuzzy-if(OSX,1,100) fuzzy-if(skiaContent,1,14) == mfrac-D-1.html mfrac-D-1-ref.html
+ == mfrac-D-2.html mfrac-D-2-ref.html
+ == mfrac-D-3.html mfrac-D-3-ref.html
+ == mfrac-D-4.html mfrac-D-4-ref.html
+ == mfrac-E-1.html mfrac-E-1-ref.html
+-test-pref(dom.webcomponents.enabled,true) == shadow-dom-1.html shadow-dom-1-ref.html
++test-pref(dom.webcomponents.shadowdom.enabled,true) == shadow-dom-1.html shadow-dom-1-ref.html
+ pref(dom.meta-viewport.enabled,true) pref(font.size.inflation.emPerLine,25) == font-inflation-1.html font-inflation-1-ref.html
+ test-pref(font.minimum-size.x-math,40) == default-font.html default-font-ref.html
+ != radicalbar-1.html about:blank
+ != radicalbar-1a.html about:blank
+ != radicalbar-1b.html about:blank
+ != radicalbar-1c.html about:blank
+ fails-if(webrender&&gtkWidget) != radicalbar-1d.html about:blank
+ != radicalbar-2.html about:blank
+diff --git a/layout/reftests/webcomponents/reftest.list b/layout/reftests/webcomponents/reftest.list
+--- a/layout/reftests/webcomponents/reftest.list
++++ b/layout/reftests/webcomponents/reftest.list
+@@ -1,23 +1,23 @@
+-pref(dom.webcomponents.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
+-pref(dom.webcomponents.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
+-pref(dom.webcomponents.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
+-pref(dom.webcomponents.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
+-pref(dom.webcomponents.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
+-pref(dom.webcomponents.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
+-pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
+-pref(dom.webcomponents.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
+-pref(dom.webcomponents.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
+-pref(dom.webcomponents.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
+-pref(dom.webcomponents.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html
+-pref(dom.webcomponents.enabled,true) == style-sharing.html style-sharing-ref.html
+-pref(dom.webcomponents.enabled,true) skip-if(!stylo||styloVsGecko) == style-sharing-across-shadow.html style-sharing-ref.html # bug 1412400
+-pref(dom.webcomponents.enabled,true) == basic-slot-1.html basic-slot-1-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-slot-2.html basic-slot-2-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-slot-3.html basic-slot-3-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-slot-4.html basic-slot-3-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-slot-5.html basic-slot-5-ref.html
+-pref(dom.webcomponents.enabled,true) == basic-slot-6.html basic-slot-6-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == cross-tree-selection-1.html cross-tree-selection-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-1.html basic-shadow-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-2.html basic-shadow-2-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-3.html basic-shadow-3-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-shadow-4.html basic-shadow-4-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == fallback-content-1.html fallback-content-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == remove-insertion-point-1.html remove-insertion-point-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == nested-insertion-point-1.html nested-insertion-point-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == update-dist-node-descendants-1.html update-dist-node-descendants-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) fuzzy-if(Android,2,7) == input-transition-1.html input-transition-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == dynamic-insertion-point-distribution-1.html dynamic-insertion-point-distribution-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == dynamic-insertion-point-distribution-2.html dynamic-insertion-point-distribution-2-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == remove-append-shadow-host-1.html remove-append-shadow-host-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == reframe-shadow-child-1.html reframe-shadow-child-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == reframe-shadow-child-2.html reframe-shadow-child-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == style-sharing.html style-sharing-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) skip-if(!stylo||styloVsGecko) == style-sharing-across-shadow.html style-sharing-ref.html # bug 1412400
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-1.html basic-slot-1-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-2.html basic-slot-2-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-3.html basic-slot-3-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-4.html basic-slot-3-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-5.html basic-slot-5-ref.html
++pref(dom.webcomponents.shadowdom.enabled,true) == basic-slot-6.html basic-slot-6-ref.html
+diff --git a/layout/style/crashtests/crashtests.list b/layout/style/crashtests/crashtests.list
+--- a/layout/style/crashtests/crashtests.list
++++ b/layout/style/crashtests/crashtests.list
+@@ -107,22 +107,22 @@ load 894245-1.html
+ load 915440.html
+ load 927734-1.html
+ load 930270-1.html
+ load 930270-2.html
+ load 945048-1.html
+ load 972199-1.html
+ load 989965-1.html
+ load 992333-1.html
+-pref(dom.webcomponents.enabled,true) load 1017798-1.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1017798-1.html
+ load 1028514-1.html
+ load 1066089-1.html
+ load 1074651-1.html
+ load 1135534.html
+-pref(dom.webcomponents.enabled,true) load 1089463-1.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1089463-1.html
+ pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1136010-1.html
+ pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
+ load 1153693-1.html
+ load 1161320-1.html
+ pref(dom.animations-api.core.enabled,true) load 1161320-2.html
+ load 1161366-1.html
+ load 1163446-1.html
+ load 1164813-1.html
+@@ -256,16 +256,16 @@ load 1409502.html
+ load 1409931.html
+ load 1410226-1.html
+ load 1410226-2.html
+ load 1411143.html
+ load 1411478.html
+ load 1413288.html
+ load 1413361.html
+ load 1415663.html
+-pref(dom.webcomponents.enabled,true) load 1415353.html
+-load 1415021.html # This should have dom.webcomponents.enabled=true, but it leaks the world, see bug 1416296.
++pref(dom.webcomponents.shadowdom.enabled,true) load 1415353.html
++load 1415021.html # This should have dom.webcomponents.shadowdom.enabled=true, but it leaks the world, see bug 1416296.
+ load 1418059.html
+ skip-if(!stylo) test-pref(dom.animations-api.core.enabled,true) load 1418867.html
+-pref(dom.webcomponents.enabled,true) load 1419554.html
++pref(dom.webcomponents.shadowdom.enabled,true) load 1419554.html
+ load 1426312.html
+ load 1439793.html
+ pref(layout.css.resizeobserver.enabled,true) load 1552911.html
+diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js
+--- a/modules/libpref/init/all.js
++++ b/modules/libpref/init/all.js
+@@ -1583,17 +1583,17 @@ pref("dom.event.contextmenu.enabled",   
+ pref("dom.event.clipboardevents.enabled",   true);
+ pref("dom.event.highrestimestamp.enabled",  true);
+ #ifdef NIGHTLY_BUILD
+ pref("dom.event.coalesce_mouse_move",       true);
+ #else
+ pref("dom.event.coalesce_mouse_move",       false);
+ #endif
+ 
+-pref("dom.webcomponents.enabled",           false);
++pref("dom.webcomponents.shadowdom.enabled", false);
+ pref("dom.webcomponents.customelements.enabled", false);
+ 
+ pref("javascript.enabled",                  true);
+ pref("javascript.options.strict",           false);
+ #ifdef DEBUG
+ pref("javascript.options.strict.debug",     false);
+ #endif
+ pref("javascript.options.unboxed_objects",  false);
+diff --git a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
+--- a/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
++++ b/testing/marionette/harness/marionette_harness/tests/unit/test_shadow_dom.py
+@@ -13,25 +13,25 @@ from marionette_driver.errors import (
+ 
+ from marionette_harness import MarionetteTestCase
+ 
+ 
+ class TestShadowDom(MarionetteTestCase):
+ 
+     def setUp(self):
+         super(TestShadowDom, self).setUp()
+-        self.marionette.set_pref("dom.webcomponents.enabled", True)
++        self.marionette.set_pref("dom.webcomponents.shadowdom.enabled", True)
+         self.marionette.navigate(self.marionette.absolute_url("test_shadow_dom.html"))
+ 
+         self.host = self.marionette.find_element(By.ID, "host")
+         self.marionette.switch_to_shadow_root(self.host)
+         self.button = self.marionette.find_element(By.ID, "button")
+ 
+     def tearDown(self):
+-        self.marionette.clear_pref("dom.webcomponents.enabled")
++        self.marionette.clear_pref("dom.webcomponents.shadowdom.enabled")
+         super(TestShadowDom, self).tearDown()
+ 
+     def test_chrome_error(self):
+         with self.marionette.using_context("chrome"):
+             self.assertRaises(UnsupportedOperationException,
+                               self.marionette.switch_to_shadow_root)
+ 
+     def test_shadow_dom(self):
+diff --git a/testing/profiles/common/user.js b/testing/profiles/common/user.js
+--- a/testing/profiles/common/user.js
++++ b/testing/profiles/common/user.js
+@@ -59,17 +59,17 @@ user_pref("app.update.enabled", false);
+ user_pref("app.update.staging.enabled", false);
+ user_pref("app.update.url.android", "");
+ // Make sure GMPInstallManager won't hit the network.
+ user_pref("media.gmp-manager.url.override", "http://{server}/dummy-gmp-manager.xml");
+ user_pref("media.gmp-manager.updateEnabled", false);
+ user_pref("media.hls.server.url", "http://{server}/tests/dom/media/test/hls");
+ user_pref("dom.w3c_touch_events.enabled", 1);
+ user_pref("layout.accessiblecaret.enabled_on_touch", false);
+-user_pref("dom.webcomponents.enabled", false);
++user_pref("dom.webcomponents.shadowdom.enabled", false);
+ user_pref("dom.webcomponents.customelements.enabled", true);
+ // Existing tests assume there is no font size inflation.
+ user_pref("font.size.inflation.emPerLine", 0);
+ user_pref("font.size.inflation.minTwips", 0);
+ // Disable the caret blinking so we get stable snapshot
+ user_pref("ui.caretBlinkTime", -1);
+ 
+ // Don't allow background tabs to be zombified, otherwise for tests that

+ 166 - 0
frg/work-js/mozilla-release/patches/1428722-1-59a1.patch

@@ -0,0 +1,166 @@
+# HG changeset patch
+# User Alastor Wu <alwu@mozilla.com>
+# Date 1515401068 -28800
+# Node ID b9056b64d45b7dbf8dd7e6284e543ea1ada7502b
+# Parent  09d464e176656814196d85c7f5be345fdd8ad4c3
+Bug 1428722 - part1 : always activate the top level frame. r=smaug
+
+For top level frame, it should also be activated when user activate its child frame.
+
+eg. A (youtube.com) -> B (ad.com), when user activate B frame, the A frame would also be activated.
+
+MozReview-Commit-ID: BP7eGKiqYJe
+
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -13198,16 +13198,32 @@ void
+ nsIDocument::SetUserHasInteracted(bool aUserHasInteracted)
+ {
+   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+           ("Document %p has been interacted by user.", this));
+   mUserHasInteracted = aUserHasInteracted;
+ }
+ 
+ void
++nsIDocument::MaybeNotifyUserActivation(nsIPrincipal* aPrincipal)
++{
++  bool isEqual = false;
++  nsresult rv = aPrincipal->Equals(NodePrincipal(), &isEqual);
++  if (NS_WARN_IF(NS_FAILED(rv))) {
++    return;
++  }
++
++  // If a child frame is actived, it would always activate the top frame and its
++  // parent frames which has same priciple.
++  if (isEqual || IsTopLevelContentDocument()) {
++    NotifyUserActivation();
++  }
++}
++
++void
+ nsIDocument::NotifyUserActivation()
+ {
+   if (mUserHasActivatedInteraction) {
+     return;
+   }
+ 
+   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+           ("Document %p has been activated by user.", this));
+@@ -13229,38 +13245,37 @@ nsIDocument::HasBeenUserActivated()
+ 
+   return mUserHasActivatedInteraction;
+ }
+ 
+ nsIDocument*
+ nsIDocument::GetFirstParentDocumentWithSamePrincipal(nsIPrincipal* aPrincipal)
+ {
+   MOZ_ASSERT(aPrincipal);
+-  nsIDocument* parent = GetSameTypeParentDocument(this);
++  nsIDocument* parent = GetSameTypeParentDocument();
+   while (parent) {
+     bool isEqual = false;
+     nsresult rv = aPrincipal->Equals(parent->NodePrincipal(), &isEqual);
+     if (NS_WARN_IF(NS_FAILED(rv))) {
+       return nullptr;
+     }
+ 
+     if (isEqual) {
+       return parent;
+     }
+-    parent = GetSameTypeParentDocument(parent);
++    parent = parent->GetSameTypeParentDocument();
+   }
+   MOZ_ASSERT(!parent);
+   return nullptr;
+ }
+ 
+ nsIDocument*
+-nsIDocument::GetSameTypeParentDocument(const nsIDocument* aDoc)
+-{
+-  MOZ_ASSERT(aDoc);
+-  nsCOMPtr<nsIDocShellTreeItem> current = aDoc->GetDocShell();
++nsIDocument::GetSameTypeParentDocument()
++{
++  nsCOMPtr<nsIDocShellTreeItem> current = GetDocShell();
+   if (!current) {
+     return nullptr;
+   }
+ 
+   nsCOMPtr<nsIDocShellTreeItem> parent;
+   current->GetSameTypeParent(getter_AddRefs(parent));
+   if (!parent) {
+     return nullptr;
+diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
+--- a/dom/base/nsIDocument.h
++++ b/dom/base/nsIDocument.h
+@@ -3093,16 +3093,21 @@ public:
+   }
+ 
+   // This would be called when document get activated by specific user gestures.
+   void NotifyUserActivation();
+ 
+   // Return true if document has interacted by specific user gestures.
+   bool HasBeenUserActivated();
+ 
++  void MaybeNotifyUserActivation(nsIPrincipal* aPrincipal);
++
++  // Return the same type parent docuement if exists, or return null.
++  nsIDocument* GetSameTypeParentDocument();
++
+   // Return the first parent document with same pricipal, return nullptr if we
+   // can't find it.
+   nsIDocument* GetFirstParentDocumentWithSamePrincipal(nsIPrincipal* aPrincipal);
+ 
+   bool HasScriptsBlockedBySandbox();
+ 
+   bool InlineScriptAllowedByCSP();
+ 
+@@ -3287,19 +3292,16 @@ protected:
+   // mFrameRequestCallbacksScheduled.  aOldShell should only be passed when
+   // mPresShell is becoming null; in that case it will be used to get hold of
+   // the relevant refresh driver.
+   void UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell = nullptr);
+ 
+   // Helper for GetScrollingElement/IsScrollingElement.
+   bool IsPotentiallyScrollable(mozilla::dom::HTMLBodyElement* aBody);
+ 
+-  // Return the same type parent docuement if exists, or return null.
+-  nsIDocument* GetSameTypeParentDocument(const nsIDocument* aDoc);
+-
+   // Helpers for GetElementsByName.
+   static bool MatchNameAttribute(mozilla::dom::Element* aElement,
+                                  int32_t aNamespaceID,
+                                  nsIAtom* aAtom, void* aData);
+   static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
+ 
+   nsCString mReferrer;
+   nsString mLastModified;
+diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
+--- a/dom/events/EventStateManager.cpp
++++ b/dom/events/EventStateManager.cpp
+@@ -919,21 +919,20 @@ EventStateManager::NotifyTargetUserActiv
+ 
+   MOZ_ASSERT(aEvent->mMessage == eKeyUp   ||
+              aEvent->mMessage == eMouseUp ||
+              aEvent->mMessage == eTouchEnd);
+   doc->NotifyUserActivation();
+ 
+   // Activate parent document which has same principle on the parent chain.
+   nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+-  nsCOMPtr<nsIDocument> parent =
+-    doc->GetFirstParentDocumentWithSamePrincipal(principal);
++  nsCOMPtr<nsIDocument> parent = doc->GetSameTypeParentDocument();
+   while (parent) {
+-    parent->NotifyUserActivation();
+-    parent = parent->GetFirstParentDocumentWithSamePrincipal(principal);
++    parent->MaybeNotifyUserActivation(principal);
++    parent = parent->GetSameTypeParentDocument();
+   }
+ }
+ 
+ void
+ EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
+ {
+   switch (aEvent->mMessage) {
+     case eQuerySelectedText:

+ 190 - 0
frg/work-js/mozilla-release/patches/1428722-2-59a1.patch

@@ -0,0 +1,190 @@
+# HG changeset patch
+# User Alastor Wu <alwu@mozilla.com>
+# Date 1515662790 -28800
+# Node ID adbad272045dee6f307751ae15037319b4e5d3bc
+# Parent  ebccfdcdf9eefc28d6b63ac8a490ef09508f72e3
+Bug 1428722 - part2 : move all user-activation implementation details to nsDocument. r=smaug
+
+In order to write tests, I would like to create an method that allows chorome js can directly set the user-activation flag.
+
+Therefore, I need to move all these details into nsDocument, then we could easily simulate the user activation.
+
+MozReview-Commit-ID: 5JrCoQc0vF7
+
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -13198,33 +13198,46 @@ void
+ nsIDocument::SetUserHasInteracted(bool aUserHasInteracted)
+ {
+   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+           ("Document %p has been interacted by user.", this));
+   mUserHasInteracted = aUserHasInteracted;
+ }
+ 
+ void
+-nsIDocument::MaybeNotifyUserActivation(nsIPrincipal* aPrincipal)
++nsIDocument::NotifyUserActivation()
++{
++  ActivateByUserGesture();
++  // Activate parent document which has same principle on the parent chain.
++  nsCOMPtr<nsIPrincipal> principal = NodePrincipal();
++  nsCOMPtr<nsIDocument> parent = GetSameTypeParentDocument();
++  while (parent) {
++    parent->MaybeActivateByUserGesture(principal);
++    parent = parent->GetSameTypeParentDocument();
++  }
++}
++
++void
++nsIDocument::MaybeActivateByUserGesture(nsIPrincipal* aPrincipal)
+ {
+   bool isEqual = false;
+   nsresult rv = aPrincipal->Equals(NodePrincipal(), &isEqual);
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return;
+   }
+ 
+   // If a child frame is actived, it would always activate the top frame and its
+   // parent frames which has same priciple.
+   if (isEqual || IsTopLevelContentDocument()) {
+-    NotifyUserActivation();
+-  }
+-}
+-
+-void
+-nsIDocument::NotifyUserActivation()
++    ActivateByUserGesture();
++  }
++}
++
++void
++nsIDocument::ActivateByUserGesture()
+ {
+   if (mUserHasActivatedInteraction) {
+     return;
+   }
+ 
+   MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug,
+           ("Document %p has been activated by user.", this));
+   mUserHasActivatedInteraction = true;
+diff --git a/dom/base/nsIDocument.h b/dom/base/nsIDocument.h
+--- a/dom/base/nsIDocument.h
++++ b/dom/base/nsIDocument.h
+@@ -3087,31 +3087,23 @@ public:
+   }
+ 
+   void SetUserHasInteracted(bool aUserHasInteracted);
+   bool UserHasInteracted()
+   {
+     return mUserHasInteracted;
+   }
+ 
+-  // This would be called when document get activated by specific user gestures.
++  // This would be called when document get activated by specific user gestures
++  // and propagate the user activation flag to its parent.
+   void NotifyUserActivation();
+ 
+   // Return true if document has interacted by specific user gestures.
+   bool HasBeenUserActivated();
+ 
+-  void MaybeNotifyUserActivation(nsIPrincipal* aPrincipal);
+-
+-  // Return the same type parent docuement if exists, or return null.
+-  nsIDocument* GetSameTypeParentDocument();
+-
+-  // Return the first parent document with same pricipal, return nullptr if we
+-  // can't find it.
+-  nsIDocument* GetFirstParentDocumentWithSamePrincipal(nsIPrincipal* aPrincipal);
+-
+   bool HasScriptsBlockedBySandbox();
+ 
+   bool InlineScriptAllowedByCSP();
+ 
+   void ReportHasScrollLinkedEffect();
+   bool HasScrollLinkedEffect() const
+   {
+     return mHasScrollLinkedEffect;
+@@ -3292,16 +3284,27 @@ protected:
+   // mFrameRequestCallbacksScheduled.  aOldShell should only be passed when
+   // mPresShell is becoming null; in that case it will be used to get hold of
+   // the relevant refresh driver.
+   void UpdateFrameRequestCallbackSchedulingState(nsIPresShell* aOldShell = nullptr);
+ 
+   // Helper for GetScrollingElement/IsScrollingElement.
+   bool IsPotentiallyScrollable(mozilla::dom::HTMLBodyElement* aBody);
+ 
++  // Return the same type parent docuement if exists, or return null.
++  nsIDocument* GetSameTypeParentDocument();
++
++  // Return the first parent document with same pricipal, return nullptr if we
++  // can't find it.
++  nsIDocument* GetFirstParentDocumentWithSamePrincipal(nsIPrincipal* aPrincipal);
++
++  // Activate the flag 'mUserHasActivatedInteraction' by specific user gestures.
++  void ActivateByUserGesture();
++  void MaybeActivateByUserGesture(nsIPrincipal* aPrincipal);
++
+   // Helpers for GetElementsByName.
+   static bool MatchNameAttribute(mozilla::dom::Element* aElement,
+                                  int32_t aNamespaceID,
+                                  nsIAtom* aAtom, void* aData);
+   static void* UseExistingNameString(nsINode* aRootNode, const nsString* aName);
+ 
+   nsCString mReferrer;
+   nsString mLastModified;
+diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp
+--- a/dom/events/EventStateManager.cpp
++++ b/dom/events/EventStateManager.cpp
+@@ -916,24 +916,16 @@ EventStateManager::NotifyTargetUserActiv
+   if (!doc || doc->HasBeenUserActivated()) {
+     return;
+   }
+ 
+   MOZ_ASSERT(aEvent->mMessage == eKeyUp   ||
+              aEvent->mMessage == eMouseUp ||
+              aEvent->mMessage == eTouchEnd);
+   doc->NotifyUserActivation();
+-
+-  // Activate parent document which has same principle on the parent chain.
+-  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+-  nsCOMPtr<nsIDocument> parent = doc->GetSameTypeParentDocument();
+-  while (parent) {
+-    parent->MaybeNotifyUserActivation(principal);
+-    parent = parent->GetSameTypeParentDocument();
+-  }
+ }
+ 
+ void
+ EventStateManager::HandleQueryContentEvent(WidgetQueryContentEvent* aEvent)
+ {
+   switch (aEvent->mMessage) {
+     case eQuerySelectedText:
+     case eQueryTextContent:
+diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
+--- a/dom/webidl/Document.webidl
++++ b/dom/webidl/Document.webidl
+@@ -447,16 +447,23 @@ partial interface Document {
+ };
+ 
+ // Extension to give chrome JS the ability to determine whether
+ // the user has interacted with the document or not.
+ partial interface Document {
+   [ChromeOnly] readonly attribute boolean userHasInteracted;
+ };
+ 
++// Extension to give chrome JS the ability to simulate activate the docuement
++// by user gesture.
++partial interface Document {
++  [ChromeOnly]
++  void notifyUserActivation();
++};
++
+ // Extension to give chrome and XBL JS the ability to determine whether
+ // the document is sandboxed without permission to run scripts
+ // and whether inline scripts are blocked by the document's CSP.
+ partial interface Document {
+   [Func="IsChromeOrXBL"] readonly attribute boolean hasScriptsBlockedBySandbox;
+   [Func="IsChromeOrXBL"] readonly attribute boolean inlineScriptAllowedByCSP;
+ };
+ 

+ 298 - 0
frg/work-js/mozilla-release/patches/1428722-3-59a1.patch

@@ -0,0 +1,298 @@
+# HG changeset patch
+# User Alastor Wu <alwu@mozilla.com>
+# Date 1515662828 -28800
+# Node ID 6f9b763bb1c9f03be9ff0420a7b52261306bd227
+# Parent  595939239d049ebe04aa488950227ca737f11ab6
+Bug 1428722 - part3 : add tests. r=smaug
+
+MozReview-Commit-ID: 4WfADcQinuQ
+
+diff --git a/toolkit/content/tests/browser/browser.ini b/toolkit/content/tests/browser/browser.ini
+--- a/toolkit/content/tests/browser/browser.ini
++++ b/toolkit/content/tests/browser/browser.ini
+@@ -23,16 +23,24 @@ support-files =
+   image.jpg
+   image_page.html
+   silentAudioTrack.webm
+ 
+ [browser_audioCompeting.js]
+ tags = audiochannel
+ [browser_audioCompeting_onlyForActiveAgent.js]
+ tags = audiochannel
++[browser_autoplay_policy_iframe_hierarchy.js]
++support-files =
++  file_autoplay_three_layers_frame1.html
++  file_autoplay_three_layers_frame2.html
++  file_autoplay_two_layers_frame1.html
++  file_autoplay_two_layers_frame2.html
++  file_video.html
++  gizmo.mp4
+ [browser_autoplay_policy_play_twice.js]
+ [browser_autoscroll_disabled.js]
+ [browser_block_autoplay_media.js]
+ tags = audiochannel
+ [browser_block_autoplay_media_pausedAfterPlay.js]
+  tags = audiochannel
+ [browser_block_autoplay_playAfterTabVisible.js]
+ tags = audiochannel
+diff --git a/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js b/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
+@@ -0,0 +1,168 @@
++/**
++ * Test whether the autoplay permission can be transfer well through different
++ * frame hierarchy. When the target frame has been interacted with the user
++ * gesture, it would has the autoplay permission, then the permission would be
++ * propagated to its parent and child frames.
++ *
++ * In this test, I use A/B/C to indicate different domain frames, and the number
++ * after the name is which layer the frame is in.
++ * Ex. A1 -> B2 -> A3,
++ * Top frame and grandchild frame is in the domain A, and child frame is in the
++ * domain B.
++ *
++ * Child frames could get permission if they have same origin as target frame's
++ * Parent frames could get permission if they have same origin as target frame's
++ * or the frame is in the top level.
++ * Ex. A1 -> B2 -> B3,
++ * A1 will always be activated no matter which level frame user activates with,
++ * since it's in the top level.
++ * B2/B3 will only be activated when user activates frame B2 or B3.
++ */
++const PAGE_A1_A2 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html";
++const PAGE_A1_B2 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html";
++const PAGE_A1_B2_C3 = "https://test1.example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
++const PAGE_A1_B2_A3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
++const PAGE_A1_B2_B3 = "https://example.org/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
++const PAGE_A1_A2_A3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html";
++const PAGE_A1_A2_B3 = "https://example.com/browser/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html";
++
++function setup_test_preference() {
++  return SpecialPowers.pushPrefEnv({"set": [
++    ["media.autoplay.enabled", false],
++    ["media.autoplay.enabled.user-gestures-needed", true]
++  ]});
++}
++
++var frameTestArray = [
++  { name: "A1_A2",    layersNum: 2, src: PAGE_A1_A2 },
++  { name: "A1_B2",    layersNum: 2, src: PAGE_A1_B2 },
++  { name: "A1_B2_C3", layersNum: 3, src: PAGE_A1_B2_C3 },
++  { name: "A1_B2_A3", layersNum: 3, src: PAGE_A1_B2_A3 },
++  { name: "A1_B2_B3", layersNum: 3, src: PAGE_A1_B2_B3 },
++  { name: "A1_A2_A3", layersNum: 3, src: PAGE_A1_A2_A3 },
++  { name: "A1_A2_B3", layersNum: 3, src: PAGE_A1_A2_B3 }
++];
++
++async function test_permission_propagation(testName, testSrc, layersNum) {
++  info(`- start test for ${testName} -`);
++  for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
++    info("- open new tab -");
++    let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
++                                                          "about:blank");
++    tab.linkedBrowser.loadURI(testSrc);
++    await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
++
++    // If the frame isn't activated, the video play will fail.
++    async function playing_video_should_fail(layersNum) {
++      for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
++        let doc;
++        if (layerIdx == 1) {
++          doc = content.document;
++        } else {
++          doc = layerIdx == 2 ? content.frames[0].document :
++                                content.frames[0].frames[0].document;
++        }
++        await doc.getElementById("v").play().catch(function() {
++          ok(true, `video in layer ${layerIdx} can't start play without user input.`);
++        });
++      }
++    }
++    await ContentTask.spawn(tab.linkedBrowser, layersNum,
++                            playing_video_should_fail);
++
++    function activate_frame(testInfo) {
++      let layerIdx = testInfo[0];
++      info(`- activate frame in layer ${layerIdx} (${testInfo[1]}) -`);
++      let doc;
++      if (layerIdx == 1) {
++        doc = content.document;
++      } else {
++        doc = layerIdx == 2 ? content.frames[0].document :
++                              content.frames[0].frames[0].document;
++      }
++      doc.notifyUserActivation();
++    }
++    await ContentTask.spawn(tab.linkedBrowser, [layerIdx, testName],
++                            activate_frame);
++
++    // If frame is activated, the video play will succeed.
++    async function playing_video_may_success(testInfo) {
++      let activeLayerIdx = testInfo[0];
++      let testName = testInfo[1];
++      let layersNum = testInfo[2];
++      for (let layerIdx = 1; layerIdx <= layersNum; layerIdx++) {
++        let doc;
++        if (layerIdx == 1) {
++          doc = content.document;
++        } else {
++          doc = layerIdx == 2 ? content.frames[0].document :
++                                content.frames[0].frames[0].document;
++        }
++        let video = doc.getElementById("v");
++        let shouldSuccess = false;
++        let isActiveLayer = layerIdx == activeLayerIdx;
++        switch (testName) {
++          case "A1_A2":
++          case "A1_A2_A3":
++            // always success to play.
++            shouldSuccess = true;
++            break;
++          case "A1_B2":
++            shouldSuccess = layerIdx == 1 ||
++                            (layerIdx == 2 && isActiveLayer);
++            break;
++          case "A1_B2_C3":
++            shouldSuccess = layerIdx == 1 ||
++                            (layerIdx >= 2 && isActiveLayer);
++            break;
++          case "A1_B2_A3":
++            shouldSuccess = layerIdx != 2 ||
++                            (layerIdx == 2 && isActiveLayer);
++            break;
++          case "A1_B2_B3":
++            shouldSuccess = layerIdx == 1 ||
++                            (layerIdx >= 2 && activeLayerIdx != 1);
++            break;
++          case "A1_A2_B3":
++            shouldSuccess = layerIdx <= 2 ||
++                            (layerIdx == 3 && isActiveLayer);
++            break;
++          default:
++            ok(false, "wrong test name.");
++            break;
++        }
++        try {
++          await video.play();
++          ok(shouldSuccess, `video in layer ${layerIdx} starts playing.`);
++        } catch (e) {
++          ok(!shouldSuccess, `video in layer ${layerIdx} fails to start.`);
++        }
++      }
++    }
++    await ContentTask.spawn(tab.linkedBrowser,
++                            [layerIdx, testName, layersNum],
++                            playing_video_may_success);
++
++    info("- remove tab -");
++    await BrowserTestUtils.removeTab(tab);
++  }
++}
++
++add_task(async function start_test() {
++  info("- setup test preference -");
++  await setup_test_preference();
++  requestLongerTimeout(2);
++
++  info("- test permission propagation in different frame hierarchy -");
++  for (let testIdx = 0; testIdx < frameTestArray.length; testIdx++) {
++    let testInfo = frameTestArray[testIdx];
++    let testName = testInfo.name;
++    let testSrc = testInfo.src;
++    let layersNum = testInfo.layersNum;
++    if (layersNum > 3) {
++      ok(false, "Not support more than 3 layers frame yet.");
++    } else {
++      await test_permission_propagation(testName, testSrc, layersNum);
++    }
++  }
++});
+diff --git a/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html b/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/file_autoplay_three_layers_frame1.html
+@@ -0,0 +1,16 @@
++<html>
++<head>
++  <title>three frame layers structure 1</title>
++</head>
++<body>
++<!--
++Embed an iframe which has two layers frames, top frame is on domain "example.com",
++child frame is on domain "example.org".
++-->
++</body>
++<video id="v" src="gizmo.mp4" controls loop></video>
++<iframe id="i" src="https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html"
++        height="600" width="800"></iframe></br>
++<script type="text/javascript">
++</script>
++</html>
+\ No newline at end of file
+diff --git a/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html b/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/file_autoplay_three_layers_frame2.html
+@@ -0,0 +1,16 @@
++<html>
++<head>
++  <title>three frame layers structure 2</title>
++</head>
++<body>
++<!--
++Embed an iframe which has two layers frames, both frames are in the domain
++"example.com".
++-->
++</body>
++<video id="v" src="gizmo.mp4" controls loop></video>
++<iframe id="i" src="https://example.com/browser/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html"
++        height="600" width="800"></iframe></br>
++<script type="text/javascript">
++</script>
++</html>
+\ No newline at end of file
+diff --git a/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html b/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/file_autoplay_two_layers_frame1.html
+@@ -0,0 +1,15 @@
++<html>
++<head>
++  <title>two frame layers structure1</title>
++</head>
++<body>
++<!--
++Embed an iframe which would always in the same domain as this frame.
++-->
++</body>
++<video id="v" src="gizmo.mp4" controls loop></video>
++<iframe id="i" src="file_video.html"
++        height="600" width="800"></iframe></br>
++<script type="text/javascript">
++</script>
++</html>
+\ No newline at end of file
+diff --git a/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html b/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/file_autoplay_two_layers_frame2.html
+@@ -0,0 +1,17 @@
++<html>
++<head>
++  <title>two frame layers structure2</title>
++</head>
++<body>
++<!--
++Embed an iframe which is from domain "example.org". This file doesn't guarantee
++all frames would be in the same domain, it depend on what domain we put this file
++on.
++-->
++</body>
++<video id="v" src="gizmo.mp4" controls loop></video>
++<iframe id="i" src="https://example.org/browser/toolkit/content/tests/browser/file_video.html"
++        height="600" width="800"></iframe></br>
++<script type="text/javascript">
++</script>
++</html>
+\ No newline at end of file

+ 1 - 1
frg/work-js/mozilla-release/patches/1429206-1-60a1.patch

@@ -2,7 +2,7 @@
 # User Jason Orendorff <jorendorff@mozilla.com>
 # Date 1513629906 21600
 # Node ID 301c61b644c24acc65c4d0b379736d1d3c7999a6
-# Parent  1a340f9fdffe75ba89acf236c1a3e01f113fd45a
+# Parent  fc64a01bbe319e411b7d61542880096249b35e2c
 Bug 1429206 - Part 1: Use js/TypeDecls.h instead of redeclaring certain types. r=jandem.
 
 This adds JS::Zone to TypeDecls.h. Arguably that is pretty borderline, but

+ 1 - 1
frg/work-js/mozilla-release/patches/1429206-3no2-60a1.patch

@@ -2,7 +2,7 @@
 # User Jason Orendorff <jorendorff@mozilla.com>
 # Date 1518458617 21600
 # Node ID eabb74b1c3bd181ff74c9d41003bc2b9aee6fe68
-# Parent  c335115274e420c6ae7a085c683539829b02bcc9
+# Parent  18e8213981cecc8d3ecf20b6ceb276cfd8798acf
 Bug 1429206 - Part 3: Rename jsobj* -> vm/JSObject*. r=jandem.
 
 diff --git a/config/check_spidermonkey_style.py b/config/check_spidermonkey_style.py

+ 305 - 0
frg/work-js/mozilla-release/patches/1429656-59a1.patch

@@ -0,0 +1,305 @@
+# HG changeset patch
+# User Jessica Jong <jjong@mozilla.com>
+# Date 1515998567 -28800
+# Node ID 52397f10a40b2dc2c2e07440e1b9c986eec112fa
+# Parent  cced5442bcd634deaaee4a52bd4bf63033aa8fd0
+Bug 1429656 - Implement ShadowRoot.activeElement. r=smaug
+
+diff --git a/dom/base/DocumentOrShadowRoot.cpp b/dom/base/DocumentOrShadowRoot.cpp
+--- a/dom/base/DocumentOrShadowRoot.cpp
++++ b/dom/base/DocumentOrShadowRoot.cpp
+@@ -1,16 +1,18 @@
+ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+ /* vim: set ts=8 sts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ #include "DocumentOrShadowRoot.h"
+ #include "mozilla/dom/StyleSheetList.h"
++#include "nsDocument.h"
++#include "nsFocusManager.h"
+ #include "XULDocument.h"
+ 
+ namespace mozilla {
+ namespace dom {
+ 
+ DocumentOrShadowRoot::DocumentOrShadowRoot(mozilla::dom::ShadowRoot& aShadowRoot)
+   : mAsNode(aShadowRoot)
+   , mKind(Kind::ShadowRoot)
+@@ -86,10 +88,53 @@ DocumentOrShadowRoot::GetElementsByTagNa
+ }
+ 
+ already_AddRefed<nsContentList>
+ DocumentOrShadowRoot::GetElementsByClassName(const nsAString& aClasses)
+ {
+   return nsContentUtils::GetElementsByClassName(&AsNode(), aClasses);
+ }
+ 
++nsIContent*
++DocumentOrShadowRoot::Retarget(nsIContent* aContent) const
++{
++  for (nsIContent* cur = aContent;
++       cur;
++       cur = cur->GetContainingShadowHost()) {
++    if (cur->SubtreeRoot() == &AsNode()) {
++      return cur;
++    }
++  }
++  return nullptr;
++}
++
++Element*
++DocumentOrShadowRoot::GetRetargetedFocusedElement()
++{
++  if (nsCOMPtr<nsPIDOMWindowOuter> window = AsNode().OwnerDoc()->GetWindow()) {
++    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
++    nsIContent* focusedContent =
++      nsFocusManager::GetFocusedDescendant(window,
++                                           nsFocusManager::eOnlyCurrentWindow,
++                                           getter_AddRefs(focusedWindow));
++    // be safe and make sure the element is from this document
++    if (focusedContent && focusedContent->OwnerDoc() == AsNode().OwnerDoc()) {
++      if (focusedContent->ChromeOnlyAccess()) {
++        focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
++      }
++
++      if (focusedContent) {
++        if (!nsDocument::IsWebComponentsEnabled(focusedContent)) {
++          return focusedContent->AsElement();
++        }
++
++        if (nsIContent* retarget = Retarget(focusedContent)) {
++          return retarget->AsElement();
++        }
++      }
++    }
++  }
++
++  return nullptr;
++}
++
+ }
+ }
+diff --git a/dom/base/DocumentOrShadowRoot.h b/dom/base/DocumentOrShadowRoot.h
+--- a/dom/base/DocumentOrShadowRoot.h
++++ b/dom/base/DocumentOrShadowRoot.h
+@@ -109,16 +109,25 @@ public:
+                          mozilla::ErrorResult&);
+ 
+   already_AddRefed<nsContentList>
+   GetElementsByClassName(const nsAString& aClasses);
+ 
+   ~DocumentOrShadowRoot() = default;
+ 
+ protected:
++  nsIContent* Retarget(nsIContent* aContent) const;
++
++  /**
++   * If focused element's subtree root is this document or shadow root, return
++   * focused element, otherwise, get the shadow host recursively until the
++   * shadow host's subtree root is this document or shadow root.
++   */
++  Element* GetRetargetedFocusedElement();
++
+   nsTArray<RefPtr<mozilla::StyleSheet>> mStyleSheets;
+   RefPtr<mozilla::dom::StyleSheetList> mDOMStyleSheets;
+ 
+   /*
+    * mIdentifierMap works as follows for IDs:
+    * 1) Attribute changes affect the table immediately (removing and adding
+    *    entries as needed).
+    * 2) Removals from the DOM affect the table immediately
+diff --git a/dom/base/FragmentOrElement.cpp b/dom/base/FragmentOrElement.cpp
+--- a/dom/base/FragmentOrElement.cpp
++++ b/dom/base/FragmentOrElement.cpp
+@@ -1115,16 +1115,25 @@ FragmentOrElement::IsLink(nsIURI** aURI)
+ nsXBLBinding*
+ FragmentOrElement::DoGetXBLBinding() const
+ {
+   MOZ_ASSERT(HasFlag(NODE_MAY_BE_IN_BINDING_MNGR));
+   const nsExtendedDOMSlots* slots = GetExistingExtendedDOMSlots();
+   return slots ? slots->mXBLBinding.get() : nullptr;
+ }
+ 
++nsIContent*
++nsIContent::GetContainingShadowHost() const
++{
++  if (mozilla::dom::ShadowRoot* shadow = GetContainingShadow()) {
++    return shadow->GetHost();
++  }
++  return nullptr;
++}
++
+ void
+ nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot)
+ {
+   MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
+   ExtendedContentSlots()->mAssignedSlot = aSlot;
+ }
+ 
+ void
+diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp
+--- a/dom/base/ShadowRoot.cpp
++++ b/dom/base/ShadowRoot.cpp
+@@ -422,16 +422,22 @@ ShadowRoot::MaybeReassignElement(Element
+       }
+       return true;
+     }
+   }
+ 
+   return false;
+ }
+ 
++Element*
++ShadowRoot::GetActiveElement()
++{
++  return GetRetargetedFocusedElement();
++}
++
+ void
+ ShadowRoot::GetInnerHTML(nsAString& aInnerHTML)
+ {
+   GetMarkup(false, aInnerHTML);
+ }
+ 
+ void
+ ShadowRoot::SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError)
+diff --git a/dom/base/ShadowRoot.h b/dom/base/ShadowRoot.h
+--- a/dom/base/ShadowRoot.h
++++ b/dom/base/ShadowRoot.h
+@@ -117,16 +117,18 @@ public:
+ 
+   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
+ 
+   void AddToIdTable(Element* aElement, nsIAtom* aId);
+   void RemoveFromIdTable(Element* aElement, nsIAtom* aId);
+ 
+   // WebIDL methods.
+   using mozilla::dom::DocumentOrShadowRoot::GetElementById;
++
++  Element* GetActiveElement();
+   void GetInnerHTML(nsAString& aInnerHTML);
+   void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
+   void StyleSheetChanged();
+ 
+   bool IsComposedDocParticipant() { return mIsComposedDocParticipant; }
+   void SetIsComposedDocParticipant(bool aIsComposedDocParticipant)
+   {
+     mIsComposedDocParticipant = aIsComposedDocParticipant;
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -3448,31 +3448,19 @@ nsDocument::GetActiveElement(nsIDOMEleme
+   el.forget(aElement);
+   return NS_OK;
+ }
+ 
+ Element*
+ nsIDocument::GetActiveElement()
+ {
+   // Get the focused element.
+-  if (nsCOMPtr<nsPIDOMWindowOuter> window = GetWindow()) {
+-    nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
+-    nsIContent* focusedContent =
+-      nsFocusManager::GetFocusedDescendant(window,
+-                                           nsFocusManager::eOnlyCurrentWindow,
+-                                           getter_AddRefs(focusedWindow));
+-    // be safe and make sure the element is from this document
+-    if (focusedContent && focusedContent->OwnerDoc() == this) {
+-      if (focusedContent->ChromeOnlyAccess()) {
+-        focusedContent = focusedContent->FindFirstNonChromeOnlyAccessContent();
+-      }
+-      if (focusedContent) {
+-        return focusedContent->AsElement();
+-      }
+-    }
++  Element* focusedElement = GetRetargetedFocusedElement();
++  if (focusedElement) {
++    return focusedElement;
+   }
+ 
+   // No focused element anywhere in this document.  Try to get the BODY.
+   RefPtr<nsHTMLDocument> htmlDoc = AsHTMLDocument();
+   if (htmlDoc) {
+     // Because of IE compatibility, return null when html document doesn't have
+     // a body.
+     return htmlDoc->GetBody();
+diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
+--- a/dom/base/nsFocusManager.cpp
++++ b/dom/base/nsFocusManager.cpp
+@@ -846,20 +846,21 @@ nsFocusManager::ContentRemoved(nsIDocume
+ {
+   NS_ENSURE_ARG(aDocument);
+   NS_ENSURE_ARG(aContent);
+ 
+   nsPIDOMWindowOuter *window = aDocument->GetWindow();
+   if (!window)
+     return NS_OK;
+ 
+-  // if the content is currently focused in the window, or is an ancestor
+-  // of the currently focused element, reset the focus within that window.
++  // if the content is currently focused in the window, or is an
++  // shadow-including inclusive ancestor of the currently focused element,
++  // reset the focus within that window.
+   nsIContent* content = window->GetFocusedNode();
+-  if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
++  if (content && nsContentUtils::ContentIsHostIncludingDescendantOf(content, aContent)) {
+     bool shouldShowFocusRing = window->ShouldShowFocusRing();
+     window->SetFocusedNode(nullptr);
+ 
+     // if this window is currently focused, clear the global focused
+     // element as well, but don't fire any events.
+     if (window == mFocusedWindow) {
+       mFocusedContent = nullptr;
+     } else {
+diff --git a/dom/base/nsIContent.h b/dom/base/nsIContent.h
+--- a/dom/base/nsIContent.h
++++ b/dom/base/nsIContent.h
+@@ -545,16 +545,24 @@ public:
+    */
+   mozilla::dom::ShadowRoot* GetContainingShadow() const
+   {
+     const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+     return slots ? slots->mContainingShadow.get() : nullptr;
+   }
+ 
+   /**
++   * Gets the shadow host if this content is in a shadow tree. That is, the host
++   * of |GetContainingShadow|, if its not null.
++   *
++   * @return The shadow host, if this is in shadow tree, or null.
++   */
++  nsIContent* GetContainingShadowHost() const;
++
++  /**
+    * Gets the assigned slot associated with this content.
+    *
+    * @return The assigned slot element or null.
+    */
+   mozilla::dom::HTMLSlotElement* GetAssignedSlot() const
+   {
+     const nsExtendedContentSlots* slots = GetExistingExtendedContentSlots();
+     return slots ? slots->mAssignedSlot.get() : nullptr;
+diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl
+--- a/dom/webidl/ShadowRoot.webidl
++++ b/dom/webidl/ShadowRoot.webidl
+@@ -15,16 +15,18 @@ enum ShadowRootMode {
+   "open",
+   "closed"
+ };
+ 
+ // https://dom.spec.whatwg.org/#shadowroot
+ [Func="nsDocument::IsWebComponentsEnabled"]
+ interface ShadowRoot : DocumentFragment
+ {
++  readonly attribute Element? activeElement;
++
+   // Shadow DOM v1
+   readonly attribute ShadowRootMode mode;
+   readonly attribute Element host;
+ 
+   // [deprecated] Shadow DOM v0
+   Element? getElementById(DOMString elementId);
+   HTMLCollection getElementsByTagName(DOMString localName);
+   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);

+ 145 - 0
frg/work-js/mozilla-release/patches/1429982-59a1.patch

@@ -0,0 +1,145 @@
+# HG changeset patch
+# User Jessica Jong <jjong@mozilla.com>
+# Date 1515998563 -28800
+# Node ID b584c2d1fb904386cd94f1da1169d115e641fa9a
+# Parent  71025b16f73c9eaba8f188dee1bfcca6949918e5
+Bug 1429982 - cloneNode on ShadowRoot interface should throw NotSupportedError. r=smaug
+
+The spec changed in order to align with the error thrown by importNode.
+
+diff --git a/dom/base/ShadowRoot.cpp b/dom/base/ShadowRoot.cpp
+--- a/dom/base/ShadowRoot.cpp
++++ b/dom/base/ShadowRoot.cpp
+@@ -579,10 +579,10 @@ ShadowRoot::ContentRemoved(nsIDocument* 
+   }
+ }
+ 
+ nsresult
+ ShadowRoot::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
+                   bool aPreallocateChildren) const
+ {
+   *aResult = nullptr;
+-  return NS_ERROR_DOM_DATA_CLONE_ERR;
++  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+ }
+diff --git a/dom/base/nsDocument.cpp b/dom/base/nsDocument.cpp
+--- a/dom/base/nsDocument.cpp
++++ b/dom/base/nsDocument.cpp
+@@ -6387,22 +6387,16 @@ nsIDocument::ImportNode(nsINode& aNode, 
+   nsINode* imported = &aNode;
+ 
+   switch (imported->NodeType()) {
+     case nsIDOMNode::DOCUMENT_NODE:
+     {
+       break;
+     }
+     case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
+-    {
+-      if (imported->IsShadowRoot()) {
+-        break;
+-      }
+-      MOZ_FALLTHROUGH;
+-    }
+     case nsIDOMNode::ATTRIBUTE_NODE:
+     case nsIDOMNode::ELEMENT_NODE:
+     case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
+     case nsIDOMNode::TEXT_NODE:
+     case nsIDOMNode::CDATA_SECTION_NODE:
+     case nsIDOMNode::COMMENT_NODE:
+     case nsIDOMNode::DOCUMENT_TYPE_NODE:
+     {
+diff --git a/dom/tests/mochitest/webcomponents/mochitest.ini b/dom/tests/mochitest/webcomponents/mochitest.ini
+--- a/dom/tests/mochitest/webcomponents/mochitest.ini
++++ b/dom/tests/mochitest/webcomponents/mochitest.ini
+@@ -1,17 +1,16 @@
+ [DEFAULT]
+ support-files =
+   inert_style.css
+   dummy_page.html
+   head.js
+ 
+ [test_bug900724.html]
+ [test_bug1017896.html]
+-[test_bug1176757.html]
+ [test_bug1276240.html]
+ [test_custom_element_callback_innerhtml.html]
+ [test_custom_element_htmlconstructor.html]
+ skip-if = os == 'android' # bug 1323645
+ support-files =
+   htmlconstructor_autonomous_tests.js
+   htmlconstructor_builtin_tests.js
+ [test_custom_element_in_shadow.html]
+@@ -32,15 +31,16 @@ support-files =
+ [test_detached_style.html]
+ [test_document_adoptnode.html]
+ [test_document_importnode.html]
+ [test_event_retarget.html]
+ [test_event_stopping.html]
+ [test_template.html]
+ [test_template_xhtml.html]
+ [test_shadowroot.html]
++[test_shadowroot_clonenode.html]
+ [test_shadowroot_inert_element.html]
+ [test_shadowroot_style.html]
+ [test_shadowroot_style_order.html]
+ [test_style_fallback_content.html]
+ skip-if = stylo # Bug 1410170
+ [test_link_prefetch.html]
+ [test_bug1269155.html]
+diff --git a/dom/tests/mochitest/webcomponents/test_bug1176757.html b/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+rename from dom/tests/mochitest/webcomponents/test_bug1176757.html
+rename to dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+--- a/dom/tests/mochitest/webcomponents/test_bug1176757.html
++++ b/dom/tests/mochitest/webcomponents/test_shadowroot_clonenode.html
+@@ -1,46 +1,46 @@
+ <!DOCTYPE HTML>
+ <html>
+ <!--
+-https://bugzilla.mozilla.org/show_bug.cgi?id=1176757
++https://bugzilla.mozilla.org/show_bug.cgi?id=1429982
+ -->
+ <head>
+-  <title>Test for Bug 1176757</title>
++  <title>Test for Bug 1429982</title>
+   <script type="text/javascript" src="head.js"></script>
+   <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body>
+ <a target="_blank"
+-  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1176757">Mozilla Bug 1176757</a>
++  href="https://bugzilla.mozilla.org/show_bug.cgi?id=1429982">Mozilla Bug 1429982</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ 
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+ 
+-/** Test for Bug 1176757 **/
++/** Test for Bug 1429982 **/
+ SimpleTest.waitForExplicitFinish();
+ setWebComponentsPrefAndCreateIframe()
+   .then((aDocument) => {
+     var element = aDocument.createElement("div");
+     var shadowRoot = element.attachShadow({mode: "open"});
+     var thrownException = false;
+ 
+     try {
+       shadowRoot.cloneNode();
+     } catch(err) {
+       thrownException = err;
+     }
+ 
+     ok(thrownException !== false, "An exception should've been thrown");
+-    is(thrownException.name, "DataCloneError", "A DataCloneError exception should've been thrown");
++    is(thrownException.name, "NotSupportedError", "A NotSupportedError exception should've been thrown");
+ 
+     SimpleTest.finish();
+   });
+ </script>
+ </pre>
+ </body>
+ </html>
+ 

+ 142 - 0
frg/work-js/mozilla-release/patches/1430299-59a1.patch

@@ -0,0 +1,142 @@
+# HG changeset patch
+# User Jessica Jong <jjong@mozilla.com>
+# Date 1516258335 -28800
+# Node ID ac355333d769a212121b18352be37d3434e8aa9e
+# Parent  2e3837789fc65cbeba4b1f0cca107d8768c65789
+Bug 1430299 - Add DocumentOrShadowRoot interface. r=smaug
+
+diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
+--- a/dom/webidl/Document.webidl
++++ b/dom/webidl/Document.webidl
+@@ -139,17 +139,16 @@ partial interface Document {
+   //(HTML only)WindowProxy open(DOMString url, DOMString name, DOMString features, optional boolean replace);
+   //(HTML only)void close();
+   //(HTML only)void write(DOMString... text);
+   //(HTML only)void writeln(DOMString... text);
+ 
+   // user interaction
+   [Pure]
+   readonly attribute WindowProxy? defaultView;
+-  readonly attribute Element? activeElement;
+   [Throws]
+   boolean hasFocus();
+   //(HTML only)         attribute DOMString designMode;
+   //(HTML only)boolean execCommand(DOMString commandId);
+   //(HTML only)boolean execCommand(DOMString commandId, boolean showUI);
+   //(HTML only)boolean execCommand(DOMString commandId, boolean showUI, DOMString value);
+   //(HTML only)boolean queryCommandEnabled(DOMString commandId);
+   //(HTML only)boolean queryCommandIndeterm(DOMString commandId);
+@@ -276,18 +275,16 @@ partial interface Document {
+ partial interface Document {
+   readonly attribute boolean hidden;
+   readonly attribute VisibilityState visibilityState;
+            attribute EventHandler onvisibilitychange;
+ };
+ 
+ // http://dev.w3.org/csswg/cssom/#extensions-to-the-document-interface
+ partial interface Document {
+-    [Constant]
+-    readonly attribute StyleSheetList styleSheets;
+     attribute DOMString? selectedStyleSheetSet;
+     readonly attribute DOMString? lastStyleSheetSet;
+     readonly attribute DOMString? preferredStyleSheetSet;
+     [Constant]
+     readonly attribute DOMStringList styleSheetSets;
+     void enableStyleSheetsForSet (DOMString? name);
+ };
+ 
+@@ -470,8 +467,9 @@ partial interface Document {
+ Document implements XPathEvaluator;
+ Document implements GlobalEventHandlers;
+ Document implements DocumentAndElementEventHandlers;
+ Document implements TouchEventHandlers;
+ Document implements ParentNode;
+ Document implements OnErrorEventHandlerForNodes;
+ Document implements GeometryUtils;
+ Document implements FontFaceSource;
++Document implements DocumentOrShadowRoot;
+diff --git a/dom/webidl/DocumentOrShadowRoot.webidl b/dom/webidl/DocumentOrShadowRoot.webidl
+new file mode 100644
+--- /dev/null
++++ b/dom/webidl/DocumentOrShadowRoot.webidl
+@@ -0,0 +1,29 @@
++/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
++/* 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/.
++ *
++ * The origin of this IDL file is
++ * https://dom.spec.whatwg.org/#documentorshadowroot
++ * http://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
++ */
++
++[NoInterfaceObject]
++interface DocumentOrShadowRoot {
++  // Not implemented yet: bug 1430308.
++  // Selection? getSelection();
++  // Not implemented yet: bug 1430301.
++  // Element? elementFromPoint (float x, float y);
++  // Not implemented yet: bug 1430301.
++  // sequence<Element> elementsFromPoint (float x, float y);
++  // Not implemented yet: bug 1430307.
++  // CaretPosition? caretPositionFromPoint (float x, float y);
++
++  readonly attribute Element? activeElement;
++  readonly attribute StyleSheetList styleSheets;
++
++  // Not implemented yet: bug 1430303.
++  // readonly attribute Element? pointerLockElement;
++  // Not implemented yet: bug 1430305.
++  // readonly attribute Element? fullscreenElement;
++};
+diff --git a/dom/webidl/ShadowRoot.webidl b/dom/webidl/ShadowRoot.webidl
+--- a/dom/webidl/ShadowRoot.webidl
++++ b/dom/webidl/ShadowRoot.webidl
+@@ -15,25 +15,23 @@ enum ShadowRootMode {
+   "open",
+   "closed"
+ };
+ 
+ // https://dom.spec.whatwg.org/#shadowroot
+ [Func="nsDocument::IsShadowDOMEnabled"]
+ interface ShadowRoot : DocumentFragment
+ {
+-  readonly attribute Element? activeElement;
+-
+   // Shadow DOM v1
+   readonly attribute ShadowRootMode mode;
+   readonly attribute Element host;
+ 
+   // [deprecated] Shadow DOM v0
+   Element? getElementById(DOMString elementId);
+   HTMLCollection getElementsByTagName(DOMString localName);
+   HTMLCollection getElementsByTagNameNS(DOMString? namespace, DOMString localName);
+   HTMLCollection getElementsByClassName(DOMString classNames);
+   [CEReactions, SetterThrows, TreatNullAs=EmptyString]
+   attribute DOMString innerHTML;
+   attribute boolean applyAuthorStyles;
+-  readonly attribute StyleSheetList styleSheets;
+ };
+ 
++ShadowRoot implements DocumentOrShadowRoot;
+diff --git a/dom/webidl/moz.build b/dom/webidl/moz.build
+--- a/dom/webidl/moz.build
++++ b/dom/webidl/moz.build
+@@ -461,16 +461,17 @@ WEBIDL_FILES = [
+     'DataTransferItemList.webidl',
+     'DecoderDoctorNotification.webidl',
+     'DedicatedWorkerGlobalScope.webidl',
+     'DelayNode.webidl',
+     'DeviceMotionEvent.webidl',
+     'Directory.webidl',
+     'Document.webidl',
+     'DocumentFragment.webidl',
++    'DocumentOrShadowRoot.webidl',
+     'DocumentTimeline.webidl',
+     'DocumentType.webidl',
+     'DOMCursor.webidl',
+     'DOMException.webidl',
+     'DOMImplementation.webidl',
+     'DominatorTree.webidl',
+     'DOMMatrix.webidl',
+     'DOMParser.webidl',

+ 2028 - 0
frg/work-js/mozilla-release/patches/1430547-59a1.patch

@@ -0,0 +1,2028 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1516012700 -3600
+# Node ID fb4cc60cfca29f7e18949c87f070085f26ead21f
+# Parent  e855470087a77f36909ec0591b54376e93bb6b8d
+Bug 1430547 - devtools-reps v0.19.0: update reps bundle from GitHub;r=Honza."
+
+MozReview-Commit-ID: HEzrPsSBiXx
+
+diff --git a/devtools/client/shared/components/reps/reps.css b/devtools/client/shared/components/reps/reps.css
+--- a/devtools/client/shared/components/reps/reps.css
++++ b/devtools/client/shared/components/reps/reps.css
+@@ -273,16 +273,22 @@
+   flex-shrink: 0;
+ }
+ 
+ /* Align with expandables siblings (where we have the arrow) */
+ .tree-node[data-expandable="false"] .tree-indent:last-of-type {
+   margin-inline-end: 15px;
+ }
+ 
++/* For non expandable root nodes, we don't have .tree-indent elements, so we declare
++   the margin on the start of the node */
++.tree-node[data-expandable="false"][aria-level="0"] {
++  padding-inline-start: 15px
++}
++
+ .tree .tree-node[data-expandable="true"] {
+   cursor: default;
+ }
+ 
+ .tree .tree-node:not(.focused):hover {
+   background-color: #F0F9FE;
+ }
+ 
+diff --git a/devtools/client/shared/components/reps/reps.js b/devtools/client/shared/components/reps/reps.js
+--- a/devtools/client/shared/components/reps/reps.js
++++ b/devtools/client/shared/components/reps/reps.js
+@@ -2,17 +2,17 @@
+ 	if(typeof exports === 'object' && typeof module === 'object')
+ 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
+ 	else if(typeof define === 'function' && define.amd)
+ 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
+ 	else {
+ 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
+ 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
+ 	}
+-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_53__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
++})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_58__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_6__) {
+ return /******/ (function(modules) { // webpackBootstrap
+ /******/ 	// The module cache
+ /******/ 	var installedModules = {};
+ /******/
+ /******/ 	// The require function
+ /******/ 	function __webpack_require__(moduleId) {
+ /******/
+ /******/ 		// Check if module is in cache
+@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
+ /******/
+ /******/ 	// Object.prototype.hasOwnProperty.call
+ /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ /******/
+ /******/ 	// __webpack_public_path__
+ /******/ 	__webpack_require__.p = "/assets/build";
+ /******/
+ /******/ 	// Load entry module and return exports
+-/******/ 	return __webpack_require__(__webpack_require__.s = 17);
++/******/ 	return __webpack_require__(__webpack_require__.s = 18);
+ /******/ })
+ /************************************************************************/
+ /******/ ([
+ /* 0 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+@@ -518,50 +518,50 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-__webpack_require__(18);
++__webpack_require__(19);
+ 
+ // Load all existing rep templates
+-const Undefined = __webpack_require__(19);
+-const Null = __webpack_require__(20);
+-const StringRep = __webpack_require__(6);
+-const LongStringRep = __webpack_require__(21);
+-const Number = __webpack_require__(22);
++const Undefined = __webpack_require__(20);
++const Null = __webpack_require__(21);
++const StringRep = __webpack_require__(7);
++const LongStringRep = __webpack_require__(22);
++const Number = __webpack_require__(23);
+ const ArrayRep = __webpack_require__(10);
+-const Obj = __webpack_require__(23);
+-const SymbolRep = __webpack_require__(24);
+-const InfinityRep = __webpack_require__(25);
+-const NaNRep = __webpack_require__(26);
+-const Accessor = __webpack_require__(27);
++const Obj = __webpack_require__(24);
++const SymbolRep = __webpack_require__(25);
++const InfinityRep = __webpack_require__(26);
++const NaNRep = __webpack_require__(27);
++const Accessor = __webpack_require__(28);
+ 
+ // DOM types (grips)
+-const Attribute = __webpack_require__(28);
+-const DateTime = __webpack_require__(29);
+-const Document = __webpack_require__(30);
+-const Event = __webpack_require__(31);
+-const Func = __webpack_require__(32);
+-const PromiseRep = __webpack_require__(36);
+-const RegExp = __webpack_require__(37);
+-const StyleSheet = __webpack_require__(38);
+-const CommentNode = __webpack_require__(39);
+-const ElementNode = __webpack_require__(40);
+-const TextNode = __webpack_require__(41);
+-const ErrorRep = __webpack_require__(42);
+-const Window = __webpack_require__(43);
+-const ObjectWithText = __webpack_require__(44);
+-const ObjectWithURL = __webpack_require__(45);
+-const GripArray = __webpack_require__(13);
+-const GripMap = __webpack_require__(14);
+-const GripMapEntry = __webpack_require__(15);
++const Attribute = __webpack_require__(29);
++const DateTime = __webpack_require__(30);
++const Document = __webpack_require__(31);
++const Event = __webpack_require__(32);
++const Func = __webpack_require__(33);
++const PromiseRep = __webpack_require__(38);
++const RegExp = __webpack_require__(39);
++const StyleSheet = __webpack_require__(40);
++const CommentNode = __webpack_require__(41);
++const ElementNode = __webpack_require__(42);
++const TextNode = __webpack_require__(43);
++const ErrorRep = __webpack_require__(44);
++const Window = __webpack_require__(45);
++const ObjectWithText = __webpack_require__(46);
++const ObjectWithURL = __webpack_require__(47);
++const GripArray = __webpack_require__(12);
++const GripMap = __webpack_require__(13);
++const GripMapEntry = __webpack_require__(14);
+ const Grip = __webpack_require__(8);
+ 
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+ let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+ 
+ /**
+@@ -734,16 +734,22 @@ function PropRep(props) {
+   }, equal), Rep(Object.assign({}, props))];
+ }
+ 
+ // Exports from this module
+ module.exports = wrapRender(PropRep);
+ 
+ /***/ }),
+ /* 6 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_6__;
++
++/***/ }),
++/* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -933,22 +939,16 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(StringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 7 */
+-/***/ (function(module, exports) {
+-
+-module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
+-
+-/***/ }),
+ /* 8 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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
+@@ -1263,33 +1263,33 @@ module.exports = Grip;
+ 
+ /***/ }),
+ /* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+-var _svgInlineReact = __webpack_require__(11);
++var _svgInlineReact = __webpack_require__(34);
+ 
+ var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const React = __webpack_require__(7);
++const React = __webpack_require__(6);
+ const PropTypes = __webpack_require__(2);
+ 
+ 
+ const svg = {
+-  "open-inspector": __webpack_require__(34),
+-  "jump-definition": __webpack_require__(35)
++  "open-inspector": __webpack_require__(36),
++  "jump-definition": __webpack_require__(37)
+ };
+ 
+ Svg.propTypes = {
+   className: PropTypes.string
+ };
+ 
+ function Svg(name, props) {
+   if (!svg[name]) {
+@@ -1456,114 +1456,16 @@ module.exports = {
+ 
+ /***/ }),
+ /* 11 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-
+-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+-
+-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+-
+-var _react = __webpack_require__(7);
+-
+-var _react2 = _interopRequireDefault(_react);
+-
+-var _propTypes = __webpack_require__(2);
+-
+-var _util = __webpack_require__(33);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+-
+-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+-
+-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+-
+-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+-
+-var process = process || { env: {} };
+-
+-var InlineSVG = function (_React$Component) {
+-    _inherits(InlineSVG, _React$Component);
+-
+-    function InlineSVG() {
+-        _classCallCheck(this, InlineSVG);
+-
+-        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
+-    }
+-
+-    _createClass(InlineSVG, [{
+-        key: 'componentWillReceiveProps',
+-        value: function componentWillReceiveProps(_ref) {
+-            var children = _ref.children;
+-
+-            if ("production" !== process.env.NODE_ENV && children != null) {
+-                console.info('<InlineSVG />: `children` prop will be ignored.');
+-            }
+-        }
+-    }, {
+-        key: 'render',
+-        value: function render() {
+-            var Element = void 0,
+-                __html = void 0,
+-                svgProps = void 0;
+-
+-            var _props = this.props,
+-                element = _props.element,
+-                raw = _props.raw,
+-                src = _props.src,
+-                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
+-
+-            if (raw === true) {
+-                Element = 'svg';
+-                svgProps = (0, _util.extractSVGProps)(src);
+-                __html = (0, _util.getSVGFromSource)(src).innerHTML;
+-            }
+-            __html = __html || src;
+-            Element = Element || element;
+-            svgProps = svgProps || {};
+-
+-            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
+-                dangerouslySetInnerHTML: { __html: __html } }));
+-        }
+-    }]);
+-
+-    return InlineSVG;
+-}(_react2.default.Component);
+-
+-exports.default = InlineSVG;
+-
+-
+-InlineSVG.defaultProps = {
+-    element: 'i',
+-    raw: false,
+-    src: ''
+-};
+-
+-InlineSVG.propTypes = {
+-    src: _propTypes.string.isRequired,
+-    element: _propTypes.string,
+-    raw: _propTypes.bool
+-};
+-
+-/***/ }),
+-/* 12 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+ /* 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/. */
+ 
+ module.exports = {
+   ELEMENT_NODE: 1,
+   ATTRIBUTE_NODE: 2,
+   TEXT_NODE: 3,
+@@ -1582,17 +1484,17 @@ module.exports = {
+   DOCUMENT_POSITION_PRECEDING: 0x02,
+   DOCUMENT_POSITION_FOLLOWING: 0x04,
+   DOCUMENT_POSITION_CONTAINS: 0x08,
+   DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+   DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+ };
+ 
+ /***/ }),
+-/* 13 */
++/* 12 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -1800,17 +1702,17 @@ maxLengthMap.set(MODE.LONG, 10);
+ module.exports = {
+   rep: wrapRender(GripArray),
+   supportsObject,
+   maxLengthMap,
+   getLength
+ };
+ 
+ /***/ }),
+-/* 14 */
++/* 13 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2008,17 +1910,17 @@ maxLengthMap.set(MODE.LONG, 10);
+ module.exports = {
+   rep: wrapRender(GripMap),
+   supportsObject,
+   maxLengthMap,
+   getLength
+ };
+ 
+ /***/ }),
+-/* 15 */
++/* 14 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2086,32 +1988,128 @@ function createGripMapEntry(key, value) 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(GripMapEntry),
+   createGripMapEntry,
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 16 */
++/* 15 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-const { get, has } = __webpack_require__(53);
++const client = __webpack_require__(16);
++const loadProperties = __webpack_require__(57);
++const node = __webpack_require__(17);
++
++module.exports = {
++  client,
++  loadProperties,
++  node
++};
++
++/***/ }),
++/* 16 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++async function enumIndexedProperties(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumIndexedProperties", e);
++    return {};
++  }
++} /* 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/. */
++
++async function enumNonIndexedProperties(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumNonIndexedProperties", e);
++    return {};
++  }
++}
++
++async function enumEntries(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumEntries();
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumEntries", e);
++    return {};
++  }
++}
++
++async function enumSymbols(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumSymbols();
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumSymbols", e);
++    return {};
++  }
++}
++
++async function getPrototype(objectClient) {
++  if (typeof objectClient.getPrototype !== "function") {
++    console.error("objectClient.getPrototype is not a function");
++    return Promise.resolve({});
++  }
++  return objectClient.getPrototype();
++}
++
++function iteratorSlice(iterator, start, end) {
++  start = start || 0;
++  const count = end ? end - start + 1 : iterator.count;
++  return iterator.slice(start, count);
++}
++
++module.exports = {
++  enumEntries,
++  enumIndexedProperties,
++  enumNonIndexedProperties,
++  enumSymbols,
++  getPrototype
++};
++
++/***/ }),
++/* 17 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const { get, has } = __webpack_require__(58);
+ const { maybeEscapePropertyName } = __webpack_require__(0);
+ const ArrayRep = __webpack_require__(10);
+-const GripArrayRep = __webpack_require__(13);
+-const GripMap = __webpack_require__(14);
+-const GripMapEntryRep = __webpack_require__(15);
++const GripArrayRep = __webpack_require__(12);
++const GripMap = __webpack_require__(13);
++const GripMapEntryRep = __webpack_require__(14);
+ 
+ const MAX_NUMERICAL_PROPERTIES = 100;
+ 
+ const NODE_TYPES = {
+   BUCKET: Symbol("[n…n]"),
+   DEFAULT_PROPERTIES: Symbol("[default properties]"),
+   ENTRIES: Symbol("<entries>"),
+   GET: Symbol("<get>"),
+@@ -2679,53 +2677,16 @@ function getClosestNonBucketNode(item) {
+   const parent = getParent(item);
+   if (!parent) {
+     return null;
+   }
+ 
+   return getClosestNonBucketNode(parent);
+ }
+ 
+-function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item))
+-  // The data is loaded when expanding the window node.
+-  && !nodeIsDefaultProperties(item);
+-}
+-
+-function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item)
+-  // The data is loaded when expanding the window node.
+-  && !nodeIsDefaultProperties(item);
+-}
+-
+-function shouldLoadItemEntries(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
+-}
+-
+-function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
+-  const value = getValue(item);
+-
+-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item);
+-}
+-
+-function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
+-  const value = getValue(item);
+-
+-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsProxy(item);
+-}
+-
+ module.exports = {
+   createNode,
+   getChildren,
+   getClosestGripNode,
+   getClosestNonBucketNode,
+   getParent,
+   getNumericalPropertiesCount,
+   getValue,
+@@ -2751,42 +2712,37 @@ module.exports = {
+   nodeIsPromise,
+   nodeIsPrototype,
+   nodeIsProxy,
+   nodeIsSetter,
+   nodeIsWindow,
+   nodeNeedsNumericalBuckets,
+   nodeSupportsNumericalBucketing,
+   setNodeChildren,
+-  shouldLoadItemEntries,
+-  shouldLoadItemIndexedProperties,
+-  shouldLoadItemNonIndexedProperties,
+-  shouldLoadItemPrototype,
+-  shouldLoadItemSymbols,
+   sortProperties,
+   NODE_TYPES,
+   // Export for testing purpose.
+   SAFE_PATH_PREFIX
+ };
+ 
+ /***/ }),
+-/* 17 */
++/* 18 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ const { MODE } = __webpack_require__(3);
+ const { REPS, getRep } = __webpack_require__(4);
+-const ObjectInspector = __webpack_require__(46);
+-const ObjectInspectorUtils = __webpack_require__(16);
++const ObjectInspector = __webpack_require__(48);
++const ObjectInspectorUtils = __webpack_require__(15);
+ 
+ const {
+   parseURLEncodedText,
+   parseURLParams,
+   maybeEscapePropertyName,
+   getGripPreviewItems
+ } = __webpack_require__(0);
+ 
+@@ -2798,23 +2754,23 @@ module.exports = {
+   parseURLEncodedText,
+   parseURLParams,
+   getGripPreviewItems,
+   ObjectInspector,
+   ObjectInspectorUtils
+ };
+ 
+ /***/ }),
+-/* 18 */
++/* 19 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 19 */
++/* 20 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2846,17 +2802,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Undefined),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 20 */
++/* 21 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2888,17 +2844,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Null),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 21 */
++/* 22 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2965,17 +2921,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(LongStringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 22 */
++/* 23 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3017,17 +2973,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Number),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 23 */
++/* 24 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3195,17 +3151,17 @@ function supportsObject(object) {
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 24 */
++/* 25 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3247,17 +3203,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(SymbolRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 25 */
++/* 26 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3295,17 +3251,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(InfinityRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 26 */
++/* 27 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3332,17 +3288,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(NaNRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 27 */
++/* 28 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3402,17 +3358,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Accessor),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 28 */
++/* 29 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3423,17 +3379,17 @@ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ // Reps
+ const {
+   getGripType,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(6);
++const { rep: StringRep } = __webpack_require__(7);
+ 
+ /**
+  * Renders DOM attribute
+  */
+ Attribute.propTypes = {
+   object: PropTypes.object.isRequired
+ };
+ 
+@@ -3463,17 +3419,17 @@ function supportsObject(grip, noGrip = f
+ }
+ 
+ module.exports = {
+   rep: wrapRender(Attribute),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 29 */
++/* 30 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3530,17 +3486,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(DateTime),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 30 */
++/* 31 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3597,17 +3553,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Document),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 31 */
++/* 32 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3704,17 +3660,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Event),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 32 */
++/* 33 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3865,17 +3821,115 @@ function supportsObject(grip, noGrip = f
+ module.exports = {
+   rep: wrapRender(FunctionRep),
+   supportsObject,
+   // exported for testing purpose.
+   getFunctionName
+ };
+ 
+ /***/ }),
+-/* 33 */
++/* 34 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++Object.defineProperty(exports, "__esModule", {
++    value: true
++});
++
++var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
++
++var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
++
++var _react = __webpack_require__(6);
++
++var _react2 = _interopRequireDefault(_react);
++
++var _propTypes = __webpack_require__(2);
++
++var _util = __webpack_require__(35);
++
++function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
++
++function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
++
++function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
++
++function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
++
++function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
++
++var process = process || { env: {} };
++
++var InlineSVG = function (_React$Component) {
++    _inherits(InlineSVG, _React$Component);
++
++    function InlineSVG() {
++        _classCallCheck(this, InlineSVG);
++
++        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
++    }
++
++    _createClass(InlineSVG, [{
++        key: 'componentWillReceiveProps',
++        value: function componentWillReceiveProps(_ref) {
++            var children = _ref.children;
++
++            if ("production" !== process.env.NODE_ENV && children != null) {
++                console.info('<InlineSVG />: `children` prop will be ignored.');
++            }
++        }
++    }, {
++        key: 'render',
++        value: function render() {
++            var Element = void 0,
++                __html = void 0,
++                svgProps = void 0;
++
++            var _props = this.props,
++                element = _props.element,
++                raw = _props.raw,
++                src = _props.src,
++                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
++
++            if (raw === true) {
++                Element = 'svg';
++                svgProps = (0, _util.extractSVGProps)(src);
++                __html = (0, _util.getSVGFromSource)(src).innerHTML;
++            }
++            __html = __html || src;
++            Element = Element || element;
++            svgProps = svgProps || {};
++
++            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
++                dangerouslySetInnerHTML: { __html: __html } }));
++        }
++    }]);
++
++    return InlineSVG;
++}(_react2.default.Component);
++
++exports.default = InlineSVG;
++
++
++InlineSVG.defaultProps = {
++    element: 'i',
++    raw: false,
++    src: ''
++};
++
++InlineSVG.propTypes = {
++    src: _propTypes.string.isRequired,
++    element: _propTypes.string,
++    raw: _propTypes.bool
++};
++
++/***/ }),
++/* 35 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+     value: true
+ });
+@@ -3920,29 +3974,29 @@ function getSVGFromSource(src) {
+ 
+ // get <svg /> element props
+ function extractSVGProps(src) {
+     var map = getSVGFromSource(src).attributes;
+     return map.length > 0 ? serializeAttrs(map) : null;
+ }
+ 
+ /***/ }),
+-/* 34 */
++/* 36 */
+ /***/ (function(module, exports) {
+ 
+ module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
+ 
+ /***/ }),
+-/* 35 */
++/* 37 */
+ /***/ (function(module, exports) {
+ 
+ module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"round\"><g id=\"arrow\" transform=\"translate(1.000000, 3.000000)\"><path d=\"M4.5,0.5 L6.5,2.5\"></path><path d=\"M4.5,2.5 L6.5,4.5\" transform=\"translate(5.500000, 3.500000) scale(1, -1) translate(-5.500000, -3.500000) \"></path><path d=\"M6.00090144,2.5 C4.67806937,2.5 3.67938478,2.5 3.00484766,2.5 C1.99304199,2.5 1.01049805,3.5168457 0.993840144,4.52403846 C0.988750751,4.54723808 0.988750751,5.87097168 0.993840144,8.49523926\" id=\"Path-2\" stroke-linejoin=\"round\"></path></g><g id=\"content-lines\" transform=\"translate(9.000000, 2.000000)\"><path d=\"M1.5,3.5 L5.5,3.5\"></path><path d=\"M0.5,1.5 L5.5,1.5\"></path><path d=\"M0.5,5.5 L5.5,5.5\"></path></g></g></svg>"
+ 
+ /***/ }),
+-/* 36 */
++/* 38 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4042,17 +4096,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(PromiseRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 37 */
++/* 39 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4101,17 +4155,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(RegExp),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 38 */
++/* 40 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4169,17 +4223,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(StyleSheet),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 39 */
++/* 41 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4188,17 +4242,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ const {
+   isGrip,
+   cropString,
+   cropMultipleLines,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(12);
++const nodeConstants = __webpack_require__(11);
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM comment node.
+  */
+ CommentNode.propTypes = {
+   object: PropTypes.object.isRequired,
+@@ -4235,17 +4289,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(CommentNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 40 */
++/* 42 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4253,19 +4307,19 @@ module.exports = {
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ 
+ // Utils
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(6);
++const { rep: StringRep } = __webpack_require__(7);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(12);
++const nodeConstants = __webpack_require__(11);
+ const Svg = __webpack_require__(9);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM element node.
+  */
+@@ -4367,17 +4421,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ElementNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 41 */
++/* 43 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4476,17 +4530,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(TextNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 42 */
++/* 44 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4560,17 +4614,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ErrorRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 43 */
++/* 45 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4638,17 +4692,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(WindowRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 44 */
++/* 46 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4657,17 +4711,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ 
+ // Reps
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+-const String = __webpack_require__(6).rep;
++const String = __webpack_require__(7).rep;
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders a grip object with textual data.
+  */
+ ObjectWithText.propTypes = {
+@@ -4703,17 +4757,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectWithText),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 45 */
++/* 47 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4769,82 +4823,75 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectWithURL),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 46 */
++/* 48 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+-var _devtoolsComponents = __webpack_require__(47);
++var _devtoolsComponents = __webpack_require__(49);
+ 
+ var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const { Component, createFactory } = __webpack_require__(7);
++const { Component, createFactory } = __webpack_require__(6);
+ const PropTypes = __webpack_require__(2);
+ const dom = __webpack_require__(1);
+ 
+ const Tree = createFactory(_devtoolsComponents2.default.Tree);
+-__webpack_require__(51);
+-
+-const classnames = __webpack_require__(52);
++__webpack_require__(55);
++
++const classnames = __webpack_require__(56);
+ 
+ const {
+   REPS: {
+     Rep,
+     Grip
+   }
+ } = __webpack_require__(4);
+ const {
+   MODE
+ } = __webpack_require__(3);
+ 
++const Utils = __webpack_require__(15);
++
+ const {
+   getChildren,
+   getClosestGripNode,
+   getParent,
+   getValue,
+   nodeHasAccessors,
+   nodeHasProperties,
+   nodeIsDefaultProperties,
+   nodeIsFunction,
+   nodeIsGetter,
+   nodeIsMapEntry,
+   nodeIsMissingArguments,
+   nodeIsOptimizedOut,
+   nodeIsPrimitive,
+   nodeIsPrototype,
+   nodeIsSetter,
+-  nodeIsWindow,
+-  shouldLoadItemEntries,
+-  shouldLoadItemIndexedProperties,
+-  shouldLoadItemNonIndexedProperties,
+-  shouldLoadItemPrototype,
+-  shouldLoadItemSymbols
+-} = __webpack_require__(16);
++  nodeIsWindow
++} = Utils.node;
+ 
+ const {
+-  enumEntries,
+-  enumIndexedProperties,
+-  enumNonIndexedProperties,
+-  getPrototype,
+-  enumSymbols
+-} = __webpack_require__(54);
++  loadItemProperties
++} = Utils.loadProperties;
+ 
+ // This implements a component that renders an interactive inspector
+ // for looking at JavaScript objects. It expects descriptions of
+ // objects from the protocol, and will dynamically fetch child
+ // properties as objects are expanded.
+ //
+ // If you want to inspect a single object, pass the name and the
+ // protocol descriptor of it:
+@@ -4959,89 +5006,42 @@ class ObjectInspector extends Component 
+       return {
+         expandedPaths: newPaths
+       };
+     });
+ 
+     if (expand === true) {
+       const gripItem = getClosestGripNode(item);
+       const value = getValue(gripItem);
+-
+       const path = item.path;
+-      const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+-
+-      let promises = [];
+-      let objectClient;
+-      const getObjectClient = () => {
+-        if (objectClient) {
+-          return objectClient;
+-        }
+-        return this.props.createObjectClient(value);
+-      };
+-
+-      if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
+-        promises.push(enumIndexedProperties(getObjectClient(), start, end));
+-      }
+-
+-      if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
+-        promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
+-      }
+-
+-      if (shouldLoadItemEntries(item, loadedProperties)) {
+-        promises.push(enumEntries(getObjectClient(), start, end));
+-      }
+-
+-      if (shouldLoadItemPrototype(item, loadedProperties)) {
+-        promises.push(getPrototype(getObjectClient()));
+-      }
+-
+-      if (shouldLoadItemSymbols(item, loadedProperties)) {
+-        promises.push(enumSymbols(getObjectClient(), start, end));
+-      }
+-
+-      if (promises.length > 0) {
+-        // Set the loading state with the pending promises.
++
++      const onItemPropertiesLoaded = loadItemProperties(item, this.props.createObjectClient, loadedProperties);
++      if (onItemPropertiesLoaded !== null) {
+         this.setState((prevState, props) => {
+           const nextLoading = new Map(prevState.loading);
+-          nextLoading.set(path, promises);
++          nextLoading.set(path, onItemPropertiesLoaded);
+           return {
+             loading: nextLoading
+           };
+         });
+ 
+-        const responses = await Promise.all(promises);
+-
+-        // Let's loop through the responses to build a single response object.
+-        const response = responses.reduce((accumulator, res) => {
+-          Object.entries(res).forEach(([k, v]) => {
+-            if (accumulator.hasOwnProperty(k)) {
+-              if (Array.isArray(accumulator[k])) {
+-                accumulator[k].push(...v);
+-              } else if (typeof accumulator[k] === "object") {
+-                accumulator[k] = Object.assign({}, accumulator[k], v);
+-              }
+-            } else {
+-              accumulator[k] = v;
+-            }
+-          });
+-          return accumulator;
+-        }, {});
++        const properties = await onItemPropertiesLoaded;
+ 
+         this.setState((prevState, props) => {
+           const nextLoading = new Map(prevState.loading);
+           nextLoading.delete(path);
+ 
+           const isRoot = this.props.roots.some(root => {
+             const rootValue = getValue(root);
+             return rootValue && rootValue.actor === value.actor;
+           });
+ 
+           return {
+             actors: isRoot ? prevState.actors : new Set(prevState.actors).add(value.actor),
+-            loadedProperties: new Map(prevState.loadedProperties).set(path, response),
++            loadedProperties: new Map(prevState.loadedProperties).set(path, properties),
+             loading: nextLoading
+           };
+         });
+       }
+     }
+   }
+ 
+   focusItem(item) {
+@@ -5205,76 +5205,72 @@ ObjectInspector.propTypes = {
+   onFocus: PropTypes.func,
+   onDoubleClick: PropTypes.func,
+   onLabelClick: PropTypes.func
+ };
+ 
+ module.exports = ObjectInspector;
+ 
+ /***/ }),
+-/* 47 */
++/* 49 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++var _tree = __webpack_require__(50);
++
++var _tree2 = _interopRequireDefault(_tree);
++
++function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
++
++module.exports = {
++  Tree: _tree2.default
++}; /* 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/. */
++
++/***/ }),
++/* 50 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+   value: true
+ });
+ 
+-var _tree = __webpack_require__(48);
+-
+-var _tree2 = _interopRequireDefault(_tree);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-exports.default = {
+-  Tree: _tree2.default
+-}; /* 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/. */
+-
+-/***/ }),
+-/* 48 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-  value: true
+-});
+-
+-var _react = __webpack_require__(7);
++var _react = __webpack_require__(6);
+ 
+ var _react2 = _interopRequireDefault(_react);
+ 
+ var _reactDomFactories = __webpack_require__(1);
+ 
+ var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
+ 
+ var _propTypes = __webpack_require__(2);
+ 
+ var _propTypes2 = _interopRequireDefault(_propTypes);
+ 
+-var _svgInlineReact = __webpack_require__(11);
++var _svgInlineReact = __webpack_require__(51);
+ 
+ var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+ 
+-var _arrow = __webpack_require__(49);
++var _arrow = __webpack_require__(53);
+ 
+ var _arrow2 = _interopRequireDefault(_arrow);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ const { Component, createFactory, createElement } = _react2.default; /* 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/. */
+ 
+-__webpack_require__(50);
++__webpack_require__(54);
+ 
+ const AUTO_EXPAND_DEPTH = 0; // depth
+ 
+ /**
+  * An arrow that displays whether its node is expanded (▼) or collapsed
+  * (▶). When its node has no children, it is hidden.
+  */
+ class ArrowExpander extends Component {
+@@ -6022,35 +6018,188 @@ class Tree extends Component {
+       style
+     }, nodes);
+   }
+ }
+ 
+ exports.default = Tree;
+ 
+ /***/ }),
+-/* 49 */
++/* 51 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++Object.defineProperty(exports, "__esModule", {
++    value: true
++});
++
++var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
++
++var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
++
++var _react = __webpack_require__(6);
++
++var _react2 = _interopRequireDefault(_react);
++
++var _propTypes = __webpack_require__(2);
++
++var _util = __webpack_require__(52);
++
++function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
++
++function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
++
++function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
++
++function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
++
++function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
++
++var process = process || { env: {} };
++
++var InlineSVG = function (_React$Component) {
++    _inherits(InlineSVG, _React$Component);
++
++    function InlineSVG() {
++        _classCallCheck(this, InlineSVG);
++
++        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
++    }
++
++    _createClass(InlineSVG, [{
++        key: 'componentWillReceiveProps',
++        value: function componentWillReceiveProps(_ref) {
++            var children = _ref.children;
++
++            if ("production" !== process.env.NODE_ENV && children != null) {
++                console.info('<InlineSVG />: `children` prop will be ignored.');
++            }
++        }
++    }, {
++        key: 'render',
++        value: function render() {
++            var Element = void 0,
++                __html = void 0,
++                svgProps = void 0;
++
++            var _props = this.props,
++                element = _props.element,
++                raw = _props.raw,
++                src = _props.src,
++                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
++
++            if (raw === true) {
++                Element = 'svg';
++                svgProps = (0, _util.extractSVGProps)(src);
++                __html = (0, _util.getSVGFromSource)(src).innerHTML;
++            }
++            __html = __html || src;
++            Element = Element || element;
++            svgProps = svgProps || {};
++
++            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
++                dangerouslySetInnerHTML: { __html: __html } }));
++        }
++    }]);
++
++    return InlineSVG;
++}(_react2.default.Component);
++
++exports.default = InlineSVG;
++
++
++InlineSVG.defaultProps = {
++    element: 'i',
++    raw: false,
++    src: ''
++};
++
++InlineSVG.propTypes = {
++    src: _propTypes.string.isRequired,
++    element: _propTypes.string,
++    raw: _propTypes.bool
++};
++
++/***/ }),
++/* 52 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++Object.defineProperty(exports, "__esModule", {
++    value: true
++});
++exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
++exports.startsWith = startsWith;
++exports.serializeAttrs = serializeAttrs;
++exports.getSVGFromSource = getSVGFromSource;
++exports.extractSVGProps = extractSVGProps;
++// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
++
++function convertReactSVGDOMProperty(str) {
++    return str.replace(/[-|:]([a-z])/g, function (g) {
++        return g[1].toUpperCase();
++    });
++}
++
++function startsWith(str, substring) {
++    return str.indexOf(substring) === 0;
++}
++
++var DataPropPrefix = 'data-';
++// Serialize `Attr` objects in `NamedNodeMap`
++function serializeAttrs(map) {
++    var ret = {};
++    for (var prop, i = 0; i < map.length; i++) {
++        var key = map[i].name;
++        if (!startsWith(key, DataPropPrefix)) {
++            prop = convertReactSVGDOMProperty(key);
++        }
++        ret[prop] = map[i].value;
++    }
++    return ret;
++}
++
++function getSVGFromSource(src) {
++    var svgContainer = document.createElement('div');
++    svgContainer.innerHTML = src;
++    var svg = svgContainer.firstElementChild;
++    svg.remove(); // deref from parent element
++    return svg;
++}
++
++// get <svg /> element props
++function extractSVGProps(src) {
++    var map = getSVGFromSource(src).attributes;
++    return map.length > 0 ? serializeAttrs(map) : null;
++}
++
++/***/ }),
++/* 53 */
+ /***/ (function(module, exports) {
+ 
+ module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
+ 
+ /***/ }),
+-/* 50 */
++/* 54 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 51 */
++/* 55 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 52 */
++/* 56 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
+   Copyright (c) 2016 Jed Watson.
+   Licensed under the MIT License (MIT), see
+   http://jedwatson.github.io/classnames
+ */
+ /* global define */
+@@ -6095,91 +6244,153 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
+ 				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+ 	} else {
+ 		window.classNames = classNames;
+ 	}
+ }());
+ 
+ 
+ /***/ }),
+-/* 53 */
+-/***/ (function(module, exports) {
+-
+-module.exports = __WEBPACK_EXTERNAL_MODULE_53__;
+-
+-/***/ }),
+-/* 54 */
++/* 57 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+-async function enumIndexedProperties(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumIndexedProperties", e);
+-    return {};
+-  }
+-} /* 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/. */
+-
+-async function enumNonIndexedProperties(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumNonIndexedProperties", e);
+-    return {};
+-  }
+-}
+-
+-async function enumEntries(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumEntries();
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumEntries", e);
+-    return {};
+-  }
+-}
+-
+-async function enumSymbols(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumSymbols();
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumSymbols", e);
+-    return {};
+-  }
+-}
+-
+-async function getPrototype(objectClient) {
+-  if (typeof objectClient.getPrototype !== "function") {
+-    console.error("objectClient.getPrototype is not a function");
+-    return Promise.resolve({});
+-  }
+-  return objectClient.getPrototype();
+-}
+-
+-function iteratorSlice(iterator, start, end) {
+-  start = start || 0;
+-  const count = end ? end - start + 1 : iterator.count;
+-  return iterator.slice(start, count);
++/* 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/. */
++
++const {
++  enumEntries,
++  enumIndexedProperties,
++  enumNonIndexedProperties,
++  getPrototype,
++  enumSymbols
++} = __webpack_require__(16);
++
++const {
++  getClosestGripNode,
++  getClosestNonBucketNode,
++  getValue,
++  nodeHasAccessors,
++  nodeHasAllEntriesInPreview,
++  nodeHasProperties,
++  nodeIsBucket,
++  nodeIsDefaultProperties,
++  nodeIsEntries,
++  nodeIsMapEntry,
++  nodeIsPrimitive,
++  nodeIsProxy,
++  nodeNeedsNumericalBuckets
++} = __webpack_require__(17);
++
++function loadItemProperties(item, createObjectClient, loadedProperties) {
++  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
++
++  let objectClient;
++  const getObjectClient = () => {
++    if (objectClient) {
++      return objectClient;
++    }
++
++    const gripItem = getClosestGripNode(item);
++    const value = getValue(gripItem);
++    return createObjectClient(value);
++  };
++
++  let loadingPromises = [];
++  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
++    loadingPromises.push(enumIndexedProperties(getObjectClient(), start, end));
++  }
++
++  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
++    loadingPromises.push(enumNonIndexedProperties(getObjectClient(), start, end));
++  }
++
++  if (shouldLoadItemEntries(item, loadedProperties)) {
++    loadingPromises.push(enumEntries(getObjectClient(), start, end));
++  }
++
++  if (shouldLoadItemPrototype(item, loadedProperties)) {
++    loadingPromises.push(getPrototype(getObjectClient()));
++  }
++
++  if (shouldLoadItemSymbols(item, loadedProperties)) {
++    loadingPromises.push(enumSymbols(getObjectClient(), start, end));
++  }
++
++  if (loadingPromises.length === 0) {
++    return null;
++  }
++
++  return Promise.all(loadingPromises).then(responses => responses.reduce((accumulator, res) => {
++    // Let's loop through the responses to build a single response object.
++    Object.entries(res).forEach(([k, v]) => {
++      if (accumulator.hasOwnProperty(k)) {
++        if (Array.isArray(accumulator[k])) {
++          accumulator[k].push(...v);
++        } else if (typeof accumulator[k] === "object") {
++          accumulator[k] = Object.assign({}, accumulator[k], v);
++        }
++      } else {
++        accumulator[k] = v;
++      }
++    });
++    return accumulator;
++  }, {}));
++}
++
++function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item))
++  // The data is loaded when expanding the window node.
++  && !nodeIsDefaultProperties(item);
++}
++
++function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item)
++  // The data is loaded when expanding the window node.
++  && !nodeIsDefaultProperties(item);
++}
++
++function shouldLoadItemEntries(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
++}
++
++function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
++  const value = getValue(item);
++
++  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item);
++}
++
++function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
++  const value = getValue(item);
++
++  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsProxy(item);
+ }
+ 
+ module.exports = {
+-  enumEntries,
+-  enumIndexedProperties,
+-  enumNonIndexedProperties,
+-  enumSymbols,
+-  getPrototype
++  loadItemProperties,
++  shouldLoadItemEntries,
++  shouldLoadItemIndexedProperties,
++  shouldLoadItemNonIndexedProperties,
++  shouldLoadItemPrototype,
++  shouldLoadItemSymbols
+ };
+ 
++/***/ }),
++/* 58 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_58__;
++
+ /***/ })
+ /******/ ]);
+ });

+ 1330 - 0
frg/work-js/mozilla-release/patches/1431740-59a1.patch

@@ -0,0 +1,1330 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1516381894 -3600
+# Node ID 0eb38895885ae0dd84dc6021f40ea2db5024a30c
+# Parent  c6b461c969c7eb7b0ed6fa807cc681fba9f3d219
+Bug 1431740 - release: bump devtools-reps to the 0.19.1 version;r=nchevobbe
+
+MozReview-Commit-ID: 29J1eofzjm8
+
+diff --git a/devtools/client/shared/components/reps/reps.js b/devtools/client/shared/components/reps/reps.js
+--- a/devtools/client/shared/components/reps/reps.js
++++ b/devtools/client/shared/components/reps/reps.js
+@@ -2,17 +2,17 @@
+ 	if(typeof exports === 'object' && typeof module === 'object')
+ 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
+ 	else if(typeof define === 'function' && define.amd)
+ 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
+ 	else {
+ 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
+ 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
+ 	}
+-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_58__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_6__) {
++})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
+ return /******/ (function(modules) { // webpackBootstrap
+ /******/ 	// The module cache
+ /******/ 	var installedModules = {};
+ /******/
+ /******/ 	// The require function
+ /******/ 	function __webpack_require__(moduleId) {
+ /******/
+ /******/ 		// Check if module is in cache
+@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
+ /******/
+ /******/ 	// Object.prototype.hasOwnProperty.call
+ /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ /******/
+ /******/ 	// __webpack_public_path__
+ /******/ 	__webpack_require__.p = "/assets/build";
+ /******/
+ /******/ 	// Load entry module and return exports
+-/******/ 	return __webpack_require__(__webpack_require__.s = 18);
++/******/ 	return __webpack_require__(__webpack_require__.s = 19);
+ /******/ })
+ /************************************************************************/
+ /******/ ([
+ /* 0 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+@@ -518,50 +518,50 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-__webpack_require__(19);
++__webpack_require__(20);
+ 
+ // Load all existing rep templates
+-const Undefined = __webpack_require__(20);
+-const Null = __webpack_require__(21);
+-const StringRep = __webpack_require__(7);
+-const LongStringRep = __webpack_require__(22);
+-const Number = __webpack_require__(23);
++const Undefined = __webpack_require__(21);
++const Null = __webpack_require__(22);
++const StringRep = __webpack_require__(6);
++const LongStringRep = __webpack_require__(23);
++const Number = __webpack_require__(24);
+ const ArrayRep = __webpack_require__(10);
+-const Obj = __webpack_require__(24);
+-const SymbolRep = __webpack_require__(25);
+-const InfinityRep = __webpack_require__(26);
+-const NaNRep = __webpack_require__(27);
+-const Accessor = __webpack_require__(28);
++const Obj = __webpack_require__(25);
++const SymbolRep = __webpack_require__(26);
++const InfinityRep = __webpack_require__(27);
++const NaNRep = __webpack_require__(28);
++const Accessor = __webpack_require__(29);
+ 
+ // DOM types (grips)
+-const Attribute = __webpack_require__(29);
+-const DateTime = __webpack_require__(30);
+-const Document = __webpack_require__(31);
+-const Event = __webpack_require__(32);
+-const Func = __webpack_require__(33);
++const Attribute = __webpack_require__(30);
++const DateTime = __webpack_require__(31);
++const Document = __webpack_require__(32);
++const Event = __webpack_require__(33);
++const Func = __webpack_require__(34);
+ const PromiseRep = __webpack_require__(38);
+ const RegExp = __webpack_require__(39);
+ const StyleSheet = __webpack_require__(40);
+ const CommentNode = __webpack_require__(41);
+ const ElementNode = __webpack_require__(42);
+ const TextNode = __webpack_require__(43);
+ const ErrorRep = __webpack_require__(44);
+ const Window = __webpack_require__(45);
+ const ObjectWithText = __webpack_require__(46);
+ const ObjectWithURL = __webpack_require__(47);
+-const GripArray = __webpack_require__(12);
+-const GripMap = __webpack_require__(13);
+-const GripMapEntry = __webpack_require__(14);
++const GripArray = __webpack_require__(13);
++const GripMap = __webpack_require__(14);
++const GripMapEntry = __webpack_require__(15);
+ const Grip = __webpack_require__(8);
+ 
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+ let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+ 
+ /**
+@@ -734,22 +734,16 @@ function PropRep(props) {
+   }, equal), Rep(Object.assign({}, props))];
+ }
+ 
+ // Exports from this module
+ module.exports = wrapRender(PropRep);
+ 
+ /***/ }),
+ /* 6 */
+-/***/ (function(module, exports) {
+-
+-module.exports = __WEBPACK_EXTERNAL_MODULE_6__;
+-
+-/***/ }),
+-/* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -939,16 +933,22 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(StringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
++/* 7 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
++
++/***/ }),
+ /* 8 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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
+@@ -1263,27 +1263,27 @@ module.exports = Grip;
+ 
+ /***/ }),
+ /* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+-var _svgInlineReact = __webpack_require__(34);
++var _svgInlineReact = __webpack_require__(11);
+ 
+ var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const React = __webpack_require__(6);
++const React = __webpack_require__(7);
+ const PropTypes = __webpack_require__(2);
+ 
+ 
+ const svg = {
+   "open-inspector": __webpack_require__(36),
+   "jump-definition": __webpack_require__(37)
+ };
+ 
+@@ -1456,16 +1456,114 @@ module.exports = {
+ 
+ /***/ }),
+ /* 11 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
++Object.defineProperty(exports, "__esModule", {
++    value: true
++});
++
++var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
++
++var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
++
++var _react = __webpack_require__(7);
++
++var _react2 = _interopRequireDefault(_react);
++
++var _propTypes = __webpack_require__(2);
++
++var _util = __webpack_require__(35);
++
++function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
++
++function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
++
++function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
++
++function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
++
++function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
++
++var process = process || { env: {} };
++
++var InlineSVG = function (_React$Component) {
++    _inherits(InlineSVG, _React$Component);
++
++    function InlineSVG() {
++        _classCallCheck(this, InlineSVG);
++
++        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
++    }
++
++    _createClass(InlineSVG, [{
++        key: 'componentWillReceiveProps',
++        value: function componentWillReceiveProps(_ref) {
++            var children = _ref.children;
++
++            if ("production" !== process.env.NODE_ENV && children != null) {
++                console.info('<InlineSVG />: `children` prop will be ignored.');
++            }
++        }
++    }, {
++        key: 'render',
++        value: function render() {
++            var Element = void 0,
++                __html = void 0,
++                svgProps = void 0;
++
++            var _props = this.props,
++                element = _props.element,
++                raw = _props.raw,
++                src = _props.src,
++                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
++
++            if (raw === true) {
++                Element = 'svg';
++                svgProps = (0, _util.extractSVGProps)(src);
++                __html = (0, _util.getSVGFromSource)(src).innerHTML;
++            }
++            __html = __html || src;
++            Element = Element || element;
++            svgProps = svgProps || {};
++
++            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
++                dangerouslySetInnerHTML: { __html: __html } }));
++        }
++    }]);
++
++    return InlineSVG;
++}(_react2.default.Component);
++
++exports.default = InlineSVG;
++
++
++InlineSVG.defaultProps = {
++    element: 'i',
++    raw: false,
++    src: ''
++};
++
++InlineSVG.propTypes = {
++    src: _propTypes.string.isRequired,
++    element: _propTypes.string,
++    raw: _propTypes.bool
++};
++
++/***/ }),
++/* 12 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
+ /* 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/. */
+ 
+ module.exports = {
+   ELEMENT_NODE: 1,
+   ATTRIBUTE_NODE: 2,
+   TEXT_NODE: 3,
+@@ -1484,17 +1582,17 @@ module.exports = {
+   DOCUMENT_POSITION_PRECEDING: 0x02,
+   DOCUMENT_POSITION_FOLLOWING: 0x04,
+   DOCUMENT_POSITION_CONTAINS: 0x08,
+   DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+   DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+ };
+ 
+ /***/ }),
+-/* 12 */
++/* 13 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -1702,17 +1800,17 @@ maxLengthMap.set(MODE.LONG, 10);
+ module.exports = {
+   rep: wrapRender(GripArray),
+   supportsObject,
+   maxLengthMap,
+   getLength
+ };
+ 
+ /***/ }),
+-/* 13 */
++/* 14 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -1910,17 +2008,17 @@ maxLengthMap.set(MODE.LONG, 10);
+ module.exports = {
+   rep: wrapRender(GripMap),
+   supportsObject,
+   maxLengthMap,
+   getLength
+ };
+ 
+ /***/ }),
+-/* 14 */
++/* 15 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -1988,38 +2086,38 @@ function createGripMapEntry(key, value) 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(GripMapEntry),
+   createGripMapEntry,
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 15 */
++/* 16 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-const client = __webpack_require__(16);
+-const loadProperties = __webpack_require__(57);
+-const node = __webpack_require__(17);
++const client = __webpack_require__(17);
++const loadProperties = __webpack_require__(55);
++const node = __webpack_require__(18);
+ 
+ module.exports = {
+   client,
+   loadProperties,
+   node
+ };
+ 
+ /***/ }),
+-/* 16 */
++/* 17 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ async function enumIndexedProperties(objectClient, start, end) {
+   try {
+     const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
+@@ -2084,32 +2182,32 @@ module.exports = {
+   enumEntries,
+   enumIndexedProperties,
+   enumNonIndexedProperties,
+   enumSymbols,
+   getPrototype
+ };
+ 
+ /***/ }),
+-/* 17 */
++/* 18 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-const { get, has } = __webpack_require__(58);
++const { get, has } = __webpack_require__(56);
+ const { maybeEscapePropertyName } = __webpack_require__(0);
+ const ArrayRep = __webpack_require__(10);
+-const GripArrayRep = __webpack_require__(12);
+-const GripMap = __webpack_require__(13);
+-const GripMapEntryRep = __webpack_require__(14);
++const GripArrayRep = __webpack_require__(13);
++const GripMap = __webpack_require__(14);
++const GripMapEntryRep = __webpack_require__(15);
+ 
+ const MAX_NUMERICAL_PROPERTIES = 100;
+ 
+ const NODE_TYPES = {
+   BUCKET: Symbol("[n…n]"),
+   DEFAULT_PROPERTIES: Symbol("[default properties]"),
+   ENTRIES: Symbol("<entries>"),
+   GET: Symbol("<get>"),
+@@ -2719,30 +2817,30 @@ module.exports = {
+   setNodeChildren,
+   sortProperties,
+   NODE_TYPES,
+   // Export for testing purpose.
+   SAFE_PATH_PREFIX
+ };
+ 
+ /***/ }),
+-/* 18 */
++/* 19 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ const { MODE } = __webpack_require__(3);
+ const { REPS, getRep } = __webpack_require__(4);
+ const ObjectInspector = __webpack_require__(48);
+-const ObjectInspectorUtils = __webpack_require__(15);
++const ObjectInspectorUtils = __webpack_require__(16);
+ 
+ const {
+   parseURLEncodedText,
+   parseURLParams,
+   maybeEscapePropertyName,
+   getGripPreviewItems
+ } = __webpack_require__(0);
+ 
+@@ -2754,23 +2852,23 @@ module.exports = {
+   parseURLEncodedText,
+   parseURLParams,
+   getGripPreviewItems,
+   ObjectInspector,
+   ObjectInspectorUtils
+ };
+ 
+ /***/ }),
+-/* 19 */
++/* 20 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 20 */
++/* 21 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2802,17 +2900,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Undefined),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 21 */
++/* 22 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2844,17 +2942,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Null),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 22 */
++/* 23 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2921,17 +3019,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(LongStringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 23 */
++/* 24 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2973,17 +3071,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Number),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 24 */
++/* 25 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3151,17 +3249,17 @@ function supportsObject(object) {
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 25 */
++/* 26 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3203,17 +3301,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(SymbolRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 26 */
++/* 27 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3251,17 +3349,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(InfinityRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 27 */
++/* 28 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3288,17 +3386,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(NaNRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 28 */
++/* 29 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3358,17 +3456,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Accessor),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 29 */
++/* 30 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3379,17 +3477,17 @@ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ // Reps
+ const {
+   getGripType,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(7);
++const { rep: StringRep } = __webpack_require__(6);
+ 
+ /**
+  * Renders DOM attribute
+  */
+ Attribute.propTypes = {
+   object: PropTypes.object.isRequired
+ };
+ 
+@@ -3419,17 +3517,17 @@ function supportsObject(grip, noGrip = f
+ }
+ 
+ module.exports = {
+   rep: wrapRender(Attribute),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 30 */
++/* 31 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3486,17 +3584,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(DateTime),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 31 */
++/* 32 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3553,17 +3651,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Document),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 32 */
++/* 33 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3660,17 +3758,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Event),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 33 */
++/* 34 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3821,114 +3919,16 @@ function supportsObject(grip, noGrip = f
+ module.exports = {
+   rep: wrapRender(FunctionRep),
+   supportsObject,
+   // exported for testing purpose.
+   getFunctionName
+ };
+ 
+ /***/ }),
+-/* 34 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-
+-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+-
+-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+-
+-var _react = __webpack_require__(6);
+-
+-var _react2 = _interopRequireDefault(_react);
+-
+-var _propTypes = __webpack_require__(2);
+-
+-var _util = __webpack_require__(35);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+-
+-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+-
+-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+-
+-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+-
+-var process = process || { env: {} };
+-
+-var InlineSVG = function (_React$Component) {
+-    _inherits(InlineSVG, _React$Component);
+-
+-    function InlineSVG() {
+-        _classCallCheck(this, InlineSVG);
+-
+-        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
+-    }
+-
+-    _createClass(InlineSVG, [{
+-        key: 'componentWillReceiveProps',
+-        value: function componentWillReceiveProps(_ref) {
+-            var children = _ref.children;
+-
+-            if ("production" !== process.env.NODE_ENV && children != null) {
+-                console.info('<InlineSVG />: `children` prop will be ignored.');
+-            }
+-        }
+-    }, {
+-        key: 'render',
+-        value: function render() {
+-            var Element = void 0,
+-                __html = void 0,
+-                svgProps = void 0;
+-
+-            var _props = this.props,
+-                element = _props.element,
+-                raw = _props.raw,
+-                src = _props.src,
+-                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
+-
+-            if (raw === true) {
+-                Element = 'svg';
+-                svgProps = (0, _util.extractSVGProps)(src);
+-                __html = (0, _util.getSVGFromSource)(src).innerHTML;
+-            }
+-            __html = __html || src;
+-            Element = Element || element;
+-            svgProps = svgProps || {};
+-
+-            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
+-                dangerouslySetInnerHTML: { __html: __html } }));
+-        }
+-    }]);
+-
+-    return InlineSVG;
+-}(_react2.default.Component);
+-
+-exports.default = InlineSVG;
+-
+-
+-InlineSVG.defaultProps = {
+-    element: 'i',
+-    raw: false,
+-    src: ''
+-};
+-
+-InlineSVG.propTypes = {
+-    src: _propTypes.string.isRequired,
+-    element: _propTypes.string,
+-    raw: _propTypes.bool
+-};
+-
+-/***/ }),
+ /* 35 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+     value: true
+@@ -4242,17 +4242,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ const {
+   isGrip,
+   cropString,
+   cropMultipleLines,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(11);
++const nodeConstants = __webpack_require__(12);
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM comment node.
+  */
+ CommentNode.propTypes = {
+   object: PropTypes.object.isRequired,
+@@ -4307,19 +4307,19 @@ module.exports = {
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ 
+ // Utils
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(7);
++const { rep: StringRep } = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(11);
++const nodeConstants = __webpack_require__(12);
+ const Svg = __webpack_require__(9);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM element node.
+  */
+@@ -4711,17 +4711,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ 
+ // Reps
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+-const String = __webpack_require__(7).rep;
++const String = __webpack_require__(6).rep;
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders a grip object with textual data.
+  */
+ ObjectWithText.propTypes = {
+@@ -4839,36 +4839,36 @@ var _devtoolsComponents = __webpack_requ
+ var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const { Component, createFactory } = __webpack_require__(6);
++const { Component, createFactory } = __webpack_require__(7);
+ const PropTypes = __webpack_require__(2);
+ const dom = __webpack_require__(1);
+ 
+ const Tree = createFactory(_devtoolsComponents2.default.Tree);
+-__webpack_require__(55);
+-
+-const classnames = __webpack_require__(56);
++__webpack_require__(53);
++
++const classnames = __webpack_require__(54);
+ 
+ const {
+   REPS: {
+     Rep,
+     Grip
+   }
+ } = __webpack_require__(4);
+ const {
+   MODE
+ } = __webpack_require__(3);
+ 
+-const Utils = __webpack_require__(15);
++const Utils = __webpack_require__(16);
+ 
+ const {
+   getChildren,
+   getClosestGripNode,
+   getParent,
+   getValue,
+   nodeHasAccessors,
+   nodeHasProperties,
+@@ -4937,17 +4937,25 @@ class ObjectInspector extends Component 
+     self.getRoots = this.getRoots.bind(this);
+   }
+ 
+   shouldComponentUpdate(nextProps, nextState) {
+     const {
+       expandedPaths,
+       loadedProperties
+     } = this.state;
+-    return this.props.roots !== nextProps.roots || expandedPaths.size !== nextState.expandedPaths.size || loadedProperties.size !== nextState.loadedProperties.size || [...expandedPaths].some(key => !nextState.expandedPaths.has(key));
++
++    if (this.props.roots !== nextProps.roots) {
++      // Since the roots changed, we assume the properties did as well. Thus we can clear
++      // the cachedNodes to avoid bugs and memory leaks.
++      this.cachedNodes.clear();
++      return true;
++    }
++
++    return expandedPaths.size !== nextState.expandedPaths.size || loadedProperties.size !== nextState.loadedProperties.size || [...expandedPaths].some(key => !nextState.expandedPaths.has(key));
+   }
+ 
+   componentWillUnmount() {
+     const { releaseActor } = this.props;
+     if (typeof releaseActor !== "function") {
+       return;
+     }
+ 
+@@ -5234,43 +5242,43 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+   value: true
+ });
+ 
+-var _react = __webpack_require__(6);
++var _react = __webpack_require__(7);
+ 
+ var _react2 = _interopRequireDefault(_react);
+ 
+ var _reactDomFactories = __webpack_require__(1);
+ 
+ var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
+ 
+ var _propTypes = __webpack_require__(2);
+ 
+ var _propTypes2 = _interopRequireDefault(_propTypes);
+ 
+-var _svgInlineReact = __webpack_require__(51);
++var _svgInlineReact = __webpack_require__(11);
+ 
+ var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+ 
+-var _arrow = __webpack_require__(53);
++var _arrow = __webpack_require__(51);
+ 
+ var _arrow2 = _interopRequireDefault(_arrow);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ const { Component, createFactory, createElement } = _react2.default; /* 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/. */
+ 
+-__webpack_require__(54);
++__webpack_require__(52);
+ 
+ const AUTO_EXPAND_DEPTH = 0; // depth
+ 
+ /**
+  * An arrow that displays whether its node is expanded (▼) or collapsed
+  * (▶). When its node has no children, it is hidden.
+  */
+ class ArrowExpander extends Component {
+@@ -6019,187 +6027,34 @@ class Tree extends Component {
+     }, nodes);
+   }
+ }
+ 
+ exports.default = Tree;
+ 
+ /***/ }),
+ /* 51 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-
+-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+-
+-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+-
+-var _react = __webpack_require__(6);
+-
+-var _react2 = _interopRequireDefault(_react);
+-
+-var _propTypes = __webpack_require__(2);
+-
+-var _util = __webpack_require__(52);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+-
+-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+-
+-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+-
+-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+-
+-var process = process || { env: {} };
+-
+-var InlineSVG = function (_React$Component) {
+-    _inherits(InlineSVG, _React$Component);
+-
+-    function InlineSVG() {
+-        _classCallCheck(this, InlineSVG);
+-
+-        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
+-    }
+-
+-    _createClass(InlineSVG, [{
+-        key: 'componentWillReceiveProps',
+-        value: function componentWillReceiveProps(_ref) {
+-            var children = _ref.children;
+-
+-            if ("production" !== process.env.NODE_ENV && children != null) {
+-                console.info('<InlineSVG />: `children` prop will be ignored.');
+-            }
+-        }
+-    }, {
+-        key: 'render',
+-        value: function render() {
+-            var Element = void 0,
+-                __html = void 0,
+-                svgProps = void 0;
+-
+-            var _props = this.props,
+-                element = _props.element,
+-                raw = _props.raw,
+-                src = _props.src,
+-                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
+-
+-            if (raw === true) {
+-                Element = 'svg';
+-                svgProps = (0, _util.extractSVGProps)(src);
+-                __html = (0, _util.getSVGFromSource)(src).innerHTML;
+-            }
+-            __html = __html || src;
+-            Element = Element || element;
+-            svgProps = svgProps || {};
+-
+-            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
+-                dangerouslySetInnerHTML: { __html: __html } }));
+-        }
+-    }]);
+-
+-    return InlineSVG;
+-}(_react2.default.Component);
+-
+-exports.default = InlineSVG;
+-
+-
+-InlineSVG.defaultProps = {
+-    element: 'i',
+-    raw: false,
+-    src: ''
+-};
+-
+-InlineSVG.propTypes = {
+-    src: _propTypes.string.isRequired,
+-    element: _propTypes.string,
+-    raw: _propTypes.bool
+-};
+-
+-/***/ }),
+-/* 52 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
+-exports.startsWith = startsWith;
+-exports.serializeAttrs = serializeAttrs;
+-exports.getSVGFromSource = getSVGFromSource;
+-exports.extractSVGProps = extractSVGProps;
+-// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
+-
+-function convertReactSVGDOMProperty(str) {
+-    return str.replace(/[-|:]([a-z])/g, function (g) {
+-        return g[1].toUpperCase();
+-    });
+-}
+-
+-function startsWith(str, substring) {
+-    return str.indexOf(substring) === 0;
+-}
+-
+-var DataPropPrefix = 'data-';
+-// Serialize `Attr` objects in `NamedNodeMap`
+-function serializeAttrs(map) {
+-    var ret = {};
+-    for (var prop, i = 0; i < map.length; i++) {
+-        var key = map[i].name;
+-        if (!startsWith(key, DataPropPrefix)) {
+-            prop = convertReactSVGDOMProperty(key);
+-        }
+-        ret[prop] = map[i].value;
+-    }
+-    return ret;
+-}
+-
+-function getSVGFromSource(src) {
+-    var svgContainer = document.createElement('div');
+-    svgContainer.innerHTML = src;
+-    var svg = svgContainer.firstElementChild;
+-    svg.remove(); // deref from parent element
+-    return svg;
+-}
+-
+-// get <svg /> element props
+-function extractSVGProps(src) {
+-    var map = getSVGFromSource(src).attributes;
+-    return map.length > 0 ? serializeAttrs(map) : null;
+-}
+-
+-/***/ }),
+-/* 53 */
+ /***/ (function(module, exports) {
+ 
+ module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
+ 
+ /***/ }),
+-/* 54 */
++/* 52 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 55 */
++/* 53 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 56 */
++/* 54 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
+   Copyright (c) 2016 Jed Watson.
+   Licensed under the MIT License (MIT), see
+   http://jedwatson.github.io/classnames
+ */
+ /* global define */
+@@ -6244,49 +6099,49 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
+ 				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+ 	} else {
+ 		window.classNames = classNames;
+ 	}
+ }());
+ 
+ 
+ /***/ }),
+-/* 57 */
++/* 55 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ const {
+   enumEntries,
+   enumIndexedProperties,
+   enumNonIndexedProperties,
+   getPrototype,
+   enumSymbols
+-} = __webpack_require__(16);
++} = __webpack_require__(17);
+ 
+ const {
+   getClosestGripNode,
+   getClosestNonBucketNode,
+   getValue,
+   nodeHasAccessors,
+   nodeHasAllEntriesInPreview,
+   nodeHasProperties,
+   nodeIsBucket,
+   nodeIsDefaultProperties,
+   nodeIsEntries,
+   nodeIsMapEntry,
+   nodeIsPrimitive,
+   nodeIsProxy,
+   nodeNeedsNumericalBuckets
+-} = __webpack_require__(17);
++} = __webpack_require__(18);
+ 
+ function loadItemProperties(item, createObjectClient, loadedProperties) {
+   const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+ 
+   let objectClient;
+   const getObjectClient = () => {
+     if (objectClient) {
+       return objectClient;
+@@ -6381,16 +6236,16 @@ module.exports = {
+   shouldLoadItemEntries,
+   shouldLoadItemIndexedProperties,
+   shouldLoadItemNonIndexedProperties,
+   shouldLoadItemPrototype,
+   shouldLoadItemSymbols
+ };
+ 
+ /***/ }),
+-/* 58 */
++/* 56 */
+ /***/ (function(module, exports) {
+ 
+-module.exports = __WEBPACK_EXTERNAL_MODULE_58__;
++module.exports = __WEBPACK_EXTERNAL_MODULE_56__;
+ 
+ /***/ })
+ /******/ ]);
+ });

+ 57 - 0
frg/work-js/mozilla-release/patches/1436575-2b-60a1.patch

@@ -0,0 +1,57 @@
+# HG changeset patch
+# User Jared Wein <jwein@mozilla.com>
+# Date 1518112651 18000
+# Node ID 89f7592e2b4615819374e347aed69333bc3a8a72
+# Parent  98f41486b8cad50658578a8271bc1386fa345bc4
+Bug 1436575 - Autofix errors from no-compare-against-boolean-literal. r=standard8
+
+MozReview-Commit-ID: 66sXxnOWDgw
+
+diff --git a/devtools/client/inspector/test/browser_inspector_menu-06-other.js b/devtools/client/inspector/test/browser_inspector_menu-06-other.js
+--- a/devtools/client/inspector/test/browser_inspector_menu-06-other.js
++++ b/devtools/client/inspector/test/browser_inspector_menu-06-other.js
+@@ -78,17 +78,17 @@ add_task(function* () {
+     let { walker } = inspector;
+     let divBefore = yield walker.querySelector(walker.rootNode, "#nestedHiddenElement");
+     let { nodes } = yield walker.children(divBefore);
+     yield selectNode(nodes[0], inspector, "test-highlight");
+ 
+     let allMenuItems = openContextMenuAndGetAllItems(inspector);
+     let deleteNode = allMenuItems.find(item => item.id === "node-menu-delete");
+     ok(deleteNode, "the popup menu has a delete menu item");
+-    ok(deleteNode.disabled == false, "the delete menu item is not disabled");
++    ok(!deleteNode.disabled, "the delete menu item is not disabled");
+     let updated = inspector.once("inspector-updated");
+ 
+     info("Triggering 'Delete Node' and waiting for inspector to update");
+     deleteNode.click();
+     yield updated;
+ 
+     let divAfter = yield walker.querySelector(walker.rootNode, "#nestedHiddenElement");
+     let nodesAfter = (yield walker.children(divAfter)).nodes;
+diff --git a/devtools/client/inspector/test/browser_inspector_menu-06-other.js.1436575-2.later b/devtools/client/inspector/test/browser_inspector_menu-06-other.js.1436575-2.later
+deleted file mode 100644
+--- a/devtools/client/inspector/test/browser_inspector_menu-06-other.js.1436575-2.later
++++ /dev/null
+@@ -1,21 +0,0 @@
+---- browser_inspector_menu-06-other.js
+-+++ browser_inspector_menu-06-other.js
+-@@ -78,17 +78,17 @@ add_task(function* () {
+-     let { walker } = inspector;
+-     let divBefore = yield walker.querySelector(walker.rootNode, "#nestedHiddenElement");
+-     let { nodes } = yield walker.children(divBefore);
+-     yield selectNode(nodes[0], inspector, "test-highlight");
+- 
+-     let allMenuItems = openContextMenuAndGetAllItems(inspector);
+-     let deleteNode = allMenuItems.find(item => item.id === "node-menu-delete");
+-     ok(deleteNode, "the popup menu has a delete menu item");
+--    ok(deleteNode.disabled == false, "the delete menu item is not disabled");
+-+    ok(!deleteNode.disabled, "the delete menu item is not disabled");
+-     let updated = inspector.once("inspector-updated");
+- 
+-     info("Triggering 'Delete Node' and waiting for inspector to update");
+-     deleteNode.click();
+-     yield updated;
+- 
+-     let divAfter = yield walker.querySelector(walker.rootNode, "#nestedHiddenElement");
+-     let nodesAfter = (yield walker.children(divAfter)).nodes;

+ 1998 - 0
frg/work-js/mozilla-release/patches/1436670-60a1.patch

@@ -0,0 +1,1998 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1519034706 -3600
+# Node ID 5b8dd2380f3eda126be82974414577a53ca64a27
+# Parent  36475c9774ed07fddacc40021fc66b0262448a26
+Bug 1436670 - Devtools Reps: update bundle to v0.20.0; r=nchevobbe.
+
+MozReview-Commit-ID: 75lSnYTx0Ql
+
+diff --git a/devtools/client/shared/components/reps/reps.css b/devtools/client/shared/components/reps/reps.css
+--- a/devtools/client/shared/components/reps/reps.css
++++ b/devtools/client/shared/components/reps/reps.css
+@@ -61,16 +61,22 @@
+ .objectBox-textNode,
+ .objectBox-string,
+ .objectBox-symbol {
+   color: var(--string-color);
+ }
+ 
+ .objectBox-string a, .objectBox-string a:visited {
+   color: currentColor;
++  text-decoration: none;
++  font-style: italic;
++}
++
++.objectBox-string a:hover {
++  text-decoration: underline;
+ }
+ 
+ .objectBox-function,
+ .objectBox-stackTrace,
+ .objectBox-profile {
+   color: var(--object-color);
+ }
+ 
+@@ -144,26 +150,33 @@
+   color: var(--node-color);
+ }
+ 
+ .angleBracket {
+   color: var(--theme-body-color);
+ }
+ 
+ /******************************************************************************/
++/* Length bubble for arraylikes and maplikes */
++
++.objectLengthBubble {
++  color: var(--null-color);
++}
++
++/******************************************************************************/
+ 
+ .objectLeftBrace,
+ .objectRightBrace,
+ .arrayLeftBracket,
+ .arrayRightBracket {
+   color: var(--object-color);
+ }
+ 
+ /******************************************************************************/
+-/* Cycle reference*/
++/* Cycle reference */
+ 
+ .objectBox-Reference {
+   font-weight: bold;
+   color: var(--reference-color);
+ }
+ 
+ [class*="objectBox"] > .objectTitle {
+   color: var(--object-color);
+diff --git a/devtools/client/shared/components/reps/reps.js b/devtools/client/shared/components/reps/reps.js
+--- a/devtools/client/shared/components/reps/reps.js
++++ b/devtools/client/shared/components/reps/reps.js
+@@ -2,17 +2,17 @@
+ 	if(typeof exports === 'object' && typeof module === 'object')
+ 		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
+ 	else if(typeof define === 'function' && define.amd)
+ 		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
+ 	else {
+ 		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
+ 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
+ 	}
+-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_7__) {
++})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_8__) {
+ return /******/ (function(modules) { // webpackBootstrap
+ /******/ 	// The module cache
+ /******/ 	var installedModules = {};
+ /******/
+ /******/ 	// The require function
+ /******/ 	function __webpack_require__(moduleId) {
+ /******/
+ /******/ 		// Check if module is in cache
+@@ -65,17 +65,17 @@ return /******/ (function(modules) { // 
+ /******/
+ /******/ 	// Object.prototype.hasOwnProperty.call
+ /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ /******/
+ /******/ 	// __webpack_public_path__
+ /******/ 	__webpack_require__.p = "/assets/build";
+ /******/
+ /******/ 	// Load entry module and return exports
+-/******/ 	return __webpack_require__(__webpack_require__.s = 19);
++/******/ 	return __webpack_require__(__webpack_require__.s = 20);
+ /******/ })
+ /************************************************************************/
+ /******/ ([
+ /* 0 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+@@ -455,16 +455,22 @@ function isURL(token) {
+     }
+     new URL(token);
+     return true;
+   } catch (e) {
+     return false;
+   }
+ }
+ 
++const ellipsisElement = span({
++  key: "more",
++  className: "more-ellipsis",
++  title: `more${ELLIPSIS}`
++}, ELLIPSIS);
++
+ module.exports = {
+   isGrip,
+   isURL,
+   cropString,
+   containsURL,
+   rawCropString,
+   sanitizeString,
+   escapeString,
+@@ -473,16 +479,17 @@ module.exports = {
+   parseURLParams,
+   parseURLEncodedText,
+   getFileName,
+   getURLDisplayString,
+   maybeEscapePropertyName,
+   getGripPreviewItems,
+   getGripType,
+   tokenSplitRegex,
++  ellipsisElement,
+   ELLIPSIS
+ };
+ 
+ /***/ }),
+ /* 1 */
+ /***/ (function(module, exports) {
+ 
+ module.exports = __WEBPACK_EXTERNAL_MODULE_1__;
+@@ -518,25 +525,24 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-__webpack_require__(20);
++__webpack_require__(21);
+ 
+ // Load all existing rep templates
+-const Undefined = __webpack_require__(21);
+-const Null = __webpack_require__(22);
+-const StringRep = __webpack_require__(6);
+-const LongStringRep = __webpack_require__(23);
++const Undefined = __webpack_require__(22);
++const Null = __webpack_require__(23);
++const StringRep = __webpack_require__(7);
+ const Number = __webpack_require__(24);
+-const ArrayRep = __webpack_require__(10);
++const ArrayRep = __webpack_require__(5);
+ const Obj = __webpack_require__(25);
+ const SymbolRep = __webpack_require__(26);
+ const InfinityRep = __webpack_require__(27);
+ const NaNRep = __webpack_require__(28);
+ const Accessor = __webpack_require__(29);
+ 
+ // DOM types (grips)
+ const Attribute = __webpack_require__(30);
+@@ -550,24 +556,24 @@ const StyleSheet = __webpack_require__(4
+ const CommentNode = __webpack_require__(41);
+ const ElementNode = __webpack_require__(42);
+ const TextNode = __webpack_require__(43);
+ const ErrorRep = __webpack_require__(44);
+ const Window = __webpack_require__(45);
+ const ObjectWithText = __webpack_require__(46);
+ const ObjectWithURL = __webpack_require__(47);
+ const GripArray = __webpack_require__(13);
+-const GripMap = __webpack_require__(14);
+-const GripMapEntry = __webpack_require__(15);
+-const Grip = __webpack_require__(8);
++const GripMap = __webpack_require__(15);
++const GripMapEntry = __webpack_require__(16);
++const Grip = __webpack_require__(9);
+ 
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+-let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, LongStringRep, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
++let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+ 
+ /**
+  * Generic rep that is using for rendering native JS types or an object.
+  * The right template used for rendering is picked automatically according
+  * to the current value type. The value must be passed is as 'object'
+  * property.
+  */
+ const Rep = function (props) {
+@@ -625,17 +631,16 @@ module.exports = {
+     ErrorRep,
+     Event,
+     Func,
+     Grip,
+     GripArray,
+     GripMap,
+     GripMapEntry,
+     InfinityRep,
+-    LongStringRep,
+     NaNRep,
+     Null,
+     Number,
+     Obj,
+     ObjectWithText,
+     ObjectWithURL,
+     PromiseRep,
+     RegExp,
+@@ -658,16 +663,163 @@ module.exports = {
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ // Dependencies
++const dom = __webpack_require__(1);
++const PropTypes = __webpack_require__(2);
++const {
++  wrapRender
++} = __webpack_require__(0);
++const { MODE } = __webpack_require__(3);
++const { span } = dom;
++
++const ModePropType = PropTypes.oneOf(
++// @TODO Change this to Object.values once it's supported in Node's version of V8
++Object.keys(MODE).map(key => MODE[key]));
++
++/**
++ * Renders an array. The array is enclosed by left and right bracket
++ * and the max number of rendered items depends on the current mode.
++ */
++ArrayRep.propTypes = {
++  mode: ModePropType,
++  object: PropTypes.array.isRequired
++};
++
++function ArrayRep(props) {
++  let {
++    object,
++    mode = MODE.SHORT
++  } = props;
++
++  let items;
++  let brackets;
++  let needSpace = function (space) {
++    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
++  };
++
++  if (mode === MODE.TINY) {
++    let isEmpty = object.length === 0;
++    if (isEmpty) {
++      items = [];
++    } else {
++      items = [span({
++        className: "more-ellipsis",
++        title: "more…"
++      }, "…")];
++    }
++    brackets = needSpace(false);
++  } else {
++    items = arrayIterator(props, object, maxLengthMap.get(mode));
++    brackets = needSpace(items.length > 0);
++  }
++
++  return span({
++    className: "objectBox objectBox-array" }, span({
++    className: "arrayLeftBracket"
++  }, brackets.left), ...items, span({
++    className: "arrayRightBracket"
++  }, brackets.right), span({
++    className: "arrayProperties",
++    role: "group" }));
++}
++
++function arrayIterator(props, array, max) {
++  let items = [];
++
++  for (let i = 0; i < array.length && i < max; i++) {
++    let config = {
++      mode: MODE.TINY,
++      delim: i == array.length - 1 ? "" : ", "
++    };
++    let item;
++
++    try {
++      item = ItemRep(Object.assign({}, props, config, {
++        object: array[i]
++      }));
++    } catch (exc) {
++      item = ItemRep(Object.assign({}, props, config, {
++        object: exc
++      }));
++    }
++    items.push(item);
++  }
++
++  if (array.length > max) {
++    items.push(span({
++      className: "more-ellipsis",
++      title: "more…"
++    }, "…"));
++  }
++
++  return items;
++}
++
++/**
++ * Renders array item. Individual values are separated by a comma.
++ */
++ItemRep.propTypes = {
++  object: PropTypes.any.isRequired,
++  delim: PropTypes.string.isRequired,
++  mode: ModePropType
++};
++
++function ItemRep(props) {
++  const { Rep } = __webpack_require__(4);
++
++  let {
++    object,
++    delim,
++    mode
++  } = props;
++  return span({}, Rep(Object.assign({}, props, {
++    object: object,
++    mode: mode
++  })), delim);
++}
++
++function getLength(object) {
++  return object.length;
++}
++
++function supportsObject(object) {
++  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
++}
++
++const maxLengthMap = new Map();
++maxLengthMap.set(MODE.SHORT, 3);
++maxLengthMap.set(MODE.LONG, 10);
++
++// Exports from this module
++module.exports = {
++  rep: wrapRender(ArrayRep),
++  supportsObject,
++  maxLengthMap,
++  getLength,
++  ModePropType
++};
++
++/***/ }),
++/* 6 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// Dependencies
+ const PropTypes = __webpack_require__(2);
+ const {
+   maybeEscapePropertyName,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+@@ -697,17 +849,17 @@ PropRep.propTypes = {
+ /**
+  * Function that given a name, a delimiter and an object returns an array
+  * of React elements representing an object property (e.g. `name: value`)
+  *
+  * @param {Object} props
+  * @return {Array} Array of React elements.
+  */
+ function PropRep(props) {
+-  const Grip = __webpack_require__(8);
++  const Grip = __webpack_require__(9);
+   const { Rep } = __webpack_require__(4);
+ 
+   let {
+     name,
+     mode,
+     equal,
+     suppressQuotes
+   } = props;
+@@ -733,17 +885,17 @@ function PropRep(props) {
+     "className": "objectEqual"
+   }, equal), Rep(Object.assign({}, props))];
+ }
+ 
+ // Exports from this module
+ module.exports = wrapRender(PropRep);
+ 
+ /***/ }),
+-/* 6 */
++/* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -754,75 +906,154 @@ const PropTypes = __webpack_require__(2)
+ const {
+   containsURL,
+   isURL,
+   escapeString,
+   getGripType,
+   rawCropString,
+   sanitizeString,
+   wrapRender,
++  isGrip,
+   tokenSplitRegex,
+   ELLIPSIS
+ } = __webpack_require__(0);
+ 
+ const dom = __webpack_require__(1);
+ const { a, span } = dom;
+ 
+ /**
+  * Renders a string. String value is enclosed within quotes.
+  */
+ StringRep.propTypes = {
+   useQuotes: PropTypes.bool,
+   escapeWhitespace: PropTypes.bool,
+   style: PropTypes.object,
+-  object: PropTypes.string.isRequired,
+-  member: PropTypes.any,
+-  cropLimit: PropTypes.number,
++  cropLimit: PropTypes.number.isRequired,
++  member: PropTypes.string,
++  object: PropTypes.object.isRequired,
+   openLink: PropTypes.func,
+   className: PropTypes.string,
+   omitLinkHref: PropTypes.bool
+ };
+ 
+ function StringRep(props) {
+   let {
+     className,
++    style,
+     cropLimit,
+-    object: text,
+-    member,
+-    style,
++    object,
+     useQuotes = true,
+     escapeWhitespace = true,
++    member,
+     openLink,
+     omitLinkHref = true
+   } = props;
+ 
++  let text = object;
++
++  const isLong = isLongString(object);
++  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
++
++  if (isLong) {
++    text = maybeCropLongString({
++      shouldCrop,
++      cropLimit
++    }, text);
++  }
++
++  text = formatText({
++    useQuotes,
++    escapeWhitespace
++  }, text);
++
++  const config = getElementConfig({
++    className,
++    style,
++    actor: object.actor
++  });
++
++  if (!isLong) {
++    if (containsURL(text)) {
++      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
++    }
++
++    // Cropping of longString has been handled before formatting.
++    text = maybeCropString({
++      isLong,
++      shouldCrop,
++      cropLimit
++    }, text);
++  }
++
++  return span(config, text);
++}
++
++function maybeCropLongString(opts, text) {
++  const {
++    shouldCrop,
++    cropLimit
++  } = opts;
++
++  const {
++    fullText,
++    initial,
++    length
++  } = text;
++
++  text = shouldCrop ? initial.substring(0, cropLimit) : fullText || initial;
++
++  if (text.length < length) {
++    text += ELLIPSIS;
++  }
++
++  return text;
++}
++
++function formatText(opts, text) {
++  let {
++    useQuotes,
++    escapeWhitespace
++  } = opts;
++
++  return useQuotes ? escapeString(text, escapeWhitespace) : sanitizeString(text);
++}
++
++function getElementConfig(opts) {
++  const {
++    className,
++    style,
++    actor
++  } = opts;
++
++  const config = {};
++
++  if (actor) {
++    config["data-link-actor-id"] = actor;
++  }
++
+   const classNames = ["objectBox", "objectBox-string"];
+   if (className) {
+     classNames.push(className);
+   }
+-  let config = { className: classNames.join(" ") };
++  config.className = classNames.join(" ");
++
+   if (style) {
+     config.style = style;
+   }
+ 
+-  if (useQuotes) {
+-    text = escapeString(text, escapeWhitespace);
+-  } else {
+-    text = sanitizeString(text);
+-  }
+-
+-  const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+-  if (!containsURL(text)) {
+-    if (shouldCrop) {
+-      text = rawCropString(text, cropLimit);
+-    }
+-    return span(config, text);
+-  }
+-
+-  return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
++  return config;
++}
++
++function maybeCropString(opts, text) {
++  const {
++    shouldCrop,
++    cropLimit
++  } = opts;
++
++  return shouldCrop ? rawCropString(text, cropLimit) : text;
+ }
+ 
+ /**
+  * Get an array of the elements representing the string, cropped if needed,
+  * with actual links.
+  *
+  * @param {String} text: The actual string to linkify.
+  * @param {Integer | null} cropLimit
+@@ -921,35 +1152,43 @@ function getCroppedString(text, offset =
+     // The string should be cropped at the beginning.
+     const cutIndex = endCropIndex - start;
+     return text.substring(cutIndex);
+   }
+ 
+   return text;
+ }
+ 
++function isLongString(object) {
++  return object && object.type === "longString";
++}
++
+ function supportsObject(object, noGrip = false) {
++  if (noGrip === false && isGrip(object)) {
++    return isLongString(object);
++  }
++
+   return getGripType(object, noGrip) == "string";
+ }
+ 
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(StringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 7 */
++/* 8 */
+ /***/ (function(module, exports) {
+ 
+-module.exports = __WEBPACK_EXTERNAL_MODULE_7__;
++module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
+ 
+ /***/ }),
+-/* 8 */
++/* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -957,17 +1196,17 @@ module.exports = __WEBPACK_EXTERNAL_MODU
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ 
+ // Dependencies
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const PropRep = __webpack_require__(5);
++const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders generic grip. Grip is client representation
+  * of remote JS object and is used as an input object
+@@ -1239,16 +1478,20 @@ function getPropValue(property) {
+ }
+ 
+ // Registration
+ function supportsObject(object, noGrip = false) {
+   if (noGrip === true || !isGrip(object)) {
+     return false;
+   }
+ 
++  if (object.class === "DeadObject") {
++    return true;
++  }
++
+   return object.preview ? typeof object.preview.ownProperties !== "undefined" : typeof object.ownPropertyLength !== "undefined";
+ }
+ 
+ const maxLengthMap = new Map();
+ maxLengthMap.set(MODE.SHORT, 3);
+ maxLengthMap.set(MODE.LONG, 10);
+ 
+ // Grip is used in propIterator and has to be defined here.
+@@ -1257,33 +1500,33 @@ let Grip = {
+   supportsObject,
+   maxLengthMap
+ };
+ 
+ // Exports from this module
+ module.exports = Grip;
+ 
+ /***/ }),
+-/* 9 */
++/* 10 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ var _svgInlineReact = __webpack_require__(11);
+ 
+ var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const React = __webpack_require__(7);
++const React = __webpack_require__(8);
+ const PropTypes = __webpack_require__(2);
+ 
+ 
+ const svg = {
+   "open-inspector": __webpack_require__(36),
+   "jump-definition": __webpack_require__(37)
+ };
+ 
+@@ -1304,187 +1547,41 @@ function Svg(name, props) {
+   }
+   props = Object.assign({}, props, { className, src: svg[name] });
+   return React.createElement(_svgInlineReact2.default, props);
+ }
+ 
+ module.exports = Svg;
+ 
+ /***/ }),
+-/* 10 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-// Dependencies
+-const dom = __webpack_require__(1);
+-const PropTypes = __webpack_require__(2);
+-const {
+-  wrapRender
+-} = __webpack_require__(0);
+-const { MODE } = __webpack_require__(3);
+-const { span } = dom;
+-
+-const ModePropType = PropTypes.oneOf(
+-// @TODO Change this to Object.values once it's supported in Node's version of V8
+-Object.keys(MODE).map(key => MODE[key]));
+-
+-/**
+- * Renders an array. The array is enclosed by left and right bracket
+- * and the max number of rendered items depends on the current mode.
+- */
+-ArrayRep.propTypes = {
+-  mode: ModePropType,
+-  object: PropTypes.array.isRequired
+-};
+-
+-function ArrayRep(props) {
+-  let {
+-    object,
+-    mode = MODE.SHORT
+-  } = props;
+-
+-  let items;
+-  let brackets;
+-  let needSpace = function (space) {
+-    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+-  };
+-
+-  if (mode === MODE.TINY) {
+-    let isEmpty = object.length === 0;
+-    if (isEmpty) {
+-      items = [];
+-    } else {
+-      items = [span({
+-        className: "more-ellipsis",
+-        title: "more…"
+-      }, "…")];
+-    }
+-    brackets = needSpace(false);
+-  } else {
+-    items = arrayIterator(props, object, maxLengthMap.get(mode));
+-    brackets = needSpace(items.length > 0);
+-  }
+-
+-  return span({
+-    className: "objectBox objectBox-array" }, span({
+-    className: "arrayLeftBracket"
+-  }, brackets.left), ...items, span({
+-    className: "arrayRightBracket"
+-  }, brackets.right), span({
+-    className: "arrayProperties",
+-    role: "group" }));
+-}
+-
+-function arrayIterator(props, array, max) {
+-  let items = [];
+-
+-  for (let i = 0; i < array.length && i < max; i++) {
+-    let config = {
+-      mode: MODE.TINY,
+-      delim: i == array.length - 1 ? "" : ", "
+-    };
+-    let item;
+-
+-    try {
+-      item = ItemRep(Object.assign({}, props, config, {
+-        object: array[i]
+-      }));
+-    } catch (exc) {
+-      item = ItemRep(Object.assign({}, props, config, {
+-        object: exc
+-      }));
+-    }
+-    items.push(item);
+-  }
+-
+-  if (array.length > max) {
+-    items.push(span({
+-      className: "more-ellipsis",
+-      title: "more…"
+-    }, "…"));
+-  }
+-
+-  return items;
+-}
+-
+-/**
+- * Renders array item. Individual values are separated by a comma.
+- */
+-ItemRep.propTypes = {
+-  object: PropTypes.any.isRequired,
+-  delim: PropTypes.string.isRequired,
+-  mode: ModePropType
+-};
+-
+-function ItemRep(props) {
+-  const { Rep } = __webpack_require__(4);
+-
+-  let {
+-    object,
+-    delim,
+-    mode
+-  } = props;
+-  return span({}, Rep(Object.assign({}, props, {
+-    object: object,
+-    mode: mode
+-  })), delim);
+-}
+-
+-function getLength(object) {
+-  return object.length;
+-}
+-
+-function supportsObject(object) {
+-  return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
+-}
+-
+-const maxLengthMap = new Map();
+-maxLengthMap.set(MODE.SHORT, 3);
+-maxLengthMap.set(MODE.LONG, 10);
+-
+-// Exports from this module
+-module.exports = {
+-  rep: wrapRender(ArrayRep),
+-  supportsObject,
+-  maxLengthMap,
+-  getLength
+-};
+-
+-/***/ }),
+ /* 11 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+     value: true
+ });
+ 
+ var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+ 
+ var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+ 
+-var _react = __webpack_require__(7);
++var _react = __webpack_require__(8);
+ 
+ var _react2 = _interopRequireDefault(_react);
+ 
+ var _propTypes = __webpack_require__(2);
+ 
+ var _util = __webpack_require__(35);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.includes(i)) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
++function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+ 
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+ function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+ 
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+ 
+ var process = process || { env: {} };
+@@ -1594,34 +1691,38 @@ module.exports = {
+ 
+ 
+ /* 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/. */
+ 
+ // Dependencies
+ const PropTypes = __webpack_require__(2);
++
++const { lengthBubble } = __webpack_require__(14);
+ const {
+   getGripType,
+   isGrip,
+-  wrapRender
++  wrapRender,
++  ellipsisElement
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
++const { ModePropType } = __webpack_require__(5);
+ 
+ /**
+  * Renders an array. The array is enclosed by left and right bracket
+  * and the max number of rendered items depends on the current mode.
+  */
+ GripArray.propTypes = {
+   object: PropTypes.object.isRequired,
+   // @TODO Change this to Object.values once it's supported in Node's version of V8
+-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
++  mode: ModePropType,
+   provider: PropTypes.object,
+   onDOMNodeMouseOver: PropTypes.func,
+   onDOMNodeMouseOut: PropTypes.func,
+   onInspectIconClick: PropTypes.func
+ };
+ 
+ function GripArray(props) {
+   let {
+@@ -1630,46 +1731,43 @@ function GripArray(props) {
+   } = props;
+ 
+   let items;
+   let brackets;
+   let needSpace = function (space) {
+     return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+   };
+ 
++  const config = {
++    "data-link-actor-id": object.actor,
++    className: "objectBox objectBox-array"
++  };
++
++  const title = getTitle(props, object);
++
+   if (mode === MODE.TINY) {
+-    let objectLength = getLength(object);
+-    let isEmpty = objectLength === 0;
+-    let ellipsis;
+-    if (!isEmpty) {
+-      ellipsis = span({
+-        className: "more-ellipsis",
+-        title: "more…"
+-      }, "…");
++    const isEmpty = getLength(object) === 0;
++
++    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
++    if (!isEmpty && object.class !== "Array") {
++      return span(config, title);
+     }
+ 
+-    let title;
+-    if (object.class != "Array") {
+-      title = object.class + " ";
+-    }
+     brackets = needSpace(false);
+-    return span({
+-      "data-link-actor-id": object.actor,
+-      className: "objectBox objectBox-array" }, title, span({
++    return span(config, title, span({
+       className: "arrayLeftBracket"
+-    }, brackets.left), ellipsis, span({
++    }, brackets.left), isEmpty ? null : ellipsisElement, span({
+       className: "arrayRightBracket"
+     }, brackets.right));
+   }
++
+   let max = maxLengthMap.get(mode);
+   items = arrayIterator(props, object, max);
+   brackets = needSpace(items.length > 0);
+ 
+-  let title = getTitle(props, object);
+-
+   return span({
+     "data-link-actor-id": object.actor,
+     className: "objectBox objectBox-array" }, title, span({
+     className: "arrayLeftBracket"
+   }, brackets.left), ...interleaveCommas(items), span({
+     className: "arrayRightBracket"
+   }, brackets.right), span({
+     className: "arrayProperties",
+@@ -1689,24 +1787,46 @@ function getLength(grip) {
+   if (!grip.preview) {
+     return 0;
+   }
+ 
+   return grip.preview.length || grip.preview.childNodesLength || 0;
+ }
+ 
+ function getTitle(props, object) {
+-  if (props.mode === MODE.TINY) {
+-    return "";
+-  }
++  let objectLength = getLength(object);
++  let isEmpty = objectLength === 0;
+ 
+   let title = props.title || object.class || "Array";
++
++  const length = lengthBubble({
++    object,
++    mode: props.mode,
++    maxLengthMap,
++    getLength
++  });
++
++  if (props.mode === MODE.TINY) {
++    if (isEmpty) {
++      return object.class === "Array" ? "" : span({
++        className: "objectTitle" }, title, " ");
++    }
++
++    let trailingSpace;
++    if (object.class === "Array") {
++      title = "";
++      trailingSpace = " ";
++    }
++
++    return span({
++      className: "objectTitle" }, title, length, trailingSpace);
++  }
++
+   return span({
+-    className: "objectTitle"
+-  }, title + " ");
++    className: "objectTitle" }, title, length, " ");
+ }
+ 
+ function getPreviewItems(grip) {
+   if (!grip.preview) {
+     return null;
+   }
+ 
+   return grip.preview.items || grip.preview.childNodes || [];
+@@ -1765,20 +1885,17 @@ function arrayIterator(props, grip, max)
+   // Handle trailing empty slots if there are some.
+   if (items.length < max && emptySlots > 0) {
+     items.push(getEmptySlotsElement(emptySlots));
+     foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+   }
+ 
+   const itemsShown = items.length + foldedEmptySlots;
+   if (gripLength > itemsShown) {
+-    items.push(span({
+-      className: "more-ellipsis",
+-      title: "more…"
+-    }, "…"));
++    items.push(ellipsisElement);
+   }
+ 
+   return items;
+ }
+ 
+ function getEmptySlotsElement(number) {
+   // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+   return `<${number} empty slot${number > 1 ? "s" : ""}>`;
+@@ -1806,40 +1923,94 @@ module.exports = {
+ 
+ /***/ }),
+ /* 14 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
++const PropTypes = __webpack_require__(2);
++
++const { wrapRender } = __webpack_require__(0);
++const { MODE } = __webpack_require__(3);
++const { ModePropType } = __webpack_require__(5);
++
++const dom = __webpack_require__(1);
++const { span } = dom;
++
++GripLengthBubble.propTypes = {
++  object: PropTypes.object.isRequired,
++  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
++  getLength: PropTypes.func.isRequired,
++  mode: ModePropType,
++  visibilityThreshold: PropTypes.number
++};
++
++function GripLengthBubble(props) {
++  const {
++    object,
++    mode = MODE.SHORT,
++    visibilityThreshold = 2,
++    maxLengthMap,
++    getLength,
++    showZeroLength = false
++  } = props;
++
++  const length = getLength(object);
++  const isEmpty = length === 0;
++  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
++  if (isEmpty && !showZeroLength || isObvious) {
++    return "";
++  }
++
++  return span({
++    className: "objectLengthBubble"
++  }, `(${length})`);
++}
++
++module.exports = {
++  lengthBubble: wrapRender(GripLengthBubble)
++};
++
++/***/ }),
++/* 15 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
+ /* 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/. */
+ 
+ // Dependencies
++
++const { lengthBubble } = __webpack_require__(14);
+ const PropTypes = __webpack_require__(2);
+ const {
+   isGrip,
+-  wrapRender
++  wrapRender,
++  ellipsisElement
+ } = __webpack_require__(0);
+-const PropRep = __webpack_require__(5);
++const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
++const { ModePropType } = __webpack_require__(5);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders an map. A map is represented by a list of its
+  * entries enclosed in curly brackets.
+  */
+ GripMap.propTypes = {
+   object: PropTypes.object,
+   // @TODO Change this to Object.values once it's supported in Node's version of V8
+-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
++  mode: ModePropType,
+   isInterestingEntry: PropTypes.func,
+   onDOMNodeMouseOver: PropTypes.func,
+   onDOMNodeMouseOut: PropTypes.func,
+   onInspectIconClick: PropTypes.func,
+   title: PropTypes.string
+ };
+ 
+ function GripMap(props) {
+@@ -1848,34 +2019,42 @@ function GripMap(props) {
+     object
+   } = props;
+ 
+   const config = {
+     "data-link-actor-id": object.actor,
+     className: "objectBox objectBox-object"
+   };
+ 
+-  if (mode === MODE.TINY) {
+-    return span(config, getTitle(props, object));
+-  }
+-
+-  let propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
+-
+-  return span(config, getTitle(props, object), span({
++  const title = getTitle(props, object);
++  const isEmpty = getLength(object) === 0;
++
++  if (isEmpty || mode === MODE.TINY) {
++    return span(config, title);
++  }
++
++  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
++
++  return span(config, title, span({
+     className: "objectLeftBrace"
+   }, " { "), ...propsArray, span({
+     className: "objectRightBrace"
+   }, " }"));
+ }
+ 
+ function getTitle(props, object) {
+-  let title = props.title || (object && object.class ? object.class : "Map");
++  const title = props.title || (object && object.class ? object.class : "Map");
+   return span({
+-    className: "objectTitle"
+-  }, title);
++    className: "objectTitle" }, title, lengthBubble({
++    object,
++    mode: props.mode,
++    maxLengthMap,
++    getLength,
++    showZeroLength: true
++  }));
+ }
+ 
+ function safeEntriesIterator(props, object, max) {
+   max = typeof max === "undefined" ? 3 : max;
+   try {
+     return entriesIterator(props, object, max);
+   } catch (err) {
+     console.error(err);
+@@ -1897,21 +2076,17 @@ function entriesIterator(props, object, 
+     indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
+       return !isInterestingEntry(t, value, name);
+     }));
+   }
+ 
+   let entries = getEntries(props, mapEntries, indexes);
+   if (entries.length < getLength(object)) {
+     // There are some undisplayed entries. Then display "…".
+-    entries.push(span({
+-      key: "more",
+-      className: "more-ellipsis",
+-      title: "more…"
+-    }, "…"));
++    entries.push(ellipsisElement);
+   }
+ 
+   return unfoldEntries(entries);
+ }
+ 
+ function unfoldEntries(items) {
+   return items.reduce((res, item, index) => {
+     if (Array.isArray(item)) {
+@@ -2008,17 +2183,17 @@ maxLengthMap.set(MODE.LONG, 10);
+ module.exports = {
+   rep: wrapRender(GripMap),
+   supportsObject,
+   maxLengthMap,
+   getLength
+ };
+ 
+ /***/ }),
+-/* 15 */
++/* 16 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2026,17 +2201,17 @@ module.exports = {
+ // Dependencies
+ const PropTypes = __webpack_require__(2);
+ // Shortcuts
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ const {
+   wrapRender
+ } = __webpack_require__(0);
+-const PropRep = __webpack_require__(5);
++const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+ /**
+  * Renders an map entry. A map entry is represented by its key, a column and its value.
+  */
+ GripMapEntry.propTypes = {
+   object: PropTypes.object,
+   // @TODO Change this to Object.values once it's supported in Node's version of V8
+   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+@@ -2086,38 +2261,38 @@ function createGripMapEntry(key, value) 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(GripMapEntry),
+   createGripMapEntry,
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 16 */
++/* 17 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-const client = __webpack_require__(17);
++const client = __webpack_require__(18);
+ const loadProperties = __webpack_require__(55);
+-const node = __webpack_require__(18);
++const node = __webpack_require__(19);
+ 
+ module.exports = {
+   client,
+   loadProperties,
+   node
+ };
+ 
+ /***/ }),
+-/* 17 */
++/* 18 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ async function enumIndexedProperties(objectClient, start, end) {
+   try {
+     const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
+@@ -2182,32 +2357,32 @@ module.exports = {
+   enumEntries,
+   enumIndexedProperties,
+   enumNonIndexedProperties,
+   enumSymbols,
+   getPrototype
+ };
+ 
+ /***/ }),
+-/* 18 */
++/* 19 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ const { get, has } = __webpack_require__(56);
+ const { maybeEscapePropertyName } = __webpack_require__(0);
+-const ArrayRep = __webpack_require__(10);
++const ArrayRep = __webpack_require__(5);
+ const GripArrayRep = __webpack_require__(13);
+-const GripMap = __webpack_require__(14);
+-const GripMapEntryRep = __webpack_require__(15);
++const GripMap = __webpack_require__(15);
++const GripMapEntryRep = __webpack_require__(16);
+ 
+ const MAX_NUMERICAL_PROPERTIES = 100;
+ 
+ const NODE_TYPES = {
+   BUCKET: Symbol("[n…n]"),
+   DEFAULT_PROPERTIES: Symbol("[default properties]"),
+   ENTRIES: Symbol("<entries>"),
+   GET: Symbol("<get>"),
+@@ -2282,16 +2457,37 @@ function nodeIsFunction(item) {
+   return value && value.class === "Function";
+ }
+ 
+ function nodeIsOptimizedOut(item) {
+   const value = getValue(item);
+   return !nodeHasChildren(item) && value && value.optimizedOut;
+ }
+ 
++function nodeIsUninitializedBinding(item) {
++  const value = getValue(item);
++  return value && value.uninitialized;
++}
++
++// Used to check if an item represents a binding that exists in a sourcemap's
++// original file content, but does not match up with a binding found in the
++// generated code.
++function nodeIsUnmappedBinding(item) {
++  const value = getValue(item);
++  return value && value.unmapped;
++}
++
++// Used to check if an item represents a binding that exists in the debugger's
++// parser result, but does not match up with a binding returned by the
++// debugger server.
++function nodeIsUnscopedBinding(item) {
++  const value = getValue(item);
++  return value && value.unscoped;
++}
++
+ function nodeIsMissingArguments(item) {
+   const value = getValue(item);
+   return !nodeHasChildren(item) && value && value.missingArguments;
+ }
+ 
+ function nodeHasProperties(item) {
+   return !nodeHasChildren(item) && nodeIsObject(item);
+ }
+@@ -2806,41 +3002,44 @@ module.exports = {
+   nodeIsMissingArguments,
+   nodeIsObject,
+   nodeIsOptimizedOut,
+   nodeIsPrimitive,
+   nodeIsPromise,
+   nodeIsPrototype,
+   nodeIsProxy,
+   nodeIsSetter,
++  nodeIsUninitializedBinding,
++  nodeIsUnmappedBinding,
++  nodeIsUnscopedBinding,
+   nodeIsWindow,
+   nodeNeedsNumericalBuckets,
+   nodeSupportsNumericalBucketing,
+   setNodeChildren,
+   sortProperties,
+   NODE_TYPES,
+   // Export for testing purpose.
+   SAFE_PATH_PREFIX
+ };
+ 
+ /***/ }),
+-/* 19 */
++/* 20 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ const { MODE } = __webpack_require__(3);
+ const { REPS, getRep } = __webpack_require__(4);
+ const ObjectInspector = __webpack_require__(48);
+-const ObjectInspectorUtils = __webpack_require__(16);
++const ObjectInspectorUtils = __webpack_require__(17);
+ 
+ const {
+   parseURLEncodedText,
+   parseURLParams,
+   maybeEscapePropertyName,
+   getGripPreviewItems
+ } = __webpack_require__(0);
+ 
+@@ -2852,23 +3051,23 @@ module.exports = {
+   parseURLEncodedText,
+   parseURLParams,
+   getGripPreviewItems,
+   ObjectInspector,
+   ObjectInspectorUtils
+ };
+ 
+ /***/ }),
+-/* 20 */
++/* 21 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 21 */
++/* 22 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2900,17 +3099,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Undefined),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 22 */
++/* 23 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -2942,93 +3141,16 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Null),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 23 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-// Dependencies
+-const PropTypes = __webpack_require__(2);
+-const {
+-  escapeString,
+-  sanitizeString,
+-  isGrip,
+-  wrapRender
+-} = __webpack_require__(0);
+-
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-
+-/**
+- * Renders a long string grip.
+- */
+-LongStringRep.propTypes = {
+-  useQuotes: PropTypes.bool,
+-  escapeWhitespace: PropTypes.bool,
+-  style: PropTypes.object,
+-  cropLimit: PropTypes.number.isRequired,
+-  member: PropTypes.string,
+-  object: PropTypes.object.isRequired
+-};
+-
+-function LongStringRep(props) {
+-  let {
+-    cropLimit,
+-    member,
+-    object,
+-    style,
+-    useQuotes = true,
+-    escapeWhitespace = true
+-  } = props;
+-  let { fullText, initial, length } = object;
+-
+-  let config = {
+-    "data-link-actor-id": object.actor,
+-    className: "objectBox objectBox-string"
+-  };
+-
+-  if (style) {
+-    config.style = style;
+-  }
+-
+-  let string = member && member.open ? fullText || initial : initial.substring(0, cropLimit);
+-
+-  if (string.length < length) {
+-    string += "\u2026";
+-  }
+-  let formattedString = useQuotes ? escapeString(string, escapeWhitespace) : sanitizeString(string);
+-  return span(config, formattedString);
+-}
+-
+-function supportsObject(object, noGrip = false) {
+-  if (noGrip === true || !isGrip(object)) {
+-    return false;
+-  }
+-  return object.type === "longString";
+-}
+-
+-// Exports from this module
+-module.exports = {
+-  rep: wrapRender(LongStringRep),
+-  supportsObject
+-};
+-
+-/***/ }),
+ /* 24 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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
+@@ -3084,19 +3206,20 @@ module.exports = {
+ 
+ /* 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/. */
+ 
+ // Dependencies
+ const PropTypes = __webpack_require__(2);
+ const {
+-  wrapRender
++  wrapRender,
++  ellipsisElement
+ } = __webpack_require__(0);
+-const PropRep = __webpack_require__(5);
++const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ const DEFAULT_TITLE = "Object";
+ 
+ /**
+@@ -3116,21 +3239,17 @@ function ObjectRep(props) {
+ 
+   if (props.mode === MODE.TINY) {
+     const tinyModeItems = [];
+     if (getTitle(props, object) !== DEFAULT_TITLE) {
+       tinyModeItems.push(getTitleElement(props, object));
+     } else {
+       tinyModeItems.push(span({
+         className: "objectLeftBrace"
+-      }, "{"), propsArray.length > 0 ? span({
+-        key: "more",
+-        className: "more-ellipsis",
+-        title: "more…"
+-      }, "…") : null, span({
++      }, "{"), propsArray.length > 0 ? ellipsisElement : null, span({
+         className: "objectRightBrace"
+       }, "}"));
+     }
+ 
+     return span({ className: "objectBox objectBox-object" }, ...tinyModeItems);
+   }
+ 
+   return span({ className: "objectBox objectBox-object" }, getTitleElement(props, object), span({
+@@ -3140,17 +3259,17 @@ function ObjectRep(props) {
+   }, " }"));
+ }
+ 
+ function getTitleElement(props, object) {
+   return span({ className: "objectTitle" }, getTitle(props, object));
+ }
+ 
+ function getTitle(props, object) {
+-  return props.title || object.class || DEFAULT_TITLE;
++  return props.title || DEFAULT_TITLE;
+ }
+ 
+ function safePropIterator(props, object, max) {
+   max = typeof max === "undefined" ? 3 : max;
+   try {
+     return propIterator(props, object, max);
+   } catch (err) {
+     console.error(err);
+@@ -3223,21 +3342,17 @@ function propIterator(props, object, max
+         continue;
+       }
+ 
+       pushPropRep(name, value);
+     }
+   }
+ 
+   if (propertiesNumber < propertiesNames.length) {
+-    elements.push(span({
+-      key: "more",
+-      className: "more-ellipsis",
+-      title: ", more…"
+-    }, "…"));
++    elements.push(ellipsisElement);
+   }
+ 
+   return elements;
+ }
+ 
+ function isInterestingProp(value) {
+   const type = typeof value;
+   return type == "boolean" || type == "number" || type == "string" && value;
+@@ -3477,17 +3592,17 @@ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ // Reps
+ const {
+   getGripType,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(6);
++const { rep: StringRep } = __webpack_require__(7);
+ 
+ /**
+  * Renders DOM attribute
+  */
+ Attribute.propTypes = {
+   object: PropTypes.object.isRequired
+ };
+ 
+@@ -3641,17 +3756,18 @@ function getTitle(grip) {
+ }
+ 
+ // Registration
+ function supportsObject(object, noGrip = false) {
+   if (noGrip === true || !isGrip(object)) {
+     return false;
+   }
+ 
+-  return object.preview && getGripType(object, noGrip) == "HTMLDocument";
++  const type = getGripType(object, noGrip);
++  return object.preview && (type === "HTMLDocument" || type === "XULDocument");
+ }
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Document),
+   supportsObject
+ };
+ 
+@@ -3671,17 +3787,17 @@ const PropTypes = __webpack_require__(2)
+ 
+ // Reps
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+ const { MODE } = __webpack_require__(3);
+-const { rep } = __webpack_require__(8);
++const { rep } = __webpack_require__(9);
+ 
+ /**
+  * Renders DOM event objects.
+  */
+ Event.propTypes = {
+   object: PropTypes.object.isRequired,
+   // @TODO Change this to Object.values once it's supported in Node's version of V8
+   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+@@ -3779,17 +3895,17 @@ const PropTypes = __webpack_require__(2)
+ // Reps
+ const {
+   getGripType,
+   isGrip,
+   cropString,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const Svg = __webpack_require__(9);
++const Svg = __webpack_require__(10);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ const IGNORED_SOURCE_URLS = ["debugger eval code"];
+ 
+ /**
+  * This component represents a template for Function objects.
+@@ -4005,17 +4121,17 @@ module.exports = "<!-- This Source Code 
+ const PropTypes = __webpack_require__(2);
+ // Dependencies
+ const {
+   getGripType,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+-const PropRep = __webpack_require__(5);
++const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders a DOM Promise object.
+  */
+@@ -4307,20 +4423,20 @@ module.exports = {
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ 
+ // Utils
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+-const { rep: StringRep } = __webpack_require__(6);
++const { rep: StringRep } = __webpack_require__(7);
+ const { MODE } = __webpack_require__(3);
+ const nodeConstants = __webpack_require__(12);
+-const Svg = __webpack_require__(9);
++const Svg = __webpack_require__(10);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM element node.
+  */
+ ElementNode.propTypes = {
+@@ -4441,17 +4557,17 @@ const PropTypes = __webpack_require__(2)
+ 
+ // Reps
+ const {
+   isGrip,
+   cropString,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const Svg = __webpack_require__(9);
++const Svg = __webpack_require__(10);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM #text node.
+  */
+ TextNode.propTypes = {
+@@ -4711,17 +4827,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ 
+ // Reps
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+-const String = __webpack_require__(6).rep;
++const String = __webpack_require__(7).rep;
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders a grip object with textual data.
+  */
+ ObjectWithText.propTypes = {
+@@ -4839,17 +4955,17 @@ var _devtoolsComponents = __webpack_requ
+ var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const { Component, createFactory } = __webpack_require__(7);
++const { Component, createFactory } = __webpack_require__(8);
+ const PropTypes = __webpack_require__(2);
+ const dom = __webpack_require__(1);
+ 
+ const Tree = createFactory(_devtoolsComponents2.default.Tree);
+ __webpack_require__(53);
+ 
+ const classnames = __webpack_require__(54);
+ 
+@@ -4858,17 +4974,17 @@ const {
+     Rep,
+     Grip
+   }
+ } = __webpack_require__(4);
+ const {
+   MODE
+ } = __webpack_require__(3);
+ 
+-const Utils = __webpack_require__(16);
++const Utils = __webpack_require__(17);
+ 
+ const {
+   getChildren,
+   getClosestGripNode,
+   getParent,
+   getValue,
+   nodeHasAccessors,
+   nodeHasProperties,
+@@ -4876,16 +4992,19 @@ const {
+   nodeIsFunction,
+   nodeIsGetter,
+   nodeIsMapEntry,
+   nodeIsMissingArguments,
+   nodeIsOptimizedOut,
+   nodeIsPrimitive,
+   nodeIsPrototype,
+   nodeIsSetter,
++  nodeIsUninitializedBinding,
++  nodeIsUnmappedBinding,
++  nodeIsUnscopedBinding,
+   nodeIsWindow
+ } = Utils.node;
+ 
+ const {
+   loadItemProperties
+ } = Utils.loadProperties;
+ 
+ // This implements a component that renders an interactive inspector
+@@ -5068,17 +5187,23 @@ class ObjectInspector extends Component 
+     let objectValue;
+     let label = item.name;
+     let itemValue = getValue(item);
+ 
+     const isPrimitive = nodeIsPrimitive(item);
+ 
+     const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
+ 
+-    if (nodeIsOptimizedOut(item)) {
++    if (nodeIsUninitializedBinding(item)) {
++      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
++    } else if (nodeIsUnmappedBinding(item)) {
++      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
++    } else if (nodeIsUnscopedBinding(item)) {
++      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
++    } else if (nodeIsOptimizedOut(item)) {
+       objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
+     } else if (nodeIsMissingArguments(item) || unavailable) {
+       objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
+     } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
+       objectValue = undefined;
+       label = this.renderGrip(item, Object.assign({}, this.props, {
+         functionName: label
+       }));
+@@ -5242,17 +5367,17 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+   value: true
+ });
+ 
+-var _react = __webpack_require__(7);
++var _react = __webpack_require__(8);
+ 
+ var _react2 = _interopRequireDefault(_react);
+ 
+ var _reactDomFactories = __webpack_require__(1);
+ 
+ var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
+ 
+ var _propTypes = __webpack_require__(2);
+@@ -6115,33 +6240,33 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ const {
+   enumEntries,
+   enumIndexedProperties,
+   enumNonIndexedProperties,
+   getPrototype,
+   enumSymbols
+-} = __webpack_require__(17);
++} = __webpack_require__(18);
+ 
+ const {
+   getClosestGripNode,
+   getClosestNonBucketNode,
+   getValue,
+   nodeHasAccessors,
+   nodeHasAllEntriesInPreview,
+   nodeHasProperties,
+   nodeIsBucket,
+   nodeIsDefaultProperties,
+   nodeIsEntries,
+   nodeIsMapEntry,
+   nodeIsPrimitive,
+   nodeIsProxy,
+   nodeNeedsNumericalBuckets
+-} = __webpack_require__(18);
++} = __webpack_require__(19);
+ 
+ function loadItemProperties(item, createObjectClient, loadedProperties) {
+   const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+ 
+   let objectClient;
+   const getObjectClient = () => {
+     if (objectClient) {
+       return objectClient;

+ 32 - 0
frg/work-js/mozilla-release/patches/1437714-60a1.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Hiroyuki Ikezoe <hikezoe@mozilla.com>
+# Date 1518819769 -32400
+# Node ID 5f7512c0b66a0dcb31144e1955ffcb35fb17dc18
+# Parent  d226b50b95ac9767ca5e17eeac32ea987598dc3b
+Bug 1437714 - Wait for 'picker-stopped' event before starting the new picker in the next test case. r=pbro
+
+MozReview-Commit-ID: LUayN3GM2BK
+
+diff --git a/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js b/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js
+--- a/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js
++++ b/devtools/client/inspector/test/browser_inspector_highlighter-keybinding_03.js
+@@ -42,17 +42,18 @@ add_task(function* () {
+   is(inspector.selection.nodeFront.id, "another",
+      "The #another DIV is still selected. Passed.");
+ 
+   function doKeyPick(args) {
+     info("Key pressed. Waiting for element to be picked");
+     testActor.synthesizeKey(args);
+     return promise.all([
+       inspector.selection.once("new-node-front"),
+-      inspector.once("inspector-updated")
++      inspector.once("inspector-updated"),
++      inspector.toolbox.once("picker-stopped")
+     ]);
+   }
+ 
+   function doKeyStop(args) {
+     info("Key pressed. Waiting for picker to be canceled");
+     testActor.synthesizeKey(args);
+     return inspector.toolbox.once("picker-stopped");
+   }

+ 31 - 0
frg/work-js/mozilla-release/patches/1437728-60a1.patch

@@ -0,0 +1,31 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1518485228 -32400
+# Node ID e480e3b7819d5b7494d612839c442985e6d66a88
+# Parent  711fe4a2d2f7230fd544ddc9af6dfd864ffa0bf6
+Bug 1437728 - Wait for a tick before removing tab in BrowserTestUtils.withNewTab. r=gijs
+
+diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
++++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+@@ -99,16 +99,20 @@ var BrowserTestUtils = {
+         url: options
+       }
+     }
+     let tab = await BrowserTestUtils.openNewForegroundTab(options);
+     let originalWindow = tab.ownerGlobal;
+     let result = await taskFn(tab.linkedBrowser);
+     let finalWindow = tab.ownerGlobal;
+     if (originalWindow == finalWindow && !tab.closing && tab.linkedBrowser) {
++      // taskFn may resolve within a tick after opening a new tab.
++      // We shouldn't remove the newly opened tab in the same tick.
++      // Wait for the next tick here.
++      await TestUtils.waitForTick();
+       await BrowserTestUtils.removeTab(tab);
+     } else {
+       Services.console.logStringMessage(
+         "BrowserTestUtils.withNewTab: Tab was already closed before " +
+         "removeTab would have been called");
+     }
+     return Promise.resolve(result);
+   },

+ 98 - 0
frg/work-js/mozilla-release/patches/1439925-60a1.patch

@@ -0,0 +1,98 @@
+# HG changeset patch
+# User Marco Zehe <mzehe@mozilla.com>
+# Date 1519290526 -3600
+# Node ID 5de6951d543f5203e493fce8215ee947093d975d
+# Parent  9e1ab5d6bf7c5c30f3a0576106d78e8bc660feab
+Bug 1439925 - Make the breadcrumbs container a proper toolbar for screen readers, and fix the localization include for its label, r=gl
+
+MozReview-Commit-ID: 14mRxfE2ucx
+
+diff --git a/devtools/client/inspector/inspector.xhtml b/devtools/client/inspector/inspector.xhtml
+--- a/devtools/client/inspector/inspector.xhtml
++++ b/devtools/client/inspector/inspector.xhtml
+@@ -36,23 +36,23 @@
+       var { BrowserLoader } = Cu.import("resource://devtools/client/shared/browser-loader.js", {});
+     }
+   </script>
+ 
+   <!-- In content, inspector.js is mapped to the dynamically generated webpack bundle -->
+   <script type="application/javascript" src="inspector.js" defer="true"></script>
+ </head>
+ <body class="theme-body" role="application">
+-  <div class="inspector-responsive-container theme-body inspector">
++  <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"
+-           data-localization-bundle="devtools/client/locales/inspector.properties">
++      <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"/>
+@@ -61,17 +61,17 @@
+         <button id="inspector-eyedropper-toggle" class="devtools-button"></button>
+         <div id="inspector-sidebar-toggle-box"></div>
+       </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="group" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
++             role="toolbar" data-localization="aria-label=inspector.breadcrumbs.label" tabindex="0"></div>
+       </div>
+     </div>
+ 
+     <!-- Splitter -->
+     <div id="inspector-splitter-box"></div>
+ 
+     <!-- Split Sidebar Container -->
+     <div id="inspector-rules-container">
+@@ -80,18 +80,17 @@
+ 
+     <!-- Sidebar Container -->
+     <div id="inspector-sidebar-container">
+       <div id="inspector-sidebar" hidden="true"></div>
+     </div>
+ 
+     <!-- Sidebar Panel Definitions -->
+     <div id="tabpanels" style="visibility: collapse">
+-      <div id="sidebar-panel-ruleview" class="theme-sidebar inspector-tabpanel"
+-           data-localization-bundle="devtools/client/locales/inspector.properties">
++      <div id="sidebar-panel-ruleview" class="theme-sidebar inspector-tabpanel">
+         <div id="ruleview-toolbar-container" class="devtools-toolbar">
+           <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>
+@@ -111,18 +110,17 @@
+         </div>
+ 
+         <div id="ruleview-container" class="ruleview">
+           <div id="ruleview-container-focusable" tabindex="-1">
+           </div>
+         </div>
+       </div>
+ 
+-      <div id="sidebar-panel-computedview" class="theme-sidebar inspector-tabpanel"
+-           data-localization-bundle="devtools/client/locales/inspector.properties">
++      <div id="sidebar-panel-computedview" class="theme-sidebar inspector-tabpanel">
+         <div id="computed-toolbar" class="devtools-toolbar">
+           <div class="devtools-searchbox has-clear-btn">
+             <input id="computed-searchbox"
+                    class="devtools-filterinput devtools-rule-searchbox"
+                    type="search"
+                    data-localization="placeholder=inspector.filterStyles.placeholder"/>
+             <button id="computed-searchinput-clear" class="devtools-searchinput-clear"></button>
+           </div>

+ 5273 - 0
frg/work-js/mozilla-release/patches/1440388-1-61a1.patch

@@ -0,0 +1,5273 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1520947855 -3600
+# Node ID aa46f0522cfd7fbda9ff99fc8711fcf0fc6b1178
+# Parent  86fa3e45686e86988ccb3f1842d0f5f0927066f4
+Bug 1440388 - devtools-reps: release v0.21.0; r=Honza.
+
+This update the reps bundle and its CSS files, and adds
+the images now used as background for some buttons.
+
+MozReview-Commit-ID: 3gNJnFRRZmt
+
+diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn
+--- a/devtools/client/jar.mn
++++ b/devtools/client/jar.mn
+@@ -327,8 +327,15 @@ devtools.jar:
+     content/netmonitor/src/assets/styles/RequestList.css (netmonitor/src/assets/styles/RequestList.css)
+     content/netmonitor/src/assets/styles/StatisticsPanel.css (netmonitor/src/assets/styles/StatisticsPanel.css)
+     content/netmonitor/src/assets/styles/StatusBar.css (netmonitor/src/assets/styles/StatusBar.css)
+     content/netmonitor/src/assets/styles/Toolbar.css (netmonitor/src/assets/styles/Toolbar.css)
+     content/netmonitor/src/assets/styles/variables.css (netmonitor/src/assets/styles/variables.css)
+     content/netmonitor/src/assets/icons/play.svg (netmonitor/src/assets/icons/play.svg)
+     content/netmonitor/index.html (netmonitor/index.html)
+     content/netmonitor/initializer.js (netmonitor/initializer.js)
++
++    # Devtools-components
++    skin/images/devtools-components/arrow.svg (themes/images/devtools-components/arrow.svg)
++
++    # Devtools-reps
++    skin/images/devtools-reps/jump-definition.svg (themes/images/devtools-reps/jump-definition.svg)
++    skin/images/devtools-reps/open-inspector.svg (themes/images/devtools-reps/open-inspector.svg)
+\ No newline at end of file
+diff --git a/devtools/client/shared/components/reps/reps.css b/devtools/client/shared/components/reps/reps.css
+--- a/devtools/client/shared/components/reps/reps.css
++++ b/devtools/client/shared/components/reps/reps.css
+@@ -10,16 +10,17 @@
+   --null-color: var(--theme-comment);
+   --object-color: var(--theme-highlight-blue);
+   --caption-color: var(--theme-highlight-blue);
+   --location-color: var(--theme-comment);
+   --source-link-color: var(--theme-highlight-blue);
+   --node-color: var(--theme-highlight-purple);
+   --reference-color: var(--theme-highlight-blue);
+   --comment-node-color: var(--theme-comment);
++  --stack-function-color: var(--theme-highlight-red);
+ }
+ 
+ .theme-firebug {
+   --number-color: #000088;
+   --string-color: #FF0000;
+   --null-color: #787878;
+   --object-color: DarkGreen;
+   --caption-color: #444444;
+@@ -63,28 +64,47 @@
+ .objectBox-symbol {
+   color: var(--string-color);
+ }
+ 
+ .objectBox-string a, .objectBox-string a:visited {
+   color: currentColor;
+   text-decoration: none;
+   font-style: italic;
++  cursor: pointer;
+ }
+ 
+ .objectBox-string a:hover {
+   text-decoration: underline;
+ }
+ 
+ .objectBox-function,
+ .objectBox-stackTrace,
+ .objectBox-profile {
+   color: var(--object-color);
+ }
+ 
++.objectBox-stackTrace-grid {
++  display: inline-grid;
++  grid-template-columns: auto auto;
++  margin-top: 3px;
++}
++
++.objectBox-stackTrace-fn::before {
++  content: "\3BB"; /* The "lambda" symbol */
++  color: var(--theme-body-color);
++  display: inline-block;
++  margin: 0 0.3em;
++}
++
++.objectBox-stackTrace-fn {
++  color: var(--stack-function-color);
++  padding-inline-start: 17px;
++}
++
+ .objectBox-Location,
+ .location {
+   color: var(--location-color);
+ }
+ 
+ .objectBox-null,
+ .objectBox-undefined,
+ .objectBox-hint,
+@@ -205,45 +225,45 @@
+ .theme-dark .caption,
+ .theme-light .caption {
+   font-weight: normal;
+ }
+ 
+ /******************************************************************************/
+ /* Open DOMNode in inspector button */
+ 
+-.open-inspector svg {
+-  fill: var(--comment-node-color);
++button.open-inspector {
++  mask: url("chrome://devtools/skin/images/devtools-reps/open-inspector.svg") no-repeat;
++  display: inline-block;
++  background-color: var(--comment-node-color);
+   height: 16px;
+-  width: 16px;
+   margin-left: .25em;
+-  cursor: pointer;
+   vertical-align: middle;
+ }
+ 
+-.objectBox-node:hover .open-inspector svg,
+-.objectBox-textNode:hover .open-inspector svg,
+-.open-inspector svg:hover {
+-  fill: var(--theme-highlight-blue);
++.objectBox-node:hover .open-inspector,
++.objectBox-textNode:hover .open-inspector,
++.open-inspector:hover {
++  background-color: var(--theme-highlight-blue);
+ }
+ 
+ /******************************************************************************/
+ /* Jump to definition button */
+ 
+-.jump-definition svg {
+-  stroke: var(--comment-node-color);
++button.jump-definition {
++  mask: url("chrome://devtools/skin/images/devtools-reps/jump-definition.svg") no-repeat;
++  display: inline-block;
++  background-color: var(--comment-node-color);
+   height: 16px;
+-  width: 16px;
+   margin-left: .25em;
+-  cursor: pointer;
+   vertical-align: middle;
+ }
+ 
+-.jump-definition svg:hover {
+-  stroke: var(--theme-highlight-blue);
++.jump-definition:hover {
++  background-color: var(--theme-highlight-blue);
+ }
+ 
+ /******************************************************************************/
+ /* "more…" ellipsis */
+ .more-ellipsis {
+   color: var(--comment-node-color);
+ }
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+@@ -265,22 +285,22 @@
+ .tree.noselect {
+   -webkit-user-select: none;
+   -moz-user-select: none;
+   -ms-user-select: none;
+   -o-user-select: none;
+   user-select: none;
+ }
+ 
+-.tree button {
+-  display: block;
++.tree .tree-node {
++  display: flex;
+ }
+ 
+-.tree .tree-node {
+-  display: flex;
++.tree .tree-node:not(.focused):hover {
++  background-color: var(--theme-selection-background-hover);
+ }
+ 
+ .tree-indent {
+   display: inline-block;
+   width: 12px;
+   margin-inline-start: 5px;
+   border-inline-start: 1px solid #A2D1FF;
+   flex-shrink: 0;
+@@ -288,53 +308,52 @@
+ 
+ /* Align with expandables siblings (where we have the arrow) */
+ .tree-node[data-expandable="false"] .tree-indent:last-of-type {
+   margin-inline-end: 15px;
+ }
+ 
+ /* For non expandable root nodes, we don't have .tree-indent elements, so we declare
+    the margin on the start of the node */
+-.tree-node[data-expandable="false"][aria-level="0"] {
++.tree-node[data-expandable="false"][aria-level="1"] {
+   padding-inline-start: 15px
+ }
+ 
+ .tree .tree-node[data-expandable="true"] {
+   cursor: default;
+ }
+ 
+-.tree .tree-node:not(.focused):hover {
+-  background-color: #F0F9FE;
++.tree-node img.arrow {
++  mask: url("chrome://devtools/skin/images/devtools-components/arrow.svg") no-repeat;
++  mask-size: 100%;
++  width: 9px;
++  height: 9px;
++  margin-inline-start: 1px;
++  margin-inline-end: 4px;
++  background-color: var(--theme-splitter-color, #9B9B9B);
++  transform: rotate(-90deg);
++  transition: transform 0.125s ease;
++  align-self: center;
+ }
+ 
++html[dir="rtl"] .tree-node img.arrow {
++  transform: rotate(90deg);
++}
++
++.tree-node img.arrow.expanded.expanded {
++  transform: rotate(0deg);
++ }
++
+ .tree .tree-node.focused {
+   color: white;
+   background-color: var(--theme-selection-background, #0a84ff);
+ }
+ 
+-.tree-node.focused .arrow svg {
+-  fill: currentColor;
+-}
+-
+-.arrow svg {
+-  fill: var(--theme-splitter-color, #9B9B9B);
+-  transition: transform 0.125s ease;
+-  width: 10px;
+-  margin-inline-end: 5px;
+-  transform: rotate(-90deg);
+-}
+-
+-html[dir="rtl"] .arrow svg,
+-.arrow svg:dir(rtl),
+-.arrow svg:-moz-locale-dir(rtl) {
+-  transform: rotate(90deg);
+-}
+-
+-.arrow.expanded.expanded svg {
+-  transform: rotate(0deg);
++.tree-node.focused img.arrow {
++  background-color: currentColor;
+ }
+ /* 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/. */
+ 
+ .tree.object-inspector .node.object-node {
+   display: inline-block;
+ }
+@@ -350,11 +369,26 @@ html[dir="rtl"] .arrow svg,
+ 
+ .tree.object-inspector .lessen,
+ .tree.object-inspector .lessen *,
+ .tree.object-inspector .lessen .object-label,
+ .tree.object-inspector .lessen .object-label * {
+   color: var(--theme-comment);
+ }
+ 
++.tree.object-inspector .block .object-label,
++.tree.object-inspector .block .object-label * {
++  color: var(--theme-body-color);
++}
++
++.tree.object-inspector .block .object-label:before {
++  content: "\2632   ";
++  font-size: 1.1em;
++}
++
+ .object-inspector .object-delimiter {
+   color: var(--theme-comment);
+ }
++
++.object-inspector .tree-node img.arrow {
++  display: inline-block;
++  vertical-align: middle;
++}
+diff --git a/devtools/client/shared/components/reps/reps.js b/devtools/client/shared/components/reps/reps.js
+--- a/devtools/client/shared/components/reps/reps.js
++++ b/devtools/client/shared/components/reps/reps.js
+@@ -1,18 +1,18 @@
+ (function webpackUniversalModuleDefinition(root, factory) {
+ 	if(typeof exports === 'object' && typeof module === 'object')
+-		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"));
++		module.exports = factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux"));
+ 	else if(typeof define === 'function' && define.amd)
+-		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react"], factory);
++		define(["devtools/client/shared/vendor/react-dom-factories", "devtools/client/shared/vendor/lodash", "devtools/client/shared/vendor/react-prop-types", "devtools/client/shared/vendor/react", "devtools/client/shared/vendor/react-redux", "devtools/client/shared/vendor/redux"], factory);
+ 	else {
+-		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"]);
++		var a = typeof exports === 'object' ? factory(require("devtools/client/shared/vendor/react-dom-factories"), require("devtools/client/shared/vendor/lodash"), require("devtools/client/shared/vendor/react-prop-types"), require("devtools/client/shared/vendor/react"), require("devtools/client/shared/vendor/react-redux"), require("devtools/client/shared/vendor/redux")) : factory(root["devtools/client/shared/vendor/react-dom-factories"], root["devtools/client/shared/vendor/lodash"], root["devtools/client/shared/vendor/react-prop-types"], root["devtools/client/shared/vendor/react"], root["devtools/client/shared/vendor/react-redux"], root["devtools/client/shared/vendor/redux"]);
+ 		for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
+ 	}
+-})(this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_56__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_8__) {
++})(typeof self !== 'undefined' ? self : this, function(__WEBPACK_EXTERNAL_MODULE_1__, __WEBPACK_EXTERNAL_MODULE_54__, __WEBPACK_EXTERNAL_MODULE_2__, __WEBPACK_EXTERNAL_MODULE_10__, __WEBPACK_EXTERNAL_MODULE_17__, __WEBPACK_EXTERNAL_MODULE_18__) {
+ return /******/ (function(modules) { // webpackBootstrap
+ /******/ 	// The module cache
+ /******/ 	var installedModules = {};
+ /******/
+ /******/ 	// The require function
+ /******/ 	function __webpack_require__(moduleId) {
+ /******/
+ /******/ 		// Check if module is in cache
+@@ -65,32 +65,32 @@ return /******/ (function(modules) { // 
+ /******/
+ /******/ 	// Object.prototype.hasOwnProperty.call
+ /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+ /******/
+ /******/ 	// __webpack_public_path__
+ /******/ 	__webpack_require__.p = "/assets/build";
+ /******/
+ /******/ 	// Load entry module and return exports
+-/******/ 	return __webpack_require__(__webpack_require__.s = 20);
++/******/ 	return __webpack_require__(__webpack_require__.s = 22);
+ /******/ })
+ /************************************************************************/
+ /******/ ([
+ /* 0 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ // Dependencies
+-const validProtocols = /^(http|https|ftp|data|javascript|resource|chrome):/i;
++const validProtocols = /^(http|https|ftp|data|resource|chrome):/i;
+ const tokenSplitRegex = /(\s|\'|\"|\\)+/;
+ const ELLIPSIS = "\u2026";
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Returns true if the given object is a grip (see RDP protocol)
+  */
+@@ -455,23 +455,40 @@ function isURL(token) {
+     }
+     new URL(token);
+     return true;
+   } catch (e) {
+     return false;
+   }
+ }
+ 
++/**
++ * Returns a new array in which `char` are interleaved between the original items.
++ *
++ * @param {Array} items
++ * @param {String} char
++ * @returns Array
++ */
++function interleave(items, char) {
++  return items.reduce((res, item, index) => {
++    if (index !== items.length - 1) {
++      return res.concat(item, char);
++    }
++    return res.concat(item);
++  }, []);
++}
++
+ const ellipsisElement = span({
+   key: "more",
+   className: "more-ellipsis",
+   title: `more${ELLIPSIS}`
+ }, ELLIPSIS);
+ 
+ module.exports = {
++  interleave,
+   isGrip,
+   isURL,
+   cropString,
+   containsURL,
+   rawCropString,
+   sanitizeString,
+   escapeString,
+   wrapRender,
+@@ -525,55 +542,56 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-__webpack_require__(21);
++__webpack_require__(23);
+ 
+ // Load all existing rep templates
+-const Undefined = __webpack_require__(22);
+-const Null = __webpack_require__(23);
++const Undefined = __webpack_require__(24);
++const Null = __webpack_require__(25);
+ const StringRep = __webpack_require__(7);
+-const Number = __webpack_require__(24);
++const Number = __webpack_require__(26);
+ const ArrayRep = __webpack_require__(5);
+-const Obj = __webpack_require__(25);
+-const SymbolRep = __webpack_require__(26);
+-const InfinityRep = __webpack_require__(27);
+-const NaNRep = __webpack_require__(28);
+-const Accessor = __webpack_require__(29);
++const Obj = __webpack_require__(27);
++const SymbolRep = __webpack_require__(28);
++const InfinityRep = __webpack_require__(29);
++const NaNRep = __webpack_require__(30);
++const Accessor = __webpack_require__(31);
+ 
+ // DOM types (grips)
+-const Attribute = __webpack_require__(30);
+-const DateTime = __webpack_require__(31);
+-const Document = __webpack_require__(32);
+-const Event = __webpack_require__(33);
+-const Func = __webpack_require__(34);
+-const PromiseRep = __webpack_require__(38);
+-const RegExp = __webpack_require__(39);
+-const StyleSheet = __webpack_require__(40);
+-const CommentNode = __webpack_require__(41);
+-const ElementNode = __webpack_require__(42);
+-const TextNode = __webpack_require__(43);
+-const ErrorRep = __webpack_require__(44);
+-const Window = __webpack_require__(45);
+-const ObjectWithText = __webpack_require__(46);
+-const ObjectWithURL = __webpack_require__(47);
++const Attribute = __webpack_require__(32);
++const DateTime = __webpack_require__(33);
++const Document = __webpack_require__(34);
++const DocumentType = __webpack_require__(35);
++const Event = __webpack_require__(36);
++const Func = __webpack_require__(12);
++const PromiseRep = __webpack_require__(37);
++const RegExp = __webpack_require__(38);
++const StyleSheet = __webpack_require__(39);
++const CommentNode = __webpack_require__(40);
++const ElementNode = __webpack_require__(41);
++const TextNode = __webpack_require__(42);
++const ErrorRep = __webpack_require__(43);
++const Window = __webpack_require__(44);
++const ObjectWithText = __webpack_require__(45);
++const ObjectWithURL = __webpack_require__(46);
+ const GripArray = __webpack_require__(13);
+ const GripMap = __webpack_require__(15);
+ const GripMapEntry = __webpack_require__(16);
+-const Grip = __webpack_require__(9);
++const Grip = __webpack_require__(8);
+ 
+ // List of all registered template.
+ // XXX there should be a way for extensions to register a new
+ // or modify an existing rep.
+-let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
++let reps = [RegExp, StyleSheet, Event, DateTime, CommentNode, ElementNode, TextNode, Attribute, Func, PromiseRep, ArrayRep, Document, DocumentType, Window, ObjectWithText, ObjectWithURL, ErrorRep, GripArray, GripMap, GripMapEntry, Grip, Undefined, Null, StringRep, Number, SymbolRep, InfinityRep, NaNRep, Accessor];
+ 
+ /**
+  * Generic rep that is using for rendering native JS types or an object.
+  * The right template used for rendering is picked automatically according
+  * to the current value type. The value must be passed is as 'object'
+  * property.
+  */
+ const Rep = function (props) {
+@@ -622,16 +640,17 @@ module.exports = {
+   Rep,
+   REPS: {
+     Accessor,
+     ArrayRep,
+     Attribute,
+     CommentNode,
+     DateTime,
+     Document,
++    DocumentType,
+     ElementNode,
+     ErrorRep,
+     Event,
+     Func,
+     Grip,
+     GripArray,
+     GripMap,
+     GripMapEntry,
+@@ -717,39 +736,41 @@ function ArrayRep(props) {
+     brackets = needSpace(items.length > 0);
+   }
+ 
+   return span({
+     className: "objectBox objectBox-array" }, span({
+     className: "arrayLeftBracket"
+   }, brackets.left), ...items, span({
+     className: "arrayRightBracket"
+-  }, brackets.right), span({
+-    className: "arrayProperties",
+-    role: "group" }));
++  }, brackets.right));
+ }
+ 
+ function arrayIterator(props, array, max) {
+   let items = [];
+ 
+   for (let i = 0; i < array.length && i < max; i++) {
+     let config = {
+       mode: MODE.TINY,
+       delim: i == array.length - 1 ? "" : ", "
+     };
+     let item;
+ 
+     try {
+-      item = ItemRep(Object.assign({}, props, config, {
++      item = ItemRep({
++        ...props,
++        ...config,
+         object: array[i]
+-      }));
++      });
+     } catch (exc) {
+-      item = ItemRep(Object.assign({}, props, config, {
++      item = ItemRep({
++        ...props,
++        ...config,
+         object: exc
+-      }));
++      });
+     }
+     items.push(item);
+   }
+ 
+   if (array.length > max) {
+     items.push(span({
+       className: "more-ellipsis",
+       title: "more…"
+@@ -771,20 +792,21 @@ ItemRep.propTypes = {
+ function ItemRep(props) {
+   const { Rep } = __webpack_require__(4);
+ 
+   let {
+     object,
+     delim,
+     mode
+   } = props;
+-  return span({}, Rep(Object.assign({}, props, {
++  return span({}, Rep({
++    ...props,
+     object: object,
+     mode: mode
+-  })), delim);
++  }), delim);
+ }
+ 
+ function getLength(object) {
+   return object.length;
+ }
+ 
+ function supportsObject(object) {
+   return Array.isArray(object) || Object.prototype.toString.call(object) === "[object Arguments]";
+@@ -817,18 +839,17 @@ module.exports = {
+ // Dependencies
+ const PropTypes = __webpack_require__(2);
+ const {
+   maybeEscapePropertyName,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+ 
+-const dom = __webpack_require__(1);
+-const { span } = dom;
++const { span } = __webpack_require__(1);
+ 
+ /**
+  * Property for Obj (local JS objects), Grip (remote JS objects)
+  * and GripMap (remote JS maps and weakmaps) reps.
+  * It's used to render object properties.
+  */
+ PropRep.propTypes = {
+   // Property name.
+@@ -849,17 +870,17 @@ PropRep.propTypes = {
+ /**
+  * Function that given a name, a delimiter and an object returns an array
+  * of React elements representing an object property (e.g. `name: value`)
+  *
+  * @param {Object} props
+  * @return {Array} Array of React elements.
+  */
+ function PropRep(props) {
+-  const Grip = __webpack_require__(9);
++  const Grip = __webpack_require__(8);
+   const { Rep } = __webpack_require__(4);
+ 
+   let {
+     name,
+     mode,
+     equal,
+     suppressQuotes
+   } = props;
+@@ -868,27 +889,28 @@ function PropRep(props) {
+   // The key can be a simple string, for plain objects,
+   // or another object for maps and weakmaps.
+   if (typeof name === "string") {
+     if (!suppressQuotes) {
+       name = maybeEscapePropertyName(name);
+     }
+     key = span({ "className": "nodeName" }, name);
+   } else {
+-    key = Rep(Object.assign({}, props, {
++    key = Rep({
++      ...props,
+       className: "nodeName",
+       object: name,
+       mode: mode || MODE.TINY,
+       defaultRep: Grip
+-    }));
++    });
+   }
+ 
+   return [key, span({
+     "className": "objectEqual"
+-  }, equal), Rep(Object.assign({}, props))];
++  }, equal), Rep({ ...props })];
+ }
+ 
+ // Exports from this module
+ module.exports = wrapRender(PropRep);
+ 
+ /***/ }),
+ /* 7 */
+ /***/ (function(module, exports, __webpack_require__) {
+@@ -925,31 +947,29 @@ const { a, span } = dom;
+ StringRep.propTypes = {
+   useQuotes: PropTypes.bool,
+   escapeWhitespace: PropTypes.bool,
+   style: PropTypes.object,
+   cropLimit: PropTypes.number.isRequired,
+   member: PropTypes.string,
+   object: PropTypes.object.isRequired,
+   openLink: PropTypes.func,
+-  className: PropTypes.string,
+-  omitLinkHref: PropTypes.bool
++  className: PropTypes.string
+ };
+ 
+ function StringRep(props) {
+   let {
+     className,
+     style,
+     cropLimit,
+     object,
+     useQuotes = true,
+     escapeWhitespace = true,
+     member,
+-    openLink,
+-    omitLinkHref = true
++    openLink
+   } = props;
+ 
+   let text = object;
+ 
+   const isLong = isLongString(object);
+   const shouldCrop = (!member || !member.open) && cropLimit && text.length > cropLimit;
+ 
+   if (isLong) {
+@@ -967,17 +987,17 @@ function StringRep(props) {
+   const config = getElementConfig({
+     className,
+     style,
+     actor: object.actor
+   });
+ 
+   if (!isLong) {
+     if (containsURL(text)) {
+-      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, omitLinkHref, openLink));
++      return span(config, ...getLinkifiedElements(text, shouldCrop && cropLimit, openLink));
+     }
+ 
+     // Cropping of longString has been handled before formatting.
+     text = maybeCropString({
+       isLong,
+       shouldCrop,
+       cropLimit
+     }, text);
+@@ -1052,21 +1072,20 @@ function maybeCropString(opts, text) {
+ }
+ 
+ /**
+  * Get an array of the elements representing the string, cropped if needed,
+  * with actual links.
+  *
+  * @param {String} text: The actual string to linkify.
+  * @param {Integer | null} cropLimit
+- * @param {Boolean} omitLinkHref: Do not create an href attribute if true.
+  * @param {Function} openLink: Function handling the link opening.
+  * @returns {Array<String|ReactElement>}
+  */
+-function getLinkifiedElements(text, cropLimit, omitLinkHref, openLink) {
++function getLinkifiedElements(text, cropLimit, openLink) {
+   const halfLimit = Math.ceil((cropLimit - ELLIPSIS.length) / 2);
+   const startCropIndex = cropLimit ? halfLimit : null;
+   const endCropIndex = cropLimit ? text.length - halfLimit : null;
+ 
+   // As we walk through the tokens of the source string, we make sure to preserve
+   // the original whitespace that separated the tokens.
+   let currentIndex = 0;
+   const items = [];
+@@ -1083,17 +1102,16 @@ function getLinkifiedElements(text, crop
+       // Update the index to match the beginning of the token.
+       currentIndex = tokenStart;
+ 
+       let linkText = getCroppedString(token, currentIndex, startCropIndex, endCropIndex);
+       if (linkText) {
+         items.push(a({
+           className: "url",
+           title: token,
+-          href: omitLinkHref === true ? null : token,
+           draggable: false,
+           onClick: openLink ? e => {
+             e.preventDefault();
+             openLink(token);
+           } : null
+         }, linkText));
+       }
+ 
+@@ -1173,36 +1191,31 @@ function supportsObject(object, noGrip =
+ 
+ module.exports = {
+   rep: wrapRender(StringRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+ /* 8 */
+-/***/ (function(module, exports) {
+-
+-module.exports = __WEBPACK_EXTERNAL_MODULE_8__;
+-
+-/***/ }),
+-/* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ 
+ // Dependencies
+ const {
++  interleave,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ const PropRep = __webpack_require__(6);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+@@ -1257,17 +1270,17 @@ function GripRep(props) {
+ 
+     return span(config, ...tinyModeItems);
+   }
+ 
+   let propsArray = safePropIterator(props, object, maxLengthMap.get(mode));
+ 
+   return span(config, getTitleElement(props, object), span({
+     className: "objectLeftBrace"
+-  }, " { "), ...propsArray, span({
++  }, " { "), ...interleave(propsArray, ", "), span({
+     className: "objectRightBrace"
+   }, " }"));
+ }
+ 
+ function getTitleElement(props, object) {
+   return span({
+     className: "objectTitle"
+   }, getTitle(props, object));
+@@ -1312,21 +1325,22 @@ function propIterator(props, object, max
+     })];
+   }
+ 
+   // Property filter. Show only interesting properties to the user.
+   let isInterestingProp = props.isInterestingProp || ((type, value) => {
+     return type == "boolean" || type == "number" || type == "string" && value.length != 0;
+   });
+ 
+-  let properties = object.preview ? object.preview.ownProperties : {};
++  let properties = object.preview ? object.preview.ownProperties || {} : {};
++
+   let propertiesLength = getPropertiesLength(object);
+ 
+   if (object.preview && object.preview.safeGetterValues) {
+-    properties = Object.assign({}, properties, object.preview.safeGetterValues);
++    properties = { ...properties, ...object.preview.safeGetterValues };
+   }
+ 
+   let indexes = getPropIndexes(properties, max, isInterestingProp);
+   if (indexes.length < max && indexes.length < propertiesLength) {
+     // There are not enough props yet. Then add uninteresting props to display them.
+     indexes = indexes.concat(getPropIndexes(properties, max - indexes.length, (t, value, name) => {
+       return !isInterestingProp(t, value, name);
+     }));
+@@ -1340,25 +1354,26 @@ function propIterator(props, object, max
+   let propsArray = getProps(props, properties, indexes, suppressQuotes);
+ 
+   // Show symbols.
+   if (object.preview && object.preview.ownSymbols) {
+     const { ownSymbols } = object.preview;
+     const length = max - indexes.length;
+ 
+     const symbolsProps = ownSymbols.slice(0, length).map(symbolItem => {
+-      return PropRep(Object.assign({}, props, {
++      return PropRep({
++        ...props,
+         mode: MODE.TINY,
+         name: symbolItem,
+         object: symbolItem.descriptor.value,
+         equal: ": ",
+         defaultRep: Grip,
+         title: null,
+         suppressQuotes
+-      }));
++      });
+     });
+ 
+     propsArray.push(...symbolsProps);
+   }
+ 
+   if (Object.keys(properties).length > max || propertiesLength > max
+   // When the object has non-enumerable properties, we don't have them in the packet,
+   // but we might want to show there's something in the object.
+@@ -1366,33 +1381,17 @@ function propIterator(props, object, max
+     // There are some undisplayed props. Then display "more...".
+     propsArray.push(span({
+       key: "more",
+       className: "more-ellipsis",
+       title: "more…"
+     }, "…"));
+   }
+ 
+-  return unfoldProps(propsArray);
+-}
+-
+-function unfoldProps(items) {
+-  return items.reduce((res, item, index) => {
+-    if (Array.isArray(item)) {
+-      res = res.concat(item);
+-    } else {
+-      res.push(item);
+-    }
+-
+-    // Interleave commas between elements
+-    if (index !== items.length - 1) {
+-      res.push(", ");
+-    }
+-    return res;
+-  }, []);
++  return propsArray;
+ }
+ 
+ /**
+  * Get props ordered by index.
+  *
+  * @param {Object} componentProps Grip Component props.
+  * @param {Object} properties Properties of the object the Grip describes.
+  * @param {Array} indexes Indexes of properties.
+@@ -1406,25 +1405,26 @@ function getProps(componentProps, proper
+     return a - b;
+   });
+ 
+   const propertiesKeys = Object.keys(properties);
+   return indexes.map(i => {
+     let name = propertiesKeys[i];
+     let value = getPropValue(properties[name]);
+ 
+-    return PropRep(Object.assign({}, componentProps, {
++    return PropRep({
++      ...componentProps,
+       mode: MODE.TINY,
+       name,
+       object: value,
+       equal: ": ",
+       defaultRep: Grip,
+       title: null,
+       suppressQuotes
+-    }));
++    });
+   });
+ }
+ 
+ /**
+  * Get the indexes of props in the object.
+  *
+  * @param {Object} properties Props object.
+  * @param {Number} max The maximum length of indexes array.
+@@ -1500,162 +1500,17 @@ let Grip = {
+   supportsObject,
+   maxLengthMap
+ };
+ 
+ // Exports from this module
+ module.exports = Grip;
+ 
+ /***/ }),
+-/* 10 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-var _svgInlineReact = __webpack_require__(11);
+-
+-var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-/* 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/. */
+-
+-const React = __webpack_require__(8);
+-const PropTypes = __webpack_require__(2);
+-
+-
+-const svg = {
+-  "open-inspector": __webpack_require__(36),
+-  "jump-definition": __webpack_require__(37)
+-};
+-
+-Svg.propTypes = {
+-  className: PropTypes.string
+-};
+-
+-function Svg(name, props) {
+-  if (!svg[name]) {
+-    throw new Error("Unknown SVG: " + name);
+-  }
+-  let className = name;
+-  if (props && props.className) {
+-    className = `${name} ${props.className}`;
+-  }
+-  if (name === "subSettings") {
+-    className = "";
+-  }
+-  props = Object.assign({}, props, { className, src: svg[name] });
+-  return React.createElement(_svgInlineReact2.default, props);
+-}
+-
+-module.exports = Svg;
+-
+-/***/ }),
+-/* 11 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-
+-var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
+-
+-var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
+-
+-var _react = __webpack_require__(8);
+-
+-var _react2 = _interopRequireDefault(_react);
+-
+-var _propTypes = __webpack_require__(2);
+-
+-var _util = __webpack_require__(35);
+-
+-function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+-
+-function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
+-
+-function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+-
+-function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
+-
+-function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
+-
+-var process = process || { env: {} };
+-
+-var InlineSVG = function (_React$Component) {
+-    _inherits(InlineSVG, _React$Component);
+-
+-    function InlineSVG() {
+-        _classCallCheck(this, InlineSVG);
+-
+-        return _possibleConstructorReturn(this, (InlineSVG.__proto__ || Object.getPrototypeOf(InlineSVG)).apply(this, arguments));
+-    }
+-
+-    _createClass(InlineSVG, [{
+-        key: 'componentWillReceiveProps',
+-        value: function componentWillReceiveProps(_ref) {
+-            var children = _ref.children;
+-
+-            if ("production" !== process.env.NODE_ENV && children != null) {
+-                console.info('<InlineSVG />: `children` prop will be ignored.');
+-            }
+-        }
+-    }, {
+-        key: 'render',
+-        value: function render() {
+-            var Element = void 0,
+-                __html = void 0,
+-                svgProps = void 0;
+-
+-            var _props = this.props,
+-                element = _props.element,
+-                raw = _props.raw,
+-                src = _props.src,
+-                otherProps = _objectWithoutProperties(_props, ['element', 'raw', 'src']);
+-
+-            if (raw === true) {
+-                Element = 'svg';
+-                svgProps = (0, _util.extractSVGProps)(src);
+-                __html = (0, _util.getSVGFromSource)(src).innerHTML;
+-            }
+-            __html = __html || src;
+-            Element = Element || element;
+-            svgProps = svgProps || {};
+-
+-            return _react2.default.createElement(Element, _extends({}, svgProps, otherProps, { src: null, children: null,
+-                dangerouslySetInnerHTML: { __html: __html } }));
+-        }
+-    }]);
+-
+-    return InlineSVG;
+-}(_react2.default.Component);
+-
+-exports.default = InlineSVG;
+-
+-
+-InlineSVG.defaultProps = {
+-    element: 'i',
+-    raw: false,
+-    src: ''
+-};
+-
+-InlineSVG.propTypes = {
+-    src: _propTypes.string.isRequired,
+-    element: _propTypes.string,
+-    raw: _propTypes.bool
+-};
+-
+-/***/ }),
+-/* 12 */
++/* 9 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -1679,728 +1534,57 @@ module.exports = {
+   DOCUMENT_POSITION_PRECEDING: 0x02,
+   DOCUMENT_POSITION_FOLLOWING: 0x04,
+   DOCUMENT_POSITION_CONTAINS: 0x08,
+   DOCUMENT_POSITION_CONTAINED_BY: 0x10,
+   DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 0x20
+ };
+ 
+ /***/ }),
+-/* 13 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-// Dependencies
+-const PropTypes = __webpack_require__(2);
+-
+-const { lengthBubble } = __webpack_require__(14);
+-const {
+-  getGripType,
+-  isGrip,
+-  wrapRender,
+-  ellipsisElement
+-} = __webpack_require__(0);
+-const { MODE } = __webpack_require__(3);
+-
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-const { ModePropType } = __webpack_require__(5);
+-
+-/**
+- * Renders an array. The array is enclosed by left and right bracket
+- * and the max number of rendered items depends on the current mode.
+- */
+-GripArray.propTypes = {
+-  object: PropTypes.object.isRequired,
+-  // @TODO Change this to Object.values once it's supported in Node's version of V8
+-  mode: ModePropType,
+-  provider: PropTypes.object,
+-  onDOMNodeMouseOver: PropTypes.func,
+-  onDOMNodeMouseOut: PropTypes.func,
+-  onInspectIconClick: PropTypes.func
+-};
+-
+-function GripArray(props) {
+-  let {
+-    object,
+-    mode = MODE.SHORT
+-  } = props;
+-
+-  let items;
+-  let brackets;
+-  let needSpace = function (space) {
+-    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
+-  };
+-
+-  const config = {
+-    "data-link-actor-id": object.actor,
+-    className: "objectBox objectBox-array"
+-  };
+-
+-  const title = getTitle(props, object);
+-
+-  if (mode === MODE.TINY) {
+-    const isEmpty = getLength(object) === 0;
+-
+-    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
+-    if (!isEmpty && object.class !== "Array") {
+-      return span(config, title);
+-    }
+-
+-    brackets = needSpace(false);
+-    return span(config, title, span({
+-      className: "arrayLeftBracket"
+-    }, brackets.left), isEmpty ? null : ellipsisElement, span({
+-      className: "arrayRightBracket"
+-    }, brackets.right));
+-  }
+-
+-  let max = maxLengthMap.get(mode);
+-  items = arrayIterator(props, object, max);
+-  brackets = needSpace(items.length > 0);
+-
+-  return span({
+-    "data-link-actor-id": object.actor,
+-    className: "objectBox objectBox-array" }, title, span({
+-    className: "arrayLeftBracket"
+-  }, brackets.left), ...interleaveCommas(items), span({
+-    className: "arrayRightBracket"
+-  }, brackets.right), span({
+-    className: "arrayProperties",
+-    role: "group" }));
+-}
+-
+-function interleaveCommas(items) {
+-  return items.reduce((res, item, index) => {
+-    if (index !== items.length - 1) {
+-      return res.concat(item, ", ");
+-    }
+-    return res.concat(item);
+-  }, []);
+-}
+-
+-function getLength(grip) {
+-  if (!grip.preview) {
+-    return 0;
+-  }
+-
+-  return grip.preview.length || grip.preview.childNodesLength || 0;
+-}
+-
+-function getTitle(props, object) {
+-  let objectLength = getLength(object);
+-  let isEmpty = objectLength === 0;
+-
+-  let title = props.title || object.class || "Array";
+-
+-  const length = lengthBubble({
+-    object,
+-    mode: props.mode,
+-    maxLengthMap,
+-    getLength
+-  });
+-
+-  if (props.mode === MODE.TINY) {
+-    if (isEmpty) {
+-      return object.class === "Array" ? "" : span({
+-        className: "objectTitle" }, title, " ");
+-    }
+-
+-    let trailingSpace;
+-    if (object.class === "Array") {
+-      title = "";
+-      trailingSpace = " ";
+-    }
+-
+-    return span({
+-      className: "objectTitle" }, title, length, trailingSpace);
+-  }
+-
+-  return span({
+-    className: "objectTitle" }, title, length, " ");
+-}
+-
+-function getPreviewItems(grip) {
+-  if (!grip.preview) {
+-    return null;
+-  }
+-
+-  return grip.preview.items || grip.preview.childNodes || [];
+-}
+-
+-function arrayIterator(props, grip, max) {
+-  let { Rep } = __webpack_require__(4);
+-
+-  let items = [];
+-  const gripLength = getLength(grip);
+-
+-  if (!gripLength) {
+-    return items;
+-  }
+-
+-  const previewItems = getPreviewItems(grip);
+-  let provider = props.provider;
+-
+-  let emptySlots = 0;
+-  let foldedEmptySlots = 0;
+-  items = previewItems.reduce((res, itemGrip) => {
+-    if (res.length >= max) {
+-      return res;
+-    }
+-
+-    let object;
+-    try {
+-      if (!provider && itemGrip === null) {
+-        emptySlots++;
+-        return res;
+-      }
+-
+-      object = provider ? provider.getValue(itemGrip) : itemGrip;
+-    } catch (exc) {
+-      object = exc;
+-    }
+-
+-    if (emptySlots > 0) {
+-      res.push(getEmptySlotsElement(emptySlots));
+-      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+-      emptySlots = 0;
+-    }
+-
+-    if (res.length < max) {
+-      res.push(Rep(Object.assign({}, props, {
+-        object,
+-        mode: MODE.TINY,
+-        // Do not propagate title to array items reps
+-        title: undefined
+-      })));
+-    }
+-
+-    return res;
+-  }, []);
+-
+-  // Handle trailing empty slots if there are some.
+-  if (items.length < max && emptySlots > 0) {
+-    items.push(getEmptySlotsElement(emptySlots));
+-    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
+-  }
+-
+-  const itemsShown = items.length + foldedEmptySlots;
+-  if (gripLength > itemsShown) {
+-    items.push(ellipsisElement);
+-  }
+-
+-  return items;
+-}
+-
+-function getEmptySlotsElement(number) {
+-  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
+-  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
+-}
+-
+-function supportsObject(grip, noGrip = false) {
+-  if (noGrip === true || !isGrip(grip)) {
+-    return false;
+-  }
+-
+-  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
+-}
+-
+-const maxLengthMap = new Map();
+-maxLengthMap.set(MODE.SHORT, 3);
+-maxLengthMap.set(MODE.LONG, 10);
+-
+-// Exports from this module
+-module.exports = {
+-  rep: wrapRender(GripArray),
+-  supportsObject,
+-  maxLengthMap,
+-  getLength
+-};
++/* 10 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_10__;
+ 
+ /***/ }),
+-/* 14 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-const PropTypes = __webpack_require__(2);
+-
+-const { wrapRender } = __webpack_require__(0);
+-const { MODE } = __webpack_require__(3);
+-const { ModePropType } = __webpack_require__(5);
+-
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-
+-GripLengthBubble.propTypes = {
+-  object: PropTypes.object.isRequired,
+-  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
+-  getLength: PropTypes.func.isRequired,
+-  mode: ModePropType,
+-  visibilityThreshold: PropTypes.number
+-};
+-
+-function GripLengthBubble(props) {
+-  const {
+-    object,
+-    mode = MODE.SHORT,
+-    visibilityThreshold = 2,
+-    maxLengthMap,
+-    getLength,
+-    showZeroLength = false
+-  } = props;
+-
+-  const length = getLength(object);
+-  const isEmpty = length === 0;
+-  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
+-  if (isEmpty && !showZeroLength || isObvious) {
+-    return "";
+-  }
+-
+-  return span({
+-    className: "objectLengthBubble"
+-  }, `(${length})`);
+-}
+-
+-module.exports = {
+-  lengthBubble: wrapRender(GripLengthBubble)
+-};
+-
+-/***/ }),
+-/* 15 */
++/* 11 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
+-// Dependencies
+-
+-const { lengthBubble } = __webpack_require__(14);
+-const PropTypes = __webpack_require__(2);
+-const {
+-  isGrip,
+-  wrapRender,
+-  ellipsisElement
+-} = __webpack_require__(0);
+-const PropRep = __webpack_require__(6);
+-const { MODE } = __webpack_require__(3);
+-const { ModePropType } = __webpack_require__(5);
+-
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-
+-/**
+- * Renders an map. A map is represented by a list of its
+- * entries enclosed in curly brackets.
+- */
+-GripMap.propTypes = {
+-  object: PropTypes.object,
+-  // @TODO Change this to Object.values once it's supported in Node's version of V8
+-  mode: ModePropType,
+-  isInterestingEntry: PropTypes.func,
+-  onDOMNodeMouseOver: PropTypes.func,
+-  onDOMNodeMouseOut: PropTypes.func,
+-  onInspectIconClick: PropTypes.func,
+-  title: PropTypes.string
+-};
+-
+-function GripMap(props) {
+-  let {
+-    mode,
+-    object
+-  } = props;
+-
+-  const config = {
+-    "data-link-actor-id": object.actor,
+-    className: "objectBox objectBox-object"
+-  };
+-
+-  const title = getTitle(props, object);
+-  const isEmpty = getLength(object) === 0;
+-
+-  if (isEmpty || mode === MODE.TINY) {
+-    return span(config, title);
+-  }
+-
+-  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
+-
+-  return span(config, title, span({
+-    className: "objectLeftBrace"
+-  }, " { "), ...propsArray, span({
+-    className: "objectRightBrace"
+-  }, " }"));
+-}
+-
+-function getTitle(props, object) {
+-  const title = props.title || (object && object.class ? object.class : "Map");
+-  return span({
+-    className: "objectTitle" }, title, lengthBubble({
+-    object,
+-    mode: props.mode,
+-    maxLengthMap,
+-    getLength,
+-    showZeroLength: true
+-  }));
+-}
+-
+-function safeEntriesIterator(props, object, max) {
+-  max = typeof max === "undefined" ? 3 : max;
+-  try {
+-    return entriesIterator(props, object, max);
+-  } catch (err) {
+-    console.error(err);
+-  }
+-  return [];
+-}
+-
+-function entriesIterator(props, object, max) {
+-  // Entry filter. Show only interesting entries to the user.
+-  let isInterestingEntry = props.isInterestingEntry || ((type, value) => {
+-    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
+-  });
+-
+-  let mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
+-
+-  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
+-  if (indexes.length < max && indexes.length < mapEntries.length) {
+-    // There are not enough entries yet, so we add uninteresting entries.
+-    indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
+-      return !isInterestingEntry(t, value, name);
+-    }));
+-  }
+-
+-  let entries = getEntries(props, mapEntries, indexes);
+-  if (entries.length < getLength(object)) {
+-    // There are some undisplayed entries. Then display "…".
+-    entries.push(ellipsisElement);
+-  }
+-
+-  return unfoldEntries(entries);
+-}
+-
+-function unfoldEntries(items) {
+-  return items.reduce((res, item, index) => {
+-    if (Array.isArray(item)) {
+-      res = res.concat(item);
+-    } else {
+-      res.push(item);
+-    }
+-
+-    // Interleave commas between elements
+-    if (index !== items.length - 1) {
+-      res.push(", ");
+-    }
+-    return res;
+-  }, []);
+-}
+-
+-/**
+- * Get entries ordered by index.
+- *
+- * @param {Object} props Component props.
+- * @param {Array} entries Entries array.
+- * @param {Array} indexes Indexes of entries.
+- * @return {Array} Array of PropRep.
+- */
+-function getEntries(props, entries, indexes) {
+-  let {
+-    onDOMNodeMouseOver,
+-    onDOMNodeMouseOut,
+-    onInspectIconClick
+-  } = props;
+-
+-  // Make indexes ordered by ascending.
+-  indexes.sort(function (a, b) {
+-    return a - b;
+-  });
+-
+-  return indexes.map((index, i) => {
+-    let [key, entryValue] = entries[index];
+-    let value = entryValue.value !== undefined ? entryValue.value : entryValue;
+-
+-    return PropRep({
+-      name: key,
+-      equal: " \u2192 ",
+-      object: value,
+-      mode: MODE.TINY,
+-      onDOMNodeMouseOver,
+-      onDOMNodeMouseOut,
+-      onInspectIconClick
+-    });
+-  });
+-}
+-
+-/**
+- * Get the indexes of entries in the map.
+- *
+- * @param {Array} entries Entries array.
+- * @param {Number} max The maximum length of indexes array.
+- * @param {Function} filter Filter the entry you want.
+- * @return {Array} Indexes of filtered entries in the map.
+- */
+-function getEntriesIndexes(entries, max, filter) {
+-  return entries.reduce((indexes, [key, entry], i) => {
+-    if (indexes.length < max) {
+-      let value = entry && entry.value !== undefined ? entry.value : entry;
+-      // Type is specified in grip's "class" field and for primitive
+-      // values use typeof.
+-      let type = (value && value.class ? value.class : typeof value).toLowerCase();
+-
+-      if (filter(type, value, key)) {
+-        indexes.push(i);
+-      }
+-    }
+-
+-    return indexes;
+-  }, []);
+-}
+-
+-function getLength(grip) {
+-  return grip.preview.size || 0;
+-}
+-
+-function supportsObject(grip, noGrip = false) {
+-  if (noGrip === true || !isGrip(grip)) {
+-    return false;
+-  }
+-  return grip.preview && grip.preview.kind == "MapLike";
+-}
+-
+-const maxLengthMap = new Map();
+-maxLengthMap.set(MODE.SHORT, 3);
+-maxLengthMap.set(MODE.LONG, 10);
+-
+-// Exports from this module
+-module.exports = {
+-  rep: wrapRender(GripMap),
+-  supportsObject,
+-  maxLengthMap,
+-  getLength
+-};
+-
+-/***/ }),
+-/* 16 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-// Dependencies
+-const PropTypes = __webpack_require__(2);
+-// Shortcuts
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-const {
+-  wrapRender
+-} = __webpack_require__(0);
+-const PropRep = __webpack_require__(6);
+-const { MODE } = __webpack_require__(3);
+-/**
+- * Renders an map entry. A map entry is represented by its key, a column and its value.
+- */
+-GripMapEntry.propTypes = {
+-  object: PropTypes.object,
+-  // @TODO Change this to Object.values once it's supported in Node's version of V8
+-  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+-  onDOMNodeMouseOver: PropTypes.func,
+-  onDOMNodeMouseOut: PropTypes.func,
+-  onInspectIconClick: PropTypes.func
+-};
+-
+-function GripMapEntry(props) {
+-  const {
+-    object
+-  } = props;
+-
+-  const {
+-    key,
+-    value
+-  } = object.preview;
+-
+-  return span({
+-    className: "objectBox objectBox-map-entry"
+-  }, ...PropRep(Object.assign({}, props, {
+-    name: key,
+-    object: value,
+-    equal: " \u2192 ",
+-    title: null,
+-    suppressQuotes: false
+-  })));
+-}
+-
+-function supportsObject(grip, noGrip = false) {
+-  if (noGrip === true) {
+-    return false;
+-  }
+-  return grip && grip.type === "mapEntry" && grip.preview;
+-}
+-
+-function createGripMapEntry(key, value) {
+-  return {
+-    type: "mapEntry",
+-    preview: {
+-      key,
+-      value
+-    }
+-  };
+-}
+-
+-// Exports from this module
+-module.exports = {
+-  rep: wrapRender(GripMapEntry),
+-  createGripMapEntry,
+-  supportsObject
+-};
+-
+-/***/ }),
+-/* 17 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-const client = __webpack_require__(18);
+-const loadProperties = __webpack_require__(55);
+-const node = __webpack_require__(19);
+-
+-module.exports = {
+-  client,
+-  loadProperties,
+-  node
+-};
+-
+-/***/ }),
+-/* 18 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-async function enumIndexedProperties(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumIndexedProperties", e);
+-    return {};
+-  }
+-} /* 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/. */
+-
+-async function enumNonIndexedProperties(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumNonIndexedProperties", e);
+-    return {};
+-  }
+-}
+-
+-async function enumEntries(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumEntries();
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumEntries", e);
+-    return {};
+-  }
+-}
+-
+-async function enumSymbols(objectClient, start, end) {
+-  try {
+-    const { iterator } = await objectClient.enumSymbols();
+-    const response = await iteratorSlice(iterator, start, end);
+-    return response;
+-  } catch (e) {
+-    console.error("Error in enumSymbols", e);
+-    return {};
+-  }
+-}
+-
+-async function getPrototype(objectClient) {
+-  if (typeof objectClient.getPrototype !== "function") {
+-    console.error("objectClient.getPrototype is not a function");
+-    return Promise.resolve({});
+-  }
+-  return objectClient.getPrototype();
+-}
+-
+-function iteratorSlice(iterator, start, end) {
+-  start = start || 0;
+-  const count = end ? end - start + 1 : iterator.count;
+-  return iterator.slice(start, count);
+-}
+-
+-module.exports = {
+-  enumEntries,
+-  enumIndexedProperties,
+-  enumNonIndexedProperties,
+-  enumSymbols,
+-  getPrototype
+-};
+-
+-/***/ }),
+-/* 19 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-const { get, has } = __webpack_require__(56);
++const { get, has } = __webpack_require__(54);
+ const { maybeEscapePropertyName } = __webpack_require__(0);
+ const ArrayRep = __webpack_require__(5);
+ const GripArrayRep = __webpack_require__(13);
+ const GripMap = __webpack_require__(15);
+ const GripMapEntryRep = __webpack_require__(16);
+ 
+ const MAX_NUMERICAL_PROPERTIES = 100;
+ 
+ const NODE_TYPES = {
+   BUCKET: Symbol("[n…n]"),
+-  DEFAULT_PROPERTIES: Symbol("[default properties]"),
++  DEFAULT_PROPERTIES: Symbol("<default properties>"),
+   ENTRIES: Symbol("<entries>"),
+   GET: Symbol("<get>"),
+   GRIP: Symbol("GRIP"),
+   MAP_ENTRY_KEY: Symbol("<key>"),
+   MAP_ENTRY_VALUE: Symbol("<value>"),
+   PROMISE_REASON: Symbol("<reason>"),
+   PROMISE_STATE: Symbol("<state>"),
+   PROMISE_VALUE: Symbol("<value>"),
+   PROXY_HANDLER: Symbol("<handler>"),
+   PROXY_TARGET: Symbol("<target>"),
+   SET: Symbol("<set>"),
+-  PROTOTYPE: Symbol("__proto__")
++  PROTOTYPE: Symbol("<prototype>"),
++  BLOCK: Symbol("☲")
+ };
+ 
+ let WINDOW_PROPERTIES = {};
+ 
+ if (typeof window === "object") {
+   WINDOW_PROPERTIES = Object.getOwnPropertyNames(window);
+ }
+ 
+@@ -2538,16 +1722,20 @@ function nodeIsWindow(item) {
+ function nodeIsGetter(item) {
+   return getType(item) === NODE_TYPES.GET;
+ }
+ 
+ function nodeIsSetter(item) {
+   return getType(item) === NODE_TYPES.SET;
+ }
+ 
++function nodeIsBlock(item) {
++  return getType(item) === NODE_TYPES.BLOCK;
++}
++
+ function nodeHasAccessors(item) {
+   return !!getNodeGetter(item) || !!getNodeSetter(item);
+ }
+ 
+ function nodeSupportsNumericalBucketing(item) {
+   // We exclude elements with entries since it's the <entries> node
+   // itself that can have buckets.
+   return nodeIsArrayLike(item) && !nodeHasEntries(item) || nodeIsEntries(item) || nodeIsBucket(item);
+@@ -2727,17 +1915,17 @@ function makeDefaultPropsBucket(properti
+     } else {
+       userPropertiesNames.push(name);
+     }
+   });
+ 
+   let nodes = makeNodesForOwnProps(userPropertiesNames, parent, ownProperties);
+ 
+   if (defaultProperties.length > 0) {
+-    const defaultPropertiesNode = createNode(parent, "[default properties]", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);
++    const defaultPropertiesNode = createNode(parent, "<default properties>", `${parentPath}/${SAFE_PATH_PREFIX}default`, null, NODE_TYPES.DEFAULT_PROPERTIES);
+ 
+     const defaultNodes = defaultProperties.map((name, index) => createNode(defaultPropertiesNode, maybeEscapePropertyName(name), `${parentPath}/${SAFE_PATH_PREFIX}bucket${index}/${name}`, ownProperties[name]));
+     nodes.push(setNodeChildren(defaultPropertiesNode, defaultNodes));
+   }
+   return nodes;
+ }
+ 
+ function makeNodesForOwnProps(propertiesNames, parent, ownProperties) {
+@@ -2751,17 +1939,17 @@ function makeNodesForProperties(objProps
+     ownSymbols,
+     prototype,
+     safeGetterValues
+   } = objProps;
+ 
+   const parentPath = parent.path;
+   const parentValue = getValue(parent);
+ 
+-  let allProperties = Object.assign({}, ownProperties, safeGetterValues);
++  let allProperties = { ...ownProperties, ...safeGetterValues };
+ 
+   // Ignore properties that are neither non-concrete nor getters/setters.
+   const propertiesNames = sortProperties(Object.keys(allProperties)).filter(name => {
+     if (!allProperties[name]) {
+       return false;
+     }
+ 
+     const properties = Object.getOwnPropertyNames(allProperties[name]);
+@@ -2799,17 +1987,17 @@ function makeNodesForProperties(objProps
+ 
+ function makeNodeForPrototype(objProps, parent) {
+   const {
+     prototype
+   } = objProps || {};
+ 
+   // Add the prototype if it exists and is not null
+   if (prototype && prototype.type !== "null") {
+-    return createNode(parent, "__proto__", `${parent.path}/__proto__`, { value: prototype }, NODE_TYPES.PROTOTYPE);
++    return createNode(parent, "<prototype>", `${parent.path}/<prototype>`, { value: prototype }, NODE_TYPES.PROTOTYPE);
+   }
+ 
+   return null;
+ }
+ 
+ function createNode(parent, name, path, contents, type = NODE_TYPES.GRIP, meta) {
+   if (contents === undefined) {
+     return null;
+@@ -2988,16 +2176,17 @@ module.exports = {
+   makeNodesForPromiseProperties,
+   makeNodesForProperties,
+   makeNumericalBuckets,
+   nodeHasAccessors,
+   nodeHasAllEntriesInPreview,
+   nodeHasChildren,
+   nodeHasEntries,
+   nodeHasProperties,
++  nodeIsBlock,
+   nodeIsBucket,
+   nodeIsDefaultProperties,
+   nodeIsEntries,
+   nodeIsFunction,
+   nodeIsGetter,
+   nodeIsMapEntry,
+   nodeIsMissingArguments,
+   nodeIsObject,
+@@ -3016,30 +2205,946 @@ module.exports = {
+   setNodeChildren,
+   sortProperties,
+   NODE_TYPES,
+   // Export for testing purpose.
+   SAFE_PATH_PREFIX
+ };
+ 
+ /***/ }),
++/* 12 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// ReactJS
++const PropTypes = __webpack_require__(2);
++
++// Reps
++const {
++  getGripType,
++  isGrip,
++  cropString,
++  wrapRender
++} = __webpack_require__(0);
++const { MODE } = __webpack_require__(3);
++
++const dom = __webpack_require__(1);
++const { span } = dom;
++
++const IGNORED_SOURCE_URLS = ["debugger eval code"];
++
++/**
++ * This component represents a template for Function objects.
++ */
++FunctionRep.propTypes = {
++  object: PropTypes.object.isRequired,
++  parameterNames: PropTypes.array,
++  onViewSourceInDebugger: PropTypes.func
++};
++
++function FunctionRep(props) {
++  let {
++    object: grip,
++    onViewSourceInDebugger
++  } = props;
++
++  let jumpToDefinitionButton;
++  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
++    jumpToDefinitionButton = dom.button({
++      className: "jump-definition",
++      draggable: false,
++      title: "Jump to definition",
++      onClick: e => {
++        // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
++        e.stopPropagation();
++        onViewSourceInDebugger(grip.location);
++      }
++    });
++  }
++
++  return span({
++    "data-link-actor-id": grip.actor,
++    className: "objectBox objectBox-function",
++    // Set dir="ltr" to prevent function parentheses from
++    // appearing in the wrong direction
++    dir: "ltr"
++  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
++}
++
++function getTitle(grip, props) {
++  const {
++    mode
++  } = props;
++
++  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
++    return null;
++  }
++
++  let title = mode === MODE.TINY ? "" : "function ";
++
++  if (grip.isGenerator) {
++    title = mode === MODE.TINY ? "* " : "function* ";
++  }
++
++  if (grip.isAsync) {
++    title = "async" + " " + title;
++  }
++
++  return span({
++    className: "objectTitle"
++  }, title);
++}
++
++/**
++ * Returns a ReactElement representing the function name.
++ *
++ * @param {Object} grip : Function grip
++ * @param {Object} props: Function rep props
++ */
++function getFunctionName(grip, props = {}) {
++  let { functionName } = props;
++  let name;
++
++  if (functionName) {
++    let end = functionName.length - 1;
++    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
++  }
++
++  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
++    name = functionName + ":" + grip.displayName;
++  } else {
++    name = cleanFunctionName(grip.userDisplayName || grip.displayName || grip.name || props.functionName || "");
++  }
++
++  return cropString(name, 100);
++}
++
++const objectProperty = /([\w\d]+)$/;
++const arrayProperty = /\[(.*?)\]$/;
++const functionProperty = /([\w\d]+)[\/\.<]*?$/;
++const annonymousProperty = /([\w\d]+)\(\^\)$/;
++
++/**
++ * Decodes an anonymous naming scheme that
++ * spider monkey implements based on "Naming Anonymous JavaScript Functions"
++ * http://johnjbarton.github.io/nonymous/index.html
++ *
++ * @param {String} name : Function name to clean up
++ * @returns String
++ */
++function cleanFunctionName(name) {
++  for (const reg of [objectProperty, arrayProperty, functionProperty, annonymousProperty]) {
++    const match = reg.exec(name);
++    if (match) {
++      return match[1];
++    }
++  }
++
++  return name;
++}
++
++function renderParams(props) {
++  const {
++    parameterNames = []
++  } = props;
++
++  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
++    res.push(span({ className: "param" }, param));
++    if (index < arr.length - 1) {
++      res.push(span({ className: "delimiter" }, ", "));
++    }
++    return res;
++  }, []);
++}
++
++// Registration
++function supportsObject(grip, noGrip = false) {
++  const type = getGripType(grip, noGrip);
++  if (noGrip === true || !isGrip(grip)) {
++    return type == "function";
++  }
++
++  return type == "Function";
++}
++
++// Exports from this module
++
++module.exports = {
++  rep: wrapRender(FunctionRep),
++  supportsObject,
++  cleanFunctionName,
++  // exported for testing purpose.
++  getFunctionName
++};
++
++/***/ }),
++/* 13 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// Dependencies
++const PropTypes = __webpack_require__(2);
++
++const { lengthBubble } = __webpack_require__(14);
++const {
++  interleave,
++  getGripType,
++  isGrip,
++  wrapRender,
++  ellipsisElement
++} = __webpack_require__(0);
++const { MODE } = __webpack_require__(3);
++
++const dom = __webpack_require__(1);
++const { span } = dom;
++const { ModePropType } = __webpack_require__(5);
++const DEFAULT_TITLE = "Array";
++
++/**
++ * Renders an array. The array is enclosed by left and right bracket
++ * and the max number of rendered items depends on the current mode.
++ */
++GripArray.propTypes = {
++  object: PropTypes.object.isRequired,
++  // @TODO Change this to Object.values once it's supported in Node's version of V8
++  mode: ModePropType,
++  provider: PropTypes.object,
++  onDOMNodeMouseOver: PropTypes.func,
++  onDOMNodeMouseOut: PropTypes.func,
++  onInspectIconClick: PropTypes.func
++};
++
++function GripArray(props) {
++  let {
++    object,
++    mode = MODE.SHORT
++  } = props;
++
++  let items;
++  let brackets;
++  let needSpace = function (space) {
++    return space ? { left: "[ ", right: " ]" } : { left: "[", right: "]" };
++  };
++
++  const config = {
++    "data-link-actor-id": object.actor,
++    className: "objectBox objectBox-array"
++  };
++
++  const title = getTitle(props, object);
++
++  if (mode === MODE.TINY) {
++    const isEmpty = getLength(object) === 0;
++
++    // Omit bracketed ellipsis for non-empty non-Array arraylikes (f.e: Sets).
++    if (!isEmpty && object.class !== "Array") {
++      return span(config, title);
++    }
++
++    brackets = needSpace(false);
++    return span(config, title, span({
++      className: "arrayLeftBracket"
++    }, brackets.left), isEmpty ? null : ellipsisElement, span({
++      className: "arrayRightBracket"
++    }, brackets.right));
++  }
++
++  let max = maxLengthMap.get(mode);
++  items = arrayIterator(props, object, max);
++  brackets = needSpace(items.length > 0);
++
++  return span({
++    "data-link-actor-id": object.actor,
++    className: "objectBox objectBox-array" }, title, span({
++    className: "arrayLeftBracket"
++  }, brackets.left), ...interleave(items, ", "), span({
++    className: "arrayRightBracket"
++  }, brackets.right), span({
++    className: "arrayProperties",
++    role: "group" }));
++}
++
++function getLength(grip) {
++  if (!grip.preview) {
++    return 0;
++  }
++
++  return grip.preview.length || grip.preview.childNodesLength || 0;
++}
++
++function getTitle(props, object) {
++  let objectLength = getLength(object);
++  let isEmpty = objectLength === 0;
++
++  let title = props.title || object.class || DEFAULT_TITLE;
++
++  const length = lengthBubble({
++    object,
++    mode: props.mode,
++    maxLengthMap,
++    getLength
++  });
++
++  if (props.mode === MODE.TINY) {
++    if (isEmpty) {
++      if (object.class === DEFAULT_TITLE) {
++        return null;
++      }
++
++      return span({ className: "objectTitle" }, `${title} `);
++    }
++
++    let trailingSpace;
++    if (object.class === DEFAULT_TITLE) {
++      title = null;
++      trailingSpace = " ";
++    }
++
++    return span({ className: "objectTitle" }, title, length, trailingSpace);
++  }
++
++  return span({ className: "objectTitle" }, title, length, " ");
++}
++
++function getPreviewItems(grip) {
++  if (!grip.preview) {
++    return null;
++  }
++
++  return grip.preview.items || grip.preview.childNodes || [];
++}
++
++function arrayIterator(props, grip, max) {
++  let { Rep } = __webpack_require__(4);
++
++  let items = [];
++  const gripLength = getLength(grip);
++
++  if (!gripLength) {
++    return items;
++  }
++
++  const previewItems = getPreviewItems(grip);
++  let provider = props.provider;
++
++  let emptySlots = 0;
++  let foldedEmptySlots = 0;
++  items = previewItems.reduce((res, itemGrip) => {
++    if (res.length >= max) {
++      return res;
++    }
++
++    let object;
++    try {
++      if (!provider && itemGrip === null) {
++        emptySlots++;
++        return res;
++      }
++
++      object = provider ? provider.getValue(itemGrip) : itemGrip;
++    } catch (exc) {
++      object = exc;
++    }
++
++    if (emptySlots > 0) {
++      res.push(getEmptySlotsElement(emptySlots));
++      foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
++      emptySlots = 0;
++    }
++
++    if (res.length < max) {
++      res.push(Rep({
++        ...props,
++        object,
++        mode: MODE.TINY,
++        // Do not propagate title to array items reps
++        title: undefined
++      }));
++    }
++
++    return res;
++  }, []);
++
++  // Handle trailing empty slots if there are some.
++  if (items.length < max && emptySlots > 0) {
++    items.push(getEmptySlotsElement(emptySlots));
++    foldedEmptySlots = foldedEmptySlots + emptySlots - 1;
++  }
++
++  const itemsShown = items.length + foldedEmptySlots;
++  if (gripLength > itemsShown) {
++    items.push(ellipsisElement);
++  }
++
++  return items;
++}
++
++function getEmptySlotsElement(number) {
++  // TODO: Use l10N - See https://github.com/devtools-html/reps/issues/141
++  return `<${number} empty slot${number > 1 ? "s" : ""}>`;
++}
++
++function supportsObject(grip, noGrip = false) {
++  if (noGrip === true || !isGrip(grip)) {
++    return false;
++  }
++
++  return grip.preview && (grip.preview.kind == "ArrayLike" || getGripType(grip, noGrip) === "DocumentFragment");
++}
++
++const maxLengthMap = new Map();
++maxLengthMap.set(MODE.SHORT, 3);
++maxLengthMap.set(MODE.LONG, 10);
++
++// Exports from this module
++module.exports = {
++  rep: wrapRender(GripArray),
++  supportsObject,
++  maxLengthMap,
++  getLength
++};
++
++/***/ }),
++/* 14 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++const PropTypes = __webpack_require__(2);
++
++const { wrapRender } = __webpack_require__(0);
++const { MODE } = __webpack_require__(3);
++const { ModePropType } = __webpack_require__(5);
++
++const dom = __webpack_require__(1);
++const { span } = dom;
++
++GripLengthBubble.propTypes = {
++  object: PropTypes.object.isRequired,
++  maxLengthMap: PropTypes.instanceOf(Map).isRequired,
++  getLength: PropTypes.func.isRequired,
++  mode: ModePropType,
++  visibilityThreshold: PropTypes.number
++};
++
++function GripLengthBubble(props) {
++  const {
++    object,
++    mode = MODE.SHORT,
++    visibilityThreshold = 2,
++    maxLengthMap,
++    getLength,
++    showZeroLength = false
++  } = props;
++
++  const length = getLength(object);
++  const isEmpty = length === 0;
++  const isObvious = [MODE.SHORT, MODE.LONG].includes(mode) && length > 0 && length <= maxLengthMap.get(mode) && length <= visibilityThreshold;
++  if (isEmpty && !showZeroLength || isObvious) {
++    return "";
++  }
++
++  return span({
++    className: "objectLengthBubble"
++  }, `(${length})`);
++}
++
++module.exports = {
++  lengthBubble: wrapRender(GripLengthBubble)
++};
++
++/***/ }),
++/* 15 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// Dependencies
++
++const { lengthBubble } = __webpack_require__(14);
++const PropTypes = __webpack_require__(2);
++const {
++  interleave,
++  isGrip,
++  wrapRender,
++  ellipsisElement
++} = __webpack_require__(0);
++const PropRep = __webpack_require__(6);
++const { MODE } = __webpack_require__(3);
++const { ModePropType } = __webpack_require__(5);
++
++const { span } = __webpack_require__(1);
++
++/**
++ * Renders an map. A map is represented by a list of its
++ * entries enclosed in curly brackets.
++ */
++GripMap.propTypes = {
++  object: PropTypes.object,
++  // @TODO Change this to Object.values once it's supported in Node's version of V8
++  mode: ModePropType,
++  isInterestingEntry: PropTypes.func,
++  onDOMNodeMouseOver: PropTypes.func,
++  onDOMNodeMouseOut: PropTypes.func,
++  onInspectIconClick: PropTypes.func,
++  title: PropTypes.string
++};
++
++function GripMap(props) {
++  let {
++    mode,
++    object
++  } = props;
++
++  const config = {
++    "data-link-actor-id": object.actor,
++    className: "objectBox objectBox-object"
++  };
++
++  const title = getTitle(props, object);
++  const isEmpty = getLength(object) === 0;
++
++  if (isEmpty || mode === MODE.TINY) {
++    return span(config, title);
++  }
++
++  const propsArray = safeEntriesIterator(props, object, maxLengthMap.get(mode));
++
++  return span(config, title, span({
++    className: "objectLeftBrace"
++  }, " { "), ...interleave(propsArray, ", "), span({
++    className: "objectRightBrace"
++  }, " }"));
++}
++
++function getTitle(props, object) {
++  const title = props.title || (object && object.class ? object.class : "Map");
++  return span({
++    className: "objectTitle" }, title, lengthBubble({
++    object,
++    mode: props.mode,
++    maxLengthMap,
++    getLength,
++    showZeroLength: true
++  }));
++}
++
++function safeEntriesIterator(props, object, max) {
++  max = typeof max === "undefined" ? 3 : max;
++  try {
++    return entriesIterator(props, object, max);
++  } catch (err) {
++    console.error(err);
++  }
++  return [];
++}
++
++function entriesIterator(props, object, max) {
++  // Entry filter. Show only interesting entries to the user.
++  let isInterestingEntry = props.isInterestingEntry || ((type, value) => {
++    return type == "boolean" || type == "number" || type == "string" && value.length != 0;
++  });
++
++  let mapEntries = object.preview && object.preview.entries ? object.preview.entries : [];
++
++  let indexes = getEntriesIndexes(mapEntries, max, isInterestingEntry);
++  if (indexes.length < max && indexes.length < mapEntries.length) {
++    // There are not enough entries yet, so we add uninteresting entries.
++    indexes = indexes.concat(getEntriesIndexes(mapEntries, max - indexes.length, (t, value, name) => {
++      return !isInterestingEntry(t, value, name);
++    }));
++  }
++
++  let entries = getEntries(props, mapEntries, indexes);
++  if (entries.length < getLength(object)) {
++    // There are some undisplayed entries. Then display "…".
++    entries.push(ellipsisElement);
++  }
++
++  return entries;
++}
++
++/**
++ * Get entries ordered by index.
++ *
++ * @param {Object} props Component props.
++ * @param {Array} entries Entries array.
++ * @param {Array} indexes Indexes of entries.
++ * @return {Array} Array of PropRep.
++ */
++function getEntries(props, entries, indexes) {
++  let {
++    onDOMNodeMouseOver,
++    onDOMNodeMouseOut,
++    onInspectIconClick
++  } = props;
++
++  // Make indexes ordered by ascending.
++  indexes.sort(function (a, b) {
++    return a - b;
++  });
++
++  return indexes.map((index, i) => {
++    let [key, entryValue] = entries[index];
++    let value = entryValue.value !== undefined ? entryValue.value : entryValue;
++
++    return PropRep({
++      name: key,
++      equal: " \u2192 ",
++      object: value,
++      mode: MODE.TINY,
++      onDOMNodeMouseOver,
++      onDOMNodeMouseOut,
++      onInspectIconClick
++    });
++  });
++}
++
++/**
++ * Get the indexes of entries in the map.
++ *
++ * @param {Array} entries Entries array.
++ * @param {Number} max The maximum length of indexes array.
++ * @param {Function} filter Filter the entry you want.
++ * @return {Array} Indexes of filtered entries in the map.
++ */
++function getEntriesIndexes(entries, max, filter) {
++  return entries.reduce((indexes, [key, entry], i) => {
++    if (indexes.length < max) {
++      let value = entry && entry.value !== undefined ? entry.value : entry;
++      // Type is specified in grip's "class" field and for primitive
++      // values use typeof.
++      let type = (value && value.class ? value.class : typeof value).toLowerCase();
++
++      if (filter(type, value, key)) {
++        indexes.push(i);
++      }
++    }
++
++    return indexes;
++  }, []);
++}
++
++function getLength(grip) {
++  return grip.preview.size || 0;
++}
++
++function supportsObject(grip, noGrip = false) {
++  if (noGrip === true || !isGrip(grip)) {
++    return false;
++  }
++  return grip.preview && grip.preview.kind == "MapLike";
++}
++
++const maxLengthMap = new Map();
++maxLengthMap.set(MODE.SHORT, 3);
++maxLengthMap.set(MODE.LONG, 10);
++
++// Exports from this module
++module.exports = {
++  rep: wrapRender(GripMap),
++  supportsObject,
++  maxLengthMap,
++  getLength
++};
++
++/***/ }),
++/* 16 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// Dependencies
++const PropTypes = __webpack_require__(2);
++// Shortcuts
++const dom = __webpack_require__(1);
++const { span } = dom;
++const {
++  wrapRender
++} = __webpack_require__(0);
++const PropRep = __webpack_require__(6);
++const { MODE } = __webpack_require__(3);
++/**
++ * Renders an map entry. A map entry is represented by its key, a column and its value.
++ */
++GripMapEntry.propTypes = {
++  object: PropTypes.object,
++  // @TODO Change this to Object.values once it's supported in Node's version of V8
++  mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
++  onDOMNodeMouseOver: PropTypes.func,
++  onDOMNodeMouseOut: PropTypes.func,
++  onInspectIconClick: PropTypes.func
++};
++
++function GripMapEntry(props) {
++  const {
++    object
++  } = props;
++
++  const {
++    key,
++    value
++  } = object.preview;
++
++  return span({
++    className: "objectBox objectBox-map-entry"
++  }, PropRep({
++    ...props,
++    name: key,
++    object: value,
++    equal: " \u2192 ",
++    title: null,
++    suppressQuotes: false
++  }));
++}
++
++function supportsObject(grip, noGrip = false) {
++  if (noGrip === true) {
++    return false;
++  }
++  return grip && grip.type === "mapEntry" && grip.preview;
++}
++
++function createGripMapEntry(key, value) {
++  return {
++    type: "mapEntry",
++    preview: {
++      key,
++      value
++    }
++  };
++}
++
++// Exports from this module
++module.exports = {
++  rep: wrapRender(GripMapEntry),
++  createGripMapEntry,
++  supportsObject
++};
++
++/***/ }),
++/* 17 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_17__;
++
++/***/ }),
++/* 18 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_18__;
++
++/***/ }),
++/* 19 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const client = __webpack_require__(20);
++const loadProperties = __webpack_require__(21);
++const node = __webpack_require__(11);
++const selection = __webpack_require__(55);
++
++module.exports = {
++  client,
++  loadProperties,
++  node,
++  selection
++};
++
++/***/ }),
+ /* 20 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
++async function enumIndexedProperties(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumProperties({ ignoreNonIndexedProperties: true });
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumIndexedProperties", e);
++    return {};
++  }
++} /* 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/. */
++
++async function enumNonIndexedProperties(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumProperties({ ignoreIndexedProperties: true });
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumNonIndexedProperties", e);
++    return {};
++  }
++}
++
++async function enumEntries(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumEntries();
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumEntries", e);
++    return {};
++  }
++}
++
++async function enumSymbols(objectClient, start, end) {
++  try {
++    const { iterator } = await objectClient.enumSymbols();
++    const response = await iteratorSlice(iterator, start, end);
++    return response;
++  } catch (e) {
++    console.error("Error in enumSymbols", e);
++    return {};
++  }
++}
++
++async function getPrototype(objectClient) {
++  if (typeof objectClient.getPrototype !== "function") {
++    console.error("objectClient.getPrototype is not a function");
++    return Promise.resolve({});
++  }
++  return objectClient.getPrototype();
++}
++
++function iteratorSlice(iterator, start, end) {
++  start = start || 0;
++  const count = end ? end - start + 1 : iterator.count;
++  return iterator.slice(start, count);
++}
++
++module.exports = {
++  enumEntries,
++  enumIndexedProperties,
++  enumNonIndexedProperties,
++  enumSymbols,
++  getPrototype
++};
++
++/***/ }),
++/* 21 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const {
++  getClosestGripNode,
++  getClosestNonBucketNode,
++  getValue,
++  nodeHasAccessors,
++  nodeHasAllEntriesInPreview,
++  nodeHasProperties,
++  nodeIsBucket,
++  nodeIsDefaultProperties,
++  nodeIsEntries,
++  nodeIsMapEntry,
++  nodeIsPrimitive,
++  nodeIsProxy,
++  nodeNeedsNumericalBuckets
++} = __webpack_require__(11);
++
++function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item))
++  // The data is loaded when expanding the window node.
++  && !nodeIsDefaultProperties(item);
++}
++
++function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item)
++  // The data is loaded when expanding the window node.
++  && !nodeIsDefaultProperties(item);
++}
++
++function shouldLoadItemEntries(item, loadedProperties = new Map()) {
++  const gripItem = getClosestGripNode(item);
++  const value = getValue(gripItem);
++
++  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
++}
++
++function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
++  const value = getValue(item);
++
++  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item);
++}
++
++function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
++  const value = getValue(item);
++
++  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsProxy(item);
++}
++
++module.exports = {
++  shouldLoadItemEntries,
++  shouldLoadItemIndexedProperties,
++  shouldLoadItemNonIndexedProperties,
++  shouldLoadItemPrototype,
++  shouldLoadItemSymbols
++};
++
++/***/ }),
++/* 22 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
+ /* 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/. */
+ 
+ const { MODE } = __webpack_require__(3);
+ const { REPS, getRep } = __webpack_require__(4);
+-const ObjectInspector = __webpack_require__(48);
+-const ObjectInspectorUtils = __webpack_require__(17);
++const ObjectInspector = __webpack_require__(47);
++const ObjectInspectorUtils = __webpack_require__(19);
+ 
+ const {
+   parseURLEncodedText,
+   parseURLParams,
+   maybeEscapePropertyName,
+   getGripPreviewItems
+ } = __webpack_require__(0);
+ 
+@@ -3051,23 +3156,23 @@ module.exports = {
+   parseURLEncodedText,
+   parseURLParams,
+   getGripPreviewItems,
+   ObjectInspector,
+   ObjectInspectorUtils
+ };
+ 
+ /***/ }),
+-/* 21 */
++/* 23 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+-/* 22 */
++/* 24 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3099,17 +3204,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Undefined),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 23 */
++/* 25 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3141,17 +3246,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Null),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 24 */
++/* 26 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3193,17 +3298,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(Number),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 25 */
++/* 27 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3284,23 +3389,24 @@ function propIterator(props, object, max
+   }
+ 
+   const elements = [];
+   const unimportantProperties = [];
+   let propertiesNumber = 0;
+   const propertiesNames = Object.keys(object);
+ 
+   const pushPropRep = (name, value) => {
+-    elements.push(PropRep(Object.assign({}, props, {
++    elements.push(PropRep({
++      ...props,
+       key: name,
+       mode: MODE.TINY,
+       name,
+       object: value,
+       equal: ": "
+-    })));
++    }));
+     propertiesNumber++;
+ 
+     if (propertiesNumber < propertiesNames.length) {
+       elements.push(", ");
+     }
+   };
+ 
+   try {
+@@ -3364,17 +3470,17 @@ function supportsObject(object) {
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 26 */
++/* 28 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3416,17 +3522,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(SymbolRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 27 */
++/* 29 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3464,17 +3570,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(InfinityRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 28 */
++/* 30 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3501,17 +3607,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(NaNRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 29 */
++/* 31 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3543,19 +3649,17 @@ function Accessor(props) {
+   if (hasGetter(object)) {
+     accessors.push("Getter");
+   }
+   if (hasSetter(object)) {
+     accessors.push("Setter");
+   }
+   const title = accessors.join(" & ");
+ 
+-  return span({ className: "objectBox objectBox-accessor" }, span({
+-    className: "objectTitle"
+-  }, title));
++  return span({ className: "objectBox objectBox-accessor objectTitle" }, title);
+ }
+ 
+ function hasGetter(object) {
+   return object && object.get && object.get.type !== "undefined";
+ }
+ 
+ function hasSetter(object) {
+   return object && object.set && object.set.type !== "undefined";
+@@ -3571,17 +3675,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Accessor),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 30 */
++/* 32 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3632,17 +3736,17 @@ function supportsObject(grip, noGrip = f
+ }
+ 
+ module.exports = {
+   rep: wrapRender(Attribute),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 31 */
++/* 33 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3699,17 +3803,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(DateTime),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 32 */
++/* 34 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3736,17 +3840,17 @@ Document.propTypes = {
+ };
+ 
+ function Document(props) {
+   let grip = props.object;
+   const location = getLocation(grip);
+   return span({
+     "data-link-actor-id": grip.actor,
+     className: "objectBox objectBox-document"
+-  }, getTitle(grip), location ? " " : null, location ? span({ className: "location" }, location) : null);
++  }, getTitle(grip), location ? span({ className: "location" }, ` ${location}`) : null);
+ }
+ 
+ function getLocation(grip) {
+   let location = grip.preview.location;
+   return location ? getURLDisplayString(location) : null;
+ }
+ 
+ function getTitle(grip) {
+@@ -3767,17 +3871,72 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Document),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 33 */
++/* 35 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++// ReactJS
++const PropTypes = __webpack_require__(2);
++
++// Reps
++const {
++  getGripType,
++  isGrip,
++  wrapRender
++} = __webpack_require__(0);
++const dom = __webpack_require__(1);
++const { span } = dom;
++
++/**
++ * Renders DOM documentType object.
++ */
++DocumentType.propTypes = {
++  object: PropTypes.object.isRequired
++};
++
++function DocumentType(props) {
++  const { object } = props;
++  let name = object && object.preview && object.preview.nodeName ? ` ${object.preview.nodeName}` : "";
++  return span({
++    "data-link-actor-id": props.object.actor,
++    className: "objectBox objectBox-document"
++  }, `<!DOCTYPE${name}>`);
++}
++
++// Registration
++function supportsObject(object, noGrip = false) {
++  if (noGrip === true || !isGrip(object)) {
++    return false;
++  }
++
++  const type = getGripType(object, noGrip);
++  return object.preview && type === "DocumentType";
++}
++
++// Exports from this module
++module.exports = {
++  rep: wrapRender(DocumentType),
++  supportsObject
++};
++
++/***/ }),
++/* 36 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -3787,41 +3946,43 @@ const PropTypes = __webpack_require__(2)
+ 
+ // Reps
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ 
+ const { MODE } = __webpack_require__(3);
+-const { rep } = __webpack_require__(9);
++const { rep } = __webpack_require__(8);
+ 
+ /**
+  * Renders DOM event objects.
+  */
+ Event.propTypes = {
+   object: PropTypes.object.isRequired,
+   // @TODO Change this to Object.values once it's supported in Node's version of V8
+   mode: PropTypes.oneOf(Object.keys(MODE).map(key => MODE[key])),
+   onDOMNodeMouseOver: PropTypes.func,
+   onDOMNodeMouseOut: PropTypes.func,
+   onInspectIconClick: PropTypes.func
+ };
+ 
+ function Event(props) {
+-  // Use `Object.assign` to keep `props` without changes because:
+-  // 1. JSON.stringify/JSON.parse is slow.
+-  // 2. Immutable.js is planned for the future.
+-  let gripProps = Object.assign({}, props, {
+-    title: getTitle(props)
+-  });
+-  gripProps.object = Object.assign({}, props.object);
+-  gripProps.object.preview = Object.assign({}, props.object.preview);
+-
+-  gripProps.object.preview.ownProperties = {};
++  let gripProps = {
++    ...props,
++    title: getTitle(props),
++    object: {
++      ...props.object,
++      preview: {
++        ...props.object.preview,
++        ownProperties: {}
++      }
++    }
++  };
++
+   if (gripProps.object.preview.target) {
+     Object.assign(gripProps.object.preview.ownProperties, {
+       target: gripProps.object.preview.target
+     });
+   }
+   Object.assign(gripProps.object.preview.ownProperties, gripProps.object.preview.properties);
+ 
+   delete gripProps.object.preview.properties;
+@@ -3874,245 +4035,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(Event),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 34 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-/* 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/. */
+-
+-// ReactJS
+-const PropTypes = __webpack_require__(2);
+-
+-// Reps
+-const {
+-  getGripType,
+-  isGrip,
+-  cropString,
+-  wrapRender
+-} = __webpack_require__(0);
+-const { MODE } = __webpack_require__(3);
+-const Svg = __webpack_require__(10);
+-
+-const dom = __webpack_require__(1);
+-const { span } = dom;
+-
+-const IGNORED_SOURCE_URLS = ["debugger eval code"];
+-
+-/**
+- * This component represents a template for Function objects.
+- */
+-FunctionRep.propTypes = {
+-  object: PropTypes.object.isRequired,
+-  parameterNames: PropTypes.array,
+-  onViewSourceInDebugger: PropTypes.func
+-};
+-
+-function FunctionRep(props) {
+-  let {
+-    object: grip,
+-    onViewSourceInDebugger
+-  } = props;
+-
+-  let jumpToDefinitionButton;
+-  if (onViewSourceInDebugger && grip.location && grip.location.url && !IGNORED_SOURCE_URLS.includes(grip.location.url)) {
+-    jumpToDefinitionButton = Svg("jump-definition", {
+-      element: "a",
+-      draggable: false,
+-      title: "Jump to definition",
+-      onClick: e => {
+-        // Stop the event propagation so we don't trigger ObjectInspector expand/collapse.
+-        e.stopPropagation();
+-        onViewSourceInDebugger(grip.location);
+-      }
+-    });
+-  }
+-
+-  return span({
+-    "data-link-actor-id": grip.actor,
+-    className: "objectBox objectBox-function",
+-    // Set dir="ltr" to prevent function parentheses from
+-    // appearing in the wrong direction
+-    dir: "ltr"
+-  }, getTitle(grip, props), getFunctionName(grip, props), "(", ...renderParams(props), ")", jumpToDefinitionButton);
+-}
+-
+-function getTitle(grip, props) {
+-  const {
+-    mode
+-  } = props;
+-
+-  if (mode === MODE.TINY && !grip.isGenerator && !grip.isAsync) {
+-    return null;
+-  }
+-
+-  let title = mode === MODE.TINY ? "" : "function ";
+-
+-  if (grip.isGenerator) {
+-    title = mode === MODE.TINY ? "* " : "function* ";
+-  }
+-
+-  if (grip.isAsync) {
+-    title = "async" + " " + title;
+-  }
+-
+-  return span({
+-    className: "objectTitle"
+-  }, title);
+-}
+-
+-// Decodes an anonymous naming scheme that
+-// spider monkey implements based on "Naming Anonymous JavaScript Functions"
+-// http://johnjbarton.github.io/nonymous/index.html
+-const objectProperty = /([\w\d]+)$/;
+-const arrayProperty = /\[(.*?)\]$/;
+-const functionProperty = /([\w\d]+)[\/\.<]*?$/;
+-const annonymousProperty = /([\w\d]+)\(\^\)$/;
+-
+-function getFunctionName(grip, props = {}) {
+-  let { functionName } = props;
+-  let name;
+-
+-  if (functionName) {
+-    let end = functionName.length - 1;
+-    functionName = functionName.startsWith('"') && functionName.endsWith('"') ? functionName.substring(1, end) : functionName;
+-  }
+-
+-  if (grip.displayName != undefined && functionName != undefined && grip.displayName != functionName) {
+-    name = functionName + ":" + grip.displayName;
+-  } else {
+-    name = grip.userDisplayName || grip.displayName || grip.name || props.functionName || "";
+-
+-    const scenarios = [objectProperty, arrayProperty, functionProperty, annonymousProperty];
+-
+-    scenarios.some(reg => {
+-      const match = reg.exec(name);
+-      if (match) {
+-        name = match[1];
+-        return true;
+-      }
+-      return false;
+-    });
+-  }
+-
+-  return cropString(name, 100);
+-}
+-
+-function renderParams(props) {
+-  const {
+-    parameterNames = []
+-  } = props;
+-
+-  return parameterNames.filter(param => param).reduce((res, param, index, arr) => {
+-    res.push(span({ className: "param" }, param));
+-    if (index < arr.length - 1) {
+-      res.push(span({ className: "delimiter" }, ", "));
+-    }
+-    return res;
+-  }, []);
+-}
+-
+-// Registration
+-function supportsObject(grip, noGrip = false) {
+-  const type = getGripType(grip, noGrip);
+-  if (noGrip === true || !isGrip(grip)) {
+-    return type == "function";
+-  }
+-
+-  return type == "Function";
+-}
+-
+-// Exports from this module
+-
+-module.exports = {
+-  rep: wrapRender(FunctionRep),
+-  supportsObject,
+-  // exported for testing purpose.
+-  getFunctionName
+-};
+-
+-/***/ }),
+-/* 35 */
+-/***/ (function(module, exports, __webpack_require__) {
+-
+-"use strict";
+-
+-
+-Object.defineProperty(exports, "__esModule", {
+-    value: true
+-});
+-exports.convertReactSVGDOMProperty = convertReactSVGDOMProperty;
+-exports.startsWith = startsWith;
+-exports.serializeAttrs = serializeAttrs;
+-exports.getSVGFromSource = getSVGFromSource;
+-exports.extractSVGProps = extractSVGProps;
+-// Transform DOM prop/attr names applicable to `<svg>` element but react-limited
+-
+-function convertReactSVGDOMProperty(str) {
+-    return str.replace(/[-|:]([a-z])/g, function (g) {
+-        return g[1].toUpperCase();
+-    });
+-}
+-
+-function startsWith(str, substring) {
+-    return str.indexOf(substring) === 0;
+-}
+-
+-var DataPropPrefix = 'data-';
+-// Serialize `Attr` objects in `NamedNodeMap`
+-function serializeAttrs(map) {
+-    var ret = {};
+-    for (var prop, i = 0; i < map.length; i++) {
+-        var key = map[i].name;
+-        if (!startsWith(key, DataPropPrefix)) {
+-            prop = convertReactSVGDOMProperty(key);
+-        }
+-        ret[prop] = map[i].value;
+-    }
+-    return ret;
+-}
+-
+-function getSVGFromSource(src) {
+-    var svgContainer = document.createElement('div');
+-    svgContainer.innerHTML = src;
+-    var svg = svgContainer.firstElementChild;
+-    svg.remove(); // deref from parent element
+-    return svg;
+-}
+-
+-// get <svg /> element props
+-function extractSVGProps(src) {
+-    var map = getSVGFromSource(src).attributes;
+-    return map.length > 0 ? serializeAttrs(map) : null;
+-}
+-
+-/***/ }),
+-/* 36 */
+-/***/ (function(module, exports) {
+-
+-module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z\"></path></svg>"
+-
+-/***/ }),
+ /* 37 */
+-/***/ (function(module, exports) {
+-
+-module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><g stroke-width=\"1\" fill=\"none\" fill-rule=\"evenodd\" stroke-linecap=\"round\"><g id=\"arrow\" transform=\"translate(1.000000, 3.000000)\"><path d=\"M4.5,0.5 L6.5,2.5\"></path><path d=\"M4.5,2.5 L6.5,4.5\" transform=\"translate(5.500000, 3.500000) scale(1, -1) translate(-5.500000, -3.500000) \"></path><path d=\"M6.00090144,2.5 C4.67806937,2.5 3.67938478,2.5 3.00484766,2.5 C1.99304199,2.5 1.01049805,3.5168457 0.993840144,4.52403846 C0.988750751,4.54723808 0.988750751,5.87097168 0.993840144,8.49523926\" id=\"Path-2\" stroke-linejoin=\"round\"></path></g><g id=\"content-lines\" transform=\"translate(9.000000, 2.000000)\"><path d=\"M1.5,3.5 L5.5,3.5\"></path><path d=\"M0.5,1.5 L5.5,1.5\"></path><path d=\"M0.5,5.5 L5.5,5.5\"></path></g></g></svg>"
+-
+-/***/ }),
+-/* 38 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4180,23 +4113,24 @@ function getTitle(object) {
+ function getProps(props, promiseState) {
+   const keys = ["state"];
+   if (Object.keys(promiseState).includes("value")) {
+     keys.push("value");
+   }
+ 
+   return keys.reduce((res, key, i) => {
+     let object = promiseState[key];
+-    res = res.concat(PropRep(Object.assign({}, props, {
++    res = res.concat(PropRep({
++      ...props,
+       mode: MODE.TINY,
+       name: `<${key}>`,
+       object,
+       equal: ": ",
+       suppressQuotes: true
+-    })));
++    }));
+ 
+     // Interleave commas between elements
+     if (i !== keys.length - 1) {
+       res.push(", ");
+     }
+ 
+     return res;
+   }, []);
+@@ -4212,17 +4146,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(PromiseRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 39 */
++/* 38 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4271,17 +4205,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(RegExp),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 40 */
++/* 39 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4339,17 +4273,17 @@ function supportsObject(object, noGrip =
+ // Exports from this module
+ 
+ module.exports = {
+   rep: wrapRender(StyleSheet),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 41 */
++/* 40 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4358,17 +4292,17 @@ module.exports = {
+ const PropTypes = __webpack_require__(2);
+ const {
+   isGrip,
+   cropString,
+   cropMultipleLines,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(12);
++const nodeConstants = __webpack_require__(9);
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM comment node.
+  */
+ CommentNode.propTypes = {
+   object: PropTypes.object.isRequired,
+@@ -4405,17 +4339,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(CommentNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 42 */
++/* 41 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4425,18 +4359,17 @@ const PropTypes = __webpack_require__(2)
+ 
+ // Utils
+ const {
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
+ const { rep: StringRep } = __webpack_require__(7);
+ const { MODE } = __webpack_require__(3);
+-const nodeConstants = __webpack_require__(12);
+-const Svg = __webpack_require__(10);
++const nodeConstants = __webpack_require__(9);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM element node.
+  */
+ ElementNode.propTypes = {
+@@ -4474,19 +4407,18 @@ function ElementNode(props) {
+ 
+     if (onDOMNodeMouseOut) {
+       Object.assign(baseConfig, {
+         onMouseOut: onDOMNodeMouseOut
+       });
+     }
+ 
+     if (onInspectIconClick) {
+-      inspectIcon = Svg("open-inspector", {
+-        element: "a",
+-        draggable: false,
++      inspectIcon = dom.button({
++        className: "open-inspector",
+         // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+         title: "Click to select the node in the inspector",
+         onClick: e => onInspectIconClick(object, e)
+       });
+     }
+   }
+ 
+   return span(baseConfig, ...elements, inspectIcon);
+@@ -4537,17 +4469,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ElementNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 43 */
++/* 42 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4557,17 +4489,16 @@ const PropTypes = __webpack_require__(2)
+ 
+ // Reps
+ const {
+   isGrip,
+   cropString,
+   wrapRender
+ } = __webpack_require__(0);
+ const { MODE } = __webpack_require__(3);
+-const Svg = __webpack_require__(10);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders DOM #text node.
+  */
+ TextNode.propTypes = {
+@@ -4604,18 +4535,18 @@ function TextNode(props) {
+ 
+     if (onDOMNodeMouseOut) {
+       Object.assign(baseConfig, {
+         onMouseOut: onDOMNodeMouseOut
+       });
+     }
+ 
+     if (onInspectIconClick) {
+-      inspectIcon = Svg("open-inspector", {
+-        element: "a",
++      inspectIcon = dom.button({
++        className: "open-inspector",
+         draggable: false,
+         // TODO: Localize this with "openNodeInInspector" when Bug 1317038 lands
+         title: "Click to select the node in the inspector",
+         onClick: e => onInspectIconClick(grip, e)
+       });
+     }
+   }
+ 
+@@ -4646,17 +4577,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(TextNode),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 44 */
++/* 43 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4664,16 +4595,17 @@ module.exports = {
+ // ReactJS
+ const PropTypes = __webpack_require__(2);
+ // Utils
+ const {
+   getGripType,
+   isGrip,
+   wrapRender
+ } = __webpack_require__(0);
++const { cleanFunctionName } = __webpack_require__(12);
+ const { MODE } = __webpack_require__(3);
+ 
+ const dom = __webpack_require__(1);
+ const { span } = dom;
+ 
+ /**
+  * Renders Error objects.
+  */
+@@ -4698,49 +4630,109 @@ function ErrorRep(props) {
+         break;
+       default:
+         throw new Error("Unknown preview kind for the Error rep.");
+     }
+   } else {
+     name = "Error";
+   }
+ 
+-  let content = props.mode === MODE.TINY ? name : `${name}: ${preview.message}`;
++  const content = [];
++
++  if (props.mode === MODE.TINY) {
++    content.push(name);
++  } else {
++    content.push(`${name}: "${preview.message}"`);
++  }
+ 
+   if (preview.stack && props.mode !== MODE.TINY) {
+-    /*
+-      * Since Reps are used in the JSON Viewer, we can't localize
+-      * the "Stack trace" label (defined in debugger.properties as
+-      * "variablesViewErrorStacktrace" property), until Bug 1317038 lands.
+-      */
+-    content = `${content}\nStack trace:\n${preview.stack}`;
++    content.push("\n", getStacktraceElements(preview));
+   }
+ 
+   return span({
+     "data-link-actor-id": object.actor,
+     className: "objectBox-stackTrace"
+   }, content);
+ }
+ 
++/**
++ * Returns a React element reprensenting the Error stacktrace, i.e. transform error.stack
++ * from:
++ *
++ * semicolon@debugger eval code:1:109
++ * jkl@debugger eval code:1:63
++ * asdf@debugger eval code:1:28
++ * @debugger eval code:1:227
++ *
++ * Into a column layout:
++ *
++ * semicolon  (<anonymous>:8:10)
++ * jkl        (<anonymous>:5:10)
++ * asdf       (<anonymous>:2:10)
++ *            (<anonymous>:11:1)
++ */
++function getStacktraceElements(preview) {
++  const stack = [];
++  preview.stack.split("\n").forEach((line, index) => {
++    if (!line) {
++      // Skip any blank lines
++      return;
++    }
++
++    let functionName;
++    let location;
++
++    // Given the input: "functionName@scriptLocation:2:100"
++    // Result:
++    // ["functionName@scriptLocation:2:100", "functionName", "scriptLocation:2:100"]
++    const result = line.match(/^(.*)@(.*)$/);
++    if (result && result.length === 3) {
++      functionName = result[1];
++
++      // If the resource was loaded by base-loader.js, the location looks like:
++      // resource://devtools/shared/base-loader.js -> resource://path/to/file.js .
++      // What's needed is only the last part after " -> ".
++      location = result[2].split(" -> ").pop();
++    }
++
++    if (!functionName) {
++      functionName = "<anonymous>";
++    }
++
++    stack.push(span({
++      key: "fn" + index,
++      className: "objectBox-stackTrace-fn"
++    }, cleanFunctionName(functionName)), span({
++      key: "location" + index,
++      className: "objectBox-stackTrace-location"
++    }, ` (${location})`));
++  });
++
++  return span({
++    key: "stack",
++    className: "objectBox-stackTrace-grid"
++  }, stack);
++}
++
+ // Registration
+ function supportsObject(object, noGrip = false) {
+   if (noGrip === true || !isGrip(object)) {
+     return false;
+   }
+   return object.preview && getGripType(object, noGrip) === "Error" || object.class === "DOMException";
+ }
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ErrorRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 45 */
++/* 44 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4780,21 +4772,24 @@ function WindowRep(props) {
+     "data-link-actor-id": object.actor,
+     className: "objectBox objectBox-Window"
+   };
+ 
+   if (mode === MODE.TINY) {
+     return span(config, getTitle(object));
+   }
+ 
+-  return span(config, getTitle(object), " ", span({ className: "location" }, getLocation(object)));
+-}
+-
+-function getTitle(object) {
++  return span(config, getTitle(object, true), span({ className: "location" }, getLocation(object)));
++}
++
++function getTitle(object, trailingSpace) {
+   let title = object.displayClass || object.class || "Window";
++  if (trailingSpace === true) {
++    title = `${title} `;
++  }
+   return span({ className: "objectTitle" }, title);
+ }
+ 
+ function getLocation(object) {
+   return getURLDisplayString(object.preview.url);
+ }
+ 
+ // Registration
+@@ -4808,17 +4803,17 @@ function supportsObject(object, noGrip =
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(WindowRep),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 46 */
++/* 45 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4844,17 +4839,17 @@ ObjectWithText.propTypes = {
+   object: PropTypes.object.isRequired
+ };
+ 
+ function ObjectWithText(props) {
+   let grip = props.object;
+   return span({
+     "data-link-actor-id": grip.actor,
+     className: "objectTitle objectBox objectBox-" + getType(grip)
+-  }, getType(grip), " ", getDescription(grip));
++  }, `${getType(grip)} `, getDescription(grip));
+ }
+ 
+ function getType(grip) {
+   return grip.class;
+ }
+ 
+ function getDescription(grip) {
+   return String({
+@@ -4873,17 +4868,17 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectWithText),
+   supportsObject
+ };
+ 
+ /***/ }),
+-/* 47 */
++/* 46 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+@@ -4939,82 +4934,117 @@ function supportsObject(grip, noGrip = f
+ 
+ // Exports from this module
+ module.exports = {
+   rep: wrapRender(ObjectWithURL),
+   supportsObject
+ };
+ 
+ /***/ }),
++/* 47 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const { createElement, createFactory, PureComponent } = __webpack_require__(10);
++const { Provider } = __webpack_require__(17);
++const ObjectInspector = createFactory(__webpack_require__(48));
++const createStore = __webpack_require__(57);
++
++class OI extends PureComponent {
++
++  constructor(props) {
++    super(props);
++    this.store = createStore(props);
++  }
++
++  getStore() {
++    return this.store;
++  }
++
++  render() {
++    return createElement(Provider, { store: this.store }, ObjectInspector(this.props));
++  }
++}
++
++module.exports = OI;
++
++/***/ }),
+ /* 48 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ var _devtoolsComponents = __webpack_require__(49);
+ 
+ var _devtoolsComponents2 = _interopRequireDefault(_devtoolsComponents);
+ 
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+ /* 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/. */
+ 
+-const { Component, createFactory } = __webpack_require__(8);
+-const PropTypes = __webpack_require__(2);
++const {
++  Component,
++  createFactory
++} = __webpack_require__(10);
+ const dom = __webpack_require__(1);
++const { connect } = __webpack_require__(17);
++const { bindActionCreators } = __webpack_require__(18);
+ 
+ const Tree = createFactory(_devtoolsComponents2.default.Tree);
+-__webpack_require__(53);
+-
+-const classnames = __webpack_require__(54);
++__webpack_require__(52);
++
++const classnames = __webpack_require__(53);
+ 
+ const {
+   REPS: {
+     Rep,
+     Grip
+   }
+ } = __webpack_require__(4);
+ const {
+   MODE
+ } = __webpack_require__(3);
+ 
+-const Utils = __webpack_require__(17);
++const Utils = __webpack_require__(19);
+ 
+ const {
+   getChildren,
+   getClosestGripNode,
+   getParent,
+   getValue,
+   nodeHasAccessors,
+   nodeHasProperties,
++  nodeIsBlock,
+   nodeIsDefaultProperties,
+   nodeIsFunction,
+   nodeIsGetter,
+   nodeIsMapEntry,
+   nodeIsMissingArguments,
+   nodeIsOptimizedOut,
+   nodeIsPrimitive,
+   nodeIsPrototype,
+   nodeIsSetter,
+   nodeIsUninitializedBinding,
+   nodeIsUnmappedBinding,
+   nodeIsUnscopedBinding,
+   nodeIsWindow
+ } = Utils.node;
+ 
+-const {
+-  loadItemProperties
+-} = Utils.loadProperties;
+-
+ // This implements a component that renders an interactive inspector
+ // for looking at JavaScript objects. It expects descriptions of
+-// objects from the protocol, and will dynamically fetch child
++// objects from the protocol, and will dynamically fetch children
+ // properties as objects are expanded.
+ //
+ // If you want to inspect a single object, pass the name and the
+ // protocol descriptor of it:
+ //
+ //  ObjectInspector({
+ //    name: "foo",
+ //    desc: { writable: true, ..., { value: { actor: "1", ... }}},
+@@ -5034,262 +5064,287 @@ const {
+ // fetched, and a primitive value that should be displayed with no
+ // children.
+ 
+ class ObjectInspector extends Component {
+   constructor(props) {
+     super();
+     this.cachedNodes = new Map();
+ 
+-    this.state = {
+-      actors: new Set(),
+-      expandedPaths: new Set(),
+-      focusedItem: null,
+-      loadedProperties: props.loadedProperties || new Map(),
+-      loading: new Map()
+-    };
+-
+     const self = this;
+ 
+-    self.getChildren = this.getChildren.bind(this);
++    self.getItemChildren = this.getItemChildren.bind(this);
+     self.renderTreeItem = this.renderTreeItem.bind(this);
+     self.setExpanded = this.setExpanded.bind(this);
+     self.focusItem = this.focusItem.bind(this);
+     self.getRoots = this.getRoots.bind(this);
+   }
+ 
+-  shouldComponentUpdate(nextProps, nextState) {
++  shouldComponentUpdate(nextProps) {
+     const {
+       expandedPaths,
+-      loadedProperties
+-    } = this.state;
+-
+-    if (this.props.roots !== nextProps.roots) {
++      loadedProperties,
++      roots
++    } = this.props;
++
++    if (roots !== nextProps.roots) {
+       // Since the roots changed, we assume the properties did as well. Thus we can clear
+       // the cachedNodes to avoid bugs and memory leaks.
+       this.cachedNodes.clear();
+       return true;
+     }
+ 
+-    return expandedPaths.size !== nextState.expandedPaths.size || loadedProperties.size !== nextState.loadedProperties.size || [...expandedPaths].some(key => !nextState.expandedPaths.has(key));
++    return expandedPaths.size !== nextProps.expandedPaths.size || loadedProperties.size !== nextProps.loadedProperties.size || [...expandedPaths].some(key => !nextProps.expandedPaths.has(key));
+   }
+ 
+   componentWillUnmount() {
+     const { releaseActor } = this.props;
+     if (typeof releaseActor !== "function") {
+       return;
+     }
+ 
+-    const { actors } = this.state;
++    const { actors } = this.props;
+     for (let actor of actors) {
+       releaseActor(actor);
+     }
+   }
+ 
+-  getChildren(item) {
++  getItemChildren(item) {
+     const {
+       loadedProperties
+-    } = this.state;
++    } = this.props;
+     const { cachedNodes } = this;
+ 
+     return getChildren({
+       loadedProperties,
+       cachedNodes,
+       item
+     });
+   }
+ 
+   getRoots() {
+     return this.props.roots;
+   }
+ 
+-  getKey(item) {
+-    return item.path;
+-  }
+-
+-  /**
+-   * This function is responsible for expanding/collapsing a given node,
+-   * which also means that it will check if we need to fetch properties,
+-   * entries, prototype and symbols for the said node. If we do, it will call
+-   * the appropriate ObjectClient functions, and change the state of the component
+-   * with the results it gets from those functions.
+-   */
+-  async setExpanded(item, expand) {
++  getNodeKey(item) {
++    return item.path || JSON.stringify(item);
++  }
++
++  setExpanded(item, expand) {
+     if (nodeIsPrimitive(item)) {
+       return;
+     }
+ 
+     const {
+-      loadedProperties
+-    } = this.state;
+-
+-    const key = this.getKey(item);
+-
+-    this.setState((prevState, props) => {
+-      const newPaths = new Set(prevState.expandedPaths);
+-      if (expand === true) {
+-        newPaths.add(key);
+-      } else {
+-        newPaths.delete(key);
+-      }
+-      return {
+-        expandedPaths: newPaths
+-      };
+-    });
++      createObjectClient,
++      loadedProperties,
++      nodeExpand,
++      nodeCollapse,
++      roots
++    } = this.props;
+ 
+     if (expand === true) {
+       const gripItem = getClosestGripNode(item);
+       const value = getValue(gripItem);
+-      const path = item.path;
+-
+-      const onItemPropertiesLoaded = loadItemProperties(item, this.props.createObjectClient, loadedProperties);
+-      if (onItemPropertiesLoaded !== null) {
+-        this.setState((prevState, props) => {
+-          const nextLoading = new Map(prevState.loading);
+-          nextLoading.set(path, onItemPropertiesLoaded);
+-          return {
+-            loading: nextLoading
+-          };
+-        });
+-
+-        const properties = await onItemPropertiesLoaded;
+-
+-        this.setState((prevState, props) => {
+-          const nextLoading = new Map(prevState.loading);
+-          nextLoading.delete(path);
+-
+-          const isRoot = this.props.roots.some(root => {
+-            const rootValue = getValue(root);
+-            return rootValue && rootValue.actor === value.actor;
+-          });
+-
+-          return {
+-            actors: isRoot ? prevState.actors : new Set(prevState.actors).add(value.actor),
+-            loadedProperties: new Map(prevState.loadedProperties).set(path, properties),
+-            loading: nextLoading
+-          };
+-        });
+-      }
++      const isRoot = value && roots.some(root => {
++        const rootValue = getValue(root);
++        return rootValue && rootValue.actor === value.actor;
++      });
++      const actor = isRoot || !value ? null : value.actor;
++      nodeExpand(item, actor, loadedProperties, createObjectClient);
++    } else {
++      nodeCollapse(item);
+     }
+   }
+ 
+   focusItem(item) {
+-    if (!this.props.disabledFocus && this.state.focusedItem !== item) {
+-      this.setState({
+-        focusedItem: item
+-      });
+-
+-      if (this.props.onFocus) {
+-        this.props.onFocus(item);
+-      }
++    const {
++      focusedItem,
++      onFocus
++    } = this.props;
++
++    if (focusedItem !== item && onFocus) {
++      onFocus(item);
+     }
+   }
+ 
+-  renderTreeItem(item, depth, focused, arrow, expanded) {
+-    let objectValue;
++  getTreeItemLabelAndValue(item, depth, expanded) {
+     let label = item.name;
+-    let itemValue = getValue(item);
+-
+     const isPrimitive = nodeIsPrimitive(item);
+ 
+-    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
++    if (nodeIsOptimizedOut(item)) {
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(optimized away)")
++      };
++    }
+ 
+     if (nodeIsUninitializedBinding(item)) {
+-      objectValue = dom.span({ className: "unavailable" }, "(uninitialized)");
+-    } else if (nodeIsUnmappedBinding(item)) {
+-      objectValue = dom.span({ className: "unavailable" }, "(unmapped)");
+-    } else if (nodeIsUnscopedBinding(item)) {
+-      objectValue = dom.span({ className: "unavailable" }, "(unscoped)");
+-    } else if (nodeIsOptimizedOut(item)) {
+-      objectValue = dom.span({ className: "unavailable" }, "(optimized away)");
+-    } else if (nodeIsMissingArguments(item) || unavailable) {
+-      objectValue = dom.span({ className: "unavailable" }, "(unavailable)");
+-    } else if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
+-      objectValue = undefined;
+-      label = this.renderGrip(item, Object.assign({}, this.props, {
+-        functionName: label
+-      }));
+-    } else if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
+-      let repsProp = Object.assign({}, this.props);
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(uninitialized)")
++      };
++    }
++
++    if (nodeIsUnmappedBinding(item)) {
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(unmapped)")
++      };
++    }
++
++    if (nodeIsUnscopedBinding(item)) {
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(unscoped)")
++      };
++    }
++
++    if (nodeIsOptimizedOut(item)) {
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(optimized away)")
++      };
++    }
++
++    const itemValue = getValue(item);
++    const unavailable = isPrimitive && itemValue && itemValue.hasOwnProperty && itemValue.hasOwnProperty("unavailable");
++
++    if (nodeIsMissingArguments(item) || unavailable) {
++      return {
++        label,
++        value: dom.span({ className: "unavailable" }, "(unavailable)")
++      };
++    }
++
++    if (nodeIsFunction(item) && !nodeIsGetter(item) && !nodeIsSetter(item) && (this.props.mode === MODE.TINY || !this.props.mode)) {
++      return {
++        label: this.renderGrip(item, {
++          ...this.props,
++          functionName: label
++        })
++      };
++    }
++
++    if (nodeHasProperties(item) || nodeHasAccessors(item) || nodeIsMapEntry(item) || isPrimitive) {
++      let repsProp = { ...this.props };
+       if (depth > 0) {
+         repsProp.mode = this.props.mode === MODE.LONG ? MODE.SHORT : MODE.TINY;
+       }
+       if (expanded) {
+         repsProp.mode = MODE.TINY;
+       }
+ 
+-      objectValue = this.renderGrip(item, repsProp);
++      return {
++        label,
++        value: this.renderGrip(item, repsProp)
++      };
++    }
++
++    return {
++      label
++    };
++  }
++
++  renderTreeItemLabel(label, item, depth, focused, expanded) {
++    if (label === null || typeof label === "undefined") {
++      return null;
+     }
+ 
+-    const hasLabel = label !== null && typeof label !== "undefined";
+-    const hasValue = typeof objectValue !== "undefined";
+-
++    const {
++      onLabelClick
++    } = this.props;
++
++    return dom.span({
++      className: "object-label",
++      onClick: onLabelClick ? event => {
++        event.stopPropagation();
++
++        // If the user selected text, bail out.
++        if (Utils.selection.documentHasSelection()) {
++          return;
++        }
++
++        onLabelClick(item, {
++          depth,
++          focused,
++          expanded,
++          setExpanded: this.setExpanded
++        });
++      } : undefined
++    }, label);
++  }
++
++  getTreeTopElementProps(item, depth, focused, expanded) {
+     const {
+       onDoubleClick,
+-      onLabelClick,
+       dimTopLevelWindow
+     } = this.props;
+ 
+-    return dom.div({
++    let parentElementProps = {
+       className: classnames("node object-node", {
+         focused,
+-        lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0)
++        lessen: !expanded && (nodeIsDefaultProperties(item) || nodeIsPrototype(item) || dimTopLevelWindow === true && nodeIsWindow(item) && depth === 0),
++        block: nodeIsBlock(item)
+       }),
+       onClick: e => {
+         e.stopPropagation();
+-        if (isPrimitive === false) {
+-          this.setExpanded(item, !expanded);
++
++        // If the user selected text, bail out.
++        if (Utils.selection.documentHasSelection()) {
++          return;
+         }
+-      },
+-      onDoubleClick: onDoubleClick ? e => {
++
++        this.setExpanded(item, !expanded);
++      }
++    };
++
++    if (onDoubleClick) {
++      parentElementProps.onDoubleClick = e => {
+         e.stopPropagation();
+         onDoubleClick(item, {
+           depth,
+           focused,
+           expanded
+         });
+-      } : null
+-    }, arrow, hasLabel ? dom.span({
+-      className: "object-label",
+-      onClick: onLabelClick ? event => {
+-        event.stopPropagation();
+-        onLabelClick(item, {
+-          depth,
+-          focused,
+-          expanded,
+-          setExpanded: this.setExpanded
+-        });
+-      } : null
+-    }, label) : null, hasLabel && hasValue ? dom.span({ className: "object-delimiter" }, ": ") : null, hasValue ? objectValue : null);
++      };
++    }
++
++    return parentElementProps;
++  }
++
++  renderTreeItem(item, depth, focused, arrow, expanded) {
++    const { label, value } = this.getTreeItemLabelAndValue(item, depth, expanded);
++    const labelElement = this.renderTreeItemLabel(label, item, depth, focused, expanded);
++    const delimiter = value && labelElement ? dom.span({ className: "object-delimiter" }, ": ") : null;
++
++    return dom.div(this.getTreeTopElementProps(item, depth, focused, expanded), arrow, labelElement, delimiter, value);
+   }
+ 
+   renderGrip(item, props) {
+     const object = getValue(item);
+-    return Rep(Object.assign({}, props, {
++    return Rep({
++      ...props,
+       object,
+       mode: props.mode || MODE.TINY,
+       defaultRep: Grip
+-    }));
++    });
+   }
+ 
+   render() {
+     const {
++      autoExpandAll = true,
+       autoExpandDepth = 1,
+-      autoExpandAll = true,
+       disabledFocus,
+-      inline,
+-      itemHeight = 20,
+-      disableWrap = false
++      disableWrap = false,
++      expandedPaths,
++      focusedItem,
++      inline
+     } = this.props;
+ 
+-    const {
+-      expandedPaths,
+-      focusedItem
+-    } = this.state;
+-
+     let roots = this.getRoots();
+     if (roots.length === 1) {
+       const root = roots[0];
+       const name = root && root.name;
+       if (nodeIsPrimitive(root) && (name === null || typeof name === "undefined")) {
+         return this.renderGrip(root, this.props);
+       }
+     }
+@@ -5298,54 +5353,49 @@ class ObjectInspector extends Component 
+       className: classnames({
+         inline,
+         nowrap: disableWrap,
+         "object-inspector": true
+       }),
+       autoExpandAll,
+       autoExpandDepth,
+       disabledFocus,
+-      itemHeight,
+-
+-      isExpanded: item => expandedPaths.has(this.getKey(item)),
++
++      isExpanded: item => expandedPaths && expandedPaths.has(this.getNodeKey(item)),
+       isExpandable: item => nodeIsPrimitive(item) === false,
+       focused: focusedItem,
+ 
+       getRoots: this.getRoots,
+       getParent,
+-      getChildren: this.getChildren,
+-      getKey: this.getKey,
++      getChildren: this.getItemChildren,
++      getKey: this.getNodeKey,
+ 
+       onExpand: item => this.setExpanded(item, true),
+       onCollapse: item => this.setExpanded(item, false),
+       onFocus: this.focusItem,
+ 
+       renderItem: this.renderTreeItem
+     });
+   }
+ }
+ 
+-ObjectInspector.displayName = "ObjectInspector";
+-
+-ObjectInspector.propTypes = {
+-  autoExpandAll: PropTypes.bool,
+-  autoExpandDepth: PropTypes.number,
+-  disabledFocus: PropTypes.bool,
+-  disableWrap: PropTypes.bool,
+-  inline: PropTypes.bool,
+-  roots: PropTypes.array,
+-  itemHeight: PropTypes.number,
+-  mode: PropTypes.oneOf(Object.values(MODE)),
+-  createObjectClient: PropTypes.func.isRequired,
+-  onFocus: PropTypes.func,
+-  onDoubleClick: PropTypes.func,
+-  onLabelClick: PropTypes.func
+-};
+-
+-module.exports = ObjectInspector;
++function mapStateToProps(state, props) {
++  return {
++    actors: state.actors,
++    expandedPaths: state.expandedPaths,
++    focusedItem: state.focusedItem,
++    loadedProperties: state.loadedProperties
++  };
++}
++
++function mapDispatchToProps(dispatch) {
++  return bindActionCreators(__webpack_require__(56), dispatch);
++}
++
++module.exports = connect(mapStateToProps, mapDispatchToProps)(ObjectInspector);
+ 
+ /***/ }),
+ /* 49 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+@@ -5367,43 +5417,35 @@ module.exports = {
+ 
+ "use strict";
+ 
+ 
+ Object.defineProperty(exports, "__esModule", {
+   value: true
+ });
+ 
+-var _react = __webpack_require__(8);
++var _react = __webpack_require__(10);
+ 
+ var _react2 = _interopRequireDefault(_react);
+ 
+ var _reactDomFactories = __webpack_require__(1);
+ 
+ var _reactDomFactories2 = _interopRequireDefault(_reactDomFactories);
+ 
+ var _propTypes = __webpack_require__(2);
+ 
+ var _propTypes2 = _interopRequireDefault(_propTypes);
+ 
+-var _svgInlineReact = __webpack_require__(11);
+-
+-var _svgInlineReact2 = _interopRequireDefault(_svgInlineReact);
+-
+-var _arrow = __webpack_require__(51);
+-
+-var _arrow2 = _interopRequireDefault(_arrow);
+-
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+ 
+-const { Component, createFactory, createElement } = _react2.default; /* 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/. */
+-
+-__webpack_require__(52);
++const { Component, createFactory } = _react2.default; /* 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/. */
++
++__webpack_require__(51);
+ 
+ const AUTO_EXPAND_DEPTH = 0; // depth
+ 
+ /**
+  * An arrow that displays whether its node is expanded (▼) or collapsed
+  * (▶). When its node has no children, it is hidden.
+  */
+ class ArrowExpander extends Component {
+@@ -5421,19 +5463,18 @@ class ArrowExpander extends Component {
+     const {
+       expanded
+     } = this.props;
+ 
+     const classNames = ["arrow"];
+     if (expanded) {
+       classNames.push("expanded");
+     }
+-    return createElement(_svgInlineReact2.default, {
+-      className: classNames.join(" "),
+-      src: _arrow2.default
++    return _reactDomFactories2.default.img({
++      className: classNames.join(" ")
+     });
+   }
+ }
+ 
+ const treeIndent = _reactDomFactories2.default.span({ className: "tree-indent" }, "\u200B");
+ 
+ class TreeNode extends Component {
+   static get propTypes() {
+@@ -5481,17 +5522,17 @@ class TreeNode extends Component {
+     const indents = Array.from({ length: depth }).fill(treeIndent);
+     let items = indents.concat(renderItem(item, depth, focused, arrow, expanded));
+ 
+     return _reactDomFactories2.default.div({
+       id,
+       className: "tree-node" + (focused ? " focused" : ""),
+       onClick: this.props.onClick,
+       role: "treeitem",
+-      "aria-level": depth,
++      "aria-level": depth + 1,
+       "aria-expanded": ariaExpanded,
+       "data-expandable": this.props.isExpandable
+     }, ...items);
+   }
+ }
+ 
+ const ArrowExpanderFactory = createFactory(ArrowExpander);
+ const TreeNodeFactory = createFactory(TreeNode);
+@@ -6154,32 +6195,26 @@ class Tree extends Component {
+ }
+ 
+ exports.default = Tree;
+ 
+ /***/ }),
+ /* 51 */
+ /***/ (function(module, exports) {
+ 
+-module.exports = "<!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --><svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z\"></path></svg>"
++// removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+ /* 52 */
+ /***/ (function(module, exports) {
+ 
+ // removed by extract-text-webpack-plugin
+ 
+ /***/ }),
+ /* 53 */
+-/***/ (function(module, exports) {
+-
+-// removed by extract-text-webpack-plugin
+-
+-/***/ }),
+-/* 54 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
+   Copyright (c) 2016 Jed Watson.
+   Licensed under the MIT License (MIT), see
+   http://jedwatson.github.io/classnames
+ */
+ /* global define */
+@@ -6213,164 +6248,387 @@ var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBP
+ 
+ 		return classes.join(' ');
+ 	}
+ 
+ 	if (typeof module !== 'undefined' && module.exports) {
+ 		module.exports = classNames;
+ 	} else if (true) {
+ 		// register as 'classnames', consistent with npm package name
+-		!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = function () {
++		!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_RESULT__ = (function () {
+ 			return classNames;
+-		}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
++		}).apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
+ 				__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+ 	} else {
+ 		window.classNames = classNames;
+ 	}
+ }());
+ 
+ 
+ /***/ }),
++/* 54 */
++/***/ (function(module, exports) {
++
++module.exports = __WEBPACK_EXTERNAL_MODULE_54__;
++
++/***/ }),
+ /* 55 */
+ /***/ (function(module, exports, __webpack_require__) {
+ 
+ "use strict";
+ 
+ 
+ /* 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/. */
+ 
++const { ELEMENT_NODE } = __webpack_require__(9);
++
++function documentHasSelection() {
++  const selection = getSelection();
++  if (!selection) {
++    return false;
++  }
++
++  const {
++    anchorNode,
++    focusNode
++  } = selection;
++
++  // When clicking the arrow, which is an inline svg element, the selection do have a type
++  // of "Range". We need to have an explicit case when the anchor and the focus node are
++  // the same and they have an arrow ancestor.
++  if (focusNode && focusNode === anchorNode && focusNode.nodeType == ELEMENT_NODE && focusNode.closest(".arrow")) {
++    return false;
++  }
++
++  return selection.type === "Range";
++}
++
++module.exports = {
++  documentHasSelection
++};
++
++/***/ }),
++/* 56 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++const {
++  getClosestGripNode,
++  getValue
++} = __webpack_require__(11); /* 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/. */
++
++const {
++  shouldLoadItemEntries,
++  shouldLoadItemIndexedProperties,
++  shouldLoadItemNonIndexedProperties,
++  shouldLoadItemPrototype,
++  shouldLoadItemSymbols
++} = __webpack_require__(21);
++
+ const {
+   enumEntries,
+   enumIndexedProperties,
+   enumNonIndexedProperties,
+   getPrototype,
+   enumSymbols
+-} = __webpack_require__(18);
+-
+-const {
+-  getClosestGripNode,
+-  getClosestNonBucketNode,
+-  getValue,
+-  nodeHasAccessors,
+-  nodeHasAllEntriesInPreview,
+-  nodeHasProperties,
+-  nodeIsBucket,
+-  nodeIsDefaultProperties,
+-  nodeIsEntries,
+-  nodeIsMapEntry,
+-  nodeIsPrimitive,
+-  nodeIsProxy,
+-  nodeNeedsNumericalBuckets
+-} = __webpack_require__(19);
+-
+-function loadItemProperties(item, createObjectClient, loadedProperties) {
+-  const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
+-
+-  let objectClient;
+-  const getObjectClient = () => {
+-    if (objectClient) {
+-      return objectClient;
+-    }
+-
+-    const gripItem = getClosestGripNode(item);
+-    const value = getValue(gripItem);
+-    return createObjectClient(value);
++} = __webpack_require__(20);
++
++/**
++ * This action is responsible for expanding a given node,
++ * which also means that it will call the action responsible to fetch properties.
++ */
++function nodeExpand(node, actor, loadedProperties, createObjectClient) {
++  return async ({ dispatch }) => {
++    dispatch({
++      type: "NODE_EXPAND",
++      data: { node }
++    });
++    dispatch(nodeLoadProperties(node, actor, loadedProperties, createObjectClient));
++  };
++}
++
++function nodeCollapse(node) {
++  return {
++    type: "NODE_COLLAPSE",
++    data: { node }
++  };
++}
++
++function nodeFocus(node) {
++  return {
++    type: "NODE_FOCUS",
++    data: { node }
+   };
+-
+-  let loadingPromises = [];
+-  if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
+-    loadingPromises.push(enumIndexedProperties(getObjectClient(), start, end));
+-  }
+-
+-  if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
+-    loadingPromises.push(enumNonIndexedProperties(getObjectClient(), start, end));
+-  }
+-
+-  if (shouldLoadItemEntries(item, loadedProperties)) {
+-    loadingPromises.push(enumEntries(getObjectClient(), start, end));
+-  }
+-
+-  if (shouldLoadItemPrototype(item, loadedProperties)) {
+-    loadingPromises.push(getPrototype(getObjectClient()));
+-  }
+-
+-  if (shouldLoadItemSymbols(item, loadedProperties)) {
+-    loadingPromises.push(enumSymbols(getObjectClient(), start, end));
+-  }
+-
+-  if (loadingPromises.length === 0) {
+-    return null;
+-  }
+-
+-  return Promise.all(loadingPromises).then(responses => responses.reduce((accumulator, res) => {
+-    // Let's loop through the responses to build a single response object.
+-    Object.entries(res).forEach(([k, v]) => {
+-      if (accumulator.hasOwnProperty(k)) {
+-        if (Array.isArray(accumulator[k])) {
+-          accumulator[k].push(...v);
+-        } else if (typeof accumulator[k] === "object") {
+-          accumulator[k] = Object.assign({}, accumulator[k], v);
+-        }
+-      } else {
+-        accumulator[k] = v;
++}
++/*
++ * This action checks if we need to fetch properties, entries, prototype and symbols
++ * for a given node. If we do, it will call the appropriate ObjectClient functions.
++ */
++function nodeLoadProperties(item, actor, loadedProperties, createObjectClient) {
++  return async ({ dispatch }) => {
++    try {
++      const gripItem = getClosestGripNode(item);
++      const value = getValue(gripItem);
++
++      const [start, end] = item.meta ? [item.meta.startIndex, item.meta.endIndex] : [];
++
++      let promises = [];
++      let objectClient;
++      const getObjectClient = () => objectClient || createObjectClient(value);
++
++      if (shouldLoadItemIndexedProperties(item, loadedProperties)) {
++        promises.push(enumIndexedProperties(getObjectClient(), start, end));
++      }
++
++      if (shouldLoadItemNonIndexedProperties(item, loadedProperties)) {
++        promises.push(enumNonIndexedProperties(getObjectClient(), start, end));
+       }
+-    });
+-    return accumulator;
+-  }, {}));
+-}
+-
+-function shouldLoadItemIndexedProperties(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeNeedsNumericalBuckets(item) && !nodeIsEntries(getClosestNonBucketNode(item))
+-  // The data is loaded when expanding the window node.
+-  && !nodeIsDefaultProperties(item);
+-}
+-
+-function shouldLoadItemNonIndexedProperties(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeHasProperties(gripItem) && !loadedProperties.has(item.path) && !nodeIsProxy(item) && !nodeIsEntries(getClosestNonBucketNode(item)) && !nodeIsBucket(item)
+-  // The data is loaded when expanding the window node.
+-  && !nodeIsDefaultProperties(item);
+-}
+-
+-function shouldLoadItemEntries(item, loadedProperties = new Map()) {
+-  const gripItem = getClosestGripNode(item);
+-  const value = getValue(gripItem);
+-
+-  return value && nodeIsEntries(getClosestNonBucketNode(item)) && !nodeHasAllEntriesInPreview(gripItem) && !loadedProperties.has(item.path) && !nodeNeedsNumericalBuckets(item);
+-}
+-
+-function shouldLoadItemPrototype(item, loadedProperties = new Map()) {
+-  const value = getValue(item);
+-
+-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item);
+-}
+-
+-function shouldLoadItemSymbols(item, loadedProperties = new Map()) {
+-  const value = getValue(item);
+-
+-  return value && !loadedProperties.has(item.path) && !nodeIsBucket(item) && !nodeIsMapEntry(item) && !nodeIsEntries(item) && !nodeIsDefaultProperties(item) && !nodeHasAccessors(item) && !nodeIsPrimitive(item) && !nodeIsProxy(item);
++
++      if (shouldLoadItemEntries(item, loadedProperties)) {
++        promises.push(enumEntries(getObjectClient(), start, end));
++      }
++
++      if (shouldLoadItemPrototype(item, loadedProperties)) {
++        promises.push(getPrototype(getObjectClient()));
++      }
++
++      if (shouldLoadItemSymbols(item, loadedProperties)) {
++        promises.push(enumSymbols(getObjectClient(), start, end));
++      }
++
++      if (promises.length > 0) {
++        const responses = await Promise.all(promises);
++        dispatch(nodePropertiesLoaded(item, actor, responses));
++      }
++    } catch (e) {
++      console.error(e);
++    }
++  };
++}
++
++function nodePropertiesLoaded(node, actor, responses) {
++  return {
++    type: "NODE_PROPERTIES_LOADED",
++    data: { node, actor, responses }
++  };
+ }
+ 
+ module.exports = {
+-  loadItemProperties,
+-  shouldLoadItemEntries,
+-  shouldLoadItemIndexedProperties,
+-  shouldLoadItemNonIndexedProperties,
+-  shouldLoadItemPrototype,
+-  shouldLoadItemSymbols
++  nodeExpand,
++  nodeCollapse,
++  nodeFocus,
++  nodeLoadProperties,
++  nodePropertiesLoaded
++};
++
++/***/ }),
++/* 57 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const { applyMiddleware, createStore } = __webpack_require__(18);
++const { thunk } = __webpack_require__(58);
++const { waitUntilService } = __webpack_require__(59);
++const reducer = __webpack_require__(60);
++
++function createInitialState(overrides) {
++  return {
++    actors: new Set(),
++    expandedPaths: new Set(),
++    focusedItem: null,
++    loadedProperties: new Map(),
++    ...overrides
++  };
++}
++
++module.exports = props => {
++  const middlewares = [thunk];
++  if (props.injectWaitService) {
++    middlewares.push(waitUntilService);
++  }
++
++  return createStore(reducer, createInitialState(props), applyMiddleware(...middlewares));
+ };
+ 
+ /***/ }),
+-/* 56 */
+-/***/ (function(module, exports) {
+-
+-module.exports = __WEBPACK_EXTERNAL_MODULE_56__;
++/* 58 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++/**
++ * A middleware that allows thunks (functions) to be dispatched.
++ * If it's a thunk, it is called with `dispatch` and `getState`,
++ * allowing the action to create multiple actions (most likely
++ * asynchronously).
++ */
++function thunk({ dispatch, getState }) {
++  return next => action => {
++    return typeof action === "function" ? action({ dispatch, getState }) : next(action);
++  };
++}
++exports.thunk = thunk;
++
++/***/ }),
++/* 59 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++/* 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/. */
++
++const WAIT_UNTIL_TYPE = "@@service/waitUntil";
++/**
++ * A middleware which acts like a service, because it is stateful
++ * and "long-running" in the background. It provides the ability
++ * for actions to install a function to be run once when a specific
++ * condition is met by an action coming through the system. Think of
++ * it as a thunk that blocks until the condition is met. Example:
++ *
++ * ```js
++ * const services = { WAIT_UNTIL: require('wait-service').NAME };
++ *
++ * { type: services.WAIT_UNTIL,
++ *   predicate: action => action.type === "ADD_ITEM",
++ *   run: (dispatch, getState, action) => {
++ *     // Do anything here. You only need to accept the arguments
++ *     // if you need them. `action` is the action that satisfied
++ *     // the predicate.
++ *   }
++ * }
++ * ```
++ */
++function waitUntilService({ dispatch, getState }) {
++  let pending = [];
++
++  function checkPending(action) {
++    const readyRequests = [];
++    const stillPending = [];
++
++    // Find the pending requests whose predicates are satisfied with
++    // this action. Wait to run the requests until after we update the
++    // pending queue because the request handler may synchronously
++    // dispatch again and run this service (that use case is
++    // completely valid).
++    for (const request of pending) {
++      if (request.predicate(action)) {
++        readyRequests.push(request);
++      } else {
++        stillPending.push(request);
++      }
++    }
++
++    pending = stillPending;
++    for (const request of readyRequests) {
++      request.run(dispatch, getState, action);
++    }
++  }
++
++  return next => action => {
++    if (action.type === WAIT_UNTIL_TYPE) {
++      pending.push(action);
++      return null;
++    }
++    const result = next(action);
++    checkPending(action);
++    return result;
++  };
++}
++
++module.exports = {
++  WAIT_UNTIL_TYPE,
++  waitUntilService
++};
++
++/***/ }),
++/* 60 */
++/***/ (function(module, exports, __webpack_require__) {
++
++"use strict";
++
++
++function reducer(state = {}, action) {
++  const {
++    type,
++    data
++  } = action;
++
++  const cloneState = overrides => ({ ...state, ...overrides });
++
++  if (type === "NODE_EXPAND") {
++    return cloneState({
++      expandedPaths: new Set(state.expandedPaths).add(data.node.path)
++    });
++  }
++
++  if (type === "NODE_COLLAPSE") {
++    const expandedPaths = new Set(state.expandedPaths);
++    expandedPaths.delete(data.node.path);
++    return cloneState({ expandedPaths });
++  }
++
++  if (type === "NODE_PROPERTIES_LOADED") {
++    // Let's loop through the responses to build a single object.
++    const properties = mergeResponses(data.responses);
++
++    return cloneState({
++      actors: data.actor ? new Set(state.actors || []).add(data.actor) : state.actors,
++      loadedProperties: new Map(state.loadedProperties).set(data.node.path, properties)
++    });
++  }
++
++  if (type === "NODE_FOCUS") {
++    return cloneState({
++      focusedItem: data.node
++    });
++  }
++
++  return state;
++} /* 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/. */
++
++
++function mergeResponses(responses) {
++  const data = {};
++
++  for (const response of responses) {
++    if (response.hasOwnProperty("ownProperties")) {
++      data.ownProperties = { ...data.ownProperties, ...response.ownProperties };
++    }
++
++    if (response.ownSymbols && response.ownSymbols.length > 0) {
++      data.ownSymbols = response.ownSymbols;
++    }
++
++    if (response.prototype) {
++      data.prototype = response.prototype;
++    }
++  }
++
++  return data;
++}
++
++module.exports = reducer;
+ 
+ /***/ })
+ /******/ ]);
+ });
+diff --git a/devtools/client/themes/images/devtools-components/arrow.svg b/devtools/client/themes/images/devtools-components/arrow.svg
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/themes/images/devtools-components/arrow.svg
+@@ -0,0 +1,6 @@
++<!-- This Source Code Form is subject to the terms of the Mozilla Public
++   - License, v. 2.0. If a copy of the MPL was not distributed with this
++   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
++<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
++  <path d="M8 13.4c-.5 0-.9-.2-1.2-.6L.4 5.2C0 4.7-.1 4.3.2 3.7S1 3 1.6 3h12.8c.6 0 1.2.1 1.4.7.3.6.2 1.1-.2 1.6l-6.4 7.6c-.3.4-.7.5-1.2.5z"/>
++</svg>
+diff --git a/devtools/client/themes/images/devtools-reps/jump-definition.svg b/devtools/client/themes/images/devtools-reps/jump-definition.svg
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/themes/images/devtools-reps/jump-definition.svg
+@@ -0,0 +1,8 @@
++<!-- This Source Code Form is subject to the terms of the Mozilla Public
++   - License, v. 2.0. If a copy of the MPL was not distributed with this
++   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
++<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg" stroke="#000" fill="none" stroke-linecap="round">
++  <path d="M5.5 3.5l2 2M5.5 7.5l2-2"/>
++  <path d="M7 5.5H4.006c-1.012 0-1.995 1.017-2.011 2.024-.005.023-.005 1.347 0 3.971" stroke-linejoin="round"/>
++  <path d="M10.5 5.5h4M9.5 3.5h5M9.5 7.5h5"/>
++</svg>
+\ No newline at end of file
+diff --git a/devtools/client/themes/images/devtools-reps/open-inspector.svg b/devtools/client/themes/images/devtools-reps/open-inspector.svg
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/themes/images/devtools-reps/open-inspector.svg
+@@ -0,0 +1,6 @@
++<!-- This Source Code Form is subject to the terms of the Mozilla Public
++   - License, v. 2.0. If a copy of the MPL was not distributed with this
++   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
++<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
++  <path d="M8,3L12,3L12,7L14,7L14,8L12,8L12,12L8,12L8,14L7,14L7,12L3,12L3,8L1,8L1,7L3,7L3,3L7,3L7,1L8,1L8,3ZM10,10L10,5L5,5L5,10L10,10Z"/>
++</svg>

+ 200 - 0
frg/work-js/mozilla-release/patches/1440388-2-61a1.patch

@@ -0,0 +1,200 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1520605910 -3600
+# Node ID 6b359e9acbf4aec0586335aa11a69bbc48447fbc
+# Parent  102ace967500d2a3c6abc1d6f0f140782f9f0e04
+Bug 1440388 - Fix failing test with new Reps bundle; r=Honza.
+
+MozReview-Commit-ID: HHfVwnDHBKH
+
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_dir.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_dir.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_dir.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_console_dir.js
+@@ -34,17 +34,17 @@ add_task(async function() {
+     });
+     arrayOiNodes = arrayOi.querySelectorAll(".node");
+   }
+ 
+   // There are 6 nodes: the root, 1, 2, {a: "a", b: "b"}, length and the proto.
+   is(arrayOiNodes.length, 6, "There is the expected number of nodes in the tree");
+   let propertiesNodes = [...arrayOi.querySelectorAll(".object-label")]
+     .map(el => el.textContent);
+-  const arrayPropertiesNames = ["0", "1", "2", "length", "__proto__"];
++  const arrayPropertiesNames = ["0", "1", "2", "length", "<prototype>"];
+   is(JSON.stringify(propertiesNodes), JSON.stringify(arrayPropertiesNames));
+ 
+   info("console.dir on a long object");
+   const obj = Array.from({length: 100}).reduce((res, _, i) => {
+     res["item-" + (i + 1).toString().padStart(3, "0")] = i + 1;
+     return res;
+   }, {});
+   await ContentTask.spawn(gBrowser.selectedBrowser, obj, function(data) {
+@@ -62,17 +62,17 @@ add_task(async function() {
+       childList: true
+     });
+     objectOiNodes = objectOi.querySelectorAll(".node");
+   }
+ 
+   // There are 102 nodes: the root, 100 "item-N" properties, and the proto.
+   is(objectOiNodes.length, 102, "There is the expected number of nodes in the tree");
+   const objectPropertiesNames = Object.getOwnPropertyNames(obj).map(name => `"${name}"`);
+-  objectPropertiesNames.push("__proto__");
++  objectPropertiesNames.push("<prototype>");
+   propertiesNodes = [...objectOi.querySelectorAll(".object-label")]
+     .map(el => el.textContent);
+   is(JSON.stringify(propertiesNodes), JSON.stringify(objectPropertiesNames));
+ });
+ 
+ function findConsoleDir(node, index) {
+   return node.querySelectorAll(".dir.message")[index];
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_inspect_cross_domain_object.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_inspect_cross_domain_object.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_inspect_cross_domain_object.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_inspect_cross_domain_object.js
+@@ -31,17 +31,17 @@ add_task(async function() {
+ 
+   info("Expanding the first object inspector");
+   await expandObjectInspector(oi1);
+ 
+   // The first object inspector now looks like:
+   // ▼ {…}
+   // |  bug: 869003
+   // |  hello: "world!"
+-  // |  ▶︎ __proto__: Object { … }
++  // |  ▶︎ <prototype>: Object { … }
+ 
+   let oi1Nodes = oi1.querySelectorAll(".node");
+   is(oi1Nodes.length, 4, "There is the expected number of nodes in the tree");
+   ok(oi1.textContent.includes("bug: 869003"), "Expected content");
+   ok(oi1.textContent.includes('hello: "world!"'), "Expected content");
+ 
+   info("Expanding the second object inspector");
+   await expandObjectInspector(oi2);
+@@ -50,17 +50,17 @@ add_task(async function() {
+   // ▼ func()
+   // |  arguments: null
+   // |  bug: 869003
+   // |  caller: null
+   // |  hello: "world!"
+   // |  length: 1
+   // |  name: "func"
+   // |  ▶︎ prototype: Object { … }
+-  // |  ▶︎ __proto__: function ()
++  // |  ▶︎ <prototype>: function ()
+ 
+   let oi2Nodes = oi2.querySelectorAll(".node");
+   is(oi2Nodes.length, 9, "There is the expected number of nodes in the tree");
+   ok(oi2.textContent.includes("arguments: null"), "Expected content");
+   ok(oi2.textContent.includes("bug: 869003"), "Expected content");
+   ok(oi2.textContent.includes("caller: null"), "Expected content");
+   ok(oi2.textContent.includes('hello: "world!"'), "Expected content");
+   ok(oi2.textContent.includes("length: 1"), "Expected content");
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_in_sidebar.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_in_sidebar.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_in_sidebar.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_in_sidebar.js
+@@ -36,17 +36,17 @@ add_task(async function() {
+     });
+     oiNodes = objectInspector.querySelectorAll(".node");
+   }
+ 
+   // There are 5 nodes: the root, a, b, c, and proto.
+   is(oiNodes.length, 5, "There is the expected number of nodes in the tree");
+   let propertiesNodes = [...objectInspector.querySelectorAll(".object-label")]
+     .map(el => el.textContent);
+-  const arrayPropertiesNames = ["a", "b", "c", "__proto__"];
++  const arrayPropertiesNames = ["a", "b", "c", "<prototype>"];
+   is(JSON.stringify(propertiesNodes), JSON.stringify(arrayPropertiesNames));
+ });
+ 
+ async function showSidebarWithContextMenu(hud, node, expectMutation) {
+   let wrapper = hud.ui.document.querySelector(".webconsole-output-wrapper");
+   let onSidebarShown = waitForNodeMutation(wrapper, { childList: true });
+ 
+   let contextMenu = await openContextMenu(hud, node);
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector.js
+@@ -43,17 +43,17 @@ add_task(async function() {
+   let arrayOiNodes = arrayOi.querySelectorAll(".node");
+ 
+   // The object inspector now looks like:
+   // ▼ […]
+   // |  0: 1
+   // |  1: 2
+   // |  ▶︎ 2: {a: "a", b: "b"}
+   // |  length: 3
+-  // |  ▶︎ __proto__
++  // |  ▶︎ <prototype>
+   is(arrayOiNodes.length, 6, "There is the expected number of nodes in the tree");
+ 
+   info("Expanding a leaf of the array object inspector");
+   let arrayOiNestedObject = arrayOiNodes[3];
+   onArrayOiMutation = waitForNodeMutation(arrayOi, {
+     childList: true
+   });
+ 
+@@ -67,19 +67,19 @@ add_task(async function() {
+ 
+   // The object inspector now looks like:
+   // ▼ […]
+   // |  0: 1
+   // |  1: 2
+   // |  ▼ 2: {…}
+   // |  |  a: "a"
+   // |  |  b: "b"
+-  // |  |  ▶︎ __proto__
++  // |  |  ▶︎ <prototype>
+   // |  length: 3
+-  // |  ▶︎ __proto__
++  // |  ▶︎ <prototype>
+   is(arrayOiNodes.length, 9, "There is the expected number of nodes in the tree");
+ 
+   info("Collapsing the root");
+   onArrayOiMutation = waitForNodeMutation(arrayOi, {
+     childList: true
+   });
+   arrayOi.querySelector(".arrow").click();
+ 
+@@ -116,11 +116,11 @@ add_task(async function() {
+     "The arrow of the root node of the tree is expanded after clicking on it");
+ 
+   let objectOiNodes = objectOi.querySelectorAll(".node");
+   // The object inspector now looks like:
+   // ▼ {…}
+   // |  c: "c"
+   // |  ▶︎ d: [3, 4]
+   // |  length: 987
+-  // |  ▶︎ __proto__
++  // |  ▶︎ <prototype>
+   is(objectOiNodes.length, 5, "There is the expected number of nodes in the tree");
+ });
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_object_inspector_while_debugging_and_inspecting.js
+@@ -50,24 +50,24 @@ add_task(async function() {
+   await onOiExpanded;
+ 
+   ok(oi.querySelector(".arrow").classList.contains("expanded"),
+     "Object inspector expanded");
+ 
+   // The object inspector now looks like:
+   // {...}
+   // |  testProp2: "testValue2"
+-  // |  __proto__: Object { ... }
++  // |  <prototype>: Object { ... }
+ 
+   let oiNodes = oi.querySelectorAll(".node");
+   is(oiNodes.length, 3, "There is the expected number of nodes in the tree");
+ 
+   ok(oiNodes[0].textContent.includes(`{\u2026}`));
+   ok(oiNodes[1].textContent.includes(`testProp2: "testValue2"`));
+-  ok(oiNodes[2].textContent.includes(`__proto__: Object { \u2026 }`));
++  ok(oiNodes[2].textContent.includes(`<prototype>: Object { \u2026 }`));
+ });
+ 
+ async function waitForFrameAdded() {
+   let target = TargetFactory.forTab(gBrowser.selectedTab);
+   let toolbox = gDevTools.getToolbox(target);
+   let thread = toolbox.threadClient;
+ 
+   info("Waiting for framesadded");

+ 134 - 0
frg/work-js/mozilla-release/patches/1440388-3-61a1.patch

@@ -0,0 +1,134 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1520605840 -3600
+# Node ID 03b4dbb07486620cc1d75f7d01369f56adadfeea
+# Parent  f1ca0add46c18b1ecec915a2add7d11f9e8d0f1f
+Bug 1440388 - Adapt JSONViewer to the new Reps bundle; r=Honza.
+
+We don't populate the href attribute on links anymore and reps require
+that we pass an openLink function to handle them.
+This had an impact on the test where we are testing those links.
+
+MozReview-Commit-ID: Hj2PBX79HZu
+
+diff --git a/devtools/client/jsonview/components/JsonPanel.js b/devtools/client/jsonview/components/JsonPanel.js
+--- a/devtools/client/jsonview/components/JsonPanel.js
++++ b/devtools/client/jsonview/components/JsonPanel.js
+@@ -83,17 +83,17 @@ define(function(require, exports, module
+       if (isObject(member.value) && member.hasChildren && member.open) {
+         return null;
+       }
+ 
+       // Render the value (summary) using Reps library.
+       return Rep(Object.assign({}, props, {
+         cropLimit: 50,
+         noGrip: true,
+-        omitLinkHref: false,
++        openLink: url => window.open(url, "_blank"),
+       }));
+     }
+ 
+     renderTree() {
+       // Append custom column for displaying values. This column
+       // Take all available horizontal space.
+       let columns = [{
+         id: "value",
+diff --git a/devtools/client/jsonview/test/browser_jsonview_url_linkification.js b/devtools/client/jsonview/test/browser_jsonview_url_linkification.js
+--- a/devtools/client/jsonview/test/browser_jsonview_url_linkification.js
++++ b/devtools/client/jsonview/test/browser_jsonview_url_linkification.js
+@@ -7,45 +7,72 @@
+ 
+ const {ELLIPSIS} = require("devtools/shared/l10n");
+ 
+ add_task(async function() {
+   info("Test short URL linkification JSON started");
+ 
+   let url = "http://example.com/";
+   let tab = await addJsonViewTab("data:application/json," + JSON.stringify([url]));
+-
+-  // eslint-disable-next-line no-shadow
+-  await ContentTask.spawn(tab.linkedBrowser, {url}, function({url}) {
+-    let {document} = content;
++  await testLinkNavigation({ browser: tab.linkedBrowser, url });
+ 
+-    let link = document.querySelector(".jsonPanelBox .treeTable .treeValueCell a");
+-    is(link.href, url, "The URL was linkified.");
+-    is(link.textContent, url, "The full URL is displayed.");
++  info("Switch back to the JSONViewer");
++  await BrowserTestUtils.switchTab(gBrowser, tab);
+ 
+-    // Click the label
+-    document.querySelector(".jsonPanelBox .treeTable .treeLabel").click();
+-    is(link.href, url, "The link target didn't change.");
+-    is(link.textContent, url, "The link text didn't change.");
+-  });
++  await testLinkNavigation({ browser: tab.linkedBrowser, url, clickLabel: true });
+ });
+ 
+ add_task(async function() {
+   info("Test long URL linkification JSON started");
+ 
+   let url = "http://example.com/" + "a".repeat(100);
+   let tab = await addJsonViewTab("data:application/json," + JSON.stringify([url]));
+ 
+-  // eslint-disable-next-line no-shadow
+-  await ContentTask.spawn(tab.linkedBrowser, {url, ELLIPSIS}, function({url, ELLIPSIS}) {
+-    let croppedUrl = url.slice(0, 24) + ELLIPSIS + url.slice(-24);
++  await testLinkNavigation({ browser: tab.linkedBrowser, url });
++
++  info("Switch back to the JSONViewer");
++  await BrowserTestUtils.switchTab(gBrowser, tab);
++
++  await testLinkNavigation({
++    browser: tab.linkedBrowser,
++    url,
++    urlText: url.slice(0, 24) + ELLIPSIS + url.slice(-24),
++    clickLabel: true,
++  });
++});
++
++/**
++ * Assert that the expected link is displayed and that clicking on it navigates to the
++ * expected url.
++ *
++ * @param {Object} option object containing:
++ *        - browser (mandatory): the browser the tab will be opened in.
++ *        - url (mandatory): The url we should navigate to.
++ *        - urlText: The expected displayed text of the url.
++ *                   Falls back to `url` if not filled
++ *        - clickLabel: Should we click the label before doing assertions.
++ */
++async function testLinkNavigation({
++  browser,
++  url,
++  urlText,
++  clickLabel = false
++}) {
++  let onTabLoaded = BrowserTestUtils.waitForNewTab(gBrowser, url);
++
++  ContentTask.spawn(browser, [urlText || url, clickLabel], (args) => {
++    const [expectedURL, shouldClickLabel] = args;
+     let {document} = content;
+ 
++    if (shouldClickLabel === true) {
++      document.querySelector(".jsonPanelBox .treeTable .treeLabel").click();
++    }
++
+     let link = document.querySelector(".jsonPanelBox .treeTable .treeValueCell a");
+-    is(link.href, url, "The URL was linkified.");
+-    is(link.textContent, url, "The full URL is displayed.");
++    is(link.textContent, expectedURL, "The expected URL is displayed.");
++    link.click();
++  });
+ 
+-    // Click the label, this crops the value.
+-    document.querySelector(".jsonPanelBox .treeTable .treeLabel").click();
+-    is(link.href, url, "The link target didn't change.");
+-    is(link.textContent, croppedUrl, "The link text was cropped.");
+-  });
+-});
++  let newTab = await onTabLoaded;
++  // We only need to check that newTab is truthy since
++  // BrowserTestUtils.waitForNewTab checks the URL.
++  ok(newTab, "The expected tab was opened.");
++}

+ 82 - 0
frg/work-js/mozilla-release/patches/1440388-4-61a1.patch

@@ -0,0 +1,82 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521186109 -3600
+# Node ID fed69455a83257de1364e97085e3e30badcbb0ab
+# Parent  03b4dbb07486620cc1d75f7d01369f56adadfeea
+Bug 1440388 - Adapt webconsole.css to the new Reps bundle; r=Honza.
+
+MozReview-Commit-ID: Dq82uvMDHVy
+
+diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css
+--- a/devtools/client/themes/webconsole.css
++++ b/devtools/client/themes/webconsole.css
+@@ -647,16 +647,17 @@ a.learn-more-link.webconsole-learn-more-
+   filter: var(--theme-icon-checked-filter);
+ }
+ 
+ .elementNode:hover .open-inspector:active,
+ .open-inspector:active {
+   filter: var(--theme-icon-checked-filter) brightness(0.9);
+ }
+ 
++
+ /* Old console frontend filters */
+ .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-button {
+   -moz-appearance: none;
+   color: inherit;
+   border-width: 0;
+   -moz-box-orient: horizontal;
+   padding: 0;
+ }
+@@ -1098,20 +1099,17 @@ a.learn-more-link.webconsole-learn-more-
+   display: none;
+ }
+ 
+ /*
+  * Open DOMNode in inspector button. Style need to be reset in the new
+  * console since its style is already defined in reps.css .
+  */
+ .webconsole-output-wrapper .open-inspector {
+-  background: unset;
+-  padding-left: unset;
+-  margin-left: unset;
+-  cursor: unset;
++  background-image: unset;
+ }
+ 
+ /* Stacktraces */
+ .webconsole-output-wrapper  .stacktrace {
+   border: 1px solid var(--attachment-border-color);
+   border-radius: 2px;
+   margin-block-end: var(--attachment-margin-block-end);
+ }
+@@ -1215,24 +1213,22 @@ body #output-container {
+ .webconsole-output-wrapper .object-inspector.tree .tree-node:hover:not(.focused) {
+   background-color: var(--object-inspector-hover-background);
+ }
+ 
+ /*
+  * Make the arrow the same color and approximately the same size of the twisty icon.
+  * Should be properly fixed in https://bugzilla.mozilla.org/show_bug.cgi?id=1307937.
+  */
+-.webconsole-output-wrapper .object-inspector.tree .tree-node .arrow svg {
+-  width: 8px;
+-  fill: #AFA8AB;
+-  margin-inline-start: 1px;
++.webconsole-output-wrapper .object-inspector.tree .tree-node .arrow {
++  background-color: #AFA8AB;
+ }
+ 
+-.theme-dark .webconsole-output-wrapper .object-inspector.tree .tree-node .arrow svg {
+-  fill: #7F7E81;
++.theme-dark .webconsole-output-wrapper .object-inspector.tree .tree-node .arrow {
++  background-color: #7F7E81;
+ }
+ 
+ /* Sidebar */
+ .sidebar {
+   display: flex;
+   grid-row: 1 / -1;
+   grid-column: -1 / -2;
+   background-color: var(--theme-sidebar-background);
+

+ 125 - 0
frg/work-js/mozilla-release/patches/1441462-60a1.patch

@@ -0,0 +1,125 @@
+# HG changeset patch
+# User Patrick Brosset <pbrosset@mozilla.com>
+# Date 1520368154 -3600
+# Node ID 8aaf118e2e16ddba53bac4c5649bb36269bf5f30
+# Parent  7a4f0708d557def104bd313f1dfbb77060269c2f
+Bug 1441462 - Avoid crashing the gridOutline when there are no fragments; r=gl
+
+MozReview-Commit-ID: 6TpYB4f33JI
+
+diff --git a/devtools/client/inspector/grids/components/GridOutline.js b/devtools/client/inspector/grids/components/GridOutline.js
+--- a/devtools/client/inspector/grids/components/GridOutline.js
++++ b/devtools/client/inspector/grids/components/GridOutline.js
+@@ -69,22 +69,22 @@ class GridOutline extends PureComponent 
+   }
+ 
+   componentWillReceiveProps({ grids }) {
+     let selectedGrid = grids.find(grid => grid.highlighted);
+ 
+     // Store the height of the grid container in the component state to prevent overflow
+     // issues. We want to store the width of the grid container as well so that the
+     // viewbox is only the calculated width of the grid outline.
+-    let { width, height } = selectedGrid
++    let { width, height } = selectedGrid && selectedGrid.gridFragments.length
+                             ? this.getTotalWidthAndHeight(selectedGrid)
+                             : { width: 0, height: 0 };
+     let showOutline;
+ 
+-    if (selectedGrid) {
++    if (selectedGrid && selectedGrid.gridFragments.length) {
+       const { cols, rows } = selectedGrid.gridFragments[0];
+ 
+       // Show the grid outline if both the rows/columns are less than or equal
+       // to their max prefs.
+       showOutline = (cols.lines.length <= GRID_OUTLINE_MAX_COLUMNS_PREF) &&
+                     (rows.lines.length <= GRID_OUTLINE_MAX_ROWS_PREF);
+     }
+ 
+@@ -382,17 +382,17 @@ class GridOutline extends PureComponent 
+       this.doHighlightCell(target, type === "mouseleave");
+       this.highlightTimeout = null;
+     }, GRID_HIGHLIGHTING_DEBOUNCE);
+   }
+ 
+   render() {
+     const { selectedGrid } = this.state;
+ 
+-    return selectedGrid ?
++    return selectedGrid && selectedGrid.gridFragments.length ?
+       dom.div(
+         {
+           id: "grid-outline-container",
+           className: "grid-outline-container",
+         },
+         this.renderOutline()
+       )
+       :
+diff --git a/devtools/client/inspector/grids/test/browser.ini b/devtools/client/inspector/grids/test/browser.ini
+--- a/devtools/client/inspector/grids/test/browser.ini
++++ b/devtools/client/inspector/grids/test/browser.ini
+@@ -28,10 +28,11 @@ support-files =
+ [browser_grids_grid-list-toggle-single-grid.js]
+ [browser_grids_grid-outline-cannot-show-outline.js]
+ [browser_grids_grid-outline-highlight-area.js]
+ [browser_grids_grid-outline-highlight-cell.js]
+ [browser_grids_grid-outline-selected-grid.js]
+ [browser_grids_grid-outline-updates-on-grid-change.js]
+ [browser_grids_grid-outline-writing-mode.js]
+ [browser_grids_highlighter-setting-rules-grid-toggle.js]
++[browser_grids_no_fragments.js]
+ [browser_grids_persist-color-palette.js]
+ [browser_grids_restored-after-reload.js]
+diff --git a/devtools/client/inspector/grids/test/browser_grids_no_fragments.js b/devtools/client/inspector/grids/test/browser_grids_no_fragments.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/grids/test/browser_grids_no_fragments.js
+@@ -0,0 +1,49 @@
++/* Any copyright is dedicated to the Public Domain.
++ http://creativecommons.org/publicdomain/zero/1.0/ */
++
++"use strict";
++
++// Tests that when grids exist but have no fragments, clicking on the checkbox doesn't
++// fail (see bug 1441462).
++
++const TEST_URI = `
++  <style type='text/css'>
++    #wrapper { display: none; }
++    .grid { display: grid; grid-template-columns: 1fr 1fr; }
++  </style>
++  <div id="wrapper">
++    <div class="grid">
++      <div>cell1</div>
++      <div>cell2</div>
++    </div>
++    <div class="grid">
++      <div>cell1</div>
++      <div>cell2</div>
++    </div>
++  </div>
++`;
++
++add_task(function* () {
++  yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
++
++  let { inspector, gridInspector } = yield openLayoutView();
++  let { document: doc } = gridInspector;
++  let { store } = inspector;
++
++  let gridList = doc.querySelector("#grid-list");
++  is(gridList.children.length, 2, "There are 2 grids in the list");
++
++  info("Try to click the first grid's checkbox");
++  let checkbox1 = gridList.children[0].querySelector("input");
++  let onCheckbox1Change = waitUntilState(store, state =>
++    state.grids[0] && state.grids[0].highlighted);
++  checkbox1.click();
++  yield onCheckbox1Change;
++
++  info("Try to click the second grid's checkbox");
++  let checkbox2 = gridList.children[1].querySelector("input");
++  let onCheckbox2Change = waitUntilState(store, state =>
++    state.grids[1] && state.grids[1].highlighted);
++  checkbox2.click();
++  yield onCheckbox2Change;
++});

+ 3007 - 0
frg/work-js/mozilla-release/patches/1441703-1-61a1.patch

@@ -0,0 +1,3007 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1520582591 -3600
+# Node ID 6a5c578363175af82fa91ca20f871d58f67fd0f6
+# Parent  75f07bf62765f9ea51ee306ceacc20afdc30f984
+Bug 1441703 - Split DevTools performance test damp.js;r=ochameau
+
+MozReview-Commit-ID: Jwfe7RLxEg2
+
+diff --git a/testing/talos/talos/tests/devtools/addon/content/damp.js b/testing/talos/talos/tests/devtools/addon/content/damp.js
+--- a/testing/talos/talos/tests/devtools/addon/content/damp.js
++++ b/testing/talos/talos/tests/devtools/addon/content/damp.js
+@@ -1,327 +1,67 @@
+ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
+-const gMgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
+ const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
+ 
+ XPCOMUtils.defineLazyGetter(this, "require", function() {
+   let { require } =
+     ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+   return require;
+ });
+ XPCOMUtils.defineLazyGetter(this, "gDevTools", function() {
+   let { gDevTools } = require("devtools/client/framework/devtools");
+   return gDevTools;
+ });
+-XPCOMUtils.defineLazyGetter(this, "EVENTS", function() {
+-  let { EVENTS } = require("devtools/client/netmonitor/src/constants");
+-  return EVENTS;
+-});
+ XPCOMUtils.defineLazyGetter(this, "TargetFactory", function() {
+   let { TargetFactory } = require("devtools/client/framework/target");
+   return TargetFactory;
+ });
+ 
+-const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
+-
+-const PAGES_BASE_URL = webserver + "/tests/devtools/addon/content/pages/";
+-const SIMPLE_URL = PAGES_BASE_URL + "simple.html";
+-const COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
+-const CUSTOM_URL = PAGES_BASE_URL + "custom/$TOOL/index.html";
+-const PANELS_IN_BACKGROUND = PAGES_BASE_URL +
+-  "custom/panels-in-background/panels-in-background.html";
+-
+ // Record allocation count in new subtests if DEBUG_DEVTOOLS_ALLOCATIONS is set to
+ // "normal". Print allocation sites to stdout if DEBUG_DEVTOOLS_ALLOCATIONS is set to
+ // "verbose".
+ const DEBUG_ALLOCATIONS = env.get("DEBUG_DEVTOOLS_ALLOCATIONS");
+ 
+ function getMostRecentBrowserWindow() {
+   return Services.wm.getMostRecentWindow("navigator:browser");
+ }
+ 
+ function getActiveTab(window) {
+   return window.gBrowser.selectedTab;
+ }
+ 
+-/* ************* Debugger Helper ***************/
+-/*
+- * These methods are used for working with debugger state changes in order
+- * to make it easier to manipulate the ui and test different behavior. These
+- * methods roughly reflect those found in debugger/new/test/mochi/head.js with
+- * a few exceptions. The `dbg` object is not exactly the same, and the methods
+- * have been simplified. We may want to consider unifying them in the future
+- */
+-
+-const DEBUGGER_POLLING_INTERVAL = 50;
+-
+-const debuggerHelper = {
+-  waitForState(dbg, predicate, msg) {
+-    return new Promise(resolve => {
+-      if (msg) {
+-        dump(`Waiting for state change: ${msg}\n`);
+-      }
+-      if (predicate(dbg.store.getState())) {
+-        if (msg) {
+-          dump(`Finished waiting for state change: ${msg}\n`);
+-        }
+-        return resolve();
+-      }
+-
+-      const unsubscribe = dbg.store.subscribe(() => {
+-        if (predicate(dbg.store.getState())) {
+-          if (msg) {
+-            dump(`Finished waiting for state change: ${msg}\n`);
+-          }
+-          unsubscribe();
+-          resolve();
+-        }
+-      });
+-      return false;
+-    });
+-  },
+-
+-  waitForDispatch(dbg, type) {
+-    return new Promise(resolve => {
+-      dbg.store.dispatch({
+-        type: "@@service/waitUntil",
+-        predicate: action => {
+-          if (action.type === type) {
+-            return action.status
+-              ? action.status === "done" || action.status === "error"
+-              : true;
+-          }
+-          return false;
+-        },
+-        run: (dispatch, getState, action) => {
+-          resolve(action);
+-        }
+-      });
+-    });
+-  },
+-
+-  async waitUntil(predicate, msg) {
+-    if (msg) {
+-      dump(`Waiting until: ${msg}\n`);
+-    }
+-    return new Promise(resolve => {
+-      const timer = setInterval(() => {
+-        if (predicate()) {
+-          clearInterval(timer);
+-          if (msg) {
+-            dump(`Finished Waiting until: ${msg}\n`);
+-          }
+-          resolve();
+-        }
+-      }, DEBUGGER_POLLING_INTERVAL);
+-    });
+-  },
+-
+-  findSource(dbg, url) {
+-    const sources = dbg.selectors.getSources(dbg.getState());
+-    return sources.find(s => (s.get("url") || "").includes(url));
+-  },
+-
+-  getCM(dbg) {
+-    const el = dbg.win.document.querySelector(".CodeMirror");
+-    return el.CodeMirror;
+-  },
+-
+-  waitForText(dbg, url, text) {
+-    return this.waitUntil(() => {
+-      // the welcome box is removed once text is displayed
+-      const welcomebox = dbg.win.document.querySelector(".welcomebox");
+-      if (welcomebox) {
+-        return false;
+-      }
+-      const cm = this.getCM(dbg);
+-      const editorText = cm.doc.getValue();
+-      return editorText.includes(text);
+-    }, "text is visible");
+-  },
+-
+-  waitForMetaData(dbg) {
+-    return this.waitUntil(
+-      () => {
+-        const state = dbg.store.getState();
+-        const source = dbg.selectors.getSelectedSource(state);
+-        // wait for metadata -- this involves parsing the file to determine its type.
+-        // if the object is empty, the data has not yet loaded
+-        const metaData = dbg.selectors.getSourceMetaData(state, source.get("id"));
+-        return !!Object.keys(metaData).length;
+-      },
+-      "has file metadata"
+-    );
+-  },
+-
+-  waitForSources(dbg, expectedSources) {
+-    const { selectors } = dbg;
+-    function countSources(state) {
+-      const sources = selectors.getSources(state);
+-      return sources.size >= expectedSources;
+-    }
+-    return this.waitForState(dbg, countSources, "count sources");
+-  },
+-
+-  async createContext(panel) {
+-    const { store, selectors, actions } = panel.getVarsForTests();
+-
+-    return {
+-      actions,
+-      selectors,
+-      getState: store.getState,
+-      win: panel.panelWin,
+-      store
+-    };
+-  },
+-
+-  selectSource(dbg, url) {
+-    dump(`Selecting source: ${url}\n`);
+-    const line = 1;
+-    const source = this.findSource(dbg, url);
+-    dbg.actions.selectLocation({ sourceId: source.get("id"), line });
+-    return this.waitForState(
+-      dbg,
+-      state => {
+-        const source = dbg.selectors.getSelectedSource(state);
+-        const isLoaded = source && source.get("loadedState") === "loaded";
+-        if (!isLoaded) {
+-          return false;
+-        }
+-
+-        // wait for symbols -- a flat map of all named variables in a file -- to be calculated.
+-        // this is a slow process and becomes slower the larger the file is
+-        return dbg.selectors.hasSymbols(state, source.toJS());
+-      },
+-      "selected source"
+-    );
+-  },
+-
+-  async addBreakpoint(dbg, line, url) {
+-    dump(`add breakpoint\n`);
+-    const source = this.findSource(dbg, url);
+-    const location = {
+-      sourceId: source.get("id"),
+-      line,
+-      column: 0
+-    };
+-    const onDispatched = debuggerHelper.waitForDispatch(dbg, "ADD_BREAKPOINT");
+-    dbg.actions.addBreakpoint(location);
+-    return onDispatched;
+-  },
+-
+-  async removeBreakpoints(dbg, line, url) {
+-    dump(`remove all breakpoints\n`);
+-    const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
+-
+-    const onBreakpointsCleared =  this.waitForState(
+-      dbg,
+-      state => !dbg.selectors.getBreakpoints(state).length
+-    );
+-    await dbg.actions.removeBreakpoints(breakpoints);
+-    return onBreakpointsCleared;
+-  },
+-
+-  async pauseDebugger(dbg, tab, testFunction, { line, file }) {
+-    await this.addBreakpoint(dbg, line, file);
+-    const onPaused = this.waitForPaused(dbg);
+-    await this.evalInContent(dbg, tab, testFunction);
+-    return onPaused;
+-  },
+-
+-  async waitForPaused(dbg) {
+-    const onLoadedScope = this.waitForLoadedScopes(dbg);
+-    const onStateChange =  this.waitForState(
+-      dbg,
+-      state => {
+-        return dbg.selectors.getSelectedScope(state) && dbg.selectors.isPaused(state);
+-      },
+-    );
+-    return Promise.all([onLoadedScope, onStateChange]);
+-  },
+-
+-  async resume(dbg) {
+-    const onResumed = this.waitForResumed(dbg);
+-    dbg.actions.resume();
+-    return onResumed;
+-  },
+-
+-  async waitForResumed(dbg) {
+-    return this.waitForState(
+-      dbg,
+-      state => !dbg.selectors.isPaused(state)
+-    );
+-  },
+-
+-  evalInContent(dbg, tab, testFunction) {
+-    dump(`Run function in content process: ${testFunction}\n`);
+-    // Load a frame script using a data URI so we can run a script
+-    // inside of the content process and trigger debugger functionality
+-    // as needed
+-    const messageManager = tab.linkedBrowser.messageManager;
+-    return messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-      `function () {
+-          content.window.eval("${testFunction}");
+-      }`
+-    ) + ")()", true);
+-  },
+-
+-  async waitForElement(dbg, name) {
+-    await this.waitUntil(() => dbg.win.document.querySelector(name));
+-    return dbg.win.document.querySelector(name);
+-  },
+-
+-  async waitForLoadedScopes(dbg) {
+-    const element = ".scopes-list .tree-node[aria-level=\"1\"]";
+-    return this.waitForElement(dbg, element);
+-  },
+-
+-  async step(dbg, stepType) {
+-    const resumed = this.waitForResumed(dbg);
+-    dbg.actions[stepType]();
+-    await resumed;
+-    return this.waitForPaused(dbg);
+-  }
+-};
+-
+-async function garbageCollect() {
+-  dump("Garbage collect\n");
+-
+-  // Minimize memory usage
+-  // mimic miminizeMemoryUsage, by only flushing JS objects via GC.
+-  // We don't want to flush all the cache like minimizeMemoryUsage,
+-  // as it slow down next executions almost like a cold start.
+-
+-  // See minimizeMemoryUsage code to justify the 3 iterations and the setTimeout:
+-  // https://searchfox.org/mozilla-central/source/xpcom/base/nsMemoryReporterManager.cpp#2574-2585
+-  for (let i = 0; i < 3; i++) {
+-    // See minimizeMemoryUsage code here to justify the GC+CC+GC:
+-    // https://searchfox.org/mozilla-central/rev/be78e6ea9b10b1f5b2b3b013f01d86e1062abb2b/dom/base/nsJSEnvironment.cpp#341-349
+-    Cu.forceGC();
+-    Cu.forceCC();
+-    Cu.forceGC();
+-    await new Promise(done => setTimeout(done, 0));
+-  }
+-}
+-
+ /* globals res:true */
+ 
+ function Damp() {
+-  // Path to the temp file where the heap snapshot file is saved. Set by
+-  // saveHeapSnapshot and read by readHeapSnapshot.
+-  this._heapSnapshotFilePath = null;
+-  // HeapSnapshot instance. Set by readHeapSnapshot, used by takeCensus.
+-  this._snapshot = null;
+-
+   Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
+ }
+ 
+ Damp.prototype = {
+ 
++  async garbageCollect() {
++    dump("Garbage collect\n");
++
++    // Minimize memory usage
++    // mimic miminizeMemoryUsage, by only flushing JS objects via GC.
++    // We don't want to flush all the cache like minimizeMemoryUsage,
++    // as it slow down next executions almost like a cold start.
++
++    // See minimizeMemoryUsage code to justify the 3 iterations and the setTimeout:
++    // https://searchfox.org/mozilla-central/source/xpcom/base/nsMemoryReporterManager.cpp#2574-2585
++    for (let i = 0; i < 3; i++) {
++      // See minimizeMemoryUsage code here to justify the GC+CC+GC:
++      // https://searchfox.org/mozilla-central/rev/be78e6ea9b10b1f5b2b3b013f01d86e1062abb2b/dom/base/nsJSEnvironment.cpp#341-349
++      Cu.forceGC();
++      Cu.forceCC();
++      Cu.forceGC();
++      await new Promise(done => setTimeout(done, 0));
++    }
++  },
++
+   /**
+    * Helper to tell when a test start and when it is finished.
+    * It helps recording its duration, but also put markers for perf-html when profiling
+    * DAMP.
+    *
+    * When this method is called, the test is considered to be starting immediately
+    * When the test is over, the returned object's `done` method should be called.
+    *
+@@ -389,788 +129,65 @@ Damp.prototype = {
+         onReload().then(resolve);
+       } else {
+         browser.addEventListener("load", resolve, {capture: true, once: true});
+       }
+       browser.reload();
+     });
+   },
+ 
+-  async openToolbox(tool = "webconsole", onLoad) {
+-    let tab = getActiveTab(getMostRecentBrowserWindow());
+-    let target = TargetFactory.forTab(tab);
+-    let onToolboxCreated = gDevTools.once("toolbox-created");
+-    let showPromise = gDevTools.showToolbox(target, tool);
+-    let toolbox = await onToolboxCreated;
+-
+-    if (typeof(onLoad) == "function") {
+-      let panel = await toolbox.getPanelWhenReady(tool);
+-      await onLoad(toolbox, panel);
+-    }
+-    await showPromise;
+-
+-    return toolbox;
+-  },
+-
+-  async closeToolbox() {
+-    let tab = getActiveTab(getMostRecentBrowserWindow());
+-    let target = TargetFactory.forTab(tab);
+-    await target.client.waitForRequestsToSettle();
+-    await gDevTools.closeToolbox(target);
+-  },
+-
+-  async saveHeapSnapshot(label) {
+-    let tab = getActiveTab(getMostRecentBrowserWindow());
+-    let target = TargetFactory.forTab(tab);
+-    let toolbox = gDevTools.getToolbox(target);
+-    let panel = toolbox.getCurrentPanel();
+-    let memoryFront = panel.panelWin.gFront;
+-
+-    let test = this.runTest(label + ".saveHeapSnapshot");
+-    this._heapSnapshotFilePath = await memoryFront.saveHeapSnapshot();
+-    test.done();
+-  },
+-
+-  readHeapSnapshot(label) {
+-    let test = this.runTest(label + ".readHeapSnapshot");
+-    this._snapshot = ChromeUtils.readHeapSnapshot(this._heapSnapshotFilePath);
+-    test.done();
+-    return Promise.resolve();
+-  },
+-
+-  async waitForNetworkRequests(label, toolbox, expectedRequests) {
+-    let test = this.runTest(label + ".requestsFinished.DAMP");
+-    await this.waitForAllRequestsFinished(expectedRequests);
+-    test.done();
+-  },
+-
+-  async _consoleBulkLoggingTest() {
+-    let TOTAL_MESSAGES = 10;
+-    let tab = await this.testSetup(SIMPLE_URL);
+-    let messageManager = tab.linkedBrowser.messageManager;
+-    let toolbox = await this.openToolbox("webconsole");
+-    let webconsole = toolbox.getPanel("webconsole");
+-
+-    // Resolve once the last message has been received.
+-    let allMessagesReceived = new Promise(resolve => {
+-      function receiveMessages(messages) {
+-        for (let m of messages) {
+-          if (m.node.textContent.includes("damp " + TOTAL_MESSAGES)) {
+-            webconsole.hud.ui.off("new-messages", receiveMessages);
+-            // Wait for the console to redraw
+-            requestAnimationFrame(resolve);
+-          }
+-        }
+-      }
+-      webconsole.hud.ui.on("new-messages", receiveMessages);
+-    });
+-
+-    // Load a frame script using a data URI so we can do logs
+-    // from the page.  So this is running in content.
+-    messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-      `function () {
+-        addMessageListener("do-logs", function () {
+-          for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
+-            content.console.log('damp', i+1, content);
+-          }
+-        });
+-      }`
+-    ) + ")()", true);
+-
+-    // Kick off the logging
+-    messageManager.sendAsyncMessage("do-logs");
+-
+-    let test = this.runTest("console.bulklog");
+-    await allMessagesReceived;
+-    test.done();
+-
+-    await this.closeToolbox();
+-    await this.testTeardown();
+-  },
+-
+-  // Log a stream of console messages, 1 per rAF.  Then record the average
+-  // time per rAF.  The idea is that the console being slow can slow down
+-  // content (i.e. Bug 1237368).
+-  async _consoleStreamLoggingTest() {
+-    let TOTAL_MESSAGES = 100;
+-    let tab = await this.testSetup(SIMPLE_URL);
+-    let messageManager = tab.linkedBrowser.messageManager;
+-    await this.openToolbox("webconsole");
+-
+-    // Load a frame script using a data URI so we can do logs
+-    // from the page.  So this is running in content.
+-    messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-      `function () {
+-        let count = 0;
+-        let startTime = content.performance.now();
+-        function log() {
+-          if (++count < ${TOTAL_MESSAGES}) {
+-            content.document.querySelector("h1").textContent += count + "\\n";
+-            content.console.log('damp', count,
+-                                content,
+-                                content.document,
+-                                content.document.body,
+-                                content.document.documentElement,
+-                                new Array(100).join(" DAMP? DAMP! "));
+-            content.requestAnimationFrame(log);
+-          } else {
+-            let avgTime = (content.performance.now() - startTime) / ${TOTAL_MESSAGES};
+-            sendSyncMessage("done", Math.round(avgTime));
+-          }
+-        }
+-        log();
+-      }`
+-    ) + ")()", true);
+-
+-    let avgTime = await new Promise(resolve => {
+-      messageManager.addMessageListener("done", (e) => {
+-        resolve(e.data);
+-      });
+-    });
+-
+-    this._results.push({
+-      name: "console.streamlog",
+-      value: avgTime
+-    });
+-
+-    await this.closeToolbox();
+-    await this.testTeardown();
+-  },
+-
+-  async _consoleObjectExpansionTest() {
+-    let tab = await this.testSetup(SIMPLE_URL);
+-    let messageManager = tab.linkedBrowser.messageManager;
+-    let toolbox = await this.openToolbox("webconsole");
+-    let webconsole = toolbox.getPanel("webconsole");
+-
+-    // Resolve once the first message is received.
+-    let onMessageReceived = new Promise(resolve => {
+-      function receiveMessages(messages) {
+-        for (let m of messages) {
+-          resolve(m);
+-        }
+-      }
+-      webconsole.hud.ui.once("new-messages", receiveMessages);
+-    });
+-
+-    // Load a frame script using a data URI so we can do logs
+-    // from the page.
+-    messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-      `function () {
+-        addMessageListener("do-dir", function () {
+-          content.console.dir(Array.from({length:1000}).reduce((res, _, i)=> {
+-            res["item_" + i] = i;
+-            return res;
+-          }, {}));
+-        });
+-      }`
+-    ) + ")()", true);
+-
+-    // Kick off the logging
+-    messageManager.sendAsyncMessage("do-dir");
+-
+-    let test = this.runTest("console.objectexpand");
+-    await onMessageReceived;
+-    const tree = webconsole.hud.ui.outputNode.querySelector(".dir.message .tree");
+-    // The tree can be collapsed since the properties are fetched asynchronously.
+-    if (tree.querySelectorAll(".node").length === 1) {
+-      // If this is the case, we wait for the properties to be fetched and displayed.
+-      await new Promise(resolve => {
+-        const observer = new MutationObserver(mutations => {
+-          resolve(mutations);
+-          observer.disconnect();
+-        });
+-        observer.observe(tree, {
+-          childList: true
+-        });
+-      });
+-    }
+-
+-    test.done();
+-
+-    await this.closeToolboxAndLog("console.objectexpanded", toolbox);
+-    await this.testTeardown();
+-  },
+-
+-async _consoleOpenWithCachedMessagesTest() {
+-  let TOTAL_MESSAGES = 100;
+-  let tab = await this.testSetup(SIMPLE_URL);
+-
+-  // Load a frame script using a data URI so we can do logs
+-  // from the page.  So this is running in content.
+-  tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(`
+-    function () {
+-      for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
+-        content.console.log('damp', i+1, content);
+-      }
+-    }`
+-  ) + ")()", true);
+-
+-  await this.openToolboxAndLog("console.openwithcache", "webconsole");
+-
+-  await this.closeToolbox();
+-  await this.testTeardown();
+-},
+-
+-  /**
+-   * Measure the time necessary to perform successive childList mutations in the content
+-   * page and update the markup-view accordingly.
+-   */
+-  async _inspectorMutationsTest() {
+-    let tab = await this.testSetup(SIMPLE_URL);
+-    let messageManager = tab.linkedBrowser.messageManager;
+-    let toolbox = await this.openToolbox("inspector");
+-    let inspector = toolbox.getPanel("inspector");
+-
+-    // Test with n=LIMIT mutations, with t=DELAY ms between each one.
+-    const LIMIT = 100;
+-    const DELAY = 5;
+-
+-    messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-      `function () {
+-        const LIMIT = ${LIMIT};
+-        addMessageListener("start-mutations-test", function () {
+-          let addElement = function(index) {
+-            if (index == LIMIT) {
+-              // LIMIT was reached, stop adding elements.
+-              return;
+-            }
+-            let div = content.document.createElement("div");
+-            content.document.body.appendChild(div);
+-            content.setTimeout(() => addElement(index + 1), ${DELAY});
+-          };
+-          addElement(0);
+-        });
+-      }`
+-    ) + ")()", false);
+-
+-    let test = this.runTest("inspector.mutations");
+-
+-    await new Promise(resolve => {
+-      let childListMutationsCounter = 0;
+-      inspector.on("markupmutation", (evt, mutations) => {
+-        let childListMutations = mutations.filter(m => m.type === "childList");
+-        childListMutationsCounter += childListMutations.length;
+-        if (childListMutationsCounter === LIMIT) {
+-          // Wait until we received exactly n=LIMIT mutations in the markup view.
+-          resolve();
+-        }
+-      });
+-
+-      messageManager.sendAsyncMessage("start-mutations-test");
+-    });
+-
+-    test.done();
+-
+-    await this.closeToolbox();
+-    await this.testTeardown();
+-  },
+-
+-  /**
+-   * Measure the time to open toolbox on the inspector with the layout tab selected.
+-   */
+-  async _inspectorLayoutTest() {
+-    let tab = await this.testSetup(SIMPLE_URL);
+-    let messageManager = tab.linkedBrowser.messageManager;
+-
+-    // Backup current sidebar tab preference
+-    let sidebarTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
+-
+-    // Set layoutview as the current inspector sidebar tab.
+-    Services.prefs.setCharPref("devtools.inspector.activeSidebar", "layoutview");
+-
+-    // Setup test page. It is a simple page containing 5000 regular nodes and 10 grid
+-    // containers.
+-    await new Promise(resolve => {
+-      messageManager.addMessageListener("setup-test-done", resolve);
+-
+-      const NODES = 5000;
+-      const GRID_NODES = 10;
+-      messageManager.loadFrameScript("data:,(" + encodeURIComponent(
+-        `function () {
+-          let div = content.document.createElement("div");
+-          div.innerHTML =
+-            new Array(${NODES}).join("<div></div>") +
+-            new Array(${GRID_NODES}).join("<div style='display:grid'></div>");
+-          content.document.body.appendChild(div);
+-          sendSyncMessage("setup-test-done");
+-        }`
+-      ) + ")()", false);
+-    });
+-
+-    // Record the time needed to open the toolbox.
+-    let test = this.runTest("inspector.layout.open");
+-    await this.openToolbox("inspector");
+-    test.done();
+-
+-    await this.closeToolbox();
+-
+-    // Restore sidebar tab preference.
+-    Services.prefs.setCharPref("devtools.inspector.activeSidebar", sidebarTab);
+-
+-    await this.testTeardown();
+-  },
+-
+-  takeCensus(label) {
+-    let test = this.runTest(label + ".takeCensus");
+-
+-    this._snapshot.takeCensus({
+-      breakdown: {
+-        by: "coarseType",
+-        objects: {
+-          by: "objectClass",
+-          then: { by: "count", bytes: true, count: true },
+-          other: { by: "count", bytes: true, count: true }
+-        },
+-        strings: {
+-          by: "internalType",
+-          then: { by: "count", bytes: true, count: true }
+-        },
+-        scripts: {
+-          by: "internalType",
+-          then: { by: "count", bytes: true, count: true }
+-        },
+-        other: {
+-          by: "internalType",
+-          then: { by: "count", bytes: true, count: true }
+-        }
+-      }
+-    });
+-
+-    test.done();
+-
+-    return Promise.resolve();
+-  },
+-
+-  /**
+-   * Wait for any pending paint.
+-   * The tool may have touched the DOM elements at the very end of the current test.
+-   * We should ensure waiting for the reflow related to these changes.
+-   */
+-  async waitForPendingPaints(toolbox) {
+-    let panel = toolbox.getCurrentPanel();
+-    // All panels have its own way of exposing their window object...
+-    let window = panel.panelWin || panel._frameWindow || panel.panelWindow;
+-
+-    let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
+-                      .getInterface(Ci.nsIDOMWindowUtils);
+-    window.performance.mark("pending paints.start");
+-    while (utils.isMozAfterPaintPending) {
+-      await new Promise(done => {
+-        window.addEventListener("MozAfterPaint", function listener() {
+-          window.performance.mark("pending paint");
+-          done();
+-        }, { once: true });
+-      });
+-    }
+-    window.performance.measure("pending paints", "pending paints.start");
+-  },
+-
+-  async openToolboxAndLog(name, tool, onLoad) {
+-    dump("Open toolbox on '" + name + "'\n");
+-    let test = this.runTest(name + ".open.DAMP");
+-    let toolbox = await this.openToolbox(tool, onLoad);
+-    test.done();
+-
+-    test = this.runTest(name + ".open.settle.DAMP");
+-    await this.waitForPendingPaints(toolbox);
+-    test.done();
+-
+-    // Force freeing memory after toolbox open as it creates a lot of objects
+-    // and for complex documents, it introduces a GC that runs during 'reload' test.
+-    await garbageCollect();
+-
+-    return toolbox;
+-  },
+-
+-  async closeToolboxAndLog(name, toolbox) {
+-    let { target } = toolbox;
+-    dump("Close toolbox on '" + name + "'\n");
+-    await target.client.waitForRequestsToSettle();
+-
+-    let test = this.runTest(name + ".close.DAMP");
+-    await gDevTools.closeToolbox(target);
+-    test.done();
+-  },
+-
+-  async reloadPageAndLog(name, toolbox, onReload) {
+-    dump("Reload page on '" + name + "'\n");
+-    let test = this.runTest(name + ".reload.DAMP");
+-    await this.reloadPage(onReload);
+-    test.done();
+-
+-    test = this.runTest(name + ".reload.settle.DAMP");
+-    await this.waitForPendingPaints(toolbox);
+-    test.done();
+-  },
+-
+-  async exportHar(label, toolbox) {
+-    let test = this.runTest(label + ".exportHar");
+-
+-    // Export HAR from the Network panel.
+-    await toolbox.getHARFromNetMonitor();
+-
+-    test.done();
+-  },
+-
+-  async _coldInspectorOpen() {
+-    await this.testSetup(SIMPLE_URL);
+-    await this.openToolboxAndLog("cold.inspector", "inspector");
+-    await this.closeToolbox();
+-    await this.testTeardown();
+-  },
+-
+-  async _panelsInBackgroundReload() {
+-    await this.testSetup(PANELS_IN_BACKGROUND);
+-
+-    // Make sure the Console and Network panels are initialized
+-    let toolbox = await this.openToolbox("webconsole");
+-    let monitor = await toolbox.selectTool("netmonitor");
+-
+-    // Select the options panel to make both the Console and Network
+-    // panel be in background.
+-    // Options panel should not do anything on page reload.
+-    await toolbox.selectTool("options");
+-
+-    // Reload the page and wait for all HTTP requests
+-    // to finish (1 doc + 600 XHRs).
+-    let payloadReady = this.waitForPayload(601, monitor.panelWin);
+-    await this.reloadPageAndLog("panelsInBackground", toolbox);
+-    await payloadReady;
+-
+-    // Clean up
+-    await this.closeToolbox();
+-    await this.testTeardown();
+-  },
+-
+-  waitForPayload(count, panelWin) {
+-    return new Promise(resolve => {
+-      let payloadReady = 0;
+-
+-      function onPayloadReady(_, id) {
+-        payloadReady++;
+-        maybeResolve();
+-      }
+-
+-      function maybeResolve() {
+-        if (payloadReady >= count) {
+-          panelWin.off(EVENTS.PAYLOAD_READY, onPayloadReady);
+-          resolve();
+-        }
+-      }
+-
+-      panelWin.on(EVENTS.PAYLOAD_READY, onPayloadReady);
+-    });
+-  },
+-
+-  async reloadInspectorAndLog(label, toolbox) {
+-    let onReload = async function() {
+-      let inspector = toolbox.getPanel("inspector");
+-      // First wait for markup view to be loaded against the new root node
+-      await inspector.once("new-root");
+-      // Then wait for inspector to be updated
+-      await inspector.once("inspector-updated");
+-    };
+-    await this.reloadPageAndLog(label + ".inspector", toolbox, onReload);
+-  },
+-
+-  async customInspector() {
+-    let url = CUSTOM_URL.replace(/\$TOOL/, "inspector");
+-    await this.testSetup(url);
+-    let toolbox = await this.openToolboxAndLog("custom.inspector", "inspector");
+-
+-    await this.reloadInspectorAndLog("custom", toolbox);
+-    await this.selectNodeWithManyRulesAndLog(toolbox);
+-    await this.closeToolboxAndLog("custom.inspector", toolbox);
+-    await this.testTeardown();
+-  },
+-
+-  /**
+-   * Measure the time necessary to select a node and display the rule view when many rules
+-   * match the element.
+-   */
+-  async selectNodeWithManyRulesAndLog(toolbox) {
+-    let inspector = toolbox.getPanel("inspector");
+-
+-    // Local helper to select a node front and wait for the ruleview to be refreshed.
+-    let selectNodeFront = (nodeFront) => {
+-      let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
+-      inspector.selection.setNodeFront(nodeFront);
+-      return onRuleViewRefreshed;
+-    };
+-
+-    let initialNodeFront = inspector.selection.nodeFront;
+-
+-    // Retrieve the node front for the test node.
+-    let root = await inspector.walker.getRootNode();
+-    let referenceNodeFront = await inspector.walker.querySelector(root, ".no-css-rules");
+-    let testNodeFront = await inspector.walker.querySelector(root, ".many-css-rules");
+-
+-    // Select test node and measure the time to display the rule view with many rules.
+-    dump("Selecting .many-css-rules test node front\n");
+-    let test = this.runTest("custom.inspector.manyrules.selectnode");
+-    await selectNodeFront(testNodeFront);
+-    test.done();
+-
+-    // Select reference node and measure the time to empty the rule view.
+-    dump("Move the selection to a node with no rules\n");
+-    test = this.runTest("custom.inspector.manyrules.deselectnode");
+-    await selectNodeFront(referenceNodeFront);
+-    test.done();
+-
+-    await selectNodeFront(initialNodeFront);
+-  },
+-
+-  async openDebuggerAndLog(label, { expectedSources, selectedFile, expectedText }) {
+-   const onLoad = async (toolbox, panel) => {
+-    const dbg = await debuggerHelper.createContext(panel);
+-    await debuggerHelper.waitForSources(dbg, expectedSources);
+-    await debuggerHelper.selectSource(dbg, selectedFile);
+-    await debuggerHelper.waitForText(dbg, selectedFile, expectedText);
+-    await debuggerHelper.waitForMetaData(dbg);
+-   };
+-   const toolbox = await this.openToolboxAndLog(label + ".jsdebugger", "jsdebugger", onLoad);
+-   return toolbox;
+-  },
+-
+-  async pauseDebuggerAndLog(tab, toolbox, { testFunction }) {
+-    const panel = await toolbox.getPanelWhenReady("jsdebugger");
+-    const dbg = await debuggerHelper.createContext(panel);
+-    const pauseLocation = { line: 22, file: "App.js" };
+-
+-    dump("Pausing debugger\n");
+-    let test = this.runTest("custom.jsdebugger.pause.DAMP");
+-    await debuggerHelper.pauseDebugger(dbg, tab, testFunction, pauseLocation);
+-    test.done();
+-
+-    await debuggerHelper.removeBreakpoints(dbg);
+-    await debuggerHelper.resume(dbg);
+-    await garbageCollect();
+-  },
+-
+-  async stepDebuggerAndLog(tab, toolbox, { testFunction }) {
+-    const panel = await toolbox.getPanelWhenReady("jsdebugger");
+-    const dbg = await debuggerHelper.createContext(panel);
+-    const stepCount = 2;
+-
+-    /*
+-     * Each Step test has a max step count of at least 200;
+-     * see https://github.com/codehag/debugger-talos-example/blob/master/src/ and the specific test
+-     * file for more information
+-     */
+-
+-    const stepTests = [
+-      {
+-        location: { line: 10194, file: "step-in-test.js" },
+-        key: "stepIn"
+-      },
+-      {
+-        location: { line: 16, file: "step-over-test.js" },
+-        key: "stepOver"
+-      },
+-      {
+-        location: { line: 998, file: "step-out-test.js" },
+-        key: "stepOut"
+-      }
+-    ];
+-
+-    for (const stepTest of stepTests) {
+-      await debuggerHelper.pauseDebugger(dbg, tab, testFunction, stepTest.location);
+-      const test = this.runTest(`custom.jsdebugger.${stepTest.key}.DAMP`);
+-      for (let i = 0; i < stepCount; i++) {
+-        await debuggerHelper.step(dbg, stepTest.key);
+-      }
+-      test.done();
+-      await debuggerHelper.removeBreakpoints(dbg);
+-      await debuggerHelper.resume(dbg);
+-      await garbageCollect();
+-    }
+-  },
+-
+-  async reloadDebuggerAndLog(label, toolbox, { expectedSources, selectedFile, expectedText }) {
+-    const onReload = async () => {
+-      const panel = await toolbox.getPanelWhenReady("jsdebugger");
+-      const dbg = await debuggerHelper.createContext(panel);
+-      await debuggerHelper.waitForDispatch(dbg, "NAVIGATE");
+-      await debuggerHelper.waitForSources(dbg, expectedSources);
+-      await debuggerHelper.waitForText(dbg, selectedFile, expectedText);
+-      await debuggerHelper.waitForMetaData(dbg);
+-    };
+-    await this.reloadPageAndLog(`${label}.jsdebugger`, toolbox, onReload);
+-  },
+-
+-  async customDebugger() {
+-    const label = "custom";
+-    let url = CUSTOM_URL.replace(/\$TOOL/, "debugger");
+-
+-    const tab = await this.testSetup(url);
+-    const debuggerTestData = {
+-      expectedSources: 7,
+-      testFunction: "window.hitBreakpoint()",
+-      selectedFile: "App.js",
+-      expectedText: "import React, { Component } from 'react';"
+-    };
+-    const toolbox = await this.openDebuggerAndLog(label, debuggerTestData);
+-    await this.reloadDebuggerAndLog(label, toolbox, debuggerTestData);
+-
+-    // these tests are only run on custom.jsdebugger
+-    await this.pauseDebuggerAndLog(tab, toolbox, debuggerTestData);
+-    await this.stepDebuggerAndLog(tab, toolbox, debuggerTestData);
+-
+-    await this.closeToolboxAndLog("custom.jsdebugger", toolbox);
+-    await this.testTeardown();
+-  },
+-
+-  async reloadConsoleAndLog(label, toolbox, expectedMessages) {
+-    let onReload = async function() {
+-      let webconsole = toolbox.getPanel("webconsole");
+-      await new Promise(done => {
+-        let messages = 0;
+-        let receiveMessages = () => {
+-          if (++messages == expectedMessages) {
+-            webconsole.hud.ui.off("new-messages", receiveMessages);
+-            done();
+-          }
+-        };
+-        webconsole.hud.ui.on("new-messages", receiveMessages);
+-      });
+-    };
+-    await this.reloadPageAndLog(label + ".webconsole", toolbox, onReload);
+-  },
+-
+-  async customConsole() {
+-    // These numbers controls the number of console api calls we do in the test
+-    let sync = 250, stream = 250, async = 250;
+-    let params = `?sync=${sync}&stream=${stream}&async=${async}`;
+-    let url = CUSTOM_URL.replace(/\$TOOL/, "console") + params;
+-    await this.testSetup(url);
+-    let toolbox = await this.openToolboxAndLog("custom.webconsole", "webconsole");
+-    await this.reloadConsoleAndLog("custom", toolbox, sync + stream + async);
+-    await this.closeToolboxAndLog("custom.webconsole", toolbox);
+-    await this.testTeardown();
+-  },
+-
+-  _getToolLoadingTests(url, label, {
+-    expectedMessages,
+-    expectedRequests,
+-    debuggerTestData
+-  }) {
+-    let tests = {
+-      async inspector() {
+-        await this.testSetup(url);
+-        let toolbox = await this.openToolboxAndLog(label + ".inspector", "inspector");
+-        await this.reloadInspectorAndLog(label, toolbox);
+-        await this.closeToolboxAndLog(label + ".inspector", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async webconsole() {
+-        await this.testSetup(url);
+-        let toolbox = await this.openToolboxAndLog(label + ".webconsole", "webconsole");
+-        await this.reloadConsoleAndLog(label, toolbox, expectedMessages);
+-        await this.closeToolboxAndLog(label + ".webconsole", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async debugger() {
+-        await this.testSetup(url);
+-        let toolbox = await this.openDebuggerAndLog(label, debuggerTestData);
+-        await this.reloadDebuggerAndLog(label, toolbox, debuggerTestData);
+-        await this.closeToolboxAndLog(label + ".jsdebugger", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async styleeditor() {
+-        await this.testSetup(url);
+-        const toolbox = await this.openToolboxAndLog(label + ".styleeditor", "styleeditor");
+-        await this.reloadPageAndLog(label + ".styleeditor", toolbox);
+-        await this.closeToolboxAndLog(label + ".styleeditor", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async performance() {
+-        await this.testSetup(url);
+-        const toolbox = await this.openToolboxAndLog(label + ".performance", "performance");
+-        await this.reloadPageAndLog(label + ".performance", toolbox);
+-        await this.closeToolboxAndLog(label + ".performance", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async netmonitor() {
+-        await this.testSetup(url);
+-        const toolbox = await this.openToolboxAndLog(label + ".netmonitor", "netmonitor");
+-        const requestsDone = this.waitForNetworkRequests(label + ".netmonitor", toolbox, expectedRequests);
+-        await this.reloadPageAndLog(label + ".netmonitor", toolbox);
+-        await requestsDone;
+-        await this.exportHar(label + ".netmonitor", toolbox);
+-        await this.closeToolboxAndLog(label + ".netmonitor", toolbox);
+-        await this.testTeardown();
+-      },
+-
+-      async saveAndReadHeapSnapshot() {
+-        await this.testSetup(url);
+-        const toolbox = await this.openToolboxAndLog(label + ".memory", "memory");
+-        await this.reloadPageAndLog(label + ".memory", toolbox);
+-        await this.saveHeapSnapshot(label);
+-        await this.readHeapSnapshot(label);
+-        await this.takeCensus(label);
+-        await this.closeToolboxAndLog(label + ".memory", toolbox);
+-        await this.testTeardown();
+-      },
+-    };
+-    // Prefix all tests with the page type (simple or complicated)
+-    for (let name in tests) {
+-      tests[label + "." + name] = tests[name];
+-      delete tests[name];
+-    }
+-    return tests;
+-  },
+-
+   async testSetup(url) {
+     let tab = await this.addTab(url);
+     await new Promise(resolve => {
+       setTimeout(resolve, this._config.rest);
+     });
+     return tab;
+   },
+ 
+-  async testTeardown(url) {
++  async testTeardown() {
+     this.closeCurrentTab();
+ 
+     // Force freeing memory now so that it doesn't happen during the next test
+-    await garbageCollect();
++    await this.garbageCollect();
+ 
+-    this._nextCommand();
++    this._runNextTest();
+   },
+ 
+   // Everything below here are common pieces needed for the test runner to function,
+   // just copy and pasted from Tart with /s/TART/DAMP
+ 
+   _win: undefined,
+   _dampTab: undefined,
+   _results: [],
+   _config: {subtests: [], repeat: 1, rest: 100},
+-  _nextCommandIx: 0,
+-  _commands: [],
++  _nextTestIndex: 0,
++  _tests: [],
+   _onSequenceComplete: 0,
+-  _nextCommand() {
+-    if (this._nextCommandIx >= this._commands.length) {
++  _runNextTest() {
++    if (this._nextTestIndex >= this._tests.length) {
+       this._onSequenceComplete();
+       return;
+     }
+-    this._commands[this._nextCommandIx++].call(this);
++
++    let test = this._tests[this._nextTestIndex++];
++
++    dump("Loading test : " + test + "\n");
++    let testMethod = require("chrome://damp/content/tests/" + test);
++
++    dump("Executing test : " + test + "\n");
++    testMethod();
+   },
+   // Each command at the array a function which must call nextCommand once it's done
+-  _doSequence(commands, onComplete) {
+-    this._commands = commands;
++  _doSequence(tests, onComplete) {
++    this._tests = tests;
+     this._onSequenceComplete = onComplete;
+     this._results = [];
+-    this._nextCommandIx = 0;
++    this._nextTestIndex = 0;
+ 
+-    this._nextCommand();
++    this._runNextTest();
+   },
+ 
+   _log(str) {
+     if (window.MozillaFileLogger && window.MozillaFileLogger.log)
+       window.MozillaFileLogger.log(str);
+ 
+     window.dump(str);
+   },
+@@ -1214,74 +231,26 @@ async _consoleOpenWithCachedMessagesTest
+     this._reportAllResults();
+     this._win.gBrowser.selectedTab = this._dampTab;
+ 
+     if (this._onTestComplete) {
+       this._onTestComplete(JSON.parse(JSON.stringify(this._results))); // Clone results
+     }
+   },
+ 
+-  /**
+-   * Start monitoring all incoming update events about network requests and wait until
+-   * a complete info about all requests is received. (We wait for the timings info
+-   * explicitly, because that's always the last piece of information that is received.)
+-   *
+-   * This method is designed to wait for network requests that are issued during a page
+-   * load, when retrieving page resources (scripts, styles, images). It has certain
+-   * assumptions that can make it unsuitable for other types of network communication:
+-   * - it waits for at least one network request to start and finish before returning
+-   * - it waits only for request that were issued after it was called. Requests that are
+-   *   already in mid-flight will be ignored.
+-   * - the request start and end times are overlapping. If a new request starts a moment
+-   *   after the previous one was finished, the wait will be ended in the "interim"
+-   *   period.
+-   * @returns a promise that resolves when the wait is done.
+-   */
+-  waitForAllRequestsFinished(expectedRequests) {
+-    let tab = getActiveTab(getMostRecentBrowserWindow());
+-    let target = TargetFactory.forTab(tab);
+-    let toolbox = gDevTools.getToolbox(target);
+-    let window = toolbox.getCurrentPanel().panelWin;
+-
+-    return new Promise(resolve => {
+-      // Explicitly waiting for specific number of requests arrived
+-      let payloadReady = 0;
+-      let timingsUpdated = 0;
+-
+-      function onPayloadReady(_, id) {
+-        payloadReady++;
+-        maybeResolve();
+-      }
+-
+-      function onTimingsUpdated(_, id) {
+-        timingsUpdated++;
+-        maybeResolve();
+-      }
+-
+-      function maybeResolve() {
+-        // Have all the requests finished yet?
+-        if (payloadReady >= expectedRequests && timingsUpdated >= expectedRequests) {
+-          // All requests are done - unsubscribe from events and resolve!
+-          window.off(EVENTS.PAYLOAD_READY, onPayloadReady);
+-          window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
+-          resolve();
+-        }
+-      }
+-
+-      window.on(EVENTS.PAYLOAD_READY, onPayloadReady);
+-      window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
+-    });
+-  },
+-
+   startAllocationTracker() {
+     const { allocationTracker } = require("devtools/shared/test-helpers/allocation-tracker");
+     return allocationTracker();
+   },
+ 
+   startTest(doneCallback, config) {
++    dump("Initialize the head file with a reference to this DAMP instance\n");
++    let head = require("chrome://damp/content/tests/head.js");
++    head.initialize(this);
++
+     this._onTestComplete = function(results) {
+       TalosParentProfiler.pause("DAMP - end");
+       doneCallback(results);
+     };
+     this._config = config;
+ 
+     this._win = Services.wm.getMostRecentWindow("navigator:browser");
+     this._dampTab = this._win.gBrowser.selectedTab;
+@@ -1290,55 +259,51 @@ async _consoleOpenWithCachedMessagesTest
+     TalosParentProfiler.resume("DAMP - start");
+ 
+     let tests = {};
+ 
+     // Run cold test only once
+     let topWindow = getMostRecentBrowserWindow();
+     if (!topWindow.coldRunDAMP) {
+       topWindow.coldRunDAMP = true;
+-      tests["cold.inspector"] = this._coldInspectorOpen;
++      tests["cold.inspector"] = "inspector/cold-open.js";
+     }
+ 
+-    tests["panelsInBackground.reload"] = this._panelsInBackgroundReload;
++    tests["panelsInBackground.reload"] = "toolbox/panels-in-background.js";
+ 
+     // Run all tests against "simple" document
+-    Object.assign(tests, this._getToolLoadingTests(SIMPLE_URL, "simple", {
+-      expectedMessages: 1,
+-      expectedRequests: 1,
+-      debuggerTestData: {
+-        expectedSources: 1,
+-        selectedFile: "simple.html",
+-        expectedText: "This is a simple page"
+-      }
+-    }));
++    tests["simple.inspector"] = "inspector/simple.js";
++    tests["simple.webconsole"] = "webconsole/simple.js";
++    tests["simple.debugger"] = "debugger/simple.js";
++    tests["simple.styleeditor"] = "styleeditor/simple.js";
++    tests["simple.performance"] = "performance/simple.js";
++    tests["simple.netmonitor"] = "netmonitor/simple.js";
++    tests["simple.saveAndReadHeapSnapshot"] = "memory/simple.js";
+ 
+     // Run all tests against "complicated" document
+-    Object.assign(tests, this._getToolLoadingTests(COMPLICATED_URL, "complicated", {
+-      expectedMessages: 7,
+-      expectedRequests: 280,
+-      debuggerTestData: {
+-        expectedSources: 14,
+-        selectedFile: "ga.js",
+-        expectedText: "Math;function ga(a,b){return a.name=b}"
+-      }
+-    }));
++    tests["complicated.inspector"] = "inspector/complicated.js";
++    tests["complicated.webconsole"] = "webconsole/complicated.js";
++    tests["complicated.debugger"] = "debugger/complicated.js";
++    tests["complicated.styleeditor"] = "styleeditor/complicated.js";
++    tests["complicated.performance"] = "performance/complicated.js";
++    tests["complicated.netmonitor"] = "netmonitor/complicated.js";
++    tests["complicated.saveAndReadHeapSnapshot"] = "memory/complicated.js";
+ 
+     // Run all tests against a document specific to each tool
+-    tests["custom.inspector"] = this.customInspector;
+-    tests["custom.debugger"] = this.customDebugger;
+-    tests["custom.webconsole"] = this.customConsole;
++    tests["custom.inspector"] = "inspector/custom.js";
++    tests["custom.debugger"] = "debugger/custom.js";
++    tests["custom.webconsole"] = "webconsole/custom.js";
+ 
+     // Run individual tests covering a very precise tool feature.
+-    tests["console.bulklog"] = this._consoleBulkLoggingTest;
+-    tests["console.streamlog"] = this._consoleStreamLoggingTest;
+-    tests["console.objectexpand"] = this._consoleObjectExpansionTest;
+-    tests["console.openwithcache"] = this._consoleOpenWithCachedMessagesTest;
+-    tests["inspector.mutations"] = this._inspectorMutationsTest;
+-    tests["inspector.layout"] = this._inspectorLayoutTest;
++    tests["console.bulklog"] = "webconsole/bulklog.js";
++    tests["console.streamlog"] = "webconsole/streamlog.js";
++    tests["console.objectexpand"] = "webconsole/objectexpand.js";
++    tests["console.openwithcache"] = "webconsole/openwithcache.js";
++    tests["inspector.mutations"] = "inspector/mutations.js";
++    tests["inspector.layout"] = "inspector/layout.js";
+     // ⚠  Adding new individual tests slows down DAMP execution ⚠
+     // ⚠  Consider contributing to custom.${tool} rather than adding isolated tests ⚠
+     // ⚠  See http://docs.firefox-dev.tools/tests/writing-perf-tests.html ⚠
+ 
+     // Filter tests via `./mach --subtests filter` command line argument
+     let filter = Services.prefs.getCharPref("talos.subtests", "");
+     if (filter) {
+       for (let name in tests) {
+@@ -1355,21 +320,22 @@ async _consoleOpenWithCachedMessagesTest
+ 
+     // Construct the sequence array while filtering tests
+     let sequenceArray = [];
+     for (var i in config.subtests) {
+       for (var r = 0; r < config.repeat; r++) {
+         if (!config.subtests[i] || !tests[config.subtests[i]]) {
+           continue;
+         }
++
+         sequenceArray.push(tests[config.subtests[i]]);
+       }
+     }
+ 
+     // Free memory before running the first test, otherwise we may have a GC
+     // related to Firefox startup or DAMP setup during the first test.
+-    garbageCollect().then(() => {
++    this.garbageCollect().then(() => {
+       this._doSequence(sequenceArray, this._doneInternal);
+     }).catch(e => {
+       dump("Exception while running DAMP tests: " + e + "\n" + e.stack + "\n");
+     });
+   }
+ }
+diff --git a/testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/panels-in-background.html b/testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/index.html
+rename from testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/panels-in-background.html
+rename to testing/talos/talos/tests/devtools/addon/content/pages/custom/panels-in-background/index.html
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/.eslintrc.js b/testing/talos/talos/tests/devtools/addon/content/tests/.eslintrc.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/.eslintrc.js
+@@ -0,0 +1,33 @@
++"use strict";
++
++module.exports = {
++  "plugins": [
++    "react"
++  ],
++  "globals": {
++    "exports": true,
++    "isWorker": true,
++    "loader": true,
++    "module": true,
++    "reportError": true,
++    "require": true,
++  },
++  "rules": {
++    "no-unused-vars": ["error", {"args": "none", "vars": "all"}],
++    // These are the rules that have been configured so far to match the
++    // devtools coding style.
++
++    // Rules from the mozilla plugin
++    "mozilla/no-aArgs": "error",
++    "mozilla/no-cpows-in-tests": "error",
++    "mozilla/no-single-arg-cu-import": "error",
++    // See bug 1224289.
++    "mozilla/reject-importGlobalProperties": "error",
++    // devtools/shared/platform is special; see the README.md in that
++    // directory for details.  We reject requires using explicit
++    // subdirectories of this directory.
++    "mozilla/reject-some-requires": ["error", "^devtools/shared/platform/(chome|content)/"],
++    "mozilla/var-only-at-top-level": "error",
++    "mozilla/use-chromeutils-import": ["error", {allowCu: true}]
++  }
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/debugger/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/complicated.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { closeToolboxAndLog, testSetup, testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++const { openDebuggerAndLog, reloadDebuggerAndLog } = require("chrome://damp/content/tests/debugger/debugger-helpers");
++
++const EXPECTED = {
++  sources: 14,
++  file: "ga.js",
++  text: "Math;function ga(a,b){return a.name=b}"
++};
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++
++  let toolbox = await openDebuggerAndLog("complicated", EXPECTED);
++  await reloadDebuggerAndLog("complicated", toolbox, EXPECTED);
++  await closeToolboxAndLog("complicated.jsdebugger", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/debugger/custom.js b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/custom.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/custom.js
+@@ -0,0 +1,91 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { closeToolboxAndLog, garbageCollect, runTest, testSetup,
++        testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
++const { createContext, openDebuggerAndLog, pauseDebugger, reloadDebuggerAndLog,
++        removeBreakpoints, resume, step } = require("chrome://damp/content/tests/debugger/debugger-helpers");
++
++const EXPECTED = {
++  sources: 7,
++  file: "App.js",
++  text: "import React, { Component } from 'react';"
++};
++
++const EXPECTED_FUNCTION = "window.hitBreakpoint()";
++
++module.exports = async function() {
++  const tab = await testSetup(PAGES_BASE_URL + "custom/debugger/index.html");
++
++  const toolbox = await openDebuggerAndLog("custom", EXPECTED);
++  await reloadDebuggerAndLog("custom", toolbox, EXPECTED);
++
++  // these tests are only run on custom.jsdebugger
++  await pauseDebuggerAndLog(tab, toolbox, EXPECTED_FUNCTION);
++  await stepDebuggerAndLog(tab, toolbox, EXPECTED_FUNCTION);
++
++  await closeToolboxAndLog("custom.jsdebugger", toolbox);
++
++  await testTeardown();
++};
++
++async function pauseDebuggerAndLog(tab, toolbox, testFunction) {
++  dump("Waiting for debugger panel\n");
++  const panel = await toolbox.getPanelWhenReady("jsdebugger");
++
++  dump("Creating context\n");
++  const dbg = await createContext(panel);
++
++  const pauseLocation = { line: 22, file: "App.js" };
++
++  dump("Pausing debugger\n");
++  let test = runTest("custom.jsdebugger.pause.DAMP");
++  await pauseDebugger(dbg, tab, testFunction, pauseLocation);
++  test.done();
++
++  await removeBreakpoints(dbg);
++  await resume(dbg);
++  await garbageCollect();
++}
++
++async function stepDebuggerAndLog(tab, toolbox, testFunction) {
++  const panel = await toolbox.getPanelWhenReady("jsdebugger");
++  const dbg = await createContext(panel);
++  const stepCount = 2;
++
++  /*
++   * Each Step test has a max step count of at least 200;
++   * see https://github.com/codehag/debugger-talos-example/blob/master/src/ and the specific test
++   * file for more information
++   */
++
++  const stepTests = [
++    {
++      location: { line: 10194, file: "step-in-test.js" },
++      key: "stepIn"
++    },
++    {
++      location: { line: 16, file: "step-over-test.js" },
++      key: "stepOver"
++    },
++    {
++      location: { line: 998, file: "step-out-test.js" },
++      key: "stepOut"
++    }
++  ];
++
++  for (const stepTest of stepTests) {
++    await pauseDebugger(dbg, tab, testFunction, stepTest.location);
++    const test = runTest(`custom.jsdebugger.${stepTest.key}.DAMP`);
++    for (let i = 0; i < stepCount; i++) {
++      await step(dbg, stepTest.key);
++    }
++    test.done();
++    await removeBreakpoints(dbg);
++    await resume(dbg);
++    await garbageCollect();
++  }
++}
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/debugger-helpers.js
+@@ -0,0 +1,277 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, reloadPageAndLog } = require("chrome://damp/content/tests/head");
++
++/*
++ * These methods are used for working with debugger state changes in order
++ * to make it easier to manipulate the ui and test different behavior. These
++ * methods roughly reflect those found in debugger/new/test/mochi/head.js with
++ * a few exceptions. The `dbg` object is not exactly the same, and the methods
++ * have been simplified. We may want to consider unifying them in the future
++ */
++
++const DEBUGGER_POLLING_INTERVAL = 50;
++
++function waitForState(dbg, predicate, msg) {
++  return new Promise(resolve => {
++    if (msg) {
++      dump(`Waiting for state change: ${msg}\n`);
++    }
++    if (predicate(dbg.store.getState())) {
++      if (msg) {
++        dump(`Finished waiting for state change: ${msg}\n`);
++      }
++      return resolve();
++    }
++
++    const unsubscribe = dbg.store.subscribe(() => {
++      if (predicate(dbg.store.getState())) {
++        if (msg) {
++          dump(`Finished waiting for state change: ${msg}\n`);
++        }
++        unsubscribe();
++        resolve();
++      }
++    });
++    return false;
++  });
++}
++
++function waitForDispatch(dbg, type) {
++  return new Promise(resolve => {
++    dbg.store.dispatch({
++      type: "@@service/waitUntil",
++      predicate: action => {
++        if (action.type === type) {
++          return action.status
++            ? action.status === "done" || action.status === "error"
++            : true;
++        }
++        return false;
++      },
++      run: (dispatch, getState, action) => {
++        resolve(action);
++      }
++    });
++  });
++}
++
++async function waitUntil(predicate, msg) {
++  if (msg) {
++    dump(`Waiting until: ${msg}\n`);
++  }
++  return new Promise(resolve => {
++    const timer = setInterval(() => {
++      if (predicate()) {
++        clearInterval(timer);
++        if (msg) {
++          dump(`Finished Waiting until: ${msg}\n`);
++        }
++        resolve();
++      }
++    }, DEBUGGER_POLLING_INTERVAL);
++  });
++}
++
++function findSource(dbg, url) {
++  const sources = dbg.selectors.getSources(dbg.getState());
++  return sources.find(s => (s.get("url") || "").includes(url));
++}
++
++function getCM(dbg) {
++  const el = dbg.win.document.querySelector(".CodeMirror");
++  return el.CodeMirror;
++}
++
++function waitForText(dbg, url, text) {
++  return waitUntil(() => {
++    // the welcome box is removed once text is displayed
++    const welcomebox = dbg.win.document.querySelector(".welcomebox");
++    if (welcomebox) {
++      return false;
++    }
++    const cm = getCM(dbg);
++    const editorText = cm.doc.getValue();
++    return editorText.includes(text);
++  }, "text is visible");
++}
++
++function waitForMetaData(dbg) {
++  return waitUntil(
++    () => {
++      const state = dbg.store.getState();
++      const source = dbg.selectors.getSelectedSource(state);
++      // wait for metadata -- this involves parsing the file to determine its type.
++      // if the object is empty, the data has not yet loaded
++      const metaData = dbg.selectors.getSourceMetaData(state, source.get("id"));
++      return !!Object.keys(metaData).length;
++    },
++    "has file metadata"
++  );
++}
++
++function waitForSources(dbg, expectedSources) {
++  const { selectors } = dbg;
++  function countSources(state) {
++    const sources = selectors.getSources(state);
++    return sources.size >= expectedSources;
++  }
++  return waitForState(dbg, countSources, "count sources");
++}
++
++async function waitForPaused(dbg) {
++  const onLoadedScope = waitForLoadedScopes(dbg);
++  const onStateChange =  waitForState(
++    dbg,
++    state => {
++      return dbg.selectors.getSelectedScope(state) && dbg.selectors.isPaused(state);
++    },
++  );
++  return Promise.all([onLoadedScope, onStateChange]);
++}
++
++async function waitForResumed(dbg) {
++  return waitForState(
++    dbg,
++    state => !dbg.selectors.isPaused(state)
++  );
++}
++
++async function waitForElement(dbg, name) {
++  await waitUntil(() => dbg.win.document.querySelector(name));
++  return dbg.win.document.querySelector(name);
++}
++
++async function waitForLoadedScopes(dbg) {
++  const element = ".scopes-list .tree-node[aria-level=\"1\"]";
++  return waitForElement(dbg, element);
++}
++
++function createContext(panel) {
++  const { store, selectors, actions } = panel.getVarsForTests();
++
++  return {
++    actions,
++    selectors,
++    getState: store.getState,
++    win: panel.panelWin,
++    store
++  };
++}
++exports.createContext = createContext;
++
++function selectSource(dbg, url) {
++  dump(`Selecting source: ${url}\n`);
++  const line = 1;
++  const source = findSource(dbg, url);
++  dbg.actions.selectLocation({ sourceId: source.get("id"), line });
++  return waitForState(
++    dbg,
++    state => {
++      const source = dbg.selectors.getSelectedSource(state);
++      const isLoaded = source && source.get("loadedState") === "loaded";
++      if (!isLoaded) {
++        return false;
++      }
++
++      // wait for symbols -- a flat map of all named variables in a file -- to be calculated.
++      // this is a slow process and becomes slower the larger the file is
++      return dbg.selectors.hasSymbols(state, source.toJS());
++    },
++    "selected source"
++  );
++}
++
++function evalInContent(dbg, tab, testFunction) {
++  dump(`Run function in content process: ${testFunction}\n`);
++  // Load a frame script using a data URI so we can run a script
++  // inside of the content process and trigger debugger functionality
++  // as needed
++  const messageManager = tab.linkedBrowser.messageManager;
++  return messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++    `function () {
++        content.window.eval("${testFunction}");
++    }`
++  ) + ")()", true);
++}
++
++async function openDebuggerAndLog(label, expected) {
++  const onLoad = async (toolbox, panel) => {
++    const dbg = await createContext(panel);
++    await waitForSources(dbg, expected.sources);
++    await selectSource(dbg, expected.file);
++    await waitForText(dbg, expected.file, expected.text);
++    await waitForMetaData(dbg);
++  };
++
++  const toolbox = await openToolboxAndLog(label + ".jsdebugger", "jsdebugger", onLoad);
++  return toolbox;
++}
++exports.openDebuggerAndLog = openDebuggerAndLog;
++
++async function reloadDebuggerAndLog(label, toolbox, expected) {
++  const onReload = async () => {
++    const panel = await toolbox.getPanelWhenReady("jsdebugger");
++    const dbg = await createContext(panel);
++    await waitForDispatch(dbg, "NAVIGATE");
++    await waitForSources(dbg, expected.sources);
++    await waitForText(dbg, expected.file, expected.text);
++    await waitForMetaData(dbg);
++  };
++  await reloadPageAndLog(`${label}.jsdebugger`, toolbox, onReload);
++}
++exports.reloadDebuggerAndLog = reloadDebuggerAndLog;
++
++async function addBreakpoint(dbg, line, url) {
++  dump(`add breakpoint\n`);
++  const source = findSource(dbg, url);
++  const location = {
++    sourceId: source.get("id"),
++    line,
++    column: 0
++  };
++  const onDispatched = waitForDispatch(dbg, "ADD_BREAKPOINT");
++  dbg.actions.addBreakpoint(location);
++  return onDispatched;
++}
++exports.addBreakpoint = addBreakpoint;
++
++async function removeBreakpoints(dbg, line, url) {
++  dump(`remove all breakpoints\n`);
++  const breakpoints = dbg.selectors.getBreakpoints(dbg.getState());
++
++  const onBreakpointsCleared =  waitForState(
++    dbg,
++    state => !dbg.selectors.getBreakpoints(state).length
++  );
++  await dbg.actions.removeBreakpoints(breakpoints);
++  return onBreakpointsCleared;
++}
++exports.removeBreakpoints = removeBreakpoints;
++
++async function pauseDebugger(dbg, tab, testFunction, { line, file }) {
++  await addBreakpoint(dbg, line, file);
++  const onPaused = waitForPaused(dbg);
++  await evalInContent(dbg, tab, testFunction);
++  return onPaused;
++}
++exports.pauseDebugger = pauseDebugger;
++
++async function resume(dbg) {
++  const onResumed = waitForResumed(dbg);
++  dbg.actions.resume();
++  return onResumed;
++}
++exports.resume = resume;
++
++async function step(dbg, stepType) {
++  const resumed = waitForResumed(dbg);
++  dbg.actions[stepType]();
++  await resumed;
++  return waitForPaused(dbg);
++}
++exports.step = step;
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/debugger/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/debugger/simple.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { closeToolboxAndLog, testSetup, testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++const { openDebuggerAndLog, reloadDebuggerAndLog } = require("chrome://damp/content/tests/debugger/debugger-helpers");
++
++const EXPECTED = {
++  sources: 1,
++  file: "simple.html",
++  text: "This is a simple page"
++};
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++
++  let toolbox = await openDebuggerAndLog("simple", EXPECTED);
++  await reloadDebuggerAndLog("simple", toolbox, EXPECTED);
++  await closeToolboxAndLog("simple.jsdebugger", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/head.js b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/head.js
+@@ -0,0 +1,157 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++/**
++ * This helper contains the public API that can be used by DAMP tests.
++ */
++
++// eslint-disable-next-line mozilla/no-define-cc-etc
++const { Ci } = require("chrome");
++const Services = require("Services");
++const { gDevTools } = require("devtools/client/framework/devtools");
++const { TargetFactory } = require("devtools/client/framework/target");
++
++const webserver = Services.prefs.getCharPref("addon.test.damp.webserver");
++
++const PAGES_BASE_URL = webserver + "/tests/devtools/addon/content/pages/";
++
++exports.PAGES_BASE_URL = PAGES_BASE_URL;
++exports.SIMPLE_URL = PAGES_BASE_URL + "simple.html";
++exports.COMPLICATED_URL = webserver + "/tests/tp5n/bild.de/www.bild.de/index.html";
++
++let damp = null;
++/*
++ * This method should be called by js before starting the tests.
++ */
++exports.initialize = function(_damp) {
++  damp = _damp;
++};
++
++function garbageCollect() {
++  return damp.garbageCollect();
++}
++exports.garbageCollect = garbageCollect;
++
++function runTest(label) {
++  return damp.runTest(label);
++}
++exports.runTest = runTest;
++
++exports.testSetup = function(url) {
++  return damp.testSetup(url);
++};
++
++exports.testTeardown = function() {
++  return damp.testTeardown();
++};
++
++exports.logTestResult = function(name, value) {
++  damp._results.push(name, value);
++};
++
++function getBrowserWindow() {
++  return Services.wm.getMostRecentWindow("navigator:browser");
++}
++exports.getBrowserWindow = getBrowserWindow;
++
++function getActiveTab() {
++  return getBrowserWindow().gBrowser.selectedTab;
++}
++exports.getActiveTab = getActiveTab;
++
++exports.getToolbox = function() {
++  let tab = getActiveTab();
++  let target = TargetFactory.forTab(tab);
++  return gDevTools.getToolbox(target);
++};
++
++/**
++ * Wait for any pending paint.
++ * The tool may have touched the DOM elements at the very end of the current test.
++ * We should ensure waiting for the reflow related to these changes.
++ */
++async function waitForPendingPaints(toolbox) {
++  let panel = toolbox.getCurrentPanel();
++  // All panels have its own way of exposing their window object...
++  let window = panel.panelWin || panel._frameWindow || panel.panelWindow;
++
++  let utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
++                    .getInterface(Ci.nsIDOMWindowUtils);
++  window.performance.mark("pending paints.start");
++  while (utils.isMozAfterPaintPending) {
++    await new Promise(done => {
++      window.addEventListener("MozAfterPaint", function listener() {
++        window.performance.mark("pending paint");
++        done();
++      }, { once: true });
++    });
++  }
++  window.performance.measure("pending paints", "pending paints.start");
++}
++
++const openToolbox = async function(tool = "webconsole", onLoad) {
++  let tab = getActiveTab();
++  let target = TargetFactory.forTab(tab);
++  let onToolboxCreated = gDevTools.once("toolbox-created");
++  let showPromise = gDevTools.showToolbox(target, tool);
++  let toolbox = await onToolboxCreated;
++
++  if (typeof(onLoad) == "function") {
++    let panel = await toolbox.getPanelWhenReady(tool);
++    await onLoad(toolbox, panel);
++  }
++  await showPromise;
++
++  return toolbox;
++};
++exports.openToolbox = openToolbox;
++
++exports.closeToolbox =  async function() {
++  let tab = getActiveTab();
++  let target = TargetFactory.forTab(tab);
++  await target.client.waitForRequestsToSettle();
++  await gDevTools.closeToolbox(target);
++};
++
++exports.openToolboxAndLog = async function(name, tool, onLoad) {
++let test = runTest(name + ".open.DAMP");
++  let toolbox = await openToolbox(tool, onLoad);
++  test.done();
++
++  test = runTest(name + ".open.settle.DAMP");
++  await waitForPendingPaints(toolbox);
++  test.done();
++
++  // Force freeing memory after toolbox open as it creates a lot of objects
++  // and for complex documents, it introduces a GC that runs during 'reload' test.
++  await garbageCollect();
++
++  return toolbox;
++};
++
++exports.closeToolboxAndLog = async function(name, toolbox) {
++  let { target } = toolbox;
++  dump("Close toolbox on '" + name + "'\n");
++  await target.client.waitForRequestsToSettle();
++
++  let test = runTest(name + ".close.DAMP");
++  await gDevTools.closeToolbox(target);
++  test.done();
++};
++
++exports.reloadPageAndLog = async function(name, toolbox, onReload) {
++  dump("Reload page on '" + name + "'\n");
++  let test = runTest(name + ".reload.DAMP");
++  await damp.reloadPage(onReload);
++  test.done();
++
++  dump("Wait for pending paints on '" + name + "'\n");
++  test = runTest(name + ".reload.settle.DAMP");
++  await waitForPendingPaints(toolbox);
++  test.done();
++};
++
++dump("Required HEAD successfully\n");
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/cold-open.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/cold-open.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/cold-open.js
+@@ -0,0 +1,16 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolbox, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++// This simple test is only called once using the flag coldRun
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++  await openToolboxAndLog("cold.inspector", "inspector");
++  await closeToolbox();
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/complicated.js
+@@ -0,0 +1,19 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
++const { openToolboxAndLog, closeToolboxAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++
++  let toolbox = await openToolboxAndLog("complicated.inspector", "inspector");
++  await reloadInspectorAndLog("complicated", toolbox);
++  await closeToolboxAndLog("complicated.inspector", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/custom.js
+@@ -0,0 +1,56 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
++const { openToolboxAndLog, closeToolboxAndLog, runTest, testSetup,
++        testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(PAGES_BASE_URL + "custom/inspector/index.html");
++
++  let toolbox = await openToolboxAndLog("custom.inspector", "inspector");
++  await reloadInspectorAndLog("custom", toolbox);
++  await selectNodeWithManyRulesAndLog(toolbox);
++  await closeToolboxAndLog("custom.inspector", toolbox);
++
++  await testTeardown();
++};
++
++/**
++ * Measure the time necessary to select a node and display the rule view when many rules
++ * match the element.
++ */
++async function selectNodeWithManyRulesAndLog(toolbox) {
++  let inspector = toolbox.getPanel("inspector");
++
++  // Local helper to select a node front and wait for the ruleview to be refreshed.
++  let selectNodeFront = (nodeFront) => {
++    let onRuleViewRefreshed = inspector.once("rule-view-refreshed");
++    inspector.selection.setNodeFront(nodeFront);
++    return onRuleViewRefreshed;
++  };
++
++  let initialNodeFront = inspector.selection.nodeFront;
++
++  // Retrieve the node front for the test node.
++  let root = await inspector.walker.getRootNode();
++  let referenceNodeFront = await inspector.walker.querySelector(root, ".no-css-rules");
++  let testNodeFront = await inspector.walker.querySelector(root, ".many-css-rules");
++
++  // Select test node and measure the time to display the rule view with many rules.
++  dump("Selecting .many-css-rules test node front\n");
++  let test = runTest("custom.inspector.manyrules.selectnode");
++  await selectNodeFront(testNodeFront);
++  test.done();
++
++  // Select reference node and measure the time to empty the rule view.
++  dump("Move the selection to a node with no rules\n");
++  test = runTest("custom.inspector.manyrules.deselectnode");
++  await selectNodeFront(referenceNodeFront);
++  test.done();
++
++  await selectNodeFront(initialNodeFront);
++}
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/inspector-helpers.js
+@@ -0,0 +1,19 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { reloadPageAndLog } = require("chrome://damp/content/tests/head");
++
++exports.reloadInspectorAndLog = async function(label, toolbox) {
++  let onReload = async function() {
++    let inspector = toolbox.getPanel("inspector");
++    // First wait for markup view to be loaded against the new root node
++    await inspector.once("new-root");
++    // Then wait for inspector to be updated
++    await inspector.once("inspector-updated");
++  };
++
++  await reloadPageAndLog(label + ".inspector", toolbox, onReload);
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/layout.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/layout.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/layout.js
+@@ -0,0 +1,52 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const Services = require("Services");
++
++const { openToolbox, closeToolbox, runTest, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  let tab = await testSetup(SIMPLE_URL);
++  let messageManager = tab.linkedBrowser.messageManager;
++
++  // Backup current sidebar tab preference
++  let sidebarTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
++
++  // Set layoutview as the current inspector sidebar tab.
++  Services.prefs.setCharPref("devtools.inspector.activeSidebar", "layoutview");
++
++  // Setup test page. It is a simple page containing 5000 regular nodes and 10 grid
++  // containers.
++  await new Promise(resolve => {
++    messageManager.addMessageListener("setup-test-done", resolve);
++
++    const NODES = 5000;
++    const GRID_NODES = 10;
++    messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++      `function () {
++        let div = content.document.createElement("div");
++        div.innerHTML =
++          new Array(${NODES}).join("<div></div>") +
++          new Array(${GRID_NODES}).join("<div style='display:grid'></div>");
++        content.document.body.appendChild(div);
++        sendSyncMessage("setup-test-done");
++      }`
++    ) + ")()", false);
++  });
++
++  // Record the time needed to open the toolbox.
++  let test = runTest("inspector.layout.open");
++  await openToolbox("inspector");
++  test.done();
++
++  await closeToolbox();
++
++  // Restore sidebar tab preference.
++  Services.prefs.setCharPref("devtools.inspector.activeSidebar", sidebarTab);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/mutations.js
+@@ -0,0 +1,63 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolbox, closeToolbox, runTest, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++/**
++ * Measure the time necessary to perform successive childList mutations in the content
++ * page and update the markup-view accordingly.
++ */
++module.exports = async function() {
++  let tab = await testSetup(SIMPLE_URL);
++  let messageManager = tab.linkedBrowser.messageManager;
++
++  let toolbox = await openToolbox("inspector");
++  let inspector = toolbox.getPanel("inspector");
++
++  // Test with n=LIMIT mutations, with t=DELAY ms between each one.
++  const LIMIT = 100;
++  const DELAY = 5;
++
++  messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++    `function () {
++      const LIMIT = ${LIMIT};
++      addMessageListener("start-mutations-test", function () {
++        let addElement = function(index) {
++          if (index == LIMIT) {
++            // LIMIT was reached, stop adding elements.
++            return;
++          }
++          let div = content.document.createElement("div");
++          content.document.body.appendChild(div);
++          content.setTimeout(() => addElement(index + 1), ${DELAY});
++        };
++        addElement(0);
++      });
++    }`
++  ) + ")()", false);
++
++  let test = runTest("inspector.mutations");
++
++  await new Promise(resolve => {
++    let childListMutationsCounter = 0;
++    inspector.on("markupmutation", (evt, mutations) => {
++      let childListMutations = mutations.filter(m => m.type === "childList");
++      childListMutationsCounter += childListMutations.length;
++      if (childListMutationsCounter === LIMIT) {
++        // Wait until we received exactly n=LIMIT mutations in the markup view.
++        resolve();
++      }
++    });
++
++    messageManager.sendAsyncMessage("start-mutations-test");
++  });
++
++  test.done();
++  await closeToolbox();
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/inspector/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/inspector/simple.js
+@@ -0,0 +1,19 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { reloadInspectorAndLog } = require("chrome://damp/content/tests/inspector/inspector-helpers");
++const { openToolboxAndLog, closeToolboxAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++
++  let toolbox = await openToolboxAndLog("simple.inspector", "inspector");
++  await reloadInspectorAndLog("simple", toolbox);
++  await closeToolboxAndLog("simple.inspector", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/memory/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/memory/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/complicated.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++const { saveHeapSnapshot, readHeapSnapshot, takeCensus } = require("chrome://damp/content/tests/memory/memory-helpers");
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++
++  const toolbox = await openToolboxAndLog("complicated.memory", "memory");
++  await reloadPageAndLog("complicated.memory", toolbox);
++
++  let heapSnapshotFilePath = await saveHeapSnapshot("complicated");
++  let snapshot = await readHeapSnapshot("complicated", heapSnapshotFilePath);
++  await takeCensus("complicated", snapshot);
++
++  await closeToolboxAndLog("complicated.memory", toolbox);
++  await testTeardown();
++};
++
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/memory/memory-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/memory/memory-helpers.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/memory-helpers.js
+@@ -0,0 +1,58 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { getToolbox, runTest } = require("chrome://damp/content/tests/head");
++
++exports.saveHeapSnapshot = async function(label) {
++  let toolbox = getToolbox();
++  let panel = toolbox.getCurrentPanel();
++  let memoryFront = panel.panelWin.gFront;
++
++  let test = runTest(label + ".saveHeapSnapshot");
++  let heapSnapshotFilePath = await memoryFront.saveHeapSnapshot();
++  test.done();
++
++  return heapSnapshotFilePath;
++};
++
++exports.readHeapSnapshot = function(label, snapshotFilePath) {
++  const ChromeUtils = require("ChromeUtils");
++  let test = runTest(label + ".readHeapSnapshot");
++  let snapshot = ChromeUtils.readHeapSnapshot(snapshotFilePath);
++  test.done();
++
++  return Promise.resolve(snapshot);
++};
++
++exports.takeCensus = function(label, snapshot) {
++  let test = runTest("complicated.takeCensus");
++  snapshot.takeCensus({
++    breakdown: {
++      by: "coarseType",
++      objects: {
++        by: "objectClass",
++        then: { by: "count", bytes: true, count: true },
++        other: { by: "count", bytes: true, count: true }
++      },
++      strings: {
++        by: "internalType",
++        then: { by: "count", bytes: true, count: true }
++      },
++      scripts: {
++        by: "internalType",
++        then: { by: "count", bytes: true, count: true }
++      },
++      other: {
++        by: "internalType",
++        then: { by: "count", bytes: true, count: true }
++      }
++    }
++  });
++
++  test.done();
++
++  return Promise.resolve();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/memory/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/memory/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/memory/simple.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++const { saveHeapSnapshot, readHeapSnapshot, takeCensus } = require("chrome://damp/content/tests/memory/memory-helpers");
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++
++  const toolbox = await openToolboxAndLog("simple.memory", "memory");
++  await reloadPageAndLog("simple.memory", toolbox);
++
++  let heapSnapshotFilePath = await saveHeapSnapshot("simple");
++  let snapshot = await readHeapSnapshot("simple", heapSnapshotFilePath);
++  await takeCensus("simple", snapshot);
++
++  await closeToolboxAndLog("simple.memory", toolbox);
++  await testTeardown();
++};
++
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/complicated.js
+@@ -0,0 +1,26 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++const { exportHar, waitForNetworkRequests } = require("chrome://damp/content/tests/netmonitor/netmonitor-helpers");
++
++const EXPECTED_REQUESTS = 280;
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++  const toolbox = await openToolboxAndLog("complicated.netmonitor", "netmonitor");
++
++  const requestsDone = waitForNetworkRequests("complicated.netmonitor", toolbox,
++    EXPECTED_REQUESTS);
++  await reloadPageAndLog("complicated.netmonitor", toolbox);
++  await requestsDone;
++
++  await exportHar("complicated.netmonitor", toolbox);
++
++  await closeToolboxAndLog("complicated.netmonitor", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/netmonitor-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/netmonitor-helpers.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/netmonitor-helpers.js
+@@ -0,0 +1,73 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { EVENTS } = require("devtools/client/netmonitor/src/constants");
++const { getToolbox, runTest } = require("chrome://damp/content/tests/head");
++
++/**
++ * Start monitoring all incoming update events about network requests and wait until
++ * a complete info about all requests is received. (We wait for the timings info
++ * explicitly, because that's always the last piece of information that is received.)
++ *
++ * This method is designed to wait for network requests that are issued during a page
++ * load, when retrieving page resources (scripts, styles, images). It has certain
++ * assumptions that can make it unsuitable for other types of network communication:
++ * - it waits for at least one network request to start and finish before returning
++ * - it waits only for request that were issued after it was called. Requests that are
++ *   already in mid-flight will be ignored.
++ * - the request start and end times are overlapping. If a new request starts a moment
++ *   after the previous one was finished, the wait will be ended in the "interim"
++ *   period.
++ * @returns a promise that resolves when the wait is done.
++ */
++function waitForAllRequestsFinished(expectedRequests) {
++  let toolbox = getToolbox();
++  let window = toolbox.getCurrentPanel().panelWin;
++
++  return new Promise(resolve => {
++    // Explicitly waiting for specific number of requests arrived
++    let payloadReady = 0;
++    let timingsUpdated = 0;
++
++    function onPayloadReady(_, id) {
++      payloadReady++;
++      maybeResolve();
++    }
++
++    function onTimingsUpdated(_, id) {
++      timingsUpdated++;
++      maybeResolve();
++    }
++
++    function maybeResolve() {
++      // Have all the requests finished yet?
++      if (payloadReady >= expectedRequests && timingsUpdated >= expectedRequests) {
++        // All requests are done - unsubscribe from events and resolve!
++        window.off(EVENTS.PAYLOAD_READY, onPayloadReady);
++        window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
++        resolve();
++      }
++    }
++
++    window.on(EVENTS.PAYLOAD_READY, onPayloadReady);
++    window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimingsUpdated);
++  });
++}
++
++exports.waitForNetworkRequests = async function(label, toolbox, expectedRequests) {
++  let test = runTest(label + ".requestsFinished.DAMP");
++  await waitForAllRequestsFinished(expectedRequests);
++  test.done();
++};
++
++exports.exportHar = async function(label, toolbox) {
++  let test = runTest(label + ".exportHar");
++
++  // Export HAR from the Network panel.
++  await toolbox.getHARFromNetMonitor();
++
++  test.done();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/netmonitor/simple.js
+@@ -0,0 +1,26 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++const { exportHar, waitForNetworkRequests } = require("chrome://damp/content/tests/netmonitor/netmonitor-helpers");
++
++const EXPECTED_REQUESTS = 1;
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++  const toolbox = await openToolboxAndLog("simple.netmonitor", "netmonitor");
++
++  const requestsDone = waitForNetworkRequests("simple.netmonitor", toolbox,
++    EXPECTED_REQUESTS);
++  await reloadPageAndLog("simple.netmonitor", toolbox);
++  await requestsDone;
++
++  await exportHar("simple.netmonitor", toolbox);
++
++  await closeToolboxAndLog("simple.netmonitor", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/performance/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/performance/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/performance/complicated.js
+@@ -0,0 +1,16 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++  const toolbox = await openToolboxAndLog("complicated.performance", "performance");
++  await reloadPageAndLog("complicated.performance", toolbox);
++  await closeToolboxAndLog("complicated.performance", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/performance/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/performance/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/performance/simple.js
+@@ -0,0 +1,16 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++  const toolbox = await openToolboxAndLog("simple.performance", "performance");
++  await reloadPageAndLog("simple.performance", toolbox);
++  await closeToolboxAndLog("simple.performance", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/complicated.js
+@@ -0,0 +1,16 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++  const toolbox = await openToolboxAndLog("complicated.styleeditor", "styleeditor");
++  await reloadPageAndLog("complicated.styleeditor", toolbox);
++  await closeToolboxAndLog("complicated.styleeditor", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/styleeditor/simple.js
+@@ -0,0 +1,16 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, reloadPageAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++  const toolbox = await openToolboxAndLog("simple.styleeditor", "styleeditor");
++  await reloadPageAndLog("simple.styleeditor", toolbox);
++  await closeToolboxAndLog("simple.styleeditor", toolbox);
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/toolbox/panels-in-background.js b/testing/talos/talos/tests/devtools/addon/content/tests/toolbox/panels-in-background.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/toolbox/panels-in-background.js
+@@ -0,0 +1,51 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { EVENTS } = require("devtools/client/netmonitor/src/constants");
++const { openToolbox, closeToolbox, reloadPageAndLog, testSetup,
++        testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  await testSetup(PAGES_BASE_URL + "custom/panels-in-background/index.html");
++
++  // Make sure the Console and Network panels are initialized
++  let toolbox = await openToolbox("webconsole");
++  let monitor = await toolbox.selectTool("netmonitor");
++
++  // Select the options panel to make both the Console and Network
++  // panel be in background.
++  // Options panel should not do anything on page reload.
++  await toolbox.selectTool("options");
++
++  // Reload the page and wait for all HTTP requests
++  // to finish (1 doc + 600 XHRs).
++  let payloadReady = waitForPayload(601, monitor.panelWin);
++  await reloadPageAndLog("panelsInBackground", toolbox);
++  await payloadReady;
++
++  await closeToolbox();
++  await testTeardown();
++};
++
++function waitForPayload(count, panelWin) {
++  return new Promise(resolve => {
++    let payloadReady = 0;
++
++    function onPayloadReady(_, id) {
++      payloadReady++;
++      maybeResolve();
++    }
++
++    function maybeResolve() {
++      if (payloadReady >= count) {
++        panelWin.off(EVENTS.PAYLOAD_READY, onPayloadReady);
++        resolve();
++      }
++    }
++
++    panelWin.on(EVENTS.PAYLOAD_READY, onPayloadReady);
++  });
++}
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/bulklog.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/bulklog.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/bulklog.js
+@@ -0,0 +1,52 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolbox, closeToolbox, getBrowserWindow, runTest, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  let TOTAL_MESSAGES = 10;
++  let tab = await testSetup(SIMPLE_URL);
++  let messageManager = tab.linkedBrowser.messageManager;
++  let toolbox = await openToolbox("webconsole");
++  let webconsole = toolbox.getPanel("webconsole");
++
++  // Resolve once the last message has been received.
++  let allMessagesReceived = new Promise(resolve => {
++    function receiveMessages(messages) {
++      for (let m of messages) {
++        if (m.node.textContent.includes("damp " + TOTAL_MESSAGES)) {
++          webconsole.hud.ui.off("new-messages", receiveMessages);
++          // Wait for the console to redraw
++          getBrowserWindow().requestAnimationFrame(resolve);
++        }
++      }
++    }
++    webconsole.hud.ui.on("new-messages", receiveMessages);
++  });
++
++  // Load a frame script using a data URI so we can do logs
++  // from the page.  So this is running in content.
++  messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++    `function () {
++      addMessageListener("do-logs", function () {
++        for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
++          content.console.log('damp', i+1, content);
++        }
++      });
++    }`
++  ) + ")()", true);
++
++  // Kick off the logging
++  messageManager.sendAsyncMessage("do-logs");
++
++  let test = runTest("console.bulklog");
++  await allMessagesReceived;
++  test.done();
++
++  await closeToolbox();
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/complicated.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/complicated.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/complicated.js
+@@ -0,0 +1,21 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, testSetup,
++        testTeardown, COMPLICATED_URL } = require("chrome://damp/content/tests/head");
++const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
++
++const EXPECTED_MESSAGES = 7;
++
++module.exports = async function() {
++  await testSetup(COMPLICATED_URL);
++
++  let toolbox = await openToolboxAndLog("complicated.webconsole", "webconsole");
++  await reloadConsoleAndLog("complicated", toolbox, EXPECTED_MESSAGES);
++  await closeToolboxAndLog("complicated.webconsole", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/custom.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/custom.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/custom.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, testSetup,
++        testTeardown, PAGES_BASE_URL } = require("chrome://damp/content/tests/head");
++const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
++
++module.exports = async function() {
++  // These numbers controls the number of console api calls we do in the test
++  let sync = 250, stream = 250, async = 250;
++  let params = `?sync=${sync}&stream=${stream}&async=${async}`;
++  let url = PAGES_BASE_URL + "custom/console/index.html" + params;
++
++  await testSetup(url);
++
++  let toolbox = await openToolboxAndLog("custom.webconsole", "webconsole");
++  await reloadConsoleAndLog("custom", toolbox, sync + stream + async);
++  await closeToolboxAndLog("custom.webconsole", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/objectexpand.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/objectexpand.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/objectexpand.js
+@@ -0,0 +1,68 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolbox, closeToolboxAndLog, getBrowserWindow, runTest, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++module.exports = async function() {
++  let tab = await testSetup(SIMPLE_URL);
++
++  let messageManager = tab.linkedBrowser.messageManager;
++  let toolbox = await openToolbox("webconsole");
++  let webconsole = toolbox.getPanel("webconsole");
++
++  // Resolve once the first message is received.
++  let onMessageReceived = new Promise(resolve => {
++    function receiveMessages(messages) {
++      for (let m of messages) {
++        resolve(m);
++      }
++    }
++    webconsole.hud.ui.once("new-messages", receiveMessages);
++  });
++
++  // Load a frame script using a data URI so we can do logs
++  // from the page.
++  messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++    `function () {
++      addMessageListener("do-dir", function () {
++        content.console.dir(Array.from({length:1000}).reduce((res, _, i)=> {
++          res["item_" + i] = i;
++          return res;
++        }, {}));
++      });
++    }`
++  ) + ")()", true);
++
++  // Kick off the logging
++  messageManager.sendAsyncMessage("do-dir");
++
++  let test = runTest("console.objectexpand");
++  await onMessageReceived;
++  const tree = webconsole.hud.ui.outputNode.querySelector(".dir.message .tree");
++
++  // The tree can be collapsed since the properties are fetched asynchronously.
++  if (tree.querySelectorAll(".node").length === 1) {
++    // If this is the case, we wait for the properties to be fetched and displayed.
++    await new Promise(resolve => {
++      const observer = new (getBrowserWindow().MutationObserver)(mutations => {
++        resolve(mutations);
++        observer.disconnect();
++      });
++      observer.observe(tree, {
++        childList: true
++      });
++    });
++  }
++
++  test.done();
++
++  await closeToolboxAndLog("console.objectexpanded", toolbox);
++
++  await testTeardown();
++};
++
++
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/openwithcache.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/openwithcache.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/openwithcache.js
+@@ -0,0 +1,31 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolbox, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++const TOTAL_MESSAGES = 100;
++
++module.exports = async function() {
++  let tab = await testSetup(SIMPLE_URL);
++
++  // Load a frame script using a data URI so we can do logs
++  // from the page.  So this is running in content.
++  tab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(`
++    function () {
++      for (var i = 0; i < ${TOTAL_MESSAGES}; i++) {
++        content.console.log('damp', i+1, content);
++      }
++    }`
++  ) + ")()", true);
++
++  await openToolboxAndLog("console.openwithcache", "webconsole");
++  await closeToolbox();
++
++  await testTeardown();
++};
++
++
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/simple.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/simple.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/simple.js
+@@ -0,0 +1,21 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolboxAndLog, closeToolboxAndLog, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++const { reloadConsoleAndLog } = require("chrome://damp/content/tests/webconsole/webconsole-helpers");
++
++const EXPECTED_MESSAGES = 1;
++
++module.exports = async function() {
++  await testSetup(SIMPLE_URL);
++
++  let toolbox = await openToolboxAndLog("simple.webconsole", "webconsole");
++  await reloadConsoleAndLog("simple", toolbox, EXPECTED_MESSAGES);
++  await closeToolboxAndLog("simple.webconsole", toolbox);
++
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/streamlog.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/streamlog.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/streamlog.js
+@@ -0,0 +1,55 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { openToolbox, closeToolbox, logTestResult, testSetup,
++        testTeardown, SIMPLE_URL } = require("chrome://damp/content/tests/head");
++
++// Log a stream of console messages, 1 per rAF.  Then record the average
++// time per rAF.  The idea is that the console being slow can slow down
++// content (i.e. Bug 1237368).
++module.exports = async function() {
++  let TOTAL_MESSAGES = 100;
++  let tab = await testSetup(SIMPLE_URL);
++  let messageManager = tab.linkedBrowser.messageManager;
++
++  await openToolbox("webconsole");
++
++  // Load a frame script using a data URI so we can do logs
++  // from the page.  So this is running in content.
++  messageManager.loadFrameScript("data:,(" + encodeURIComponent(
++    `function () {
++      let count = 0;
++      let startTime = content.performance.now();
++      function log() {
++        if (++count < ${TOTAL_MESSAGES}) {
++          content.document.querySelector("h1").textContent += count + "\\n";
++          content.console.log('damp', count,
++                              content,
++                              content.document,
++                              content.document.body,
++                              content.document.documentElement,
++                              new Array(100).join(" DAMP? DAMP! "));
++          content.requestAnimationFrame(log);
++        } else {
++          let avgTime = (content.performance.now() - startTime) / ${TOTAL_MESSAGES};
++          sendSyncMessage("done", Math.round(avgTime));
++        }
++      }
++      log();
++    }`
++  ) + ")()", true);
++
++  let avgTime = await new Promise(resolve => {
++    messageManager.addMessageListener("done", (e) => {
++      resolve(e.data);
++    });
++  });
++
++  logTestResult("console.streamlog", avgTime);
++
++  await closeToolbox();
++  await testTeardown();
++};
+diff --git a/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/webconsole-helpers.js b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/webconsole-helpers.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/tests/webconsole/webconsole-helpers.js
+@@ -0,0 +1,24 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { reloadPageAndLog } = require("chrome://damp/content/tests/head");
++
++exports.reloadConsoleAndLog = async function(label, toolbox, expectedMessages) {
++  let onReload = async function() {
++    let webconsole = toolbox.getPanel("webconsole");
++    await new Promise(done => {
++      let messages = 0;
++      let receiveMessages = () => {
++        if (++messages == expectedMessages) {
++          webconsole.hud.ui.off("new-messages", receiveMessages);
++          done();
++        }
++      };
++      webconsole.hud.ui.on("new-messages", receiveMessages);
++    });
++  };
++  await reloadPageAndLog(label + ".webconsole", toolbox, onReload);
++};

+ 463 - 0
frg/work-js/mozilla-release/patches/1441703-2-61a1.patch

@@ -0,0 +1,463 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1520582720 -3600
+# Node ID a4d12775fbf5890995a3f69c0d95eb38217f8acf
+# Parent  777f25ec442e3540915dc447899e0500f20be7b4
+Bug 1441703 - Define all DAMP tests in a single file;r=ochameau
+
+MozReview-Commit-ID: 5XLDwdfGyTh
+
+diff --git a/testing/talos/talos/tests/devtools/addon/content/addon-test-frontend.js b/testing/talos/talos/tests/devtools/addon/content/addon-test-frontend.js
+--- a/testing/talos/talos/tests/devtools/addon/content/addon-test-frontend.js
++++ b/testing/talos/talos/tests/devtools/addon/content/addon-test-frontend.js
+@@ -111,18 +111,18 @@ function triggerStart() {
+   $("hide-during-run").style.display = "none";
+   $("show-during-run").style.display = "block";
+   $("run-results").innerHTML = "";
+ 
+   runTest(config, doneTest);
+ }
+ 
+ function deselectAll() {
+-  for (var test in defaultConfig.subtests) {
+-    $("subtest-" + test).checked = false;
++  for (var test of defaultConfig.subtests) {
++    $("subtest-" + test.name).checked = false;
+   }
+ }
+ 
+ // E.g. returns "world" for key "hello", "2014" for key "year", and "" for key "dummy":
+ // http://localhost/x.html#hello=world&x=12&year=2014
+ function getUriHashValue(key) {
+   var k = String(key) + "=";
+   var uriVars = unescape(document.location.hash).substr(1).split("&");
+@@ -138,21 +138,21 @@ function getUriHashValue(key) {
+ //        Any errors will express as either javascript errors or not reading the args correctly.
+ //        This is not an "official" part of the UI, and when used in talos, will fail early
+ //        enough to not cause "weird" issues too late.
+ function updateOptionsFromUrl() {
+   var uriTests = getUriHashValue("tests");
+   var tests = uriTests ? JSON.parse(uriTests) : [];
+ 
+   if (tests.length) {
+-    for (var d in defaultConfig.subtests) {
+-      $("subtest-" + d).checked = false;
++    for (var test of defaultConfig.subtests) {
++      $("subtest-" + test.name).checked = false;
+       for (var t in tests) {
+-        if (tests[t] == d) {
+-          $("subtest-" + d).checked = true;
++        if (tests[t] == test.name) {
++          $("subtest-" + test.name).checked = true;
+         }
+       }
+     }
+   }
+ }
+ 
+ function init() {
+   updateOptionsFromUrl();
+diff --git a/testing/talos/talos/tests/devtools/addon/content/damp-tests.js b/testing/talos/talos/tests/devtools/addon/content/damp-tests.js
+new file mode 100644
+--- /dev/null
++++ b/testing/talos/talos/tests/devtools/addon/content/damp-tests.js
+@@ -0,0 +1,131 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++/**
++ * This is the registry for all DAMP tests. Tests will be run in the order specified by
++ * the DAMP_TESTS array.
++ *
++ * A test is defined with the following properties:
++ * - {String} name: the name of the test (should match the path when possible)
++ * - {String} path: the path to the test file under
++ *   testing/talos/talos/tests/devtools/addon/content/tests/
++ * - {String} description: Test description
++ * - {Boolean} disabled: set to true to skip the test
++ * - {Boolean} cold: set to true to run the test only during the first run of the browser
++ */
++
++window.DAMP_TESTS = [
++  {
++    name: "inspector.cold-open",
++    path: "inspector/cold-open.js",
++    description: "Measure first open toolbox on inspector panel",
++    cold: true
++  },
++  // Run all tests against "simple" document
++  {
++    name: "simple.webconsole",
++    path: "webconsole/simple.js",
++    description: "Measure open/close toolbox on webconsole panel against simple document"
++  }, {
++    name: "simple.inspector",
++    path: "inspector/simple.js",
++    description: "Measure open/close toolbox on inspector panel against simple document"
++  }, {
++    name: "simple.debugger",
++    path: "debugger/simple.js",
++    description: "Measure open/close toolbox on debugger panel against simple document"
++  }, {
++    name: "simple.styleeditor",
++    path: "styleeditor/simple.js",
++    description: "Measure open/close toolbox on style editor panel against simple document"
++  }, {
++    name: "simple.performance",
++    path: "performance/simple.js",
++    description: "Measure open/close toolbox on performance panel against simple document"
++  }, {
++    name: "simple.netmonitor",
++    path: "netmonitor/simple.js",
++    description: "Measure open/close toolbox on network monitor panel against simple document"
++  }, {
++    name: "simple.memory",
++    path: "memory/simple.js",
++    description: "Measure open/close toolbox on memory panel and save/read heap snapshot against simple document"
++  },
++  // Run all tests against "complicated" document
++  {
++    name: "complicated.webconsole",
++    path: "webconsole/complicated.js",
++    description: "Measure open/close toolbox on webconsole panel against complicated document"
++  }, {
++    name: "complicated.inspector",
++    path: "inspector/complicated.js",
++    description: "Measure open/close toolbox on inspector panel against complicated document"
++  }, {
++    name: "complicated.debugger",
++    path: "debugger/complicated.js",
++    description: "Measure open/close toolbox on debugger panel against complicated document"
++  }, {
++    name: "complicated.styleeditor",
++    path: "styleeditor/complicated.js",
++    description: "Measure open/close toolbox on style editor panel against complicated document"
++  }, {
++    name: "complicated.performance",
++    path: "performance/complicated.js",
++    description: "Measure open/close toolbox on performance panel against complicated document"
++  }, {
++    name: "complicated.netmonitor",
++    path: "netmonitor/complicated.js",
++    description: "Measure open/close toolbox on network monitor panel against complicated document"
++  }, {
++    name: "complicated.memory",
++    path: "memory/complicated.js",
++    description: "Measure open/close toolbox on memory panel and save/read heap snapshot against complicated document"
++  },
++  // Run all tests against a document specific to each tool
++  {
++    name: "custom.webconsole",
++    path: "webconsole/custom.js"
++  }, {
++    name: "custom.inspector",
++    path: "inspector/custom.js"
++  }, {
++    name: "custom.debugger",
++    path: "debugger/custom.js"
++  },
++  // Run individual tests covering a very precise tool feature.
++  {
++    name: "console.bulklog",
++    path: "webconsole/bulklog.js",
++    description: "Measure time for a bunch of sync console.log statements to appear"
++  }, {
++    name: "console.streamlog",
++    path: "webconsole/streamlog.js",
++    description: "Measure rAF on page during a stream of console.log statements"
++  }, {
++    name: "console.objectexpand",
++    path: "webconsole/objectexpand.js",
++    description: "Measure time to expand a large object and close the console"
++  }, {
++    name: "console.openwithcache",
++    path: "webconsole/openwithcache.js",
++    description: "Measure time to render last logged messages in console for a page with 100 logged messages"
++  }, {
++    name: "inspector.mutations",
++    path: "inspector/mutations.js",
++    description: "Measure the time to perform childList mutations when inspector is enabled"
++  }, {
++    name: "inspector.layout",
++    path: "inspector/layout.js",
++    description: "Measure the time to open/close toolbox on inspector with layout tab against big document with grid containers"
++  }, {
++    name: "panelsInBackground.reload",
++    path: "toolbox/panels-in-background.js",
++    description: "Measure page reload time when all panels are in background"
++  },
++  // ⚠  Adding new individual tests slows down DAMP execution ⚠
++  // ⚠  Consider contributing to custom.${tool} rather than adding isolated tests ⚠
++  // ⚠  See http://docs.firefox-dev.tools/tests/writing-perf-tests.html ⚠
++];
+diff --git a/testing/talos/talos/tests/devtools/addon/content/damp.html b/testing/talos/talos/tests/devtools/addon/content/damp.html
+--- a/testing/talos/talos/tests/devtools/addon/content/damp.html
++++ b/testing/talos/talos/tests/devtools/addon/content/damp.html
+@@ -1,87 +1,28 @@
+ <html>
+   <head>
+ <meta charset="UTF-8"/>
+ <title>DAMP - Devtools At Maximum Performance</title>
+ 
++<script src="damp-tests.js"></script>
+ <script type="application/x-javascript">
+ // Empty subtests interpreted as all subtests, since otherwise meaningless.
+ var config = {subtests: [], repeat: 1};
+ 
+ var defaultConfig = {
+   repeat: 1,
+   rest: 100,
+-  subtests: {
+-    "cold.inspector": true,
+-
+-    "simple.webconsole": true,
+-    "simple.inspector": true,
+-    "simple.debugger": true,
+-    "simple.styleeditor": true,
+-    "simple.performance": true,
+-    "simple.netmonitor": true,
+-    "simple.saveAndReadHeapSnapshot": true,
+-
+-    "complicated.webconsole": true,
+-    "complicated.inspector": true,
+-    "complicated.debugger": true,
+-    "complicated.styleeditor": true,
+-    "complicated.performance": true,
+-    "complicated.netmonitor": true,
+-    "complicated.saveAndReadHeapSnapshot": true,
+-
+-    "custom.webconsole": true,
+-    "custom.inspector": true,
+-    "custom.debugger": true,
+-
+-    "console.bulklog": true,
+-    "console.streamlog": true,
+-    "console.objectexpand": true,
+-    "console.openwithcache": true,
+-    "inspector.mutations": true,
+-    "inspector.layout": true,
+-
+-    "panelsInBackground.reload": true,
+-  }
+-};
+-
+-var testsInfo = {
+-  "cold.inspector": "Measure first open toolbox on inspector panel",
+-
+-  "simple.webconsole": "Measure open/close toolbox on webconsole panel against simple document",
+-  "simple.inspector": "Measure open/close toolbox on inspector panel against simple document",
+-  "simple.debugger": "Measure open/close toolbox on debugger panel against simple document",
+-  "simple.styleeditor": "Measure open/close toolbox on style editor panel against simple document",
+-  "simple.performance": "Measure open/close toolbox on performance panel against simple document",
+-  "simple.netmonitor": "Measure open/close toolbox on network monitor panel against simple document",
+-  "simple.saveAndReadHeapSnapshot": "Measure open/close toolbox on memory panel and save/read heap snapshot against simple document",
+-
+-  "complicated.webconsole": "Measure open/close toolbox on webconsole panel against complicated document",
+-  "complicated.inspector": "Measure open/close toolbox on inspector panel against complicated document",
+-  "complicated.debugger": "Measure open/close toolbox on debugger panel against complicated document",
+-  "complicated.styleeditor": "Measure open/close toolbox on style editor panel against complicated document",
+-  "complicated.performance": "Measure open/close toolbox on performance panel against complicated document",
+-  "complicated.netmonitor": "Measure open/close toolbox on network monitor panel against complicated document",
+-  "complicated.saveAndReadHeapSnapshot": "Measure open/close toolbox on memory panel and save/read heap snapshot against complicated document",
+-
+-  "console.bulklog": "Measure time for a bunch of sync console.log statements to appear",
+-  "console.streamlog": "Measure rAF on page during a stream of console.log statements",
+-  "console.objectexpand": "Measure time to expand a large object and close the console",
+-  "console.openwithcache": "Measure time to render last logged messages in console for a page with 100 logged messages",
+-  "inspector.mutations": "Measure the time to perform childList mutations when inspector is enabled",
+-  "inspector.layout": "Measure the time to open/close toolbox on inspector with layout tab against big document with grid containers",
+-
+-  "panelsInBackground.reload": "Measure page reload time when all panels are in background",
++  subtests: window.DAMP_TESTS // from damp-tests.js
+ };
+ 
+ function updateConfig() {
+   config = {subtests: []};
+-  for (var test in defaultConfig.subtests) {
+-    if ($("subtest-" + test).checked) { // eslint-disable-line no-undef
++  for (var test of defaultConfig.subtests) {
++    if ($("subtest-" + test.name).checked) { // eslint-disable-line no-undef
+       config.subtests.push(test);
+     }
+   }
+ 
+   var repeat = $("repeat").value; // eslint-disable-line no-undef
+   config.repeat = isNaN(repeat) ? 1 : repeat;
+ 
+   // use 1ms rest as a minimum.
+@@ -100,23 +41,24 @@ function updateConfig() {
+   </ul>
+ 
+ Utilities:
+   <a href="pages/simple.html">simple page</a>&nbsp;&nbsp;&nbsp;
+   <a href="http://localhost/tests/tp5n/bild.de/www.bild.de/index.html">complicated page</a>&nbsp;&nbsp;&nbsp;
+ <br/><br/>
+ <b>Configure DAMP</b> (CTRL-F5 to reset to talos defaults) <button type="button" onclick="deselectAll()">Deselect all tests</button><br/>
+ <script>
+-  for (var test in defaultConfig.subtests) {
+-
++  for (let test of defaultConfig.subtests) {
++    let checked = test.disabled ? "unchecked" : "checked";
+     // eslint-disable-next-line no-unsanitized/method
+-    document.write('<input type="checkbox" id="subtest-' + test + '" ' + (defaultConfig.subtests[test] ? "" : "un") + "checked>"
+-                  + test + "</input>"
+-                  + '<span style="color:grey">&nbsp;&nbsp;&nbsp;' + testsInfo[test] + "</span>"
+-                  + "<br/>");
++    document.write(`<input type="checkbox" id="subtest-${test.name}" ${checked}>
++                      ${test.name}
++                    </input>
++                    <span style="color:grey">&nbsp;&nbsp;&nbsp;${test.description}</span>
++                    <br/>`);
+   }
+ </script>
+   <br/>
+   Repeat: <input id="repeat" type="text" size=2 value="1" onchange="updateConfig()"/> times<br/>
+   Delay before starting a measured animation: <input id="rest" type="text" size=4 value="10"/> ms<br/>
+ 
+   <button type="button" id="start-test-button" onclick="triggerStart()">Start Devtools At Maximum Performance tests</button>&nbsp;&nbsp;&nbsp;
+   <div id="run-results"></div>
+diff --git a/testing/talos/talos/tests/devtools/addon/content/damp.js b/testing/talos/talos/tests/devtools/addon/content/damp.js
+--- a/testing/talos/talos/tests/devtools/addon/content/damp.js
++++ b/testing/talos/talos/tests/devtools/addon/content/damp.js
+@@ -31,17 +31,16 @@ function getActiveTab(window) {
+ 
+ /* globals res:true */
+ 
+ function Damp() {
+   Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
+ }
+ 
+ Damp.prototype = {
+-
+   async garbageCollect() {
+     dump("Garbage collect\n");
+ 
+     // Minimize memory usage
+     // mimic miminizeMemoryUsage, by only flushing JS objects via GC.
+     // We don't want to flush all the cache like minimizeMemoryUsage,
+     // as it slow down next executions almost like a cold start.
+ 
+@@ -137,17 +136,17 @@ Damp.prototype = {
+   async testSetup(url) {
+     let tab = await this.addTab(url);
+     await new Promise(resolve => {
+       setTimeout(resolve, this._config.rest);
+     });
+     return tab;
+   },
+ 
+-  async testTeardown() {
++  async testTeardown(url) {
+     this.closeCurrentTab();
+ 
+     // Force freeing memory now so that it doesn't happen during the next test
+     await this.garbageCollect();
+ 
+     this._runNextTest();
+   },
+ 
+@@ -253,85 +252,39 @@ Damp.prototype = {
+     this._config = config;
+ 
+     this._win = Services.wm.getMostRecentWindow("navigator:browser");
+     this._dampTab = this._win.gBrowser.selectedTab;
+     this._win.gBrowser.selectedBrowser.focus(); // Unfocus the URL bar to avoid caret blink
+ 
+     TalosParentProfiler.resume("DAMP - start");
+ 
+-    let tests = {};
++    // Filter tests via `./mach --subtests filter` command line argument
++    let filter = Services.prefs.getCharPref("talos.subtests", "");
++
++    let tests = config.subtests.filter(test => !test.disabled)
++                               .filter(test => test.name.includes(filter));
++
++    if (tests.length === 0) {
++      dump("ERROR: Unable to find any test matching '" + filter + "'\n");
++    }
+ 
+     // Run cold test only once
+     let topWindow = getMostRecentBrowserWindow();
+-    if (!topWindow.coldRunDAMP) {
+-      topWindow.coldRunDAMP = true;
+-      tests["cold.inspector"] = "inspector/cold-open.js";
+-    }
+-
+-    tests["panelsInBackground.reload"] = "toolbox/panels-in-background.js";
+-
+-    // Run all tests against "simple" document
+-    tests["simple.inspector"] = "inspector/simple.js";
+-    tests["simple.webconsole"] = "webconsole/simple.js";
+-    tests["simple.debugger"] = "debugger/simple.js";
+-    tests["simple.styleeditor"] = "styleeditor/simple.js";
+-    tests["simple.performance"] = "performance/simple.js";
+-    tests["simple.netmonitor"] = "netmonitor/simple.js";
+-    tests["simple.saveAndReadHeapSnapshot"] = "memory/simple.js";
+-
+-    // Run all tests against "complicated" document
+-    tests["complicated.inspector"] = "inspector/complicated.js";
+-    tests["complicated.webconsole"] = "webconsole/complicated.js";
+-    tests["complicated.debugger"] = "debugger/complicated.js";
+-    tests["complicated.styleeditor"] = "styleeditor/complicated.js";
+-    tests["complicated.performance"] = "performance/complicated.js";
+-    tests["complicated.netmonitor"] = "netmonitor/complicated.js";
+-    tests["complicated.saveAndReadHeapSnapshot"] = "memory/complicated.js";
+-
+-    // Run all tests against a document specific to each tool
+-    tests["custom.inspector"] = "inspector/custom.js";
+-    tests["custom.debugger"] = "debugger/custom.js";
+-    tests["custom.webconsole"] = "webconsole/custom.js";
+-
+-    // Run individual tests covering a very precise tool feature.
+-    tests["console.bulklog"] = "webconsole/bulklog.js";
+-    tests["console.streamlog"] = "webconsole/streamlog.js";
+-    tests["console.objectexpand"] = "webconsole/objectexpand.js";
+-    tests["console.openwithcache"] = "webconsole/openwithcache.js";
+-    tests["inspector.mutations"] = "inspector/mutations.js";
+-    tests["inspector.layout"] = "inspector/layout.js";
+-    // ⚠  Adding new individual tests slows down DAMP execution ⚠
+-    // ⚠  Consider contributing to custom.${tool} rather than adding isolated tests ⚠
+-    // ⚠  See http://docs.firefox-dev.tools/tests/writing-perf-tests.html ⚠
+-
+-    // Filter tests via `./mach --subtests filter` command line argument
+-    let filter = Services.prefs.getCharPref("talos.subtests", "");
+-    if (filter) {
+-      for (let name in tests) {
+-        if (!name.includes(filter)) {
+-          delete tests[name];
+-        }
+-      }
+-      if (Object.keys(tests).length == 0) {
+-        dump("ERROR: Unable to find any test matching '" + filter + "'\n");
+-        this._doneInternal();
+-        return;
+-      }
++    if (topWindow.coldRunDAMPDone) {
++      tests = tests.filter(test => !test.cold);
++    } else {
++      topWindow.coldRunDAMPDone = true;
+     }
+ 
+     // Construct the sequence array while filtering tests
+     let sequenceArray = [];
+-    for (var i in config.subtests) {
+-      for (var r = 0; r < config.repeat; r++) {
+-        if (!config.subtests[i] || !tests[config.subtests[i]]) {
+-          continue;
+-        }
+-
+-        sequenceArray.push(tests[config.subtests[i]]);
++    for (let test of tests) {
++      for (let r = 0; r < config.repeat; r++) {
++        sequenceArray.push(test.path);
+       }
+     }
+ 
+     // Free memory before running the first test, otherwise we may have a GC
+     // related to Firefox startup or DAMP setup during the first test.
+     this.garbageCollect().then(() => {
+       this._doSequence(sequenceArray, this._doneInternal);
+     }).catch(e => {

+ 12441 - 0
frg/work-js/mozilla-release/patches/1442465-4_2-61a1.patch

@@ -0,0 +1,12441 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1521425805 -32400
+# Node ID ba58e9052ab972dfad832bb33d35652500fbe54c
+# Parent  3575447ccb6bf1bdf75c58bfc35c767d4e7b70fb
+Bug 1442465 - Part 4.2: Stop unnecessarily awaiting on BrowserTestUtils.removeTab (simple part). r=dao
+
+diff --git a/accessible/tests/browser/general/browser_test_doc_creation.js b/accessible/tests/browser/general/browser_test_doc_creation.js
+--- a/accessible/tests/browser/general/browser_test_doc_creation.js
++++ b/accessible/tests/browser/general/browser_test_doc_creation.js
+@@ -52,14 +52,14 @@ add_task(async function testDocumentCrea
+       let accServiceContent =
+         Cc["@mozilla.org/accessibilityService;1"].getService(
+           Ci.nsIAccessibilityService);
+       ok(!!accServiceContent.getAccessibleFromCache(content.document),
+         "Document accessible is in cache.");
+     });
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ 
+   accService = null;
+   await shutdownAccessibilityService();
+ });
+diff --git a/accessible/tests/browser/general/browser_test_urlbar.js.1442465-4_2.later b/accessible/tests/browser/general/browser_test_urlbar.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/accessible/tests/browser/general/browser_test_urlbar.js.1442465-4_2.later
+@@ -0,0 +1,18 @@
++--- browser_test_urlbar.js
+++++ browser_test_urlbar.js
++@@ -21,14 +21,14 @@ add_task(async function testAutocomplete
++   info("Waiting for accessibility to be created for the richlistbox");
++   let richlistbox = document.getAnonymousElementByAttribute(urlbarPopup, "anonid", "richlistbox");
++   await BrowserTestUtils.waitForCondition(() => accService.getAccessibleFor(richlistbox));
++ 
++   info("Confirming that the special case is handled in XULListboxAccessible");
++   let accessible = accService.getAccessibleFor(richlistbox);
++   is(accessible.role, ROLE_COMBOBOX_LIST, "Right role");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ registerCleanupFunction(async function() {
++   await shutdownAccessibilityService();
++ });
+diff --git a/accessible/tests/browser/states/browser_test_visibility.js b/accessible/tests/browser/states/browser_test_visibility.js
+--- a/accessible/tests/browser/states/browser_test_visibility.js
++++ b/accessible/tests/browser/states/browser_test_visibility.js
+@@ -30,17 +30,17 @@ async function runTest(browser, accDoc) 
+   // first item is scrolled off now (testcase for bug 768786)
+   let firstLi = getAcc("li_first");
+   testStates(firstLi, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+ 
+   let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+   // Accessibles in background tab should have offscreen state and no
+   // invisible state.
+   testStates(getAcc("div"), STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ }
+ 
+ addAccessibleTask(`
+   <div id="div" style="border:2px solid blue; width: 500px; height: 110vh;"></div>
+   <input id="input_scrolledoff">
+   <ul style="border:2px solid red; width: 100px; height: 50px; overflow: auto;">
+     <li id="li_first">item1</li><li>item2</li><li>item3</li>
+     <li>item4</li><li>item5</li><li id="li_last">item6</li>
+diff --git a/browser/base/content/test/about/browser_aboutCertError.js b/browser/base/content/test/about/browser_aboutCertError.js
+--- a/browser/base/content/test/about/browser_aboutCertError.js
++++ b/browser/base/content/test/about/browser_aboutCertError.js
+@@ -43,17 +43,17 @@ add_task(async function checkReturnToAbo
+ 
+     await ContentTaskUtils.waitForEvent(this, "pageshow", true);
+   });
+ 
+   is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
+   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
+   is(gBrowser.currentURI.spec, "about:home", "Went back");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function checkReturnToPreviousPage() {
+   info("Loading a bad cert page and making sure 'return to previous page' goes back");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
+   let browser = gBrowser.selectedBrowser;
+ 
+   info("Loading and waiting for the cert error");
+@@ -78,17 +78,17 @@ add_task(async function checkReturnToPre
+ 
+     await ContentTaskUtils.waitForEvent(this, "pageshow", true);
+   });
+ 
+   is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
+   is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
+   is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function checkBadStsCert() {
+   info("Loading a badStsCert and making sure exception button doesn't show up");
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
+   let browser = gBrowser.selectedBrowser;
+ 
+   info("Loading and waiting for the cert error");
+@@ -98,17 +98,17 @@ add_task(async function checkBadStsCert(
+ 
+   let exceptionButtonHidden = await ContentTask.spawn(browser, null, async function() {
+     let doc = content.document;
+     let exceptionButton = doc.getElementById("exceptionDialogButton");
+     return exceptionButton.hidden;
+   });
+   ok(exceptionButtonHidden, "Exception button is hidden");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // This checks that the appinfo.appBuildID starts with a date string,
+ // which is required for the misconfigured system time check.
+ add_task(async function checkAppBuildIDIsDate() {
+   let appBuildID = Services.appinfo.appBuildID;
+   let year = parseInt(appBuildID.substr(0, 4), 10);
+   let month = parseInt(appBuildID.substr(4, 2), 10);
+@@ -169,17 +169,17 @@ add_task(async function checkWrongSystem
+   isnot(message.divDisplay, "none", "Wrong time message information is visible");
+   ok(message.text.includes("clock appears to show the wrong time"),
+      "Correct error message found");
+   ok(message.text.includes("expired.example.com"), "URL found in error message");
+   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
+   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
+   ok(message.learnMoreLink.includes("time-errors"), "time-errors in the Learn More URL");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // pretend we have a negatively skewed (behind) system time
+   serverDate = new Date();
+   serverDate.setYear(serverDate.getFullYear() + 1);
+   serverDateFmt = formatter.format(serverDate);
+ 
+   skew = Math.floor((Date.now() - serverDate.getTime()) / 1000);
+   await SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
+@@ -189,39 +189,39 @@ add_task(async function checkWrongSystem
+ 
+   isnot(message.divDisplay, "none", "Wrong time message information is visible");
+   ok(message.text.includes("clock appears to show the wrong time"),
+      "Correct error message found");
+   ok(message.text.includes("expired.example.com"), "URL found in error message");
+   ok(message.systemDate.includes(localDateFmt), "correct local date displayed");
+   ok(message.actualDate.includes(serverDateFmt), "correct server date displayed");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // pretend we only have a slightly skewed system time, four hours
+   skew = 60 * 60 * 4;
+   await SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
+ 
+   info("Loading a bad cert page with an only slightly skewed clock");
+   message = await setUpPage();
+ 
+   is(message.divDisplay, "none", "Wrong time message information is not visible");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // now pretend we have no skewed system time
+   skew = 0;
+   await SpecialPowers.pushPrefEnv({set: [[PREF_BLOCKLIST_CLOCK_SKEW_SECONDS, skew]]});
+ 
+   info("Loading a bad cert page with no skewed clock");
+   message = await setUpPage();
+ 
+   is(message.divDisplay, "none", "Wrong time message information is not visible");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function checkAdvancedDetails() {
+   info("Loading a bad cert page and verifying the main error and advanced details section");
+   let browser;
+   let certErrorLoaded;
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+     gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_CERT);
+@@ -273,17 +273,17 @@ add_task(async function checkAdvancedDet
+      "Correct error message found");
+   ok(message.text.includes("HTTP Strict Transport Security: false"),
+      "Correct HSTS value found");
+   ok(message.text.includes("HTTP Public Key Pinning: false"),
+      "Correct HPKP value found");
+   let certChain = getCertChain(message.securityInfoAsString);
+   ok(message.text.includes(certChain), "Found certificate chain");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function checkAdvancedDetailsForHSTS() {
+   info("Loading a bad STS cert page and verifying the advanced details section");
+   let browser;
+   let certErrorLoaded;
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+     gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, BAD_STS_CERT);
+@@ -342,17 +342,17 @@ add_task(async function checkAdvancedDet
+      "Correct error message found");
+   ok(message.text.includes("HTTP Strict Transport Security: false"),
+      "Correct HSTS value found");
+   ok(message.text.includes("HTTP Public Key Pinning: true"),
+      "Correct HPKP value found");
+   let certChain = getCertChain(message.securityInfoAsString);
+   ok(message.text.includes(certChain), "Found certificate chain");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function checkUnknownIssuerLearnMoreLink() {
+   info("Loading a cert error for self-signed pages and checking the correct link is shown");
+   let browser;
+   let certErrorLoaded;
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+     gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, UNKNOWN_ISSUER);
+@@ -364,17 +364,17 @@ add_task(async function checkUnknownIssu
+   await certErrorLoaded;
+ 
+   let href = await ContentTask.spawn(browser, null, async function() {
+     let learnMoreLink = content.document.getElementById("learnMoreLink");
+     return learnMoreLink.href;
+   });
+   ok(href.endsWith("security-error"), "security-error in the Learn More URL");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ function getCertChain(securityInfoAsString) {
+   let certChain = "";
+   const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
+                        .getService(Ci.nsISerializationHelper);
+   let securityInfo = serhelper.deserializeObject(securityInfoAsString);
+   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
+diff --git a/browser/base/content/test/about/browser_aboutNetError.js b/browser/base/content/test/about/browser_aboutNetError.js
+--- a/browser/base/content/test/about/browser_aboutNetError.js
++++ b/browser/base/content/test/about/browser_aboutNetError.js
+@@ -38,10 +38,10 @@ add_task(async function checkReturnToPre
+     is(prefResetButton.getAttribute("autofocus"), "true", "prefResetButton has autofocus");
+     prefResetButton.click();
+ 
+     await ContentTaskUtils.waitForEvent(this, "pageshow", true);
+ 
+     is(content.document.documentURI, LOW_TLS_VERSION_, "Should not be showing page");
+   });
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/about/browser_aboutStopReload.js b/browser/base/content/test/about/browser_aboutStopReload.js
+--- a/browser/base/content/test/about/browser_aboutStopReload.js
++++ b/browser/base/content/test/about/browser_aboutStopReload.js
+@@ -15,49 +15,49 @@ add_task(async function checkDontShowSto
+   let stopReloadContainer = document.getElementById("stop-reload-button");
+   let stopReloadContainerObserver = new MutationObserver(stopReloadMutationCallback);
+ 
+   await waitForNoAnimation(stopReloadContainer);
+   stopReloadContainerObserver.observe(stopReloadContainer, { attributeFilter: ["animate"]});
+   let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser,
+                                                         opening: "about:robots",
+                                                         waitForStateStop: true});
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   Assert.ok(true, "Test finished: stop-reload does not animate when navigating to local URI on new tab");
+   stopReloadContainerObserver.disconnect();
+ });
+ 
+ add_task(async function checkDontShowStopFromLocalURI() {
+   let stopReloadContainer = document.getElementById("stop-reload-button");
+   let stopReloadContainerObserver = new MutationObserver(stopReloadMutationCallback);
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser,
+                                                         opening: "about:robots",
+                                                         waitForStateStop: true});
+   await waitForNoAnimation(stopReloadContainer);
+   stopReloadContainerObserver.observe(stopReloadContainer, { attributeFilter: ["animate"]});
+   await BrowserTestUtils.loadURI(tab.linkedBrowser, "about:mozilla");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   Assert.ok(true, "Test finished: stop-reload does not animate when navigating between local URIs");
+   stopReloadContainerObserver.disconnect();
+ });
+ 
+ add_task(async function checkDontShowStopFromNonLocalURI() {
+   let stopReloadContainer = document.getElementById("stop-reload-button");
+   let stopReloadContainerObserver = new MutationObserver(stopReloadMutationCallback);
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser,
+                                                         opening: "https://example.com",
+                                                         waitForStateStop: true});
+   await waitForNoAnimation(stopReloadContainer);
+   stopReloadContainerObserver.observe(stopReloadContainer, { attributeFilter: ["animate"]});
+   await BrowserTestUtils.loadURI(tab.linkedBrowser, "about:mozilla");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   Assert.ok(true, "Test finished: stop-reload does not animate when navigating to local URI from non-local URI");
+   stopReloadContainerObserver.disconnect();
+ });
+ 
+ add_task(async function checkDoShowStopOnNewTab() {
+   let stopReloadContainer = document.getElementById("stop-reload-button");
+   let animatePromise = getAnimatePromise(stopReloadContainer);
+diff --git a/browser/base/content/test/about/browser_aboutStopReload.js.1442465-4_2.later b/browser/base/content/test/about/browser_aboutStopReload.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/about/browser_aboutStopReload.js.1442465-4_2.later
+@@ -0,0 +1,54 @@
++--- browser_aboutStopReload.js
+++++ browser_aboutStopReload.js
++@@ -65,17 +65,17 @@ add_task(async function checkDoShowStopO
++ 
++   await waitForNoAnimation(stopReloadContainer);
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser,
++                                                         opening: "https://example.com",
++                                                         waitForStateStop: true});
++   await stopPromise;
++   await waitForNoAnimation(stopReloadContainer);
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   info("Test finished: stop-reload shows stop when navigating to non-local URI during tab opening");
++ });
++ 
++ add_task(async function checkAnimateStopOnTabAfterTabFinishesOpening() {
++   let stopReloadContainer = document.getElementById("stop-reload-button");
++ 
++   await waitForNoAnimation(stopReloadContainer);
++@@ -83,17 +83,17 @@ add_task(async function checkAnimateStop
++                                                         waitForStateStop: true});
++   await BrowserTestUtils.waitForCondition(() => {
++     info("Waiting for tabAnimationsInProgress to equal 0, currently " + gBrowser.tabAnimationsInProgress);
++     return !gBrowser.tabAnimationsInProgress;
++   });
++   let animatePromise = getAnimatePromise(stopReloadContainer);
++   await BrowserTestUtils.loadURI(tab.linkedBrowser, "https://example.com");
++   await animatePromise;
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   info("Test finished: stop-reload animates when navigating to non-local URI on new tab after tab has opened");
++ });
++ 
++ add_task(async function checkDoShowStopFromLocalURI() {
++   let stopReloadContainer = document.getElementById("stop-reload-button");
++ 
++   await waitForNoAnimation(stopReloadContainer);
++@@ -103,12 +103,12 @@ add_task(async function checkDoShowStopF
++   await BrowserTestUtils.waitForCondition(() => {
++     info("Waiting for tabAnimationsInProgress to equal 0, currently " + gBrowser.tabAnimationsInProgress);
++     return !gBrowser.tabAnimationsInProgress;
++   });
++   let animatePromise = getAnimatePromise(stopReloadContainer);
++   await BrowserTestUtils.loadURI(tab.linkedBrowser, "https://example.com");
++   await animatePromise;
++   await waitForNoAnimation(stopReloadContainer);
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   info("Test finished: stop-reload animates when navigating to non-local URI from local URI");
++ });
+diff --git a/browser/base/content/test/alerts/browser_notification_open_settings.js b/browser/base/content/test/alerts/browser_notification_open_settings.js
+--- a/browser/base/content/test/alerts/browser_notification_open_settings.js
++++ b/browser/base/content/test/alerts/browser_notification_open_settings.js
+@@ -22,17 +22,17 @@ add_task(async function test_settingsOpe
+     let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL);
+     info("simulate a notifications-open-settings notification");
+     let uri = NetUtil.newURI("https://example.com");
+     let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
+     Services.obs.notifyObservers(principal, "notifications-open-settings");
+     let tab = await tabPromise;
+     ok(tab, "The notification settings tab opened");
+     await syncPaneLoadedPromise;
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+ 
+ add_task(async function test_settingsOpen_button() {
+   let pm = Services.perms;
+   info("Adding notification permission");
+   pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
+ 
+@@ -64,15 +64,15 @@ add_task(async function test_settingsOpe
+       openSettingsMenuItem.click();
+ 
+       info("Waiting for notification settings tab");
+       let tab = await tabPromise;
+       ok(tab, "The notification settings tab opened");
+ 
+       await syncPaneLoadedPromise;
+       await closePromise;
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     });
+   } finally {
+     info("Removing notification permission");
+     pm.remove(makeURI(notificationURL), "desktop-notification");
+   }
+ });
+diff --git a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
+--- a/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
++++ b/browser/base/content/test/captivePortal/browser_CaptivePortalWatcher_1.js
+@@ -51,39 +51,39 @@ let testcases = [
+     }
+ 
+     // Simulate clicking the button. The portal tab should be opened and
+     // selected and the button should hide.
+     let tab = await clickButtonAndExpectNewPortalTab();
+     testPortalTabSelectedAndButtonNotVisible();
+ 
+     // Close the tab. The button should become visible.
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     ensureNoPortalTab(win);
+     testShowLoginPageButtonVisibility(notification, "visible");
+ 
+     // When the button is clicked, a new portal tab should be opened and
+     // selected.
+     tab = await clickButtonAndExpectNewPortalTab();
+ 
+     // Open another arbitrary tab. The button should become visible. When it's clicked,
+     // the portal tab should be selected.
+     let anotherTab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser);
+     testShowLoginPageButtonVisibility(notification, "visible");
+     button.click();
+     is(win.gBrowser.selectedTab, tab, "The captive portal tab should be selected.");
+ 
+     // Close the portal tab and select the arbitrary tab. The button should become
+     // visible and when it's clicked, a new portal tab should be opened.
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     win.gBrowser.selectedTab = anotherTab;
+     testShowLoginPageButtonVisibility(notification, "visible");
+     tab = await clickButtonAndExpectNewPortalTab();
+ 
+-    await BrowserTestUtils.removeTab(anotherTab);
++    BrowserTestUtils.removeTab(anotherTab);
+     await freePortal(true);
+     ensureNoPortalTab(win);
+     ensureNoPortalNotification(win);
+     await closeWindowAndWaitForXulWindowVisible(win);
+   },
+ ];
+ 
+ for (let testcase of testcases) {
+diff --git a/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js b/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js
+--- a/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js
++++ b/browser/base/content/test/captivePortal/browser_captivePortal_certErrorUI.js
+@@ -73,10 +73,10 @@ add_task(async function checkCaptivePort
+   info("Waiting for error tab to be reloaded after the captive portal was freed.");
+   await errorTabReloaded;
+   await ContentTask.spawn(browser, null, () => {
+     let doc = content.document;
+     ok(!doc.body.classList.contains("captiveportal"),
+        "Captive portal error page UI is not visible.");
+   });
+ 
+-  await BrowserTestUtils.removeTab(errorTab);
++  BrowserTestUtils.removeTab(errorTab);
+ });
+diff --git a/browser/base/content/test/contextMenu/browser_contextmenu_linkopen.js.1442465-4_2.later b/browser/base/content/test/contextMenu/browser_contextmenu_linkopen.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/contextMenu/browser_contextmenu_linkopen.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_contextmenu_linkopen.js
+++++ browser_contextmenu_linkopen.js
++@@ -60,10 +60,10 @@ async function activateContextAndWaitFor
++ 
++ add_task(async function test_select_text_link() {
++   let testTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, RESOURCE_LINK);
++   for (let elementID of ["test-link", "test-image-link", "svg-with-link", "svg-with-relative-link"]) {
++     for (let where of ["tab", "window", "privatewindow"]) {
++       await activateContextAndWaitFor("#" + elementID, where);
++     }
++   }
++-  await BrowserTestUtils.removeTab(testTab);
+++  BrowserTestUtils.removeTab(testTab);
++ });
+diff --git a/browser/base/content/test/favicons/browser_favicon_load.js.1442465-4_2.later b/browser/base/content/test/favicons/browser_favicon_load.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/favicons/browser_favicon_load.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_favicon_load.js
+++++ browser_favicon_load.js
++@@ -125,17 +125,17 @@ async function doTest(aTestPage, aFavico
++   // Open the tab.
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, aTestPage);
++   // Waiting for favicon requests are all made.
++   await observer.promise;
++   // Waiting for favicon loaded.
++   await promiseWaitOnFaviconLoaded;
++ 
++   // Close the tab.
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ async function setupTailingPreference(aTailingEnabled) {
++   await SpecialPowers.pushPrefEnv({"set": [
++       ["network.http.tailing.enabled", aTailingEnabled]
++   ]});
++ }
++ 
+diff --git a/browser/base/content/test/favicons/browser_multiple_icons_in_short_timeframe.js.1442465-4_2.later b/browser/base/content/test/favicons/browser_multiple_icons_in_short_timeframe.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/favicons/browser_multiple_icons_in_short_timeframe.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_multiple_icons_in_short_timeframe.js
+++++ browser_multiple_icons_in_short_timeframe.js
++@@ -31,10 +31,10 @@ add_task(async function() {
++     head.appendChild(link2);
++   });
++ 
++   await promiseIcon;
++   // The test must have at least one pass.
++   Assert.equal(gBrowser.getIcon(), ROOT + "two.png",
++                "The expected icon has been set");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/base/content/test/favicons/browser_preferred_icons.js.1442465-4_2.later b/browser/base/content/test/favicons/browser_preferred_icons.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/favicons/browser_preferred_icons.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_preferred_icons.js
+++++ browser_preferred_icons.js
++@@ -35,17 +35,17 @@ function createLinks(linkInfos) {
++     }
++   });
++ }
++ 
++ add_task(async function setup() {
++   const URL = ROOT + "discovery.html";
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
++   registerCleanupFunction(async function() {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ });
++ 
++ add_task(async function prefer_svg() {
++   let promise = waitIcon(ROOT + "icon.svg");
++   await createLinks([
++     { href: ROOT + "icon.ico",
++       type: "image/x-icon"
+diff --git a/browser/base/content/test/favicons/browser_rich_icons.js.1442465-4_2.later b/browser/base/content/test/favicons/browser_rich_icons.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/favicons/browser_rich_icons.js.1442465-4_2.later
+@@ -0,0 +1,33 @@
++--- browser_rich_icons.js
+++++ browser_rich_icons.js
++@@ -42,17 +42,17 @@ add_task(async function test_richIcons()
++   // Because there is debounce logic in ContentLinkHandler.jsm to reduce the
++   // favicon loads, we have to wait some time before checking that icon was
++   // stored properly.
++   await BrowserTestUtils.waitForCondition(() => {
++     tabIconUri = gBrowser.getIcon();
++     return tabIconUri != null;
++   }, "wait for icon load to finish", 100, 20);
++   is(tabIconUri, EXPECTED_ICON, "should use the non-rich icon for the tab");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function test_maskIcons() {
++   const URL = ROOT + "file_mask_icon.html";
++   // First we need to clear the failed favicons cache, since previous tests
++   // likely added this non-existing icon, and useDefaultIcon would skip it.
++   PlacesUtils.favicons.removeFailedFavicon(makeURI("http://mochi.test:8888/favicon.ico"));
++   const EXPECTED_ICON = "http://mochi.test:8888/favicon.ico";
++@@ -60,10 +60,10 @@ add_task(async function test_maskIcons()
++ 
++   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
++ 
++   await BrowserTestUtils.waitForCondition(() => {
++     tabIconUri = gBrowser.getIcon();
++     return tabIconUri != null;
++   }, "wait for icon load to finish", 100, 20);
++   is(tabIconUri, EXPECTED_ICON, "should ignore the mask icons and load the root favicon");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/base/content/test/forms/browser_selectpopup.js b/browser/base/content/test/forms/browser_selectpopup.js
+--- a/browser/base/content/test/forms/browser_selectpopup.js
++++ b/browser/base/content/test/forms/browser_selectpopup.js
+@@ -214,17 +214,17 @@ async function doSelectTests(contentType
+   EventUtils.synthesizeKey("KEY_Tab", {shiftKey: true});
+   is((await getInputEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of input events");
+   is((await getChangeEvents()), isWindows ? 2 : 1, "Tab away from select with change - number of change events");
+   is((await getClickEvents()), 2, "Tab away from select with change - number of click events");
+ 
+   is(selectPopup.lastChild.previousSibling.label, "Seven", "Spaces collapsed");
+   is(selectPopup.lastChild.label, "\xA0\xA0Eight\xA0\xA0", "Non-breaking spaces not collapsed");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function setup() {
+   await SpecialPowers.pushPrefEnv({
+     "set": [
+       ["dom.select_popup_in_parent.enabled", true],
+       ["dom.forms.select.customstyling", true]
+     ]
+@@ -276,17 +276,17 @@ add_task(async function() {
+   await popupHiddenPromise;
+ 
+   ok(true, "Popup hidden when select is removed");
+ 
+   // Finally, try it when the tab is closed while the select popup is open.
+   await openSelectPopup(selectPopup, "click", "#one");
+ 
+   popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await popupHiddenPromise;
+ 
+   ok(true, "Popup hidden when tab is closed");
+ });
+ 
+ // This test opens a select popup that is isn't a frame and has some translations applied.
+ add_task(async function() {
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_TRANSLATED);
+@@ -343,17 +343,17 @@ add_task(async function() {
+ 
+     let popupRect = selectPopup.getBoundingClientRect();
+     is(popupRect.left, expectedX, "step " + (stepIndex + 1) + " x");
+     is(popupRect.top, expectedY, "step " + (stepIndex + 1) + " y");
+ 
+     await hideSelectPopup(selectPopup);
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // Test that we get the right events when a select popup is changed.
+ add_task(async function test_event_order() {
+   const URL = "data:text/html," + escape(PAGECONTENT_SMALL);
+   await BrowserTestUtils.withNewTab({
+     gBrowser,
+     url: URL,
+@@ -580,17 +580,17 @@ async function performLargePopupTests(wi
+ // This test checks select elements with a large number of options to ensure that
+ // the popup appears within the browser area.
+ add_task(async function test_large_popup() {
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+ 
+   await performLargePopupTests(window);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // This test checks the same as the previous test but in a new smaller window.
+ add_task(async function test_large_popup_in_small_window() {
+   let newwin = await BrowserTestUtils.openNewBrowserWindow({ width: 400, height: 400 });
+ 
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+   let browserLoadedPromise = BrowserTestUtils.browserLoaded(newwin.gBrowser.selectedBrowser);
+@@ -666,17 +666,17 @@ add_task(async function test_select_sear
+       ["dom.forms.selectSearch", true],
+     ],
+   });
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_GROUPS);
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+ 
+   await performSelectSearchTests(window);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await SpecialPowers.popPrefEnv();
+ });
+ 
+ // This test checks that a mousemove event is fired correctly at the menu and
+ // not at the browser, ensuring that any mouse capture has been cleared.
+ add_task(async function test_mousemove_correcttarget() {
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+@@ -706,17 +706,17 @@ add_task(async function test_mousemove_c
+     await openSelectPopup(selectPopup, "click");
+     let popupHiddenPromise = BrowserTestUtils.waitForEvent(selectPopup, "popuphidden");
+     let sizeModeChanged = BrowserTestUtils.waitForEvent(window, "sizemodechange");
+     BrowserFullScreen();
+     await sizeModeChanged;
+     await popupHiddenPromise;
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // This test checks when a <select> element has some options with altered display values.
+ add_task(async function test_somehidden() {
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_SOMEHIDDEN);
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+ 
+   let selectPopup = document.getElementById("ContentSelectDropdown").menupopup;
+@@ -736,17 +736,17 @@ add_task(async function test_somehidden(
+   let idx = 1;
+   while (child) {
+     is(getComputedStyle(child).display, child.label.indexOf("Visible") > 0 ? "-moz-box" : "none",
+        "Item " + (idx++) + " is visible");
+     child = child.nextSibling;
+   }
+ 
+   await hideSelectPopup(selectPopup, "escape");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // This test checks that the popup is closed when the select element is blurred.
+ add_task(async function test_blur_hides_popup() {
+   const pageUrl = "data:text/html," + escape(PAGECONTENT_SMALL);
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+ 
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+@@ -768,10 +768,10 @@ add_task(async function test_blur_hides_
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+     content.document.getElementById("one").blur();
+   });
+ 
+   await popupHiddenPromise;
+ 
+   ok(true, "Blur closed popup");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/forms/browser_selectpopup_colors.js b/browser/base/content/test/forms/browser_selectpopup_colors.js
+--- a/browser/base/content/test/forms/browser_selectpopup_colors.js
++++ b/browser/base/content/test/forms/browser_selectpopup_colors.js
+@@ -289,17 +289,17 @@ async function testSelectColors(select, 
+   while (child) {
+     testOptionColors(idx, child, menulist);
+     idx++;
+     child = child.nextSibling;
+   }
+ 
+   if (!options.leaveOpen) {
+     await hideSelectPopup(selectPopup, "escape");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ }
+ 
+ add_task(async function setup() {
+   await SpecialPowers.pushPrefEnv({
+     "set": [
+       ["dom.select_popup_in_parent.enabled", true],
+       ["dom.forms.select.customstyling", true]
+@@ -478,17 +478,17 @@ add_task(async function test_select_with
+ 
+   let menulist = document.getElementById("ContentSelectDropdown");
+   let selectPopup = menulist.menupopup;
+   let scrollBox = selectPopup.scrollBox;
+   is(scrollBox.scrollTop, scrollBox.scrollTopMax,
+     "The popup should be scrolled to the bottom of the list (where the selected item is)");
+ 
+   await hideSelectPopup(selectPopup, "escape");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function test_select_inherited_colors_on_options_dont_get_unique_rules_if_rule_set_on_select() {
+   let options = {
+     selectColor: "rgb(0, 0, 255)",
+     selectBgColor: "rgb(255, 255, 255)",
+     selectTextShadow: "rgb(0, 0, 255) 1px 1px 2px",
+     leaveOpen: true
+@@ -523,10 +523,10 @@ add_task(async function test_select_inhe
+     "text-shadow": "rgb(0, 0, 0) 1px 1px 2px"
+   }), true, "There should be a rule specific to option3 and it should have text-shadow: rgb(0, 0, 0) 1px 1px 2px");
+ 
+ 
+   let menulist = document.getElementById("ContentSelectDropdown");
+   let selectPopup = menulist.menupopup;
+ 
+   await hideSelectPopup(selectPopup, "escape");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/forms/browser_selectpopup_searchfocus.js b/browser/base/content/test/forms/browser_selectpopup_searchfocus.js
+--- a/browser/base/content/test/forms/browser_selectpopup_searchfocus.js
++++ b/browser/base/content/test/forms/browser_selectpopup_searchfocus.js
+@@ -32,11 +32,11 @@ add_task(async function test_focus_on_se
+   searchInput.scrollIntoView();
+   let searchFocused = BrowserTestUtils.waitForEvent(searchInput, "focus");
+   await EventUtils.synthesizeMouseAtCenter(searchInput, {}, window);
+   await searchFocused;
+ 
+   is(selectPopup.state, "open", "select popup should still be open after clicking on the search field");
+ 
+   await hideSelectPopup(selectPopup, "escape");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/base/content/test/general/browser_addCertException.js b/browser/base/content/test/general/browser_addCertException.js
+--- a/browser/base/content/test/general/browser_addCertException.js
++++ b/browser/base/content/test/general/browser_addCertException.js
+@@ -11,17 +11,17 @@
+ // the site, including showing the right identity box and control center icons.
+ add_task(async function() {
+   await BrowserTestUtils.openNewForegroundTab(gBrowser);
+   await loadBadCertPage("https://expired.example.com");
+   checkControlPanelIcons();
+   let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                               .getService(Ci.nsICertOverrideService);
+   certOverrideService.clearValidityOverride("expired.example.com", -1);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Check for the correct icons in the identity box and control center.
+ function checkControlPanelIcons() {
+   let { gIdentityHandler } = gBrowser.ownerGlobal;
+   gIdentityHandler._identityBox.click();
+   document.getElementById("identity-popup-security-expander").click();
+ 
+diff --git a/browser/base/content/test/general/browser_addKeywordSearch.js b/browser/base/content/test/general/browser_addKeywordSearch.js
+--- a/browser/base/content/test/general/browser_addKeywordSearch.js
++++ b/browser/base/content/test/general/browser_addKeywordSearch.js
+@@ -72,10 +72,10 @@ add_task(async function() {
+       });
+ 
+       let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+       contextMenu.hidePopup();
+       await popupHiddenPromise;
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_bookmark_titles.js b/browser/base/content/test/general/browser_bookmark_titles.js
+--- a/browser/base/content/test/general/browser_bookmark_titles.js
++++ b/browser/base/content/test/general/browser_bookmark_titles.js
+@@ -62,17 +62,17 @@ add_task(async function() {
+ 
+   // The offline mode test is only good if the page failed to load.
+   await ContentTask.spawn(browser, null, function() {
+     Assert.equal(content.document.documentURI.substring(0, 14), "about:neterror",
+       "Offline mode successfully simulated network outage.");
+   });
+   await checkBookmark(url, title);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // Bookmark the current page and confirm that the new bookmark has the expected
+ // title. (Then delete the bookmark.)
+ async function checkBookmark(url, expected_title) {
+   Assert.equal(gBrowser.selectedBrowser.currentURI.spec, url,
+     "Trying to bookmark the expected uri");
+ 
+diff --git a/browser/base/content/test/general/browser_bookmark_titles.js.1442465-4_2.later b/browser/base/content/test/general/browser_bookmark_titles.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/general/browser_bookmark_titles.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmark_titles.js
+++++ browser_bookmark_titles.js
++@@ -80,17 +80,17 @@ add_task(async function check_override_b
++   let promiseLoaded = promisePageLoaded(browser);
++   BrowserTestUtils.loadURI(browser, url);
++ 
++   await promiseLoaded;
++ 
++   // Test that a bookmark of this URI gets the correct title if we provide one
++   await checkBookmarkedPageTitle(url, default_title, "An overridden title");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ // Bookmark a page and confirm that the new bookmark has the expected title.
++ // (Then delete the bookmark.)
++ async function checkBookmarkedPageTitle(url, default_title, overridden_title) {
++   let promiseBookmark = PlacesTestUtils.waitForNotification("onItemAdded",
++     (id, parentId, index, type, itemUrl) => itemUrl.equals(Services.io.newURI(url)));
++ 
+diff --git a/browser/base/content/test/general/browser_bug1261299.js b/browser/base/content/test/general/browser_bug1261299.js
+--- a/browser/base/content/test/general/browser_bug1261299.js
++++ b/browser/base/content/test/general/browser_bug1261299.js
+@@ -25,17 +25,17 @@ add_task(async function test_content_and
+ 
+   gURLBar.value = "test.mozilla.org";
+   await gURLBar.focus();
+   await BrowserTestUtils.synthesizeKey("KEY_ArrowRight",
+       {shiftKey: true, ctrlKey: true}, gBrowser.selectedBrowser);
+   selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
+   is(selectedText, "test.mozilla.org", "The macOS services got the selected chrome text");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // Test switching active selection.
+ // Each tab has a content selection and when you switch to that tab, its selection becomes
+ // active aka the current selection.
+ // Expect: The active selection is what is being sent to OSX service menu.
+ 
+ add_task(async function test_active_selection_switches_properly() {
+@@ -61,11 +61,11 @@ add_task(async function test_active_sele
+   await BrowserTestUtils.switchTab(gBrowser, tab1);
+   selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
+   is(selectedText, "Write something here", "The macOS services got the selected content text");
+ 
+   await BrowserTestUtils.switchTab(gBrowser, tab2);
+   selectedText = DOMWindowUtils.GetSelectionAsPlaintext();
+   is(selectedText, "Nothing available", "The macOS services got the selected content text");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/base/content/test/general/browser_bug1299667.js b/browser/base/content/test/general/browser_bug1299667.js
+--- a/browser/base/content/test/general/browser_bug1299667.js
++++ b/browser/base/content/test/general/browser_bug1299667.js
+@@ -40,12 +40,12 @@ add_task(async function() {
+   is(node.getAttribute("historyindex"), "-1", "second item historyindex");
+ 
+   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+   event.target.hidePopup();
+   await popupHiddenPromise;
+   info("Hidden popup");
+ 
+   let onClose = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabClose");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await onClose;
+   info("Tab closed");
+ });
+diff --git a/browser/base/content/test/general/browser_bug553455.js b/browser/base/content/test/general/browser_bug553455.js
+--- a/browser/base/content/test/general/browser_bug553455.js
++++ b/browser/base/content/test/general/browser_bug553455.js
+@@ -252,17 +252,17 @@ async function test_disabledInstall() {
+   await closePromise;
+ 
+   try {
+     ok(Services.prefs.getBoolPref("xpinstall.enabled"), "Installation should be enabled");
+   } catch (e) {
+     ok(false, "xpinstall.enabled should be set");
+   }
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   let installs = await getInstalls();
+   is(installs.length, 0, "Shouldn't be any pending installs");
+ },
+ 
+ async function test_blockedInstall() {
+   let notificationPromise = waitForNotification("addon-install-blocked");
+   let triggers = encodeURIComponent(JSON.stringify({
+     "XPI": "amosigned.xpi"
+@@ -508,17 +508,18 @@ async function test_sequential() {
+   // Should have the next add-on's confirmation dialog
+   is(container.childNodes.length, 1, "Should be one item listed");
+   is(container.childNodes[0].firstChild.getAttribute("value"), "Theme Test", "Should have the right add-on");
+ 
+   Services.perms.remove(makeURI("http://example.com"), "install");
+   let closePromise = waitForNotificationClose();
+   cancelInstallDialog(installDialog);
+   await closePromise;
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ },
+ 
+ async function test_allUnverified() {
+   // This test is only relevant if using the new doorhanger UI and allowing
+   // unsigned add-ons
+   if (!Services.prefs.getBoolPref("xpinstall.customConfirmationUI", false) ||
+       Services.prefs.getBoolPref("xpinstall.signatures.required", true) ||
+       AppConstants.MOZ_REQUIRE_SIGNING) {
+@@ -928,17 +929,17 @@ async function test_cancel() {
+   await new Promise(resolve => executeSoon(resolve));
+ 
+   ok(!PopupNotifications.isPanelOpen, "Notification should be closed");
+ 
+   let installs = await getInstalls();
+   is(installs.length, 0, "Should be no pending install");
+ 
+   Services.perms.remove(makeURI("http://example.com/"), "install");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ },
+ 
+ async function test_failedSecurity() {
+   Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
+   setupRedirect({
+     "Location": TESTROOT + "amosigned.xpi"
+   });
+ 
+diff --git a/browser/base/content/test/general/browser_bug575561.js b/browser/base/content/test/general/browser_bug575561.js
+--- a/browser/base/content/test/general/browser_bug575561.js
++++ b/browser/base/content/test/general/browser_bug575561.js
+@@ -88,10 +88,10 @@ async function testLink(aLinkIndexOrFunc
+       link.click();
+       return link.href;
+     });
+   }
+ 
+   info(`Waiting on load of ${href}`);
+   let loaded = await promise;
+   is(loaded, href, "loaded the right document");
+-  await BrowserTestUtils.removeTab(appTab);
++  BrowserTestUtils.removeTab(appTab);
+ }
+diff --git a/browser/base/content/test/general/browser_bug581253.js b/browser/base/content/test/general/browser_bug581253.js
+--- a/browser/base/content/test/general/browser_bug581253.js
++++ b/browser/base/content/test/general/browser_bug581253.js
+@@ -7,17 +7,17 @@ var testTag = "581253_tag";
+ 
+ add_task(async function test_remove_bookmark_with_tag_via_edit_bookmark() {
+   waitForExplicitFinish();
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ 
+   registerCleanupFunction(async function() {
+     await PlacesUtils.bookmarks.eraseEverything();
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     await PlacesTestUtils.clearHistory();
+   });
+ 
+   await PlacesUtils.bookmarks.insert({
+     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+     title: "",
+     url: testURL,
+   });
+diff --git a/browser/base/content/test/general/browser_bug596687.js b/browser/base/content/test/general/browser_bug596687.js
+--- a/browser/base/content/test/general/browser_bug596687.js
++++ b/browser/base/content/test/general/browser_bug596687.js
+@@ -10,16 +10,16 @@ add_task(async function test() {
+   }
+ 
+   function onTabAttrModified() {
+     gotTabAttrModified = true;
+   }
+ 
+   tab.addEventListener("TabClose", onTabClose);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   ok(gotTabClose, "should have got the TabClose event");
+   ok(!gotTabAttrModified, "shouldn't have got the TabAttrModified event after TabClose");
+ 
+   tab.removeEventListener("TabClose", onTabClose);
+   tab.removeEventListener("TabAttrModified", onTabAttrModified);
+ });
+diff --git a/browser/base/content/test/general/browser_bug623893.js b/browser/base/content/test/general/browser_bug623893.js
+--- a/browser/base/content/test/general/browser_bug623893.js
++++ b/browser/base/content/test/general/browser_bug623893.js
+@@ -2,22 +2,22 @@ add_task(async function test() {
+   await BrowserTestUtils.withNewTab("data:text/plain;charset=utf-8,1", async function(browser) {
+     BrowserTestUtils.loadURI(browser, "data:text/plain;charset=utf-8,2");
+     await BrowserTestUtils.browserLoaded(browser);
+ 
+     BrowserTestUtils.loadURI(browser, "data:text/plain;charset=utf-8,3");
+     await BrowserTestUtils.browserLoaded(browser);
+ 
+     await duplicate(0, "maintained the original index");
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+     await duplicate(-1, "went back");
+     await duplicate(1, "went forward");
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   });
+ });
+ 
+ function promiseGetIndex(browser) {
+   return ContentTask.spawn(browser, null, function() {
+     let shistory = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsISHistory);
+     return shistory.index;
+diff --git a/browser/base/content/test/general/browser_bug676619.js.1442465-4_2.later b/browser/base/content/test/general/browser_bug676619.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/general/browser_bug676619.js.1442465-4_2.later
+@@ -0,0 +1,40 @@
++--- browser_bug676619.js
+++++ browser_bug676619.js
++@@ -47,17 +47,17 @@ async function testLink(link, name) {
++ async function testLocation(link, url) {
++   let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
++ 
++   ContentTask.spawn(gBrowser.selectedBrowser, link, contentLink => {
++     content.document.getElementById(contentLink).click();
++   });
++ 
++   let tab = await tabPromise;
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ async function runTest(url) {
++   let tab = BrowserTestUtils.addTab(gBrowser, url);
++   gBrowser.selectedTab = tab;
++ 
++   let browser = gBrowser.getBrowserForTab(tab);
++   await BrowserTestUtils.browserLoaded(browser);
++@@ -65,17 +65,17 @@ async function runTest(url) {
++   await testLink("link1", "test.txt");
++   await testLink("link2", "video.ogg");
++   await testLink("link3", "just some video");
++   await testLink("link4", "with-target.txt");
++   await testLink("link5", "javascript.txt");
++   await testLink("link6", "test.blob");
++   await testLocation("link7", "http://example.com/");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ add_task(async function() {
++   requestLongerTimeout(3);
++   waitForExplicitFinish();
++ 
++   await runTest("http://mochi.test:8888/browser/browser/base/content/test/general/download_page.html");
++   await runTest("https://example.com:443/browser/browser/base/content/test/general/download_page.html");
+diff --git a/browser/base/content/test/general/browser_clipboard_pastefile.js b/browser/base/content/test/general/browser_clipboard_pastefile.js
+--- a/browser/base/content/test/general/browser_clipboard_pastefile.js
++++ b/browser/base/content/test/general/browser_clipboard_pastefile.js
+@@ -50,10 +50,10 @@ add_task(async function() {
+       resolve();
+     }, {capture: true, once: true});
+ 
+     EventUtils.synthesizeKey("v", { accelKey: true });
+   });
+ 
+   document.documentElement.removeChild(textbox);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_contentAltClick.js b/browser/base/content/test/general/browser_contentAltClick.js
+--- a/browser/base/content/test/general/browser_contentAltClick.js
++++ b/browser/base/content/test/general/browser_contentAltClick.js
+@@ -36,17 +36,17 @@ async function clean_up() {
+   for (let download of downloads) {
+     await downloadList.remove(download);
+     await download.finalize(true);
+   }
+   // Remove download history.
+   await PlacesTestUtils.clearHistory();
+ 
+   Services.prefs.clearUserPref("browser.altClickSave");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+ 
+ add_task(async function test_alt_click() {
+   await setup();
+ 
+   let downloadList = await Downloads.getList(Downloads.ALL);
+   let downloads = [];
+   let downloadView;
+diff --git a/browser/base/content/test/general/browser_contextmenu_childprocess.js b/browser/base/content/test/general/browser_contextmenu_childprocess.js
+--- a/browser/base/content/test/general/browser_contextmenu_childprocess.js
++++ b/browser/base/content/test/general/browser_contextmenu_childprocess.js
+@@ -15,17 +15,17 @@ add_task(async function() {
+   await popupShownPromise;
+ 
+   checkMenu(contextMenu);
+ 
+   let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
+   contextMenu.hidePopup();
+   await popupHiddenPromise;
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ function checkItems(menuitem, arr) {
+   for (let i = 0; i < arr.length; i += 2) {
+     let str = arr[i];
+     let details = arr[i + 1];
+     if (str == "---") {
+       is(menuitem.localName, "menuseparator", "menuseparator");
+diff --git a/browser/base/content/test/general/browser_contextmenu_input.js b/browser/base/content/test/general/browser_contextmenu_input.js
+--- a/browser/base/content/test/general/browser_contextmenu_input.js
++++ b/browser/base/content/test/general/browser_contextmenu_input.js
+@@ -247,10 +247,10 @@ add_task(async function test_text_input_
+     // various text elements even though it is set to type "page". That bug
+     // should remove the need for next line.
+     maybeScreenshotsPresent: true,
+     skipFocusChange: true
+   });
+ });
+ 
+ add_task(async function test_cleanup() {
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/general/browser_decoderDoctor.js b/browser/base/content/test/general/browser_decoderDoctor.js
+--- a/browser/base/content/test/general/browser_decoderDoctor.js
++++ b/browser/base/content/test/general/browser_decoderDoctor.js
+@@ -66,17 +66,17 @@ async function test_decoder_doctor_notif
+     if (!tabChecker) {
+       ok(false, "Test implementation error: Missing tabChecker");
+       return;
+     }
+     let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser);
+     button.click();
+     let openedTab = await awaitNewTab;
+     tabChecker(openedTab);
+-    await BrowserTestUtils.removeTab(openedTab);
++    BrowserTestUtils.removeTab(openedTab);
+   });
+ }
+ 
+ function tab_checker_for_sumo(expectedPath) {
+   return function(openedTab) {
+     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+     let url = baseURL + expectedPath;
+     is(openedTab.linkedBrowser.currentURI.spec, url,
+diff --git a/browser/base/content/test/general/browser_documentnavigation.js b/browser/base/content/test/general/browser_documentnavigation.js
+--- a/browser/base/content/test/general/browser_documentnavigation.js
++++ b/browser/base/content/test/general/browser_documentnavigation.js
+@@ -120,17 +120,17 @@ add_task(async function() {
+ add_task(async function() {
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage2);
+ 
+   await expectFocusOnF6(false, "main-window", gURLBar.inputField,
+                                 false, "basic focus content page and second tab urlbar");
+   await expectFocusOnF6(false, "html2", "html2",
+                                 true, "basic focus content page with second tab");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Shift+F6 should navigate backwards. There's only one document here so the effect
+ // is the same.
+ add_task(async function() {
+   gURLBar.focus();
+   await expectFocusOnF6(true, "html1", "html1",
+                                true, "back focus content page");
+@@ -211,17 +211,17 @@ add_task(async function() {
+ 
+   // Now go backwards
+ 
+   await expectFocusOnF6(false, "html3", "body3",
+                                 true, "back focus with contenteditable body");
+   await expectFocusOnF6(false, "main-window", gURLBar.inputField,
+                                 false, "back focus with contenteditable body urlbar");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Navigation with a frameset loaded
+ add_task(async function() {
+   await BrowserTestUtils.openNewForegroundTab(gBrowser,
+     "http://mochi.test:8888/browser/browser/base/content/test/general/file_documentnavigation_frameset.html");
+ 
+   gURLBar.focus();
+@@ -242,12 +242,12 @@ add_task(async function() {
+                                true, "back focus on frameset frame 2");
+   await expectFocusOnF6(true, "htmlframe2", "htmlframe2",
+                                true, "back focus on frameset frame 1");
+   await expectFocusOnF6(true, "htmlframe1", "htmlframe1",
+                                true, "back focus on frameset frame 0");
+   await expectFocusOnF6(true, "main-window", gURLBar.inputField,
+                                false, "back focus on frameset frame urlbar");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // XXXndeakin add tests for browsers inside of panels
+diff --git a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+--- a/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
++++ b/browser/base/content/test/general/browser_domFullscreen_fullscreenMode.js
+@@ -232,10 +232,10 @@ add_task(async function() {
+     // Exit fullscreen mode if we are still in
+     if (window.fullScreen) {
+       info("> Cleanup");
+       executeSoon(() => BrowserFullScreen());
+       await waitForFullscreenChanges(FS_CHANGE_SIZE);
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js b/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
+--- a/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
++++ b/browser/base/content/test/general/browser_e10s_about_page_triggeringprincipal.js
+@@ -69,17 +69,17 @@ add_task(async function test_principal_c
+       let contentPolicyType = channel.loadInfo.externalContentPolicyType;
+       is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+         "sanity check - loading a top level document");
+ 
+       let loadingPrincipal = channel.loadInfo.loadingPrincipal;
+       is(loadingPrincipal, null,
+          "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal");
+     });
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+ 
+ add_task(async function test_principal_right_click_open_link_in_new_tab() {
+   await kAboutPagesRegistered;
+   await SpecialPowers.pushPrefEnv({
+     "set": [["security.sandbox.content.level", 1]],
+   });
+@@ -114,11 +114,11 @@ add_task(async function test_principal_r
+       let contentPolicyType = channel.loadInfo.externalContentPolicyType;
+       is(contentPolicyType, Ci.nsIContentPolicy.TYPE_DOCUMENT,
+         "sanity check - loading a top level document");
+ 
+       let loadingPrincipal = channel.loadInfo.loadingPrincipal;
+       is(loadingPrincipal, null,
+          "sanity check - load of TYPE_DOCUMENT must have a null loadingPrincipal");
+     });
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+diff --git a/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js b/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js
+--- a/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js
++++ b/browser/base/content/test/general/browser_invalid_uri_back_forward_manipulation.js
+@@ -26,10 +26,10 @@ add_task(async function checkBackFromInv
+       // Be paranoid we *are* actually seeing this other page load, not some kind of race
+       // for if/when we do start firing pageshow for the error page...
+       function(e) { return gBrowser.currentURI.spec == "about:robots"; }
+     );
+     gBrowser.goBack();
+     await promiseOtherPageLoaded;
+     ok(gBrowser.webNavigation.canGoForward, "Should be able to go forward from previous page.");
+   }
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_keywordBookmarklets.js.1442465-4_2.later b/browser/base/content/test/general/browser_keywordBookmarklets.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/general/browser_keywordBookmarklets.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_keywordBookmarklets.js
+++++ browser_keywordBookmarklets.js
++@@ -7,17 +7,17 @@ add_task(async function test_keyword_boo
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
++   let bm = await PlacesUtils.bookmarks.insert({
++     parentGuid: PlacesUtils.bookmarks.unfiledGuid,
++     title: "bookmarklet",
++     url: "javascript:'1';"
++   });
++ 
++   registerCleanupFunction(async function() {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++     await PlacesUtils.bookmarks.remove(bm);
++   });
++ 
++   let originalPrincipal = gBrowser.contentPrincipal;
++   let originalPrincipalURI = await getPrincipalURI(tab.linkedBrowser);
++ 
++   await PlacesUtils.keywords.insert({ keyword: "bm", url: "javascript:'1';" });
++ 
+diff --git a/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js b/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js
+--- a/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js
++++ b/browser/base/content/test/general/browser_modifiedclick_inherit_principal.js
+@@ -20,11 +20,11 @@ add_task(async function() {
+    info("Added element");
+    await BrowserTestUtils.synthesizeMouseAtCenter("a", {button: 1}, browser);
+    let newTab = await newTabPromise;
+    is(newTab.linkedBrowser.contentPrincipal.origin, "http://example.com",
+       "Principal should be for example.com");
+    await BrowserTestUtils.switchTab(gBrowser, newTab);
+    info(gURLBar.value);
+    isnot(gURLBar.value, "", "URL bar should not be empty.");
+-   await BrowserTestUtils.removeTab(newTab);
++   BrowserTestUtils.removeTab(newTab);
+  });
+ });
+diff --git a/browser/base/content/test/general/browser_newTabDrop.js b/browser/base/content/test/general/browser_newTabDrop.js
+--- a/browser/base/content/test/general/browser_newTabDrop.js
++++ b/browser/base/content/test/general/browser_newTabDrop.js
+@@ -1,11 +1,11 @@
+ registerCleanupFunction(async function cleanup() {
+   while (gBrowser.tabs.length > 1) {
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+   }
+   Services.search.currentEngine = originalEngine;
+   let engine = Services.search.getEngineByName("MozSearch");
+   Services.search.removeEngine(engine);
+ });
+ 
+ let originalEngine;
+ add_task(async function test_setup() {
+@@ -82,16 +82,16 @@ async function drop(dragData, expectedTa
+   EventUtils.synthesizeDrop(dragSrcElement, newTabButton, dragData, "link", window);
+ 
+   let tabsOpened = false;
+   if (awaitTabOpen) {
+     await awaitTabOpen;
+     info("Got TabOpen event");
+     tabsOpened = true;
+     for (let tab of openedTabs) {
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     }
+   }
+   is(tabsOpened, !!expectedTabOpenCount, `Tabs for ${dragDataString} should only open if any of dropped items are valid`);
+ 
+   await awaitDrop;
+   ok(true, "Got drop event");
+ }
+diff --git a/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
+--- a/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
++++ b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
+@@ -7,17 +7,17 @@ const TEST_HTTP = "http://example.org/";
+ // Test for bug 1338375.
+ add_task(async function() {
+   // Open file:// page.
+   let dir = getChromeDir(getResolvedURI(gTestPath));
+   dir.append(TEST_FILE);
+   const uriString = Services.io.newFileURI(dir).spec;
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uriString);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+   let browser = tab.linkedBrowser;
+ 
+   // Set pref to open in new window.
+   Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+   registerCleanupFunction(function() {
+     Services.prefs.clearUserPref("browser.link.open_newwindow");
+   });
+diff --git a/browser/base/content/test/general/browser_page_style_menu.js b/browser/base/content/test/general/browser_page_style_menu.js
+--- a/browser/base/content/test/general/browser_page_style_menu.js
++++ b/browser/base/content/test/general/browser_page_style_menu.js
+@@ -88,10 +88,10 @@ add_task(async function() {
+       }
+     });
+     return contentValidLinks;
+   });
+ 
+   ok(items.length, "At least one item in the menu");
+   is(items.length, validLinks, "all valid links found");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_page_style_menu_update.js b/browser/base/content/test/general/browser_page_style_menu_update.js
+--- a/browser/base/content/test/general/browser_page_style_menu_update.js
++++ b/browser/base/content/test/general/browser_page_style_menu_update.js
+@@ -58,10 +58,10 @@ add_task(async function() {
+   });
+ 
+   gPageStyleMenu.fillPopup(menupopup);
+   // gPageStyleMenu empties out the menu between opens, so we need
+   // to get a new reference to the selected menuitem
+   selected = menupopup.querySelector("menuitem[checked='true']");
+   is(selected.getAttribute("label"), "1", "Should now have stylesheet 1 selected");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js b/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js
+--- a/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js
++++ b/browser/base/content/test/general/browser_remoteWebNavigation_postdata.js
+@@ -36,14 +36,14 @@ add_task(async function test_remoteWebNa
+       "Content-Length: 7\r\n" +
+       "Content-Type: application/x-www-form-urlencoded\r\n" +
+       "\r\n" +
+       "success";
+ 
+     openUILinkIn(path, "tab", null, makeInputStream(postdata));
+ 
+   });
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   await new Promise(resolve => {
+     server.stop(function() { resolve(); });
+   });
+ });
+diff --git a/browser/base/content/test/general/browser_storagePressure_notification.js b/browser/base/content/test/general/browser_storagePressure_notification.js
+--- a/browser/base/content/test/general/browser_storagePressure_notification.js
++++ b/browser/base/content/test/general/browser_storagePressure_notification.js
+@@ -37,18 +37,18 @@ async function testOverUsageThresholdNot
+   let prefBtn = notification.getElementsByTagName("button")[1];
+   let aboutPrefPromise = openAboutPrefPromise();
+   prefBtn.doCommand();
+   await aboutPrefPromise;
+   let aboutPrefTab = gBrowser.selectedTab;
+   let prefDoc = gBrowser.selectedBrowser.contentDocument;
+   let siteDataGroup = prefDoc.getElementById("siteDataGroup");
+   is_element_visible(siteDataGroup, "Should open to the siteDataGroup section in about:preferences");
+-  await BrowserTestUtils.removeTab(aboutPrefTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(aboutPrefTab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ // Test only displaying notification once within the given interval
+ add_task(async function() {
+   const TEST_NOTIFICATION_INTERVAL_MS = 2000;
+   await SpecialPowers.pushPrefEnv({set: [["browser.storageManager.pressureNotification.minIntervalMS", TEST_NOTIFICATION_INTERVAL_MS]]});
+   // Commenting this to see if we really need it
+   // await SpecialPowers.pushPrefEnv({set: [["privacy.reduceTimerPrecision", false]]});
+diff --git a/browser/base/content/test/general/browser_tabDrop.js b/browser/base/content/test/general/browser_tabDrop.js
+--- a/browser/base/content/test/general/browser_tabDrop.js
++++ b/browser/base/content/test/general/browser_tabDrop.js
+@@ -1,11 +1,11 @@
+ registerCleanupFunction(async function cleanup() {
+   while (gBrowser.tabs.length > 1) {
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+   }
+   Services.search.currentEngine = originalEngine;
+   let engine = Services.search.getEngineByName("MozSearch");
+   Services.search.removeEngine(engine);
+ });
+ 
+ let originalEngine;
+ add_task(async function test_setup() {
+@@ -86,16 +86,16 @@ async function drop(dragData, expectedTa
+   };
+   EventUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, dragData, "link", window, undefined, event);
+   let tabsOpened = false;
+   if (awaitTabOpen) {
+     await awaitTabOpen;
+     info("Got TabOpen event");
+     tabsOpened = true;
+     for (let tab of openedTabs) {
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     }
+   }
+   is(tabsOpened, !!expectedTabOpenCount, `Tabs for ${dragDataString} should only open if any of dropped items are valid`);
+ 
+   await awaitDrop;
+   ok(true, "Got drop event");
+ }
+diff --git a/browser/base/content/test/general/browser_tab_close_dependent_window.js b/browser/base/content/test/general/browser_tab_close_dependent_window.js
+--- a/browser/base/content/test/general/browser_tab_close_dependent_window.js
++++ b/browser/base/content/test/general/browser_tab_close_dependent_window.js
+@@ -2,17 +2,17 @@
+ 
+ add_task(async function closing_tab_with_dependents_should_close_window() {
+   info("Opening window");
+   let win = await BrowserTestUtils.openNewBrowserWindow();
+ 
+   info("Opening tab with data URI");
+   let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, `data:text/html,<html%20onclick="W=window.open()"><body%20onbeforeunload="W.close()">`);
+   info("Closing original tab in this window.");
+-  await BrowserTestUtils.removeTab(win.gBrowser.tabs[0]);
++  BrowserTestUtils.removeTab(win.gBrowser.tabs[0]);
+   info("Clicking into the window");
+   let depTabOpened = BrowserTestUtils.waitForEvent(win.gBrowser.tabContainer, "TabOpen");
+   await BrowserTestUtils.synthesizeMouse("html", 0, 0, {}, tab.linkedBrowser);
+ 
+   let openedTab = (await depTabOpened).target;
+   info("Got opened tab");
+ 
+   let windowClosedPromise = BrowserTestUtils.windowClosed(win);
+diff --git a/browser/base/content/test/general/browser_tabs_close_beforeunload.js b/browser/base/content/test/general/browser_tabs_close_beforeunload.js
+--- a/browser/base/content/test/general/browser_tabs_close_beforeunload.js
++++ b/browser/base/content/test/general/browser_tabs_close_beforeunload.js
+@@ -34,13 +34,13 @@ add_task(async function() {
+   let closeBtn = document.getAnonymousElementByAttribute(secondTab, "anonid", "close-button");
+   info("closing second tab (which will self-close in beforeunload)");
+   closeBtn.click();
+   ok(secondTab.closing, "Second tab should be marked as closing synchronously.");
+   ok(!secondTab.linkedBrowser, "Second tab's browser should be dead");
+   ok(!firstTab.closing, "First tab should not be closing");
+   ok(firstTab.linkedBrowser, "First tab's browser should be alive");
+   info("closing first tab");
+-  await BrowserTestUtils.removeTab(firstTab);
++  BrowserTestUtils.removeTab(firstTab);
+ 
+   ok(firstTab.closing, "First tab should be marked as closing");
+   ok(!firstTab.linkedBrowser, "First tab's browser should be dead");
+ });
+diff --git a/browser/base/content/test/general/browser_unloaddialogs.js b/browser/base/content/test/general/browser_unloaddialogs.js
+--- a/browser/base/content/test/general/browser_unloaddialogs.js
++++ b/browser/base/content/test/general/browser_unloaddialogs.js
+@@ -30,12 +30,12 @@ var testUrls =
+   ];
+ 
+ add_task(async function() {
+   for (let url of testUrls) {
+     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+     ok(true, "Loaded page " + url);
+     // Wait one turn of the event loop before closing, so everything settles.
+     await new Promise(resolve => setTimeout(resolve, 0));
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     ok(true, "Closed page " + url + " without timeout");
+   }
+ });
+diff --git a/browser/base/content/test/general/browser_windowactivation.js b/browser/base/content/test/general/browser_windowactivation.js
+--- a/browser/base/content/test/general/browser_windowactivation.js
++++ b/browser/base/content/test/general/browser_windowactivation.js
+@@ -92,18 +92,18 @@ add_task(async function reallyRunTests()
+ 
+   gBrowser.selectedTab = tab1;
+ 
+   // Start the test.
+   sendGetBackgroundRequest(true);
+ 
+   await testFinished.promise;
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+   otherWindow = null;
+ });
+ 
+ function sendGetBackgroundRequest(ifChanged) {
+   browser1.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged });
+   browser2.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged });
+ }
+ 
+diff --git a/browser/base/content/test/metaTags/browser_bad_meta_tags.js.1442465-4_2.later b/browser/base/content/test/metaTags/browser_bad_meta_tags.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/metaTags/browser_bad_meta_tags.js.1442465-4_2.later
+@@ -0,0 +1,16 @@
++--- browser_bad_meta_tags.js
+++++ browser_bad_meta_tags.js
++@@ -14,12 +14,12 @@ const TEST_PATH = getRootDirectory(gTest
++ add_task(async function test_bad_meta_tags() {
++   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH);
++ 
++   // Wait until places has stored the page info
++   const pageInfo = await waitForPageInfo(TEST_PATH);
++   is(pageInfo.description, "description", "did not collect a og:description because meta tag was malformed");
++   is(pageInfo.previewImageURL.href, "http://test.com/twitter-image.jpg", "did not collect og:image because of invalid loading principal");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await PlacesUtils.history.clear();
++ });
++ 
+diff --git a/browser/base/content/test/metaTags/browser_meta_tags.js.1442465-4_2.later b/browser/base/content/test/metaTags/browser_meta_tags.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/metaTags/browser_meta_tags.js.1442465-4_2.later
+@@ -0,0 +1,36 @@
++--- browser_meta_tags.js
+++++ browser_meta_tags.js
++@@ -13,17 +13,17 @@ const TEST_PATH = getRootDirectory(gTest
++ add_task(async function test_metadata() {
++   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH);
++ 
++   // Wait until places has stored the page info
++   const pageInfo = await waitForPageInfo(TEST_PATH);
++   is(pageInfo.description, "og:description", "got the correct description");
++   is(pageInfo.previewImageURL.href, "https://test.com/og-image-secure-url.jpg", "got the correct preview image");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await PlacesUtils.history.clear();
++ });
++ 
++ /**
++  * This test is almost like the previous one except it opens a second tab to
++  * make sure the extra tab does not cause the debounce logic to be skipped. If
++  * incorrectly skipped, the updated metadata would not include the delayed meta.
++  */
++@@ -34,12 +34,12 @@ add_task(async function multiple_tabs() 
++   // desired URL in a background tab, which results in its timers being throttled.
++   gBrowser.addTab();
++ 
++   // Wait until places has stored the page info
++   const pageInfo = await waitForPageInfo(TEST_PATH);
++   is(pageInfo.description, "og:description", "got the correct description");
++   is(pageInfo.previewImageURL.href, "https://test.com/og-image-secure-url.jpg", "got the correct preview image");
++ 
++-  await BrowserTestUtils.removeTab(tab);
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await PlacesUtils.history.clear();
++ });
+diff --git a/browser/base/content/test/performance/browser_favicon_load.js b/browser/base/content/test/performance/browser_favicon_load.js
+--- a/browser/base/content/test/performance/browser_favicon_load.js
++++ b/browser/base/content/test/performance/browser_favicon_load.js
+@@ -123,17 +123,17 @@ async function doTest(aTestPage, aFavico
+   // Open the tab.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, aTestPage);
+   // Waiting for favicon requests are all made.
+   await observer.promise;
+   // Waiting for favicon loaded.
+   await promiseWaitOnFaviconLoaded;
+ 
+   // Close the tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ async function setupTailingPreference(aTailingEnabled) {
+   await SpecialPowers.pushPrefEnv({"set": [
+       ["network.http.tailing.enabled", aTailingEnabled]
+   ]});
+ }
+ 
+diff --git a/browser/base/content/test/performance/browser_startup_content.js b/browser/base/content/test/performance/browser_startup_content.js
+--- a/browser/base/content/test/performance/browser_startup_content.js
++++ b/browser/base/content/test/performance/browser_startup_content.js
+@@ -86,10 +86,10 @@ add_task(async function() {
+     }
+     for (let file in loadedList[scriptType]) {
+       info(file);
+       if (kDumpAllStacks && loadedList[scriptType][file])
+         info(loadedList[scriptType][file]);
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/performance/browser_tabopen_reflows.js b/browser/base/content/test/performance/browser_tabopen_reflows.js
+--- a/browser/base/content/test/performance/browser_tabopen_reflows.js
++++ b/browser/base/content/test/performance/browser_tabopen_reflows.js
+@@ -34,11 +34,11 @@ add_task(async function() {
+     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+     BrowserOpenTab();
+     await BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "transitionend",
+         false, e => e.propertyName === "max-width");
+     await switchDone;
+   }, EXPECTED_REFLOWS);
+ 
+   let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await switchDone;
+ });
+diff --git a/browser/base/content/test/performance/browser_tabstrip_overflow_underflow_reflows.js.1442465-4_2.later b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow_reflows.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/performance/browser_tabstrip_overflow_underflow_reflows.js.1442465-4_2.later
+@@ -0,0 +1,58 @@
++--- browser_tabstrip_overflow_underflow_reflows.js
+++++ browser_tabstrip_overflow_underflow_reflows.js
++@@ -61,17 +61,17 @@ add_task(async function() {
++     await switchDone;
++     await BrowserTestUtils.waitForCondition(() => {
++       return gBrowser.tabContainer.arrowScrollbox.hasAttribute("scrolledtoend");
++     });
++   }, [], window);
++ 
++   await withReflowObserver(async function(dirtyFrame) {
++     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
++-    await BrowserTestUtils.removeTab(gBrowser.selectedTab, { animate: true });
+++    BrowserTestUtils.removeTab(gBrowser.selectedTab, { animate: true });
++     await switchDone;
++   }, [], window);
++ 
++   // At this point, we have an overflowed tab strip, and we've got the last tab
++   // selected. This should mean that the first tab is scrolled out of view.
++   // Let's test that we don't reflow when switching to that first tab.
++   let lastTab = gBrowser.selectedTab;
++   let arrowScrollbox = gBrowser.tabContainer.arrowScrollbox;
++@@ -87,17 +87,17 @@ add_task(async function() {
++     await BrowserTestUtils.switchTab(gBrowser, firstTab);
++     await BrowserTestUtils.waitForCondition(() => {
++       return gBrowser.tabContainer.arrowScrollbox.hasAttribute("scrolledtostart");
++     });
++   }, [], window);
++ 
++   // Okay, now close the last tab. The tabstrip should stay overflowed, but removing
++   // one more after that should underflow it.
++-  await BrowserTestUtils.removeTab(lastTab);
+++  BrowserTestUtils.removeTab(lastTab);
++ 
++   Assert.ok(gBrowser.tabContainer.hasAttribute("overflow"),
++             "Tabs should still be overflowed.");
++ 
++   // Depending on the size of the window, it might take one or more tab
++   // removals to put the tab strip out of the overflow state, so we'll just
++   // keep testing removals until that occurs.
++   while (gBrowser.tabContainer.hasAttribute("overflow")) {
++@@ -105,16 +105,16 @@ add_task(async function() {
++     if (gBrowser.selectedTab !== lastTab) {
++       await BrowserTestUtils.switchTab(gBrowser, lastTab);
++     }
++ 
++     // ... and make sure we don't flush layout when closing it, and exiting
++     // the overflowed state.
++     await withReflowObserver(async function() {
++       let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
++-      await BrowserTestUtils.removeTab(lastTab, { animate: true });
+++      BrowserTestUtils.removeTab(lastTab, { animate: true });
++       await switchDone;
++       await BrowserTestUtils.waitForCondition(() => !lastTab.isConnected);
++     }, EXPECTED_UNDERFLOW_REFLOWS, window);
++   }
++ 
++   await removeAllButFirstTab();
++ });
+diff --git a/browser/base/content/test/performance/browser_tabswitch_reflows.js b/browser/base/content/test/performance/browser_tabswitch_reflows.js
+--- a/browser/base/content/test/performance/browser_tabswitch_reflows.js
++++ b/browser/base/content/test/performance/browser_tabswitch_reflows.js
+@@ -38,10 +38,10 @@ add_task(async function() {
+   await firstSwitchDone;
+ 
+   await withReflowObserver(async function() {
+     let switchDone = BrowserTestUtils.waitForEvent(window, "TabSwitchDone");
+     gBrowser.selectedTab = origTab;
+     await switchDone;
+   }, EXPECTED_REFLOWS);
+ 
+-  await BrowserTestUtils.removeTab(otherTab);
++  BrowserTestUtils.removeTab(otherTab);
+ });
+diff --git a/browser/base/content/test/permissions/browser_reservedkey.js b/browser/base/content/test/permissions/browser_reservedkey.js
+--- a/browser/base/content/test/permissions/browser_reservedkey.js
++++ b/browser/base/content/test/permissions/browser_reservedkey.js
+@@ -31,17 +31,17 @@ add_task(async function test_reserved_sh
+   EventUtils.sendString("OPQ");
+ 
+   is(document.getElementById("kt_reserved").getAttribute("count"), "2", "reserved='true' with preference on");
+   is(document.getElementById("kt_notreserved").getAttribute("count"), "0", "reserved='false' with preference on");
+   is(document.getElementById("kt_reserveddefault").getAttribute("count"), "1", "default reserved with preference on");
+ 
+   document.documentElement.removeChild(container);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // This test checks that Alt+<key> and F10 cannot be blocked when the preference is set.
+ if (!navigator.platform.includes("Mac")) {
+   add_task(async function test_accesskeys_menus() {
+     await new Promise(resolve => {
+       SpecialPowers.pushPrefEnv({"set": [["permissions.default.shortcuts", 2]]}, resolve);
+     });
+@@ -78,17 +78,17 @@ if (!navigator.platform.includes("Mac"))
+     await popupShown;
+ 
+     ok(true, "File menu opened");
+ 
+     popupHidden = BrowserTestUtils.waitForEvent(filePopup, "popuphidden");
+     filePopup.hidePopup();
+     await popupHidden;
+ 
+-    await BrowserTestUtils.removeTab(tab1);
++    BrowserTestUtils.removeTab(tab1);
+   });
+ }
+ 
+ // There is a <key> element for Backspace with reserved="false", so make sure that it is not
+ // treated as a blocked shortcut key.
+ add_task(async function test_backspace() {
+   await new Promise(resolve => {
+     SpecialPowers.pushPrefEnv({"set": [["permissions.default.shortcuts", 2]]}, resolve);
+diff --git a/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js b/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js
+--- a/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js
++++ b/browser/base/content/test/permissions/browser_temporary_permissions_tabs.js
+@@ -55,12 +55,12 @@ add_task(async function testTempPermissi
+ 
+   Assert.notEqual(geoIcon.boxObject.width, 0, "geo anchor should be visible");
+ 
+   await BrowserTestUtils.switchTab(gBrowser, tab1);
+ 
+   Assert.equal(geoIcon.boxObject.width, 0, "geo anchor should not be visible");
+ 
+   SitePermissions.remove(uri, id, tab2.linkedBrowser);
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+diff --git a/browser/base/content/test/popupNotifications/browser_reshow_in_background.js b/browser/base/content/test/popupNotifications/browser_reshow_in_background.js
+--- a/browser/base/content/test/popupNotifications/browser_reshow_in_background.js
++++ b/browser/base/content/test/popupNotifications/browser_reshow_in_background.js
+@@ -45,11 +45,11 @@ add_task(async function test_background_
+   Assert.equal(seenEvents[0], "showing", "Should have said popup was showing.");
+   Assert.equal(seenEvents[1], "shown", "Should have said popup was shown.");
+ 
+   let panelHidden =
+     BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popuphidden");
+   PopupNotifications.remove(notification);
+   await panelHidden;
+ 
+-  await BrowserTestUtils.removeTab(tabB);
+-  await BrowserTestUtils.removeTab(tabC);
++  BrowserTestUtils.removeTab(tabB);
++  BrowserTestUtils.removeTab(tabC);
+ });
+diff --git a/browser/base/content/test/popups/browser_popup_frames.js b/browser/base/content/test/popups/browser_popup_frames.js
+--- a/browser/base/content/test/popups/browser_popup_frames.js
++++ b/browser/base/content/test/popups/browser_popup_frames.js
+@@ -77,11 +77,11 @@ add_task(async function test_opening_blo
+ 
+   await BrowserTestUtils.waitForCondition(() =>
+     !gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"));
+   ok(!gBrowser.getNotificationBox().getNotificationWithValue("popup-blocked"),
+      "Should no longer have notification");
+   ok(document.getElementById("page-report-button").hidden,
+      "URL bar popup indicator should be hidden");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js b/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
+--- a/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
++++ b/browser/base/content/test/siteIdentity/browser_insecureLoginForms.js
+@@ -152,11 +152,11 @@ add_task(async function test_ignoring_wi
+        "Using expected icon image in the Control Center subview");
+ 
+     ok(Array.every(document.querySelectorAll("[when-loginforms=insecure]"),
+                    element => is_hidden(element)),
+        "All messages should be hidden.");
+ 
+     gIdentityHandler._identityPopup.hidden = true;
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+diff --git a/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js b/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
+--- a/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
++++ b/browser/base/content/test/siteIdentity/browser_mixed_content_cert_override.js
+@@ -44,11 +44,11 @@ add_task(async function() {
+   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+   checkIdentityPopup("connection-mixed-passive-loaded.svg");
+ 
+   // remove cert exception
+   let certOverrideService = Cc["@mozilla.org/security/certoverride;1"]
+                               .getService(Ci.nsICertOverrideService);
+   certOverrideService.clearValidityOverride("self-signed.example.com", -1);
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+diff --git a/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js b/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
+--- a/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
++++ b/browser/base/content/test/siteIdentity/browser_no_mcb_on_http_site.js
+@@ -97,10 +97,10 @@ add_task(async function test3() {
+   });
+ 
+   ok(true, "test3 passed");
+ });
+ 
+ // ------------------------------------------------------
+ 
+ add_task(async function cleanup() {
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js
+--- a/browser/base/content/test/tabPrompts/browser_multiplePrompts.js
++++ b/browser/base/content/test/tabPrompts/browser_multiplePrompts.js
+@@ -63,10 +63,10 @@ add_task(async function() {
+         Services.tm.dispatchToMainThread(resolve);
+       });
+     }
+   }
+ 
+   let prompts = tab.linkedBrowser.parentNode.querySelectorAll("tabmodalprompt");
+   is(prompts.length, 0, "Prompts should all be dismissed.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js
+--- a/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js
++++ b/browser/base/content/test/tabPrompts/browser_openPromptInBackgroundTab.js
+@@ -70,10 +70,10 @@ add_task(async function() {
+ 
+   // This is sync in non-e10s, but in e10s we need to wait for this, so yield anyway.
+   // Note that the switchTab promise doesn't actually guarantee anything about *which*
+   // tab ends up as selected when its event fires, so using that here wouldn't work.
+   await openedTabSelectedPromise;
+   // should be switched back
+   ok(openedTab.selected, "Ta-dah, the other tab should now be selected again!");
+ 
+-  await BrowserTestUtils.removeTab(openedTab);
++  BrowserTestUtils.removeTab(openedTab);
+ });
+diff --git a/browser/base/content/test/tabs/browser_allow_process_switches_despite_related_browser.js b/browser/base/content/test/tabs/browser_allow_process_switches_despite_related_browser.js
+--- a/browser/base/content/test/tabs/browser_allow_process_switches_despite_related_browser.js
++++ b/browser/base/content/test/tabs/browser_allow_process_switches_despite_related_browser.js
+@@ -4,24 +4,24 @@
+ const DUMMY_FILE = "dummy_page.html";
+ const DATA_URI = "data:text/html,Hi";
+ const DATA_URI_SOURCE = "view-source:" + DATA_URI;
+ 
+ // Test for bug 1328829.
+ add_task(async function() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DATA_URI);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ 
+   let promiseTab = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI_SOURCE);
+   BrowserViewSource(tab.linkedBrowser);
+   let viewSourceTab = await promiseTab;
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(viewSourceTab);
++    BrowserTestUtils.removeTab(viewSourceTab);
+   });
+ 
+   let dummyPage = getChromeDir(getResolvedURI(gTestPath));
+   dummyPage.append(DUMMY_FILE);
+   const uriString = Services.io.newFileURI(dummyPage).spec;
+ 
+   let viewSourceBrowser = viewSourceTab.linkedBrowser;
+   let promiseLoad =
+diff --git a/browser/base/content/test/tabs/browser_contextmenu_openlink_after_tabnavigated.js b/browser/base/content/test/tabs/browser_contextmenu_openlink_after_tabnavigated.js
+--- a/browser/base/content/test/tabs/browser_contextmenu_openlink_after_tabnavigated.js
++++ b/browser/base/content/test/tabs/browser_contextmenu_openlink_after_tabnavigated.js
+@@ -33,11 +33,11 @@ add_task(async function test_contextmenu
+   contextMenu.hidePopup();
+ 
+   await BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
+   const newTabURL = await ContentTask.spawn(newTab.linkedBrowser, null, async function() {
+     return content.location.href;
+   });
+   is(newTabURL, "http://example.com/", "Got the expected URL loaded in the new tab");
+ 
+-  await BrowserTestUtils.removeTab(newTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/tabs/browser_navigatePinnedTab.js b/browser/base/content/test/tabs/browser_navigatePinnedTab.js
+--- a/browser/base/content/test/tabs/browser_navigatePinnedTab.js
++++ b/browser/base/content/test/tabs/browser_navigatePinnedTab.js
+@@ -44,15 +44,15 @@ add_task(async function() {
+     link.click();
+   });
+   info("Created & clicked link");
+   let extraTab = await newTabPromise;
+   info("Got a new tab");
+   await ContentTask.spawn(extraTab.linkedBrowser, null, async function() {
+     is(content.opener, null, "No opener should be available");
+   });
+-  await BrowserTestUtils.removeTab(extraTab);
++  BrowserTestUtils.removeTab(extraTab);
+ });
+ 
+ 
+ registerCleanupFunction(function() {
+   gBrowser.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js b/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js
+--- a/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js
++++ b/browser/base/content/test/tabs/browser_new_web_tab_in_file_process_pref.js
+@@ -39,17 +39,17 @@ add_task(async function() {
+     // Check that http tab opened from JS in file:// page is in same process.
+     let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, TEST_HTTP, true);
+     await ContentTask.spawn(fileBrowser, TEST_HTTP, uri => {
+       content.open(uri, "_blank");
+     });
+     let httpTab = await promiseTabOpened;
+     let httpBrowser = httpTab.linkedBrowser;
+     registerCleanupFunction(async function() {
+-      await BrowserTestUtils.removeTab(httpTab);
++      BrowserTestUtils.removeTab(httpTab);
+     });
+     await CheckBrowserInPid(httpBrowser, filePid,
+       "Check that new http tab opened from file loaded in file content process.");
+ 
+     // Check that reload doesn't break the file content process affinity.
+     if (httpTab != gBrowser.selectedTab) {
+       httpTab = await BrowserTestUtils.switchTab(gBrowser, httpTab);
+       httpBrowser = httpTab.linkedBrowser;
+diff --git a/browser/base/content/test/tabs/browser_open_newtab_start_observer_notification.js b/browser/base/content/test/tabs/browser_open_newtab_start_observer_notification.js
+--- a/browser/base/content/test/tabs/browser_open_newtab_start_observer_notification.js
++++ b/browser/base/content/test/tabs/browser_open_newtab_start_observer_notification.js
+@@ -13,10 +13,10 @@ add_task(async function test_browser_ope
+   // because we want to be sure that it triggers the event to fire, since
+   // it's very close to where various user-actions are triggered.
+   BrowserOpenTab();
+   await observerFiredPromise;
+   const tab = gBrowser.selectedTab;
+ 
+   ok(true, "browser-open-newtab-start observer not called");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/tabs/browser_opened_file_tab_navigated_to_web.js b/browser/base/content/test/tabs/browser_opened_file_tab_navigated_to_web.js
+--- a/browser/base/content/test/tabs/browser_opened_file_tab_navigated_to_web.js
++++ b/browser/base/content/test/tabs/browser_opened_file_tab_navigated_to_web.js
+@@ -8,28 +8,28 @@ add_task(async function() {
+   let dir = getChromeDir(getResolvedURI(gTestPath));
+   dir.append(TEST_FILE);
+   const uriString = Services.io.newFileURI(dir).spec;
+   const openedUriString = uriString + "?opened";
+ 
+   // Open first file:// page.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uriString);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ 
+   // Open new file:// tab from JavaScript in first file:// page.
+   let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, openedUriString);
+   await ContentTask.spawn(tab.linkedBrowser, openedUriString, uri => {
+     content.open(uri, "_blank");
+   });
+ 
+   let openedTab = await promiseTabOpened;
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(openedTab);
++    BrowserTestUtils.removeTab(openedTab);
+   });
+ 
+   let openedBrowser = openedTab.linkedBrowser;
+   await BrowserTestUtils.browserLoaded(openedBrowser);
+ 
+   // Ensure that new file:// tab can be navigated to web content.
+   openedBrowser.loadURI("http://example.org/");
+   let href = await BrowserTestUtils.browserLoaded(openedBrowser);
+diff --git a/browser/base/content/test/tabs/browser_overflowScroll.js.1442465-4_2.later b/browser/base/content/test/tabs/browser_overflowScroll.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/tabs/browser_overflowScroll.js.1442465-4_2.later
+@@ -0,0 +1,15 @@
++--- browser_overflowScroll.js
+++++ browser_overflowScroll.js
++@@ -74,11 +74,11 @@ add_task(async function() {
++   isLeft(element, "Scrolled one page of tabs with a double click");
++ 
++   EventUtils.synthesizeMouseAtCenter(upButton, {clickCount: 3});
++   var firstScrollableLeft = left(firstScrollable());
++   ok(left(scrollbox) <= firstScrollableLeft, "Scrolled to the start with a triple click " +
++      "(" + left(scrollbox) + " <= " + firstScrollableLeft + ")");
++ 
++   while (tabs.length > 1) {
++-    await BrowserTestUtils.removeTab(gBrowser.tabs[0]);
+++    BrowserTestUtils.removeTab(gBrowser.tabs[0]);
++   }
++ });
+diff --git a/browser/base/content/test/tabs/browser_preloadedBrowser_zoom.js.1442465-4_2.later b/browser/base/content/test/tabs/browser_preloadedBrowser_zoom.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/tabs/browser_preloadedBrowser_zoom.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_preloadedBrowser_zoom.js
+++++ browser_preloadedBrowser_zoom.js
++@@ -31,17 +31,17 @@ add_task(async function test_default_zoo
++ 
++ /**
++  * Helper to open about:newtab and zoom then check matching preloaded zoom
++  */
++ async function zoomNewTab(changeZoom, message) {
++   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:newtab");
++   changeZoom();
++   const level = tab.linkedBrowser.fullZoom;
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   await checkPreloadedZoom(level, `${message}: ${level}`);
++ }
++ 
++ add_task(async function test_preloaded_zoom_out() {
++   await zoomNewTab(() => FullZoom.reduce(), "zoomed out applied to preloaded");
++ });
++ 
+diff --git a/browser/base/content/test/tabs/browser_reload_deleted_file.js b/browser/base/content/test/tabs/browser_reload_deleted_file.js
+--- a/browser/base/content/test/tabs/browser_reload_deleted_file.js
++++ b/browser/base/content/test/tabs/browser_reload_deleted_file.js
+@@ -15,17 +15,17 @@ add_task(async function() {
+   let uniqueName = uuidGenerator.generateUUID().toString();
+   dummyPage.copyTo(disappearingPage, uniqueName);
+   disappearingPage.append(uniqueName);
+ 
+   // Get file:// URI for new page and load in a new tab.
+   const uriString = Services.io.newFileURI(disappearingPage).spec;
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, uriString);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ 
+   // Delete the page, simulate a click of the reload button and check that we
+   // get a neterror page.
+   disappearingPage.remove(false);
+   document.getElementById("reload-button").doCommand();
+   await BrowserTestUtils.waitForErrorPage(tab.linkedBrowser);
+   await ContentTask.spawn(tab.linkedBrowser, null, function() {
+diff --git a/browser/base/content/test/tabs/browser_tabSpinnerProbe.js b/browser/base/content/test/tabs/browser_tabSpinnerProbe.js
+--- a/browser/base/content/test/tabs/browser_tabSpinnerProbe.js
++++ b/browser/base/content/test/tabs/browser_tabSpinnerProbe.js
+@@ -72,17 +72,17 @@ async function testProbe(aProbe) {
+   let tabHangPromise = hangContentProcess(hangBrowser, delayTime);
+   histogram.clear();
+   let hangTabSwitch = BrowserTestUtils.switchTab(gBrowser, hangTab);
+   await tabHangPromise;
+   await hangTabSwitch;
+ 
+   // Now we should have a hang in our histogram.
+   let snapshot = histogram.snapshot();
+-  await BrowserTestUtils.removeTab(hangTab);
++  BrowserTestUtils.removeTab(hangTab);
+   ok(sum(snapshot.counts) > 0,
+    `Spinner probe should now have a value in some bucket`);
+ }
+ 
+ add_task(async function setup() {
+   await SpecialPowers.pushPrefEnv({
+     set: [
+       ["dom.ipc.processCount", 1],
+diff --git a/browser/base/content/test/tabs/browser_tabSwitchPrintPreview.js b/browser/base/content/test/tabs/browser_tabSwitchPrintPreview.js
+--- a/browser/base/content/test/tabs/browser_tabSwitchPrintPreview.js
++++ b/browser/base/content/test/tabs/browser_tabSwitchPrintPreview.js
+@@ -17,11 +17,11 @@ add_task(async function() {
+     is(gBrowser.selectedTab, PrintPreviewListener._printPreviewTab, "Selected tab should be the print preview tab");
+     gBrowser.selectedTab = tab;
+     isnot(gBrowser.selectedTab, tab, "Selected tab should still not be the tab we added");
+     is(gBrowser.selectedTab, PrintPreviewListener._printPreviewTab, "Selected tab should still be the print preview tab");
+     let tabSwitched = BrowserTestUtils.switchTab(gBrowser, () => { PrintUtils.exitPrintPreview(); });
+     await BrowserTestUtils.waitForCondition(() => !gInPrintPreviewMode, "should no longer be in print preview mode");
+     await tabSwitched;
+     is(gBrowser.selectedTab, originalTab, "Selected tab should be back to the original tab that we print previewed");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+diff --git a/browser/base/content/test/tabs/browser_tabswitch_updatecommands.js b/browser/base/content/test/tabs/browser_tabswitch_updatecommands.js
+--- a/browser/base/content/test/tabs/browser_tabswitch_updatecommands.js
++++ b/browser/base/content/test/tabs/browser_tabswitch_updatecommands.js
+@@ -11,11 +11,11 @@ add_task(async function() {
+   function countUpdates(event) { updates++; }
+   let updater = document.getElementById("editMenuCommandSetAll");
+   updater.addEventListener("commandupdate", countUpdates, true);
+   await BrowserTestUtils.switchTab(gBrowser, tab1);
+ 
+   is(updates, 1, "only one command update per tab switch");
+ 
+   updater.removeEventListener("commandupdate", countUpdates, true);
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/base/content/test/tabs/browser_viewsource_of_data_URI_in_file_process.js b/browser/base/content/test/tabs/browser_viewsource_of_data_URI_in_file_process.js
+--- a/browser/base/content/test/tabs/browser_viewsource_of_data_URI_in_file_process.js
++++ b/browser/base/content/test/tabs/browser_viewsource_of_data_URI_in_file_process.js
+@@ -28,16 +28,16 @@ add_task(async function() {
+     });
+     is(dataPid, filePid, "Check that data URI loaded in file content process.");
+ 
+     // Make sure we can view-source on the data URI page.
+     let promiseTab = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI_SOURCE);
+     BrowserViewSource(fileBrowser);
+     let viewSourceTab = await promiseTab;
+     registerCleanupFunction(async function() {
+-      await BrowserTestUtils.removeTab(viewSourceTab);
++      BrowserTestUtils.removeTab(viewSourceTab);
+     });
+     await ContentTask.spawn(viewSourceTab.linkedBrowser, DATA_URI_SOURCE, uri => {
+       is(content.document.documentURI, uri,
+          "Check that a view-source page was loaded.");
+     });
+   });
+ });
+diff --git a/browser/base/content/test/urlbar/browser_action_keyword.js b/browser/base/content/test/urlbar/browser_action_keyword.js
+--- a/browser/base/content/test/urlbar/browser_action_keyword.js
++++ b/browser/base/content/test/urlbar/browser_action_keyword.js
+@@ -12,17 +12,17 @@ add_task(async function setup() {
+                                       url: TEST_URL + "?q=%s" });
+   await PlacesUtils.keywords.insert({ keyword: "post",
+                                       url: TEST_URL,
+                                       postData: "q=%s" });
+   registerCleanupFunction(async function() {
+     await PlacesUtils.keywords.remove("get");
+     await PlacesUtils.keywords.remove("post");
+     while (gBrowser.tabs.length > 1) {
+-      await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++      BrowserTestUtils.removeTab(gBrowser.selectedTab);
+     }
+   });
+ });
+ 
+ add_task(async function get_keyword() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
+ 
+   let result = await promise_first_result("get something");
+diff --git a/browser/base/content/test/urlbar/browser_action_searchengine.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_action_searchengine.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_action_searchengine.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_action_searchengine.js
+++++ browser_action_searchengine.js
++@@ -9,17 +9,17 @@ add_task(async function() {
++   Services.search.currentEngine = engine;
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
++ 
++   registerCleanupFunction(async function() {
++     Services.search.currentEngine = originalEngine;
++     Services.search.removeEngine(engine);
++     try {
++-      await BrowserTestUtils.removeTab(tab);
+++      BrowserTestUtils.removeTab(tab);
++     } catch (ex) { /* tab may have already been closed in case of failure */ }
++     await PlacesUtils.history.clear();
++   });
++ 
++   await promiseAutocompleteResultPopup("open a search");
++   let result = await waitForAutocompleteResultAt(0);
++   isnot(result, null, "Should have a result");
++   is(result.getAttribute("url"),
+diff --git a/browser/base/content/test/urlbar/browser_action_searchengine_alias.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_action_searchengine_alias.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_action_searchengine_alias.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_action_searchengine_alias.js
+++++ browser_action_searchengine_alias.js
++@@ -10,17 +10,17 @@ add_task(async function() {
++   Services.search.currentEngine = engine;
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
++ 
++   registerCleanupFunction(async function() {
++     Services.search.currentEngine = originalEngine;
++     Services.search.removeEngine(engine);
++     try {
++-      await BrowserTestUtils.removeTab(tab);
+++      BrowserTestUtils.removeTab(tab);
++     } catch (ex) { /* tab may have already been closed in case of failure */ }
++     await PlacesUtils.history.clear();
++   });
++ 
++   await promiseAutocompleteResultPopup("moz open a search");
++   let result = await waitForAutocompleteResultAt(0);
++   ok(result.hasAttribute("image"), "Result should have an image attribute");
++   ok(result.getAttribute("image") === engine.iconURI.spec,
+diff --git a/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_autocomplete_autoselect.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_autocomplete_autoselect.js
+++++ browser_autocomplete_autoselect.js
++@@ -30,17 +30,17 @@ function is_selected_one_off(index) {
++ 
++ add_task(async function() {
++   let maxResults = Services.prefs.getIntPref("browser.urlbar.maxRichResults");
++   Services.prefs.setBoolPref(ONEOFF_URLBAR_PREF, true);
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
++   registerCleanupFunction(async function() {
++     await PlacesUtils.history.clear();
++     Services.prefs.clearUserPref(ONEOFF_URLBAR_PREF);
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ 
++   let visits = [];
++   repeat(maxResults, i => {
++     visits.push({
++       uri: makeURI("http://example.com/autocomplete/?" + i),
++     });
++   });
+diff --git a/browser/base/content/test/urlbar/browser_autocomplete_cursor.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_autocomplete_cursor.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_autocomplete_cursor.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_autocomplete_cursor.js
+++++ browser_autocomplete_cursor.js
++@@ -11,10 +11,10 @@ add_task(async function() {
++   is(gURLBar.popup.richlistbox.selectedIndex, 0, "Should have selected something");
++ 
++   EventUtils.synthesizeKey("KEY_ArrowRight");
++   await promisePopupHidden(gURLBar.popup);
++ 
++   is(gURLBar.selectionStart, 5, "Should have moved the cursor");
++   is(gURLBar.selectionEnd, 5, "And not selected anything");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_autocomplete_edit_completed.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_autocomplete_edit_completed.js
+++++ browser_autocomplete_edit_completed.js
++@@ -3,17 +3,17 @@ add_task(async function() {
++ 
++   await PlacesTestUtils.addVisits([
++     { uri: makeURI("http://example.com/foo") },
++     { uri: makeURI("http://example.com/foo/bar") },
++   ]);
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
++   registerCleanupFunction(async function() {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++     await PlacesUtils.history.clear();
++   });
++ 
++   await promiseAutocompleteResultPopup("http://example.com");
++   await waitForAutocompleteResultAt(1);
++ 
++   let popup = gURLBar.popup;
++   let list = popup.richlistbox;
+diff --git a/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js b/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js
+--- a/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js
++++ b/browser/base/content/test/urlbar/browser_autocomplete_enter_race.js
+@@ -2,17 +2,17 @@
+ 
+ add_task(async function setup() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+   let bm = await PlacesUtils.bookmarks.insert({ parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+                                                 url: "http://example.com/?q=%s",
+                                                 title: "test" });
+   registerCleanupFunction(async function() {
+     await PlacesUtils.bookmarks.remove(bm);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+   await PlacesUtils.keywords.insert({ keyword: "keyword",
+                                       url: "http://example.com/?q=%s" });
+   // Needs at least one success.
+   ok(true, "Setup complete");
+ });
+ 
+ add_task(async function test_keyword() {
+diff --git a/browser/base/content/test/urlbar/browser_autocomplete_no_title.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_autocomplete_no_title.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_autocomplete_no_title.js.1442465-4_2.later
+@@ -0,0 +1,20 @@
++--- browser_autocomplete_no_title.js
+++++ browser_autocomplete_no_title.js
++@@ -1,16 +1,16 @@
++ add_task(async function() {
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:mozilla");
++   await PlacesUtils.history.clear();
++   const uri = "http://bug1060642.example.com/beards/are/pretty/great";
++   await PlacesTestUtils.addVisits([{ uri, title: "" }]);
++   registerCleanupFunction(async function() {
++     await PlacesUtils.history.clear();
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ 
++   await promiseAutocompleteResultPopup("bug1060642");
++   let result = await waitForAutocompleteResultAt(1);
++   is(result._titleText.textContent, "bug1060642.example.com", "Result title should be as expected");
++ 
++   gURLBar.popup.hidePopup();
++   await promisePopupHidden(gURLBar.popup);
+diff --git a/browser/base/content/test/urlbar/browser_dragdropURL.js b/browser/base/content/test/urlbar/browser_dragdropURL.js
+--- a/browser/base/content/test/urlbar/browser_dragdropURL.js
++++ b/browser/base/content/test/urlbar/browser_dragdropURL.js
+@@ -5,17 +5,17 @@ const DRAG_URL = "http://www.example.com
+ const DRAG_FORBIDDEN_URL = "chrome://browser/content/aboutDialog.xul";
+ const DRAG_TEXT = "Firefox is awesome";
+ const DRAG_TEXT_URL = "http://example.com/?q=Firefox+is+awesome";
+ const DRAG_WORD = "Firefox";
+ const DRAG_WORD_URL = "http://example.com/?q=Firefox";
+ 
+ registerCleanupFunction(async function cleanup() {
+   while (gBrowser.tabs.length > 1) {
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+   }
+   Services.search.currentEngine = originalEngine;
+   let engine = Services.search.getEngineByName("MozSearch");
+   Services.search.removeEngine(engine);
+ });
+ 
+ let originalEngine;
+ add_task(async function test_setup() {
+diff --git a/browser/base/content/test/urlbar/browser_moz_action_link.js b/browser/base/content/test/urlbar/browser_moz_action_link.js
+--- a/browser/base/content/test/urlbar/browser_moz_action_link.js
++++ b/browser/base/content/test/urlbar/browser_moz_action_link.js
+@@ -20,12 +20,12 @@ add_task(async function() {
+     let newTab = gBrowser.selectedTab;
+     await BrowserTestUtils.switchTab(gBrowser, tab);
+     await BrowserTestUtils.switchTab(gBrowser, newTab);
+     is(gBrowser.selectedTab, newTab, "Switched to new tab again!");
+     is(gURLBar.value, "about:blank", "URL bar should be displaying about:blank after tab switch");
+     // Finally, check that directly setting it produces the right results, too:
+     URLBarSetURI(makeURI(uri));
+     is(gURLBar.value, "about:blank", "URL bar should still be displaying about:blank");
+-    await BrowserTestUtils.removeTab(newTab);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(newTab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+diff --git a/browser/base/content/test/urlbar/browser_new_tab_urlbar_reset.js b/browser/base/content/test/urlbar/browser_new_tab_urlbar_reset.js
+--- a/browser/base/content/test/urlbar/browser_new_tab_urlbar_reset.js
++++ b/browser/base/content/test/urlbar/browser_new_tab_urlbar_reset.js
+@@ -6,11 +6,11 @@
+  */
+ add_task(async function() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", false);
+   await promiseAutocompleteResultPopup("m");
+   ok(gURLBar.popupOpen, "The popup is open");
+   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", false);
+   await promiseAutocompleteResultPopup("m");
+   ok(gURLBar.popupOpen, "The popup is open");
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
+--- a/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
++++ b/browser/base/content/test/urlbar/browser_tabMatchesInAwesomebar.js
+@@ -69,33 +69,33 @@ add_task(async function step_4() {
+   });
+ 
+   await ensure_opentabs_match_db();
+ });
+ 
+ add_task(async function step_5() {
+   info("Running step 5 - remove tab immediately");
+   let tab = BrowserTestUtils.addTab(gBrowser, "about:logo");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await ensure_opentabs_match_db();
+ });
+ 
+ add_task(async function step_6() {
+   info("Running step 6 - check swapBrowsersAndCloseOther preserves registered switch-to-tab result");
+   let tabToKeep = BrowserTestUtils.addTab(gBrowser);
+   let tab = BrowserTestUtils.addTab(gBrowser);
+   tab.linkedBrowser.loadURI("about:mozilla");
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ 
+   gBrowser.updateBrowserRemoteness(tabToKeep.linkedBrowser, tab.linkedBrowser.isRemoteBrowser);
+   gBrowser.swapBrowsersAndCloseOther(tabToKeep, tab);
+ 
+   await ensure_opentabs_match_db();
+ 
+-  await BrowserTestUtils.removeTab(tabToKeep);
++  BrowserTestUtils.removeTab(tabToKeep);
+ 
+   await ensure_opentabs_match_db();
+ });
+ 
+ add_task(async function step_7() {
+   info("Running step 7 - close all tabs");
+ 
+   Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand");
+diff --git a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js
+--- a/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js
++++ b/browser/base/content/test/urlbar/browser_urlbarAboutHomeLoading.js
+@@ -14,17 +14,17 @@ add_task(async function clearURLBarAfter
+     newTabBrowser.addEventListener("Initialized", function() {
+       resolve(gBrowser.selectedTab);
+     }, {capture: true, once: true});
+   });
+   document.getElementById("home-button").click();
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   is(gURLBar.value, "", "URL bar should be empty");
+   is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Same as above, but open the tab without passing the URL immediately
+  * which changes behaviour in tabbrowser.xml.
+  */
+ add_task(async function clearURLBarAfterParentProcessURLInExistingTab() {
+   let tab = await new Promise(resolve => {
+@@ -34,17 +34,17 @@ add_task(async function clearURLBarAfter
+       resolve(gBrowser.selectedTab);
+     }, {capture: true, once: true});
+     newTabBrowser.loadURI("about:preferences");
+   });
+   document.getElementById("home-button").click();
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   is(gURLBar.value, "", "URL bar should be empty");
+   is(tab.linkedBrowser.userTypedValue, null, "The browser should have no recorded userTypedValue");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Load about:home directly from an about:newtab page. Because it is an
+  * 'initial' page, we need to treat this specially if the user actually
+  * loads a page like this from the URL bar.
+  */
+ add_task(async function clearURLBarAfterManuallyLoadingAboutHome() {
+@@ -58,17 +58,17 @@ add_task(async function clearURLBarAfter
+   gURLBar.value = "about:home";
+   gURLBar.select();
+   let aboutHomeLoaded = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, "about:home");
+   EventUtils.sendKey("return");
+   await aboutHomeLoaded;
+ 
+   is(gURLBar.value, "", "URL bar should be empty");
+   is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Ensure we don't show 'about:home' in the URL bar temporarily in new tabs
+  * while we're switching remoteness (when the URL we're loading and the
+  * default content principal are different).
+  */
+ add_task(async function dontTemporarilyShowAboutHome() {
+diff --git a/browser/base/content/test/urlbar/browser_urlbarEnter.js b/browser/base/content/test/urlbar/browser_urlbarEnter.js
+--- a/browser/base/content/test/urlbar/browser_urlbarEnter.js
++++ b/browser/base/content/test/urlbar/browser_urlbarEnter.js
+@@ -14,17 +14,17 @@ add_task(async function() {
+   EventUtils.synthesizeKey("KEY_Enter");
+   await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+ 
+   // Check url bar and selected tab.
+   is(gURLBar.textValue, TEST_VALUE, "Urlbar should preserve the value on return keypress");
+   is(gBrowser.selectedTab, tab, "New URL was loaded in the current tab");
+ 
+   // Cleanup.
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   info("Alt+Return keypress");
+   // due to bug 691608, we must wait for the load event, else isTabEmpty() will
+   // return true on e10s for this tab, so it will be reused even with altKey.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, START_VALUE);
+ 
+@@ -35,11 +35,11 @@ add_task(async function() {
+   // wait for the new tab to appear.
+   await tabOpenPromise;
+ 
+   // Check url bar and selected tab.
+   is(gURLBar.textValue, TEST_VALUE, "Urlbar should preserve the value on return keypress");
+   isnot(gBrowser.selectedTab, tab, "New URL was loaded in a new tab");
+ 
+   // Cleanup.
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js b/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js
+--- a/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js
++++ b/browser/base/content/test/urlbar/browser_urlbarHashChangeProxyState.js
+@@ -101,11 +101,11 @@ add_task(async function() {
+ 
+   is(gURLBar.textValue, url, "URL bar visible value should be correct when the page loads from about:newtab");
+   is(gURLBar.value, url, "URL bar value should be correct when the page loads from about:newtab");
+   let identityBox = document.getElementById("identity-box");
+   ok(identityBox.classList.contains("verifiedDomain"),
+      "Identity box should know we're doing SSL when the page loads from about:newtab");
+   is(gURLBar.getAttribute("pageproxystate"), "valid",
+      "URL bar is in valid page proxy state when SSL page with hash loads from about:newtab");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js b/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js
+--- a/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js
++++ b/browser/base/content/test/urlbar/browser_urlbarKeepStateAcrossTabSwitches.js
+@@ -14,17 +14,17 @@ add_task(async function() {
+   gURLBar.value = input;
+   gURLBar.select();
+   EventUtils.sendKey("return");
+   await errorPageLoaded;
+   is(gURLBar.textValue, input, "Text is still in URL bar");
+   await BrowserTestUtils.switchTab(gBrowser, tab.previousSibling);
+   await BrowserTestUtils.switchTab(gBrowser, tab);
+   is(gURLBar.textValue, input, "Text is still in URL bar after tab switch");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Invalid URIs fail differently (that is, immediately, in the loadURI call)
+  * if keyword searches are turned off. Test that this works, too.
+  */
+ add_task(async function() {
+   let input = "To be or not to be-that is the question";
+@@ -39,11 +39,11 @@ add_task(async function() {
+   EventUtils.sendKey("return");
+   await errorPageLoaded;
+   is(gURLBar.textValue, input, "Text is still in URL bar");
+   is(tab.linkedBrowser.userTypedValue, input, "Text still stored on browser");
+   await BrowserTestUtils.switchTab(gBrowser, tab.previousSibling);
+   await BrowserTestUtils.switchTab(gBrowser, tab);
+   is(gURLBar.textValue, input, "Text is still in URL bar after tab switch");
+   is(tab.linkedBrowser.userTypedValue, input, "Text still stored on browser");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/base/content/test/urlbar/browser_urlbarOneOffs_searchSuggestions.js b/browser/base/content/test/urlbar/browser_urlbarOneOffs_searchSuggestions.js
+--- a/browser/base/content/test/urlbar/browser_urlbarOneOffs_searchSuggestions.js
++++ b/browser/base/content/test/urlbar/browser_urlbarOneOffs_searchSuggestions.js
+@@ -45,17 +45,17 @@ add_task(async function oneOffReturnAfte
+ 
+   let resultsPromise =
+     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
+                                    `http://mochi.test:8888/?terms=foobar`);
+   EventUtils.synthesizeKey("KEY_Enter");
+   await resultsPromise;
+ 
+   await PlacesTestUtils.clearHistory();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // Clicks a one-off engine after selecting a search suggestion.
+ add_task(async function oneOffClickAfterSuggestion() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ 
+   let typedValue = "foo";
+   await promiseAutocompleteResultPopup(typedValue, window, true);
+@@ -74,17 +74,17 @@ add_task(async function oneOffClickAfter
+   let oneOffs = gURLBar.popup.oneOffSearchButtons.getSelectableButtons(true);
+   let resultsPromise =
+     BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
+                                    `http://mochi.test:8888/?terms=foobar`);
+   EventUtils.synthesizeMouseAtCenter(oneOffs[0], {});
+   await resultsPromise;
+ 
+   await PlacesTestUtils.clearHistory();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function overridden_engine_not_reused() {
+   info("An overridden search suggestion item should not be reused by a search with another engine");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+     let typedValue = "foo";
+     await promiseAutocompleteResultPopup(typedValue, window, true);
+     await BrowserTestUtils.waitForCondition(suggestionsPresent,
+@@ -101,17 +101,17 @@ add_task(async function overridden_engin
+     // Run again the query, check the label has been replaced.
+     await promiseAutocompleteResultPopup(typedValue, window, true);
+     await BrowserTestUtils.waitForCondition(suggestionsPresent,
+                                             "waiting for suggestions");
+     assertState(0, -1, "foo");
+     let newLabel = gURLBar.popup.richlistbox.children[1].label;
+     Assert.notEqual(newLabel, label, "The label should have been updated");
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+ });
+ 
+ function assertState(result, oneOff, textValue = undefined) {
+   Assert.equal(gURLBar.popup.selectedIndex, result,
+                "Expected result should be selected");
+   Assert.equal(gURLBar.popup.oneOffSearchButtons.selectedButtonIndex, oneOff,
+                "Expected one-off should be selected");
+   if (textValue !== undefined) {
+diff --git a/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js b/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js
+--- a/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js
++++ b/browser/base/content/test/urlbar/browser_urlbarRaceWithTabs.js
+@@ -45,13 +45,13 @@ add_task(async function hitEnterLoadInRi
+   let newTab = (await newTabCreatedPromise).target;
+   info("Created new tab; waiting for either tab to load");
+   let newTabLoadedPromise = BrowserTestUtils.browserLoaded(newTab.linkedBrowser, false, kURL);
+   newTabLoadedPromise.then(() => info("New tab loaded"));
+   await Promise.race([newTabLoadedPromise, oldTabLoadedPromise]);
+   is(newTab.linkedBrowser.currentURI.spec, "about:newtab", "New tab still has about:newtab");
+   is(oldTab.linkedBrowser.currentURI.spec, kURL, "Old tab loaded URL");
+   info("Closing new tab");
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+   info("Closing old tab");
+-  await BrowserTestUtils.removeTab(oldTab);
++  BrowserTestUtils.removeTab(oldTab);
+   info("Finished");
+ });
+diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
+--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
++++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions.js
+@@ -32,17 +32,17 @@ add_task(async function clickSuggestion(
+                "Expected suggestion engine");
+   let item = gURLBar.popup.richlistbox.getItemAtIndex(idx);
+ 
+   let uri = Services.search.currentEngine.getSubmission(suggestion).uri;
+   let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser,
+                                                    false, uri.spec);
+   item.click();
+   await loadPromise;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ async function testPressEnterOnSuggestion(expectedUrl = null, keyModifiers = {}) {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+   gURLBar.focus();
+   await promiseAutocompleteResultPopup("foo");
+   let [idx, suggestion, engineName] = await promiseFirstSuggestion();
+   Assert.equal(engineName,
+@@ -56,17 +56,17 @@ async function testPressEnterOnSuggestio
+   let promiseLoad = waitForDocLoadAndStopIt(expectedUrl);
+ 
+   for (let i = 0; i < idx; ++i) {
+     EventUtils.synthesizeKey("KEY_ArrowDown");
+   }
+   EventUtils.synthesizeKey("KEY_Enter", keyModifiers);
+ 
+   await promiseLoad;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function plainEnterOnSuggestion() {
+   await testPressEnterOnSuggestion();
+ });
+ 
+ add_task(async function ctrlEnterOnSuggestion() {
+   await testPressEnterOnSuggestion("http://www.foofoo.com/",
+diff --git a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
+--- a/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
++++ b/browser/base/content/test/urlbar/browser_urlbarSearchSuggestions_opt-out.js
+@@ -70,17 +70,17 @@ add_task(async function new_tab() {
+   let popupPromise = promisePopupShown(gURLBar.popup);
+   // openNewForegroundTab doesn't focus the urlbar.
+   await BrowserTestUtils.synthesizeKey("t", { accelKey: true }, gBrowser.selectedBrowser);
+   await popupPromise;
+   Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
+   assertVisible(true);
+   assertFooterVisible(false);
+   Assert.equal(gURLBar.popup._matchCount, 0, "popup should have no results");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
+ });
+ 
+ add_task(async function privateWindow() {
+   // Since suggestions are disabled in private windows, the notification should
+   // not appear even when suggestions are otherwise enabled.
+   setupVisibleHint();
+   let win = await BrowserTestUtils.openNewBrowserWindow({ private: true });
+diff --git a/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js b/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js
+--- a/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js
++++ b/browser/base/content/test/urlbar/browser_urlbarTrimURLs.js
+@@ -1,15 +1,15 @@
+ add_task(async function() {
+   const PREF_TRIMURLS = "browser.urlbar.trimURLs";
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ 
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     Services.prefs.clearUserPref(PREF_TRIMURLS);
+     URLBarSetURI();
+   });
+ 
+   Services.prefs.setBoolPref(PREF_TRIMURLS, true);
+ 
+   testVal("http://mozilla.org/", "mozilla.org");
+   testVal("https://mozilla.org/", "https://mozilla.org");
+diff --git a/browser/base/content/test/urlbar/browser_urlbar_blanking.js b/browser/base/content/test/urlbar/browser_urlbar_blanking.js
+--- a/browser/base/content/test/urlbar/browser_urlbar_blanking.js
++++ b/browser/base/content/test/urlbar/browser_urlbar_blanking.js
+@@ -3,17 +3,17 @@
+ add_task(async function() {
+   for (let page of gInitialPages) {
+     if (page == "about:newtab") {
+       // New tab preloading makes this a pain to test, so skip
+       continue;
+     }
+     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, page);
+     ok(!gURLBar.value, "The URL bar should be empty if we load a plain " + page + " page.");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+ 
+ add_task(async function() {
+   const URI = "http://www.example.com/browser/browser/base/content/test/urlbar/file_blank_but_not_blank.html";
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URI);
+   is(gURLBar.value, URI, "The URL bar should match the URI");
+   let browserLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+@@ -25,11 +25,11 @@ add_task(async function() {
+   // When reloading, the javascript: uri we're using will throw an exception.
+   // That's deliberate, so we need to tell mochitest to ignore it:
+   SimpleTest.expectUncaughtException(true);
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+     // This is sync, so by the time we return we should have changed the URL bar.
+     content.location.reload();
+   });
+   ok(!!gURLBar.value, "URL bar should not be blank.");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   SimpleTest.expectUncaughtException(false);
+ });
+diff --git a/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js b/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js
+--- a/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js
++++ b/browser/base/content/test/urlbar/browser_urlbar_stop_pending.js
+@@ -33,17 +33,17 @@ add_task(async function() {
+   let pageLoadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   gURLBar.value = expectedURLBarChange;
+   gURLBar.handleCommand();
+   is(gURLBar.value, expectedURLBarChange, "Should not have changed URL bar value synchronously.");
+   await pageLoadPromise;
+   ok(sawChange, "The URL bar change handler should have been called by the time the page was loaded");
+   obs.disconnect();
+   obs = null;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Check that if we:
+  * 1) middle-click a link to a separate page whose server doesn't respond
+  * 2) we switch to that tab and stop the request
+  *
+  * The URL bar continues to contain the URL of the page we wanted to visit.
+@@ -75,18 +75,18 @@ add_task(async function() {
+   let newTab = (await newTabPromise).target;
+   await BrowserTestUtils.switchTab(gBrowser, newTab);
+   is(gURLBar.value, SLOW_HOST, "Should have slow page in URL bar");
+   let browserStoppedPromise = BrowserTestUtils.browserStopped(newTab.linkedBrowser, null, true);
+   BrowserStop();
+   await browserStoppedPromise;
+ 
+   is(gURLBar.value, SLOW_HOST, "Should still have slow page in URL bar after stop");
+-  await BrowserTestUtils.removeTab(newTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ /**
+  * Check that if we:
+  * 1) middle-click a link to a separate page whose server doesn't respond
+  * 2) we alter the URL on that page to some other server that doesn't respond
+  * 3) we stop the request
+  *
+  * The URL bar continues to contain the second URL.
+@@ -128,12 +128,12 @@ add_task(async function() {
+   await browserStoppedPromise;
+ 
+   is(gURLBar.value, SLOW_HOST2, "Should have second slow page in URL bar");
+   browserStoppedPromise = BrowserTestUtils.browserStopped(newTab.linkedBrowser, null, true);
+   BrowserStop();
+   await browserStoppedPromise;
+ 
+   is(gURLBar.value, SLOW_HOST2, "Should still have second slow page in URL bar after stop");
+-  await BrowserTestUtils.removeTab(newTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/base/content/test/urlbar/browser_urlbar_whereToOpen.js.1442465-4_2.later b/browser/base/content/test/urlbar/browser_urlbar_whereToOpen.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/base/content/test/urlbar/browser_urlbar_whereToOpen.js.1442465-4_2.later
+@@ -0,0 +1,72 @@
++--- browser_urlbar_whereToOpen.js
+++++ browser_urlbar_whereToOpen.js
++@@ -35,17 +35,17 @@ add_task(async function openInTab() {
++     info(test.desc);
++ 
++     Preferences.set("browser.urlbar.openintab", test.pref);
++     let where = gURLBar._whereToOpen(test.event);
++     is(where, "tab", "URL would be loaded in a new tab");
++   }
++ 
++   // Clean up.
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function keepEmptyTab() {
++   // Open an empty tab.
++   let tab = gBrowser.selectedTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, EMPTY_TAB);
++ 
++   for (let test of [{pref: false, event: META_CLICK,
++                      desc: "Meta+click, empty tab, default prefs"},
++@@ -54,17 +54,17 @@ add_task(async function keepEmptyTab() {
++     info(test.desc);
++ 
++     Preferences.set("browser.urlbar.openintab", test.pref);
++     let where = gURLBar._whereToOpen(test.event);
++     is(where, "tab", "URL would be loaded in a new tab");
++   }
++ 
++   // Clean up.
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function reuseEmptyTab() {
++   // Open an empty tab.
++   let tab = gBrowser.selectedTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, EMPTY_TAB);
++ 
++   for (let test of [{pref: false, event: ALT_ENTER,
++                      desc: "Alt+Enter, empty tab, default prefs"},
++@@ -74,17 +74,17 @@ add_task(async function reuseEmptyTab() 
++                      desc: "Normal click, empty tab, openInTab"}]) {
++     info(test.desc);
++     Preferences.set("browser.urlbar.openintab", test.pref);
++     let where = gURLBar._whereToOpen(test.event);
++     is(where, "current", "New URL would reuse the current empty tab");
++   }
++ 
++   // Clean up.
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function openInCurrentTab() {
++   for (let test of [{pref: false, url: NON_EMPTY_TAB, event: ENTER,
++                      desc: "Enter, non-empty tab, default prefs"},
++                     {pref: false, url: NON_EMPTY_TAB, event: CLICK,
++                      desc: "Normal click, non-empty tab, default prefs"},
++                     {pref: false, url: EMPTY_TAB, event: ENTER,
++@@ -102,11 +102,11 @@ add_task(async function openInCurrentTab
++     // Open a new tab.
++     let tab = gBrowser.selectedTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, test.url);
++ 
++     Preferences.set("browser.urlbar.openintab", test.pref);
++     let where = gURLBar._whereToOpen(test.event);
++     is(where, "current", "URL would open in the current tab");
++ 
++     // Clean up.
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   }
++ });
+diff --git a/browser/base/content/test/webextensions/browser_extension_sideloading.js b/browser/base/content/test/webextensions/browser_extension_sideloading.js
+--- a/browser/base/content/test/webextensions/browser_extension_sideloading.js
++++ b/browser/base/content/test/webextensions/browser_extension_sideloading.js
+@@ -147,17 +147,17 @@ add_task(async function() {
+ 
+   let [addon1, addon2, addon3, addon4] = await AddonManager.getAddonsByIDs([ID1, ID2, ID3, ID4]);
+   ok(addon1.seen, "Addon should be marked as seen");
+   is(addon1.userDisabled, true, "Addon 1 should still be disabled");
+   is(addon2.userDisabled, true, "Addon 2 should still be disabled");
+   is(addon3.userDisabled, true, "Addon 3 should still be disabled");
+   is(addon4.userDisabled, true, "Addon 4 should still be disabled");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Should still have 3 entries in the hamburger menu
+   await PanelUI.show();
+ 
+   addons = PanelUI.addonNotificationContainer;
+   is(addons.children.length, 3, "Have 3 menu entries for sideloaded extensions");
+ 
+   // Click the second sideloaded extension and wait for the notification
+@@ -251,10 +251,10 @@ add_task(async function() {
+   isnot(menuButton.getAttribute("badge-status"), "addon-alert", "Should no longer have addon alert badge");
+ 
+   await new Promise(resolve => setTimeout(resolve, 100));
+ 
+   for (let addon of [addon1, addon2, addon3, addon4]) {
+     addon.uninstall();
+   }
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/base/content/test/webextensions/browser_extension_update_background.js b/browser/base/content/test/webextensions/browser_extension_update_background.js
+--- a/browser/base/content/test/webextensions/browser_extension_update_background.js
++++ b/browser/base/content/test/webextensions/browser_extension_update_background.js
+@@ -110,17 +110,17 @@ async function backgroundUpdateTest(url,
+   is(list.childElementCount, 1, "Permissions list contains 1 entry");
+ 
+   // Cancel the update.
+   panel.secondaryButton.click();
+ 
+   addon = await AddonManager.getAddonByID(id);
+   is(addon.version, "1.0", "Should still be running the old version");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Alert badge and hamburger menu items should be gone
+   is(getBadgeStatus(), "", "Addon alert badge should be gone");
+ 
+   await PanelUI.show();
+   addons = PanelUI.addonNotificationContainer;
+   is(addons.children.length, 0, "Update menu entries should be gone");
+   await PanelUI.hide();
+@@ -155,17 +155,17 @@ async function backgroundUpdateTest(url,
+   // Wait for the permission prompt and accept it this time
+   updatePromise = waitForUpdate(addon);
+   panel = await popupPromise;
+   panel.button.click();
+ 
+   addon = await updatePromise;
+   is(addon.version, "2.0", "Should have upgraded to the new version");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   is(getBadgeStatus(), "", "Addon alert badge should be gone");
+ 
+   // Should have recorded 1 canceled followed by 1 accepted update.
+   expectTelemetry(["updateRejected", "updateAccepted"]);
+ 
+   addon.uninstall();
+   await SpecialPowers.popPrefEnv();
+diff --git a/browser/base/content/test/webextensions/browser_permissions_unsigned.js b/browser/base/content/test/webextensions/browser_permissions_unsigned.js
+--- a/browser/base/content/test/webextensions/browser_permissions_unsigned.js
++++ b/browser/base/content/test/webextensions/browser_permissions_unsigned.js
+@@ -32,10 +32,10 @@ add_task(async function test_unsigned() 
+   // cancel the install
+   let promise = promiseInstallEvent({id: ID}, "onInstallCancelled");
+   panel.secondaryButton.click();
+   await promise;
+ 
+   let addon = await AddonManager.getAddonByID(ID);
+   is(addon, null, "Extension is not installed");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js b/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
+--- a/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
++++ b/browser/base/content/test/webextensions/browser_update_interactive_noprompt.js
+@@ -42,17 +42,17 @@ async function testUpdateNoPrompt(filena
+   await updatePromise;
+ 
+   addon = await AddonManager.getAddonByID(id);
+   is(addon.version, updateVersion, "Should have upgraded");
+ 
+   ok(!sawPopup, "Should not have seen a permission notification");
+   PopupNotifications.panel.removeEventListener("popupshown", popupListener);
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   addon.uninstall();
+ }
+ 
+ // Test that we don't see a prompt when updating from a legacy
+ // extension to a webextension.
+ add_task(() => testUpdateNoPrompt("browser_legacy.xpi",
+                                   "legacy_update@tests.mozilla.org", "1.1"));
+ 
+diff --git a/browser/base/content/test/webextensions/head.js b/browser/base/content/test/webextensions/head.js
+--- a/browser/base/content/test/webextensions/head.js
++++ b/browser/base/content/test/webextensions/head.js
+@@ -345,17 +345,17 @@ async function testInstallMethod(install
+       ok(!result, "Installation was cancelled");
+       is(addon, null, "Extension is not installed");
+     } else {
+       ok(result, "Installation completed");
+       isnot(addon, null, "Extension is installed");
+       addon.uninstall();
+     }
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ 
+   // A few different tests for each installation method:
+   // 1. Start installation of an extension that requests no permissions,
+   //    verify the notification contents, then cancel the install
+   await runOnce(NO_PERMS_XPI, true);
+ 
+   // 2. Same as #1 but with an extension that requests some permissions.
+@@ -463,17 +463,17 @@ async function interactiveUpdateTest(aut
+   panel = await popupPromise;
+   panel.button.click();
+ 
+   addon = await updatePromise;
+   is(addon.version, "2.0", "Should have upgraded");
+ 
+   await checkPromise;
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   addon.uninstall();
+   await SpecialPowers.popPrefEnv();
+ }
+ 
+ // The tests in this directory install a bunch of extensions but they
+ // need to uninstall them before exiting, as a stray leftover extension
+ // after one test can foul up subsequent tests.
+ // So, add a task to run before any tests that grabs a list of all the
+diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
+--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
++++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_anim.js
+@@ -44,17 +44,17 @@ var gTests = [
+       is(gBrowser.selectedTab.getAttribute("sharing"), "",
+          "the new tab doesn't have the 'sharing' attribute");
+       is(tab.getAttribute("sharing"), aSharing,
+          "the tab still has the 'sharing' attribute");
+       isnot(window.getComputedStyle(icon).display, "none",
+             "the animated sharing icon of the tab is now visible");
+ 
+       // Ensure the icon disappears when selecting the tab.
+-      await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++      BrowserTestUtils.removeTab(gBrowser.selectedTab);
+       ok(tab.selected, "the tab with ongoing sharing is selected again");
+       is(window.getComputedStyle(icon).display, "none",
+          "the animated sharing icon is gone after selecting the tab again");
+ 
+       // And finally verify the attribute is removed when closing the stream.
+       await closeStream();
+ 
+       // TODO(Bug 1304997): Fix the race in closeStream() and remove this
+diff --git a/browser/base/content/test/webrtc/head.js b/browser/base/content/test/webrtc/head.js
+--- a/browser/base/content/test/webrtc/head.js
++++ b/browser/base/content/test/webrtc/head.js
+@@ -549,10 +549,10 @@ async function runTests(tests, options =
+ 
+   for (let testCase of tests) {
+     info(testCase.desc);
+     await testCase.run(browser);
+     await cleanup();
+   }
+ 
+   // Some tests destroy the original tab and leave a new one in its place.
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+diff --git a/browser/components/contextualidentity/test/browser/browser_aboutURLs.js b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
+--- a/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
++++ b/browser/components/contextualidentity/test/browser/browser_aboutURLs.js
+@@ -38,11 +38,11 @@ add_task(async function() {
+ 
+   for (let url of aboutURLs) {
+     info("Loading about:" + url);
+     let tab = BrowserTestUtils.addTab(gBrowser, "about:" + url, {userContextId: 1});
+     await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ 
+     ok(true, "Done loading about:" + url);
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_blobUrl.js b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
+--- a/browser/components/contextualidentity/test/browser/browser_blobUrl.js
++++ b/browser/components/contextualidentity/test/browser/browser_blobUrl.js
+@@ -64,12 +64,12 @@ add_task(async function test() {
+       } catch (e) {
+         resolve("SendThrew");
+       }
+     });
+   }).then(status => {
+     is(status, "SendSucceeded", "Using a blob URI within a single user context id should work");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab3);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab3);
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_eme.js b/browser/components/contextualidentity/test/browser/browser_eme.js
+--- a/browser/components/contextualidentity/test/browser/browser_eme.js
++++ b/browser/components/contextualidentity/test/browser/browser_eme.js
+@@ -169,13 +169,13 @@ add_task(async function test() {
+ 
+     let map = session.keyStatuses;
+ 
+     // Check that there is no media key here.
+     is(map.size, 0, "No media key should be here for the personal container.");
+   });
+ 
+   // Close default container tab.
+-  await BrowserTestUtils.removeTab(defaultContainer.tab);
++  BrowserTestUtils.removeTab(defaultContainer.tab);
+ 
+   // Close personal container tab.
+-  await BrowserTestUtils.removeTab(personalContainer.tab);
++  BrowserTestUtils.removeTab(personalContainer.tab);
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_favicon.js b/browser/components/contextualidentity/test/browser/browser_favicon.js
+--- a/browser/components/contextualidentity/test/browser/browser_favicon.js
++++ b/browser/components/contextualidentity/test/browser/browser_favicon.js
+@@ -126,11 +126,11 @@ add_task(async function test() {
+         true, PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE, {
+           onComplete() {
+             resolve();
+           },
+         },
+         tabInfo.browser.contentPrincipal);
+     });
+ 
+-    await BrowserTestUtils.removeTab(tabInfo.tab);
++    BrowserTestUtils.removeTab(tabInfo.tab);
+   }
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
+--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
++++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
+@@ -191,28 +191,28 @@ add_task(async function test_EME_forgetT
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "empty_file.html", userContextId);
+ 
+     // Setup EME Key.
+     emeSessionIds[userContextId] = await setupEMEKey(tabs[userContextId].browser);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   // Clear all EME data for a given domain with originAttributes pattern.
+   let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].
+                getService(Ci.mozIGeckoMediaPluginChromeService);
+   mps.forgetThisSite(TEST_HOST, JSON.stringify({}));
+ 
+   // Open tabs again to check EME keys have been cleared.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "empty_file.html", userContextId);
+ 
+     // Check whether EME Key has been cleared.
+     await checkEMEKey(tabs[userContextId].browser, emeSessionIds[userContextId]);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
+--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
++++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
+@@ -52,17 +52,17 @@ add_task(async function test_cookie_getC
+     // Load the page in 2 different contexts and set a cookie
+     // which should only be visible in that context.
+     let value = USER_CONTEXTS[userContextId];
+ 
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "file_reflect_cookie_into_title.html?" + value, userContextId);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   // Check that cookies have been set properly.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+     ok(enumerator.hasMoreElements(), "Cookies available");
+ 
+     let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+diff --git a/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
+--- a/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
++++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
+@@ -112,17 +112,17 @@ add_task(async function test_quota_clear
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "empty_file.html", userContextId);
+ 
+     // Setup an entry for the indexedDB.
+     await setupIndexedDB(tabs[userContextId].browser);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   // Using quota manager to clear all indexed DB for a given domain.
+   let caUtils = {};
+   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js",
+                                       caUtils);
+   let httpURI = caUtils.makeURI("http://" + TEST_HOST);
+   let httpPrincipal = Services.scriptSecurityManager
+@@ -132,11 +132,11 @@ add_task(async function test_quota_clear
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "empty_file.html", userContextId);
+ 
+     // Check whether indexed DB has been cleared.
+     await checkIndexedDB(tabs[userContextId].browser);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
+--- a/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
++++ b/browser/components/contextualidentity/test/browser/browser_forgetaboutsite.js
+@@ -126,17 +126,17 @@ async function test_cookie_cleared() {
+     // Load the page in 2 different contexts and set a cookie
+     // which should only be visible in that context.
+     let value = USER_CONTEXTS[userContextId];
+ 
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "file_reflect_cookie_into_title.html?" + value, userContextId);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+   // Check that cookies have been set properly.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+     ok(enumerator.hasMoreElements(), "Cookies available");
+ 
+     let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+     Assert.equal(foundCookie.name, COOKIE_NAME, "Check cookie name");
+@@ -194,17 +194,17 @@ async function test_cache_cleared() {
+ // Image Cache
+ async function test_image_cache_cleared() {
+   let tabs = [];
+ 
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context to cache image.
+     tabs[userContextId] = await openTabInUserContext("http://localhost:" + gHttpServer.identity.primaryPort + "/loadImage.html",
+                                                       userContextId);
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   let expectedHits = USER_CONTEXTS.length;
+ 
+   // Check that image cache works with the userContextId.
+   is(gHits, expectedHits, "The image should be loaded" + expectedHits + "times.");
+ 
+   // Reset the cache count.
+@@ -213,17 +213,17 @@ async function test_image_cache_cleared(
+   // Forget the site.
+   await ForgetAboutSite.removeDataFromDomain("localhost:" + gHttpServer.identity.primaryPort + "/");
+ 
+   // Load again.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     // Open our tab in the given user context to cache image.
+     tabs[userContextId] = await openTabInUserContext("http://localhost:" + gHttpServer.identity.primaryPort + "/loadImage.html",
+                                                       userContextId);
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   // Check that image cache was cleared and the server gets another two hits.
+   is(gHits, expectedHits, "The image should be loaded" + expectedHits + "times.");
+ }
+ 
+ // Offline Storage
+ async function test_storage_cleared() {
+@@ -261,17 +261,17 @@ async function test_storage_cleared() {
+           let res = storeRequest.result;
+           Assert.equal(res.userContext, arg.userContext, "Check the indexedDB value");
+           done();
+         };
+       });
+     });
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabInfo.tab);
++    BrowserTestUtils.removeTab(tabInfo.tab);
+   }
+ 
+   // Forget the site.
+   await ForgetAboutSite.removeDataFromDomain(TEST_HOST);
+ 
+   // Open the tab again without setting the localStorage and check that the
+   // local storage has been cleared or not.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+@@ -298,17 +298,17 @@ async function test_storage_cleared() {
+         db.transaction(["obj"], "readonly");
+         Assert.ok(false, "The indexedDB should not exist");
+       } catch (e) {
+         Assert.equal(e.name, "NotFoundError", "The indexedDB does not exist as expected");
+       }
+     });
+ 
+     // Close the tab.
+-    await BrowserTestUtils.removeTab(tabInfo.tab);
++    BrowserTestUtils.removeTab(tabInfo.tab);
+   }
+ }
+ 
+ add_task(async function setup() {
+   // Make sure userContext is enabled.
+   await SpecialPowers.pushPrefEnv({"set": [
+     ["privacy.userContext.enabled", true]
+   ]});
+diff --git a/browser/components/contextualidentity/test/browser/browser_newtabButton.js b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
+--- a/browser/components/contextualidentity/test/browser/browser_newtabButton.js
++++ b/browser/components/contextualidentity/test/browser/browser_newtabButton.js
+@@ -26,17 +26,17 @@ add_task(async function test_menu_with_t
+     ok(contextIdItem, `User context id ${i} exists`);
+ 
+     let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+     EventUtils.synthesizeMouseAtCenter(contextIdItem, {});
+ 
+     let tab = await waitForTabPromise;
+ 
+     is(tab.getAttribute("usercontextid"), i, `New tab has UCI equal ${i}`);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+ 
+ add_task(async function test_menu_without_timeout() {
+   await SpecialPowers.pushPrefEnv({"set": [
+       ["privacy.userContext.enabled", true],
+       ["privacy.userContext.longPressBehavior", 1]
+   ]});
+@@ -58,17 +58,17 @@ add_task(async function test_menu_withou
+     ok(contextIdItem, `User context id ${i} exists`);
+ 
+     let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+     EventUtils.synthesizeMouseAtCenter(contextIdItem, {});
+ 
+     let tab = await waitForTabPromise;
+ 
+     is(tab.getAttribute("usercontextid"), i, `New tab has UCI equal ${i}`);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+ 
+ add_task(async function test_no_menu() {
+   await SpecialPowers.pushPrefEnv({"set": [
+       ["privacy.userContext.enabled", true],
+       ["privacy.userContext.longPressBehavior", 0]
+   ]});
+diff --git a/browser/components/contextualidentity/test/browser/browser_relatedTab.js b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
+--- a/browser/components/contextualidentity/test/browser/browser_relatedTab.js
++++ b/browser/components/contextualidentity/test/browser/browser_relatedTab.js
+@@ -5,27 +5,27 @@
+  */
+ 
+ add_task(async function() {
+   let tab = BrowserTestUtils.addTab(gBrowser, "about:blank", {userContextId: 1});
+ 
+   gBrowser.selectedTab = tab;
+   let relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {relatedToCurrent: true});
+   is(relatedTab.getAttribute("usercontextid"), 1, "Related tab (relatedToCurrent) inherits current tab's usercontextid");
+-  await BrowserTestUtils.removeTab(relatedTab);
++  BrowserTestUtils.removeTab(relatedTab);
+ 
+   gBrowser.selectedTab = tab;
+   relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {relatedToCurrent: true, userContextId: 2});
+   is(relatedTab.getAttribute("usercontextid"), 2, "Related tab (relatedToCurrent) with overridden usercontextid");
+-  await BrowserTestUtils.removeTab(relatedTab);
++  BrowserTestUtils.removeTab(relatedTab);
+ 
+   gBrowser.selectedTab = tab;
+   relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {referrerURI: gBrowser.currentURI});
+   is(relatedTab.getAttribute("usercontextid"), 1, "Related tab (referrer) inherits current tab's usercontextid");
+-  await BrowserTestUtils.removeTab(relatedTab);
++  BrowserTestUtils.removeTab(relatedTab);
+ 
+   gBrowser.selectedTab = tab;
+   relatedTab = BrowserTestUtils.addTab(gBrowser, "about:blank", {referrerURI: gBrowser.currentURI, userContextId: 2});
+   is(relatedTab.getAttribute("usercontextid"), 2, "Related tab (referrer) with overridden usercontextid");
+-  await BrowserTestUtils.removeTab(relatedTab);
++  BrowserTestUtils.removeTab(relatedTab);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
+--- a/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
++++ b/browser/components/contextualidentity/test/browser/browser_restore_getCookiesWithOriginAttributes.js
+@@ -81,17 +81,17 @@ add_task(async function test_cookie_getC
+     // Load the page in different contexts and set a cookie
+     // which should only be visible in that context.
+     let value = USER_CONTEXTS[userContextId];
+ 
+     // Open our tab in the given user context.
+     tabs[userContextId] = await openTabInUserContext(TEST_URL + "file_reflect_cookie_into_title.html?" + value, userContextId);
+ 
+     // Close this tab.
+-    await BrowserTestUtils.removeTab(tabs[userContextId].tab);
++    BrowserTestUtils.removeTab(tabs[userContextId].tab);
+   }
+ 
+   // Check that cookies have been set properly.
+   for (let userContextId of Object.keys(USER_CONTEXTS)) {
+     let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+     ok(enumerator.hasMoreElements(), "Cookies available");
+ 
+     let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+diff --git a/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
+--- a/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
++++ b/browser/components/contextualidentity/test/browser/browser_usercontextid_tabdrop.js
+@@ -40,18 +40,18 @@ add_task(async function() {
+ 
+     // referrer is empty when urls are dragged to new or existing tabs.
+     // If this changes in the future, it would be okay to send the referrer
+     // in this case because we are creating a new tab with the default
+     // usercontextid as the original tab.
+     Assert.equal(content.document.referrer, "", "referrer should be empty");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ /**
+  * When dragging an URL to a new tab, the new tab should have the same
+  * userContextId as the original tab.
+  */
+ add_task(async function() {
+   let tab = BrowserTestUtils.addTab(gBrowser, "http://example.com/", {userContextId: 1});
+@@ -87,18 +87,18 @@ add_task(async function() {
+ 
+     // referrer is empty when urls are dragged to new or existing tabs.
+     // If this changes in the future, it would be okay to send the referrer
+     // in this case because we are creating a new tab with the same
+     // usercontextid as the original tab.
+     Assert.equal(content.document.referrer, "", "referrer should be empty");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ /**
+  * When dragging a URL from one tab or link on a tab to an existing tab, the
+  * existing tab should not change its userContextId.
+  * Ex: if you drag a link from tab 1 with userContext 1 to tab 2 with
+  * userContext 2, the link will open in tab 2 with userContext 2.
+  */
+@@ -124,11 +124,11 @@ add_task(async function() {
+ 
+     // referrer is empty when urls are dragged to new or existing tabs.
+     // If this changes in the future, we should ensure that we are not sending
+     // a referrer for this case!  When opening links across user contexts, we
+     // don't want the referrer to follow the user from one context to another.
+     Assert.equal(content.document.referrer, "", "referrer should be empty");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/contextualidentity/test/browser/browser_windowOpen.js b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
+--- a/browser/components/contextualidentity/test/browser/browser_windowOpen.js
++++ b/browser/components/contextualidentity/test/browser/browser_windowOpen.js
+@@ -30,10 +30,10 @@ add_task(async function test() {
+   let newWin = await BrowserTestUtils.waitForNewWindow();
+   let newTab = newWin.gBrowser.selectedTab;
+ 
+   await BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
+   is(newTab.getAttribute("usercontextid"), 1, "New tab has UCI equal 1");
+ 
+   info("Closing the new window and tab...");
+   await BrowserTestUtils.closeWindow(newWin);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/customizableui/test/browser_947914_button_history.js.1442465-4_2.later b/browser/components/customizableui/test/browser_947914_button_history.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/browser_947914_button_history.js.1442465-4_2.later
+@@ -0,0 +1,34 @@
++--- browser_947914_button_history.js
+++++ browser_947914_button_history.js
++@@ -4,17 +4,17 @@
++ 
++ "use strict";
++ 
++ const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
++ 
++ add_task(async function() {
++   info("Check history button existence and functionality");
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH + "dummy_history_item.html");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PATH); // will 404, but we don't care.
++ 
++   CustomizableUI.addWidgetToArea("history-panelmenu", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
++   registerCleanupFunction(() => CustomizableUI.reset());
++ 
++   await waitForOverflowButtonShown();
++ 
++@@ -36,11 +36,11 @@ add_task(async function() {
++   let historyItems = document.getElementById("appMenu_historyMenu");
++   let historyItemForURL = historyItems.querySelector("toolbarbutton.bookmark-item[label='Happy History Hero']");
++   ok(historyItemForURL, "Should have a history item for the history we just made.");
++   EventUtils.synthesizeMouseAtCenter(historyItemForURL, {});
++   await browserLoaded;
++   is(gBrowser.currentURI.spec, TEST_PATH + "dummy_history_item.html", "Should have expected page load");
++ 
++   await panelHiddenPromise;
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   info("Menu panel was closed");
++ });
+diff --git a/browser/components/customizableui/test/browser_967000_button_charEncoding.js b/browser/components/customizableui/test/browser_967000_button_charEncoding.js
+--- a/browser/components/customizableui/test/browser_967000_button_charEncoding.js
++++ b/browser/components/customizableui/test/browser_967000_button_charEncoding.js
+@@ -48,16 +48,16 @@ add_task(async function() {
+   is(characterEncodingView.querySelectorAll("#PanelUI-characterEncodingView-autodetect toolbarbutton[checked='true']").length,
+      1,
+      "There should be 1 checked detector.");
+ 
+   panelHidePromise = promisePanelHidden(window);
+   PanelUI.hide();
+   await panelHidePromise;
+ 
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ });
+ 
+ add_task(async function asyncCleanup() {
+   // reset the panel to the default state
+   await resetCustomization();
+   ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+ });
+diff --git a/browser/components/customizableui/test/browser_987640_charEncoding.js b/browser/components/customizableui/test/browser_987640_charEncoding.js
+--- a/browser/components/customizableui/test/browser_987640_charEncoding.js
++++ b/browser/components/customizableui/test/browser_987640_charEncoding.js
+@@ -46,16 +46,16 @@ add_task(async function() {
+   charEncodingButton.click();
+   tabLoadPromise = promiseTabLoadEvent(gBrowser.selectedTab, TEST_PAGE);
+   initialEncoding.click();
+   await tabLoadPromise;
+   await PanelUI.show();
+   charEncodingButton.click();
+   checkedButtons = characterEncodingView.querySelectorAll("toolbarbutton[checked='true']");
+   is(checkedButtons[0].getAttribute("label"), "Unicode", "The encoding was reset to Unicode");
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ });
+ 
+ add_task(async function asyncCleanup() {
+   // reset the panel to the default state
+   await resetCustomization();
+   ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
+ });
+diff --git a/browser/components/customizableui/test/browser_backfwd_enabled_post_customize.js.1442465-4_2.later b/browser/components/customizableui/test/browser_backfwd_enabled_post_customize.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/browser_backfwd_enabled_post_customize.js.1442465-4_2.later
+@@ -0,0 +1,15 @@
++--- browser_backfwd_enabled_post_customize.js
+++++ browser_backfwd_enabled_post_customize.js
++@@ -27,11 +27,11 @@ add_task(async function test_back_forwar
++ 
++   is(backButton.getAttribute("disabled"), "true", "Back button should be disabled in customize mode");
++   is(forwardButton.getAttribute("disabled"), "true", "Forward button should be disabled in customize mode");
++ 
++   await endCustomizing();
++   ok(!backButton.hasAttribute("disabled"), "Back button shouldn't be disabled after customize mode");
++   ok(!forwardButton.hasAttribute("disabled"), "Forward button shouldn't be disabled after customize mode");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
+diff --git a/browser/components/customizableui/test/browser_customization_context_menus.js.1442465-4_2.later b/browser/components/customizableui/test/browser_customization_context_menus.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/customizableui/test/browser_customization_context_menus.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_customization_context_menus.js
+++++ browser_customization_context_menus.js
++@@ -69,17 +69,17 @@ add_task(async function tabstrip_context
++     ["---"],
++     [".viewCustomizeToolbar", true]
++   );
++   checkContextMenu(contextMenu, expectedEntries);
++ 
++   let hiddenPromise = popupHidden(contextMenu);
++   contextMenu.hidePopup();
++   await hiddenPromise;
++-  await BrowserTestUtils.removeTab(extraTab);
+++  BrowserTestUtils.removeTab(extraTab);
++ });
++ 
++ // Right-click on an empty bit of extra toolbar should
++ // show a context menu with moving options disabled,
++ // and a toggle option for the extra toolbar
++ add_task(async function empty_toolbar_context() {
++   let contextMenu = document.getElementById("toolbar-context-menu");
++   let shownPromise = popupShown(contextMenu);
+diff --git a/browser/components/customizableui/test/browser_exit_background_customize_mode.js b/browser/components/customizableui/test/browser_exit_background_customize_mode.js
+--- a/browser/components/customizableui/test/browser_exit_background_customize_mode.js
++++ b/browser/components/customizableui/test/browser_exit_background_customize_mode.js
+@@ -27,10 +27,10 @@ add_task(async function test_exit_backgr
+   Assert.equal(gBrowser.tabContainer.querySelector("tab[customizemode=true]"),
+                null,
+                "Should not have a tab marked as being the customize tab now.");
+ 
+   await startCustomizing();
+   is(gBrowser.tabs.length, 3, "Should have 3 tabs now");
+ 
+   await endCustomizing();
+-  await BrowserTestUtils.removeTab(custTab);
++  BrowserTestUtils.removeTab(custTab);
+ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_addons.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_addons.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_addons.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_block_about_addons.js
+++++ browser_policy_block_about_addons.js
++@@ -20,10 +20,10 @@ add_task(async function test_about_addon
++     // There is currently a testing-specific race condition that causes this test
++     // to fail, but it is not a problem if we test after the first page load.
++     // Until the race condition is fixed, just make sure to test this *after*
++     // testing the page load.
++     is(Services.policies.isAllowed("about:addons"), false,
++        "Policy Engine should report about:addons as not allowed");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_config.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_config.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_config.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_block_about_config.js
+++++ browser_policy_block_about_config.js
++@@ -18,10 +18,10 @@ add_task(async function test_about_confi
++     // There is currently a testing-specific race condition that causes this test
++     // to fail, but it is not a problem if we test after the first page load.
++     // Until the race condition is fixed, just make sure to test this *after*
++     // testing the page load.
++     is(Services.policies.isAllowed("about:config"), false,
++        "Policy Engine should report about:config as not allowed");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_profiles.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_profiles.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_profiles.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_block_about_profiles.js
+++++ browser_policy_block_about_profiles.js
++@@ -20,10 +20,10 @@ add_task(async function test_about_profi
++     // There is currently a testing-specific race condition that causes this test
++     // to fail, but it is not a problem if we test after the first page load.
++     // Until the race condition is fixed, just make sure to test this *after*
++     // testing the page load.
++     is(Services.policies.isAllowed("about:profiles"), false,
++        "Policy Engine should report about:profiles as not allowed");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_support.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_support.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_about_support.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_block_about_support.js
+++++ browser_policy_block_about_support.js
++@@ -20,10 +20,10 @@ add_task(async function test_about_suppo
++     // There is currently a testing-specific race condition that causes this test
++     // to fail, but it is not a problem if we test after the first page load.
++     // Until the race condition is fixed, just make sure to test this *after*
++     // testing the page load.
++     is(Services.policies.isAllowed("about:support"), false,
++        "Policy Engine should report about:support as not allowed");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_block_set_desktop_background.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_block_set_desktop_background.js
+++++ browser_policy_block_set_desktop_background.js
++@@ -29,10 +29,10 @@ add_task(async function test_check_set_d
++ 
++   let buttonElement = document.getElementById("context-setDesktopBackground");
++   is(buttonElement.hidden, true,
++      "The \"Set Desktop Background\" context menu element should be hidden");
++ 
++   let promiseContextMenuHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
++   contextMenu.hidePopup();
++   await promiseContextMenuHidden;
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/browser_policy_set_homepage.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_set_homepage.js
+++++ browser_policy_set_homepage.js
++@@ -161,10 +161,10 @@ add_task(async function homepage_test_lo
++        "Homepage input should be disabled");
++     is(content.document.getElementById("useCurrent").disabled, true,
++        "\"Use Current Page\" button should be disabled");
++     is(content.document.getElementById("useBookmark").disabled, true,
++        "\"Use Bookmark...\" button should be disabled");
++     is(content.document.getElementById("restoreDefaultHomePage").disabled, true,
++        "\"Restore to Default\" button should be disabled");
++   });
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/disable_app_update/browser_policy_disable_app_update.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_policy_disable_app_update.js
+++++ browser_policy_disable_app_update.js
++@@ -19,10 +19,10 @@ add_task(async function test_update_pref
++   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
++     let updateRadioGroup = content.document.getElementById("updateRadioGroup");
++     is(updateRadioGroup.disabled, true,
++        "Update choices should be diabled when app update is locked by policy");
++     is(updateRadioGroup.value, "manual",
++        "Update choice should be set to \"manual\" when app update is disabled by policy");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js.1442465-4_2.later b/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/enterprisepolicies/tests/browser/disable_developer_tools/browser_policy_disable_developer_tools.js.1442465-4_2.later
+@@ -0,0 +1,25 @@
++--- browser_policy_disable_developer_tools.js
+++++ browser_policy_disable_developer_tools.js
++@@ -25,20 +25,20 @@ add_task(async function test_updates_pos
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testURL, false);
++ 
++   info("Check that devtools menu items are hidden");
++   let toolsMenu = window.document.getElementById("webDeveloperMenu");
++   ok(toolsMenu.hidden, "The Web Developer item of the tools menu is hidden");
++   let hamburgerMenu = window.document.getElementById("appMenu-developer-button");
++   ok(hamburgerMenu.hidden, "The Web Developer item of the hamburger menu is hidden");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ const expectErrorPage = async function(url) {
++   info(`Wait for ${url} to open the net error page`);
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
++   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
++     ok(content.document.documentURI.startsWith("about:neterror"),
++        "DevTools about: page should display the net error page");
++   });
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ };
+diff --git a/browser/components/extensions/test/browser/browser_ext_autocompletepopup.js b/browser/components/extensions/test/browser/browser_ext_autocompletepopup.js
+--- a/browser/components/extensions/test/browser/browser_ext_autocompletepopup.js
++++ b/browser/components/extensions/test/browser/browser_ext_autocompletepopup.js
+@@ -75,10 +75,10 @@ add_task(async function testAutocomplete
+   clickBrowserAction(extension);
+   bowser = await awaitExtensionPanel(extension);
+   ok(!!bowser, "panel opened with browser");
+   await testDatalist(bowser, document);
+   closeBrowserAction(extension);
+   await new Promise(resolve => setTimeout(resolve, 0));
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js b/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
+--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
++++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup_preload.js
+@@ -77,17 +77,17 @@ add_task(async function testBrowserActio
+   is(browserAction.pendingPopup, null, "Pending popup was cleared");
+   is(browserAction.pendingPopupTimeout, null, "Pending popup timeout was cleared");
+ 
+   await promisePopupShown(getBrowserActionPopup(extension));
+   await closeBrowserAction(extension);
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function testBrowserActionDisabled() {
+   // Make sure the mouse isn't hovering over the browserAction widget.
+   EventUtils.synthesizeMouseAtCenter(gURLBar, {type: "mouseover"}, window);
+ 
+   let extension = ExtensionTestUtils.loadExtension({
+     manifest: {
+diff --git a/browser/components/extensions/test/browser/browser_ext_contentscript_animate.js b/browser/components/extensions/test/browser/browser_ext_contentscript_animate.js
+--- a/browser/components/extensions/test/browser/browser_ext_contentscript_animate.js
++++ b/browser/components/extensions/test/browser/browser_ext_contentscript_animate.js
+@@ -37,17 +37,17 @@ add_task(async function test_animate() {
+       },
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("contentScriptAnimate");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_KeyframeEffect() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+ 
+   let extension = ExtensionTestUtils.loadExtension({
+     manifest: {
+       "content_scripts": [
+@@ -86,10 +86,10 @@ add_task(async function test_KeyframeEff
+       },
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("contentScriptKeyframeEffect");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js b/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
+--- a/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
++++ b/browser/components/extensions/test/browser/browser_ext_contentscript_connect.js
+@@ -58,10 +58,10 @@ add_task(async function() {
+       },
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("contentscript_connect.pass");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus.js b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus.js
+@@ -40,17 +40,17 @@ add_task(async function() {
+ 
+   contentAreaContextMenu = await openContextMenu("body");
+   item = contentAreaContextMenu.getElementsByAttribute("label", "Click me!");
+   is(item.length, 1, "contextMenu item for page was found");
+   await closeContextMenu();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+ 
+ add_task(async function() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ 
+   gBrowser.selectedTab = tab1;
+ 
+   let extension = ExtensionTestUtils.loadExtension({
+@@ -369,17 +369,17 @@ add_task(async function() {
+   await extension.awaitMessage("removed");
+ 
+   let contentAreaContextMenu = await openContextMenu("#img1");
+   items = contentAreaContextMenu.getElementsByAttribute("ext-type", "top-level-menu");
+   is(items.length, 0, "top level item was not found (after removeAll()");
+   await closeContextMenu();
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+ 
+ add_task(async function testRemoveAllWithTwoExtensions() {
+   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+   const manifest = {permissions: ["contextMenus"]};
+ 
+   const first = ExtensionTestUtils.loadExtension({manifest, background() {
+     browser.contextMenus.create({title: "alpha", contexts: ["all"]});
+@@ -443,10 +443,10 @@ add_task(async function testRemoveAllWit
+   await closeExtensionContextMenu(beta);
+ 
+   // Confirm only gamma is left.
+   await confirmMenuItems("gamma");
+   await closeContextMenu();
+ 
+   await first.unload();
+   await second.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_checkboxes.js
+@@ -115,10 +115,10 @@ add_task(async function() {
+   extensionMenuRoot = await openExtensionContextMenu();
+   items = confirmCheckboxStates(extensionMenuRoot, [false, true, true]);
+   await closeExtensionContextMenu(items[2]);
+ 
+   result = await extension.awaitMessage("contextmenus-click");
+   confirmOnClickData(result, 3, true, false);
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_commands.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_commands.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_commands.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_commands.js
+@@ -71,11 +71,11 @@ add_task(async function() {
+   // open a page so page action works
+   const PAGE = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context.html?test=commands";
+   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ 
+   ok(await testContext("open_sidebar_action"), "_execute_sidebar_action worked");
+   ok(await testContext("open_browser_action"), "_execute_browser_action worked");
+   ok(await testContext("open_page_action"), "_execute_page_action worked");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_icons.js
+@@ -65,17 +65,17 @@ add_task(async function test_root_icon()
+ 
+   contextMenu = document.getElementById("contentAreaContextMenu");
+   topLevelMenuItem = contextMenu.getElementsByAttribute("label", "child");
+ 
+   confirmContextMenuIcon(topLevelMenuItem);
+   await closeContextMenu();
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_child_icon() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
+ 
+   let blackIconData = "iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QYGEhkO2P07+gAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAARSURBVCjPY2AYBaNgFAxPAAAD3gABo0ohTgAAAABJRU5ErkJggg==";
+   const IMAGE_ARRAYBUFFER_BLACK = imageBufferFromDataURI(blackIconData);
+ 
+@@ -167,10 +167,10 @@ add_task(async function test_child_icon(
+   confirmContextMenuIcon(contextMenuChild2, "blue_icon.png");
+ 
+   let contextMenuChild3 = contextMenu.getElementsByAttribute("label", "child3")[0];
+   confirmContextMenuIcon(contextMenuChild3, "green_icon.png");
+ 
+   await closeContextMenu();
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_onclick.js
+@@ -188,17 +188,17 @@ add_task(async function() {
+ 
+       // Remove context menu for the next iteration of the test. And just to get
+       // more coverage, let's use removeAll instead of remove.
+       extension.sendMessage(pageOne, "removeAll");
+       await extension.awaitMessage("next");
+     }
+   }
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+ 
+ add_task(async function test_onclick_modifiers() {
+   const manifest = {
+     permissions: ["contextMenus"],
+   };
+ 
+   function background() {
+@@ -237,11 +237,11 @@ add_task(async function test_onclick_mod
+ 
+     const meta = await click({metaKey: true});
+     is(meta.modifiers.join(), "Command", "Correct modifier: Command");
+   }
+ 
+   const altShift = await click({altKey: true, shiftKey: true});
+   is(altShift.modifiers.sort().join(), "Alt,Shift", "Correct modifiers: Shift+Alt");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_radioGroups.js
+@@ -91,10 +91,10 @@ add_task(async function() {
+   extensionMenuRoot = await openExtensionContextMenu();
+   items = confirmRadioGroupStates(extensionMenuRoot, [true, false, true]);
+   await closeExtensionContextMenu(items[0]);
+ 
+   result = await extension.awaitMessage("contextmenus-click");
+   confirmOnClickData(result, 1, true, true);
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_uninstall.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_uninstall.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_uninstall.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_uninstall.js
+@@ -75,10 +75,10 @@ add_task(async function() {
+   contextMenu = await openContextMenu("#img1");
+ 
+   // Confirm that no extension menu items exist.
+   topLevelExtensionMenuItems = contextMenu.getElementsByAttribute("ext-type", "top-level-menu");
+   is(topLevelExtensionMenuItems.length, 0, "no top level extension menu items should exist");
+ 
+   await closeContextMenu();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_contextMenus_urlPatterns.js b/browser/components/extensions/test/browser/browser_ext_contextMenus_urlPatterns.js
+--- a/browser/components/extensions/test/browser/browser_ext_contextMenus_urlPatterns.js
++++ b/browser/components/extensions/test/browser/browser_ext_contextMenus_urlPatterns.js
+@@ -259,10 +259,10 @@ add_task(async function() {
+   expected = [
+     ["documentUrlPatterns-patternMatches-contextAll", true],
+     ["documentUrlPatterns-patternMatches-contextFrame", true],
+   ];
+   await confirmContextMenuItems(contextMenu, expected);
+   await closeContextMenu();
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow.js
+@@ -122,17 +122,17 @@ add_task(async function test_devtools_in
+      "Got the expected tabId from devtool.inspectedWindow.tabId called in a devtool_page iframe");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_devtools_inspectedWindow_eval() {
+   const TEST_TARGET_URL = "http://mochi.test:8888/";
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_TARGET_URL);
+ 
+   function devtools_page() {
+     browser.test.onMessage.addListener(async (msg, ...args) => {
+@@ -244,17 +244,17 @@ add_task(async function test_devtools_in
+   }
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * This test asserts that both the page and the panel can use devtools.inspectedWindow.
+  * See regression in Bug https://bugzilla.mozilla.org/show_bug.cgi?id=1392531
+  */
+ add_task(async function test_devtools_inspectedWindow_eval_in_page_and_panel() {
+   const TEST_TARGET_URL = "http://mochi.test:8888/";
+@@ -362,10 +362,10 @@ add_task(async function test_devtools_in
+   info("Wait for response from the panel");
+   ({evalResult} = await extension.awaitMessage(`inspectedWindow-panel-eval-result`));
+   Assert.deepEqual(evalResult, TEST_TARGET_URL, "Got the expected eval result in the panel");
+ 
+   // Cleanup
+   await gDevTools.closeToolbox(target);
+   await target.destroy();
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_eval_bindings.js
+@@ -184,10 +184,10 @@ add_task(async function test_devtools_in
+   info("Split console has been opened as expected");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_inspectedWindow_reload.js
+@@ -49,17 +49,17 @@ async function runReloadTestCase({urlPar
+ 
+   // Run the test case.
+   await testCase(extension);
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ }
+ 
+ add_task(async function test_devtools_inspectedWindow_reload_ignore_cache() {
+   function background() {
+     // Wait until the devtools page is ready to run the test.
+     browser.runtime.onMessage.addListener(async (msg) => {
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_network.js b/browser/components/extensions/test/browser/browser_ext_devtools_network.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_network.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_network.js
+@@ -138,17 +138,17 @@ add_task(async function test_devtools_ne
+   await extension.awaitMessage("tabUpdated");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Test for `chrome.devtools.network.getHAR()` API
+  */
+ add_task(async function test_devtools_network_get_har() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+   let extension = ExtensionTestUtils.loadExtension(extData);
+@@ -193,17 +193,17 @@ add_task(async function test_devtools_ne
+ 
+   // Shutdown
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Test for `chrome.devtools.network.onRequestFinished()` API
+  */
+ add_task(async function test_devtools_network_on_request_finished() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+   let extension = ExtensionTestUtils.loadExtension(extData);
+@@ -246,10 +246,10 @@ add_task(async function test_devtools_ne
+ 
+   // Shutdown
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_page.js b/browser/components/extensions/test/browser/browser_ext_devtools_page.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_page.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_page.js
+@@ -149,17 +149,17 @@ add_task(async function test_devtools_pa
+   await extension.awaitMessage("content_script_port_received");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * This test file ensures that:
+  *
+  * - the devtools_page can exchange messages with an extension tab page
+  */
+ 
+@@ -272,10 +272,10 @@ add_task(async function test_devtools_pa
+   await extension.awaitMessage("extension_tab_port_received");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_panel.js
+@@ -85,17 +85,17 @@ add_task(async function test_theme_name_
+ 
+   await testThemeSwitching(extension);
+ 
+   await gDevTools.closeToolbox(target);
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_devtools_page_panels_create() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+ 
+   async function devtools_page() {
+     const result = {
+       devtoolsPageTabId: browser.devtools.inspectedWindow.tabId,
+@@ -298,17 +298,17 @@ add_task(async function test_devtools_pa
+   is(toolToggledResults.panelShown, 3, "panel.onShown listener has been called three times");
+   is(toolToggledResults.panelHidden, 3, "panel.onHidden listener has been called three times");
+ 
+   await gDevTools.closeToolbox(target);
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_devtools_page_panels_switch_toolbox_host() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+ 
+   function devtools_panel() {
+     const hasDevToolsAPINamespace = "devtools" in browser;
+ 
+@@ -422,17 +422,17 @@ add_task(async function test_devtools_pa
+   await extension.awaitMessage("devtools_panel_shown");
+   await extension.awaitMessage("devtools_panel_loaded");
+ 
+   await gDevTools.closeToolbox(target);
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_devtools_page_invalid_panel_urls() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/");
+ 
+   async function devtools_page() {
+     const matchInvalidPanelURL = /must be a relative URL/;
+     const matchInvalidIconURL = /be one of \[""\], or match the format "strictRelativeUrl"/;
+@@ -554,10 +554,10 @@ add_task(async function test_devtools_pa
+ 
+   await extension.awaitMessage("test_invalid_devtools_panel_urls_done");
+ 
+   await gDevTools.closeToolbox(target);
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements.js
+@@ -115,10 +115,10 @@ add_task(async function test_devtools_pa
+      "Got the expected onSelectionChanged once the tab has been completely reloaded");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
+@@ -170,10 +170,10 @@ add_task(async function test_devtools_pa
+ 
+   is(inspector.sidebar.getTabPanel(sidebarIds[2]), undefined,
+      "The third registered sidebar has been removed");
+ 
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_find.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_find.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_find.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_ext_find.js
+++++ browser_ext_find.js
++@@ -175,10 +175,10 @@ add_task(async function testAboutFind() 
++       "permissions": ["find", "tabs"],
++     },
++     background,
++   });
++ 
++   await extension.startup();
++   await extension.awaitMessage("done");
++   await extension.unload();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_menus.js b/browser/components/extensions/test/browser/browser_ext_menus.js
+--- a/browser/components/extensions/test/browser/browser_ext_menus.js
++++ b/browser/components/extensions/test/browser/browser_ext_menus.js
+@@ -90,17 +90,17 @@ add_task(async function test_actionConte
+     is(separator.tagName, "menuseparator", "Separator after last menu item");
+ 
+     await closeActionContextMenu(popup.firstChild);
+     const {info, tab} = await extension.awaitMessage("click");
+     is(info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
+     is(tab.id, tabId, "Click event tab ID is correct");
+   }
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await extension.unload();
+ });
+ 
+ add_task(async function test_tabContextMenu() {
+   const first = ExtensionTestUtils.loadExtension({
+     manifest: {
+       permissions: ["menus"],
+     },
+@@ -163,17 +163,17 @@ add_task(async function test_tabContextM
+   is(beta.label, "beta", "Second menu item label is correct");
+ 
+   await closeTabContextMenu(beta);
+   const click = await first.awaitMessage("click");
+   is(click.info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
+   is(click.tab.id, tabId, "Click event tab ID is correct");
+   is(click.info.frameId, undefined, "no frameId on chrome");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await first.unload();
+   await second.unload();
+ });
+ 
+ add_task(async function test_onclick_frameid() {
+   const manifest = {
+     permissions: ["menus"],
+   };
+@@ -201,17 +201,17 @@ add_task(async function test_onclick_fra
+   }
+ 
+   let info = await click("body");
+   is(info.frameId, 0, "top level click");
+   info = await click("frame");
+   isnot(info.frameId, undefined, "frame click, frameId is not undefined");
+   isnot(info.frameId, 0, "frame click, frameId probably okay");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await extension.unload();
+ });
+ 
+ add_task(async function test_multiple_contexts_init() {
+   const manifest = {
+     permissions: ["menus"],
+   };
+ 
+@@ -247,17 +247,17 @@ add_task(async function test_multiple_co
+ 
+   const popup = await openSubmenu(items[0]);
+   is(popup.firstChild.label, "child", "Correct child menu item");
+   await closeExtensionContextMenu(popup.firstChild);
+ 
+   const info = await extension.awaitMessage("click");
+   is(info.menuItemId, "child", "onClicked the correct item");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await extension.unload();
+ });
+ 
+ add_task(async function test_tools_menu() {
+   const first = ExtensionTestUtils.loadExtension({
+     manifest: {
+       permissions: ["menus"],
+     },
+@@ -302,12 +302,12 @@ add_task(async function test_tools_menu(
+   is(gamma.getAttribute("label"), "gamma", "Third menu item label is correct");
+ 
+   closeToolsMenu(gamma);
+ 
+   const click = await second.awaitMessage("click");
+   is(click.info.pageUrl, "http://example.com/", "Click info pageUrl is correct");
+   is(click.tab.id, tabId, "Click event tab ID is correct");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await first.unload();
+   await second.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_menus.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_menus.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_menus.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_ext_menus.js
+++++ browser_ext_menus.js
++@@ -148,17 +148,17 @@ add_task(async function test_hiddenPageA
++ 
++   is(dontShowItem.label, "Don\u2019t Show in Address Bar", "Correct first child");
++   is(separator.tagName, "menuseparator", "Correct second child");
++   is(manageItem.label, "Manage Extension\u2026", "Correct third child");
++ 
++   await closeChromeContextMenu(menu.id);
++   await closeChromeContextMenu(BrowserPageActions.panelNode.id);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await extension.unload();
++ });
++ 
++ add_task(async function test_tabContextMenu() {
++   const first = ExtensionTestUtils.loadExtension({
++     manifest: {
++       permissions: ["menus"],
++     },
+diff --git a/browser/components/extensions/test/browser/browser_ext_menus_event_order.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_menus_event_order.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_menus_event_order.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_ext_menus_event_order.js
+++++ browser_ext_menus_event_order.js
++@@ -72,10 +72,10 @@ add_task(async function test_menus_click
++     const menu = await openToolsMenu();
++     let menuitem = menu.querySelector("menuitem[label='item in tools menu']");
++     ok(menuitem, "Tools menu item should exist");
++     await closeToolsMenu(menuitem);
++     await verifyResults("tools_menu");
++   }
++ 
++   await extension.unload();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_menus_events.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_menus_events.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_menus_events.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_ext_menus_events.js
+++++ browser_ext_menus_events.js
++@@ -115,17 +115,17 @@ async function testShowHideEvent({menuCr
++     extension.sendMessage("optional-menu-shown-with-permissions");
++     let shownEvent2 = await extension.awaitMessage("onShown-event-data2");
++     Assert.deepEqual(shownEvent2, expectedShownEventWithPermissions,
++                      "expected onShown info when host permissions are enabled");
++     await doCloseMenu();
++   }
++ 
++   await extension.unload();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ // Make sure that we won't trigger onShown when extensions cannot add menus.
++ add_task(async function test_no_show_hide_for_unsupported_menu() {
++   let extension = ExtensionTestUtils.loadExtension({
++     background() {
++       let events = [];
++       browser.menus.onShown.addListener(data => events.push(data));
+diff --git a/browser/components/extensions/test/browser/browser_ext_menus_refresh.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_menus_refresh.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_menus_refresh.js.1442465-4_2.later
+@@ -0,0 +1,71 @@
++--- browser_ext_menus_refresh.js
+++++ browser_ext_menus_refresh.js
++@@ -175,17 +175,17 @@ add_task(async function refresh_menus_wi
++     contexts: ["tab"],
++     async doOpenMenu() {
++       await openTabContextMenu();
++     },
++     async doCloseMenu() {
++       await closeTabContextMenu();
++     },
++   });
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function refresh_menus_with_tools_menu() {
++   await testRefreshMenusWhileVisible({
++     contexts: ["tools_menu"],
++     async doOpenMenu() {
++       await openToolsMenu();
++     },
++@@ -201,17 +201,17 @@ add_task(async function refresh_menus_wi
++     contexts: ["page"],
++     async doOpenMenu() {
++       await openContextMenu("body");
++     },
++     async doCloseMenu() {
++       await closeExtensionContextMenu();
++     },
++   });
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function refresh_without_menus_at_onShown() {
++   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE);
++   let extension = loadExtensionWithMenusApi();
++   await extension.startup();
++ 
++   const doOpenMenu = () => openContextMenu("body");
++@@ -238,17 +238,17 @@ add_task(async function refresh_without_
++   await doCloseMenu();
++   await doOpenMenu();
++   await extension.awaitMessage("onShown fired");
++   elem = extension.getXULElementByMenuId("too late");
++   is(elem.getAttribute("label"), "the menu item", "previously registered item");
++   await doCloseMenu();
++ 
++   await extension.unload();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function refresh_menus_with_browser_action() {
++   let extension = loadExtensionWithMenusApi();
++   let other_extension = loadExtensionWithMenusApi();
++   await extension.startup();
++   await other_extension.startup();
++ 
++@@ -339,10 +339,10 @@ add_task(async function refresh_menus_du
++   // should be used.
++   elem = extension.getXULElementByMenuId("item1");
++   is(elem, null, "menu item 1 should be hidden");
++   elem = extension.getXULElementByMenuId("item2");
++   is(elem.getAttribute("label"), "item2", "menu item 2 should be shown");
++ 
++   await closeActionContextMenu();
++   await extension.unload();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_openPanel.js b/browser/components/extensions/test/browser/browser_ext_openPanel.js
+--- a/browser/components/extensions/test/browser/browser_ext_openPanel.js
++++ b/browser/components/extensions/test/browser/browser_ext_openPanel.js
+@@ -116,24 +116,24 @@ add_task(async function test_openPopup_r
+   closePageAction(extension);
+   await new Promise(resolve => setTimeout(resolve, 0));
+ 
+   await click("#openSidebarAction");
+ 
+   await BrowserTestUtils.synthesizeMouseAtCenter("#closeSidebarAction", {}, gBrowser.selectedBrowser);
+   await BrowserTestUtils.waitForCondition(() => !SidebarUI.isOpen);
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await extension.unload();
+ 
+   extensionData.manifest.permissions = ["activeTab"];
+   extension = ExtensionTestUtils.loadExtension(extensionData);
+   await extension.startup();
+   await extension.awaitMessage("ready");
+ 
+   await click("#openBrowserAction");
+   testActiveTab(extension, true);
+   closeBrowserAction(extension);
+   await new Promise(resolve => setTimeout(resolve, 0));
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_optionsPage_browser_style.js b/browser/components/extensions/test/browser/browser_ext_optionsPage_browser_style.js
+--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_browser_style.js
++++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_browser_style.js
+@@ -93,17 +93,17 @@ async function testOptionsBrowserStyle(o
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+ 
+   await extension.startup();
+   await extension.awaitMessage("options-ui-ready");
+ 
+   extension.sendMessage("check-style", optionsUI, assertMessage);
+   await extension.awaitFinish("options-ui-browser_style");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ }
+ 
+ add_task(async function test_options_without_setting_browser_style() {
+   await testOptionsBrowserStyle({
+     "page": "options.html",
+   }, "Expected correct style when browser_style is excluded");
+diff --git a/browser/components/extensions/test/browser/browser_ext_optionsPage_modals.js b/browser/components/extensions/test/browser/browser_ext_optionsPage_modals.js
+--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_modals.js
++++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_modals.js
+@@ -76,12 +76,12 @@ add_task(async function test_tab_options
+   info("Close the tab modal prompt");
+   dialogs[0].onButtonClick(0);
+ 
+   await extension.awaitFinish("options-ui-modals");
+ 
+   Assert.equal(stack.querySelectorAll("tabmodalprompt").length, 0,
+                "Expect the tab modal to be closed");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js b/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js
+--- a/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js
++++ b/browser/components/extensions/test/browser/browser_ext_optionsPage_privileges.js
+@@ -57,10 +57,10 @@ add_task(async function test_tab_options
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("options-ui-privileges");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_context.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_pageAction_context.js.1442465-4_2.later
+@@ -0,0 +1,16 @@
++--- browser_ext_pageAction_context.js
+++++ browser_ext_pageAction_context.js
++@@ -282,12 +282,12 @@ add_task(async function testNavigationCl
++   info("Set tab-specific data.");
++   await setTabSpecificData();
++ 
++   info("Push history state. Does not cause navigation.");
++   await historyPushState(url + "/path");
++   await expectTabSpecificData("history.pushState() does not clear tab-specific data");
++ 
++   for (let tab of tabs) {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   }
++   await extension.unload();
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
++++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+@@ -193,17 +193,17 @@ add_task(async function testPageActionPo
+   await extension.unload();
+ 
+   let node = document.getElementById(pageActionId);
+   is(node, null, "pageAction image removed from document");
+ 
+   let panel = document.getElementById(panelId);
+   is(panel, null, "pageAction panel removed from document");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ 
+ add_task(async function testPageActionSecurity() {
+   const URL = "chrome://browser/content/browser.xul";
+ 
+   let apis = ["browser_action", "page_action"];
+ 
+diff --git a/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_pageAction_show_matches.js.1442465-4_2.later
+@@ -0,0 +1,66 @@
++--- browser_ext_pageAction_show_matches.js
+++++ browser_ext_pageAction_show_matches.js
++@@ -99,21 +99,21 @@ add_task(async function test_pageAction_
++       let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, true, true);
++       await check(extension, tab, expected, msg + " (new)");
++ 
++       info("Check switched tab.");
++       await BrowserTestUtils.switchTab(gBrowser, switchTab);
++       await BrowserTestUtils.switchTab(gBrowser, tab);
++       await check(extension, tab, expected, msg + " (switched)");
++ 
++-      await BrowserTestUtils.removeTab(tab);
+++      BrowserTestUtils.removeTab(tab);
++     }
++     await extension.unload();
++   }
++-  await BrowserTestUtils.removeTab(switchTab);
+++  BrowserTestUtils.removeTab(switchTab);
++ });
++ 
++ add_task(async function test_pageAction_default_show_install() {
++   info("Check show_matches and hide_matches are respected when installing the extension");
++   for (let [i, test] of tests.entries()) {
++     info(`test ${i}: ${test.name}`);
++     for (let expected of [true, false]) {
++       let j = test.shown.indexOf(expected);
++@@ -124,18 +124,18 @@ add_task(async function test_pageAction_
++       await extension.startup();
++       let msg = `test ${i} url ${j}: page action is ${expected ? "shown" : "hidden"} for ${urls[j]}`;
++       await check(extension, installTab, expected, msg + " (active)");
++ 
++       // initialTab has not been activated after installation, so we have not evaluated whether the page
++       // action should be shown in it. Check that pageAction.isShown works anyways.
++       await sendMessage(extension, "isShown", {tabId: getId(initialTab)}, expected, msg + " (inactive)");
++ 
++-      await BrowserTestUtils.removeTab(initialTab);
++-      await BrowserTestUtils.removeTab(installTab);
+++      BrowserTestUtils.removeTab(initialTab);
+++      BrowserTestUtils.removeTab(installTab);
++       await extension.unload();
++     }
++   }
++ });
++ 
++ add_task(async function test_pageAction_history() {
++   info("Check match patterns are reevaluated when using history.pushState");
++   let url1 = "http://example.com/";
++@@ -155,17 +155,17 @@ add_task(async function test_pageAction_
++   info("Use hide()");
++   await sendMessage(extension, "hide", getId(tab));
++   await check(extension, tab, false, "page action is still hidden");
++ 
++   info("Use history.pushState to revert to first url");
++   await historyPushState(tab, url1);
++   await check(extension, tab, false, "hide() has more precedence than pattern matching");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await extension.unload();
++ });
++ 
++ add_task(async function test_pageAction_all_urls() {
++   info("Check <all_urls> is not allowed in hide_matches");
++   let extension = getExtension({
++     "show_matches": ["*://mochi.test/*"],
++     "hide_matches": ["<all_urls>"],
+diff --git a/browser/components/extensions/test/browser/browser_ext_popup_focus.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_popup_focus.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_popup_focus.js.1442465-4_2.later
+@@ -0,0 +1,34 @@
++--- browser_ext_popup_focus.js
+++++ browser_ext_popup_focus.js
++@@ -29,17 +29,17 @@ add_task(async function testPageActionFo
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE);
++ 
++   await extension.startup();
++   let finish = extension.awaitFinish("focused");
++   await clickPageAction(extension);
++   await finish;
++   await closePageAction(extension);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await extension.unload();
++ });
++ 
++ add_task(async function testBrowserActionFocus() {
++   let extension = ExtensionTestUtils.loadExtension({
++     manifest: {
++       "browser_action": {"default_popup": "popup.html"},
++     },
++@@ -61,11 +61,11 @@ add_task(async function testBrowserActio
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, DUMMY_PAGE);
++   let finish = extension.awaitFinish("focused");
++   await clickBrowserAction(extension);
++   await finish;
++ 
++   await closeBrowserAction(extension);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await extension.unload();
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
+--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
++++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage.js
+@@ -204,17 +204,17 @@ add_tasks(async function test_inline_opt
+   ]);
+ 
+   extension.sendMessage("ports-done");
+ 
+   await extension.awaitFinish("options-ui");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_tasks(async function test_tab_options(extraOptions) {
+   info(`Test options opened in a tab (${JSON.stringify(extraOptions)})`);
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+ 
+   let extension = await loadExtension(Object.assign({}, extraOptions, {
+@@ -307,17 +307,17 @@ add_tasks(async function test_tab_option
+         browser.test.notifyFail("options-ui-tab");
+       }
+     },
+   }));
+ 
+   await extension.awaitFinish("options-ui-tab");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_tasks(async function test_options_no_manifest(extraOptions) {
+   info(`Test with no manifest key (${JSON.stringify(extraOptions)})`);
+ 
+   let extension = await loadExtension(Object.assign({}, extraOptions, {
+     manifest: {
+       applications: {gecko: {id: "no_options@tests.mozilla.org"}},
+diff --git a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js
+--- a/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js
++++ b/browser/components/extensions/test/browser/browser_ext_runtime_openOptionsPage_uninstall.js
+@@ -90,12 +90,12 @@ add_task(async function test_inline_opti
+   });
+ 
+   await extension.awaitMessage("options-ui-open");
+   await extension.unload();
+ 
+   is(gBrowser.selectedBrowser.currentURI.spec, "about:addons",
+      "Add-on manager tab should still be open");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js b/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js
+--- a/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js
++++ b/browser/components/extensions/test/browser/browser_ext_runtime_setUninstallURL.js
+@@ -18,17 +18,17 @@ async function makeAndInstallXPI(id, bac
+ 
+   info(`installing ${xpi.path}`);
+   let addon = await AddonManager.installTemporaryAddon(xpi);
+   info("installed");
+ 
+   // A WebExtension is started asynchronously, we have our test extension
+   // open a new tab to signal that the background script has executed.
+   let loadTab = await loadPromise;
+-  await BrowserTestUtils.removeTab(loadTab);
++  BrowserTestUtils.removeTab(loadTab);
+ 
+   return addon;
+ }
+ 
+ 
+ add_task(async function test_setuninstallurl_badargs() {
+   async function background() {
+     await browser.test.assertRejects(
+@@ -85,10 +85,10 @@ add_task(async function test_setuninstal
+   // look for a new tab with the uninstall url.
+   let uninstallPromise = BrowserTestUtils.waitForNewTab(gBrowser, "http://example.com/addon_uninstalled");
+ 
+   addon.uninstall(true);
+   info("uninstalled");
+ 
+   let uninstalledTab = await uninstallPromise;
+   isnot(uninstalledTab, null, "opened tab with uninstall url");
+-  await BrowserTestUtils.removeTab(uninstalledTab);
++  BrowserTestUtils.removeTab(uninstalledTab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
+--- a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
++++ b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed.js
+@@ -59,20 +59,20 @@ add_task(async function test_sessions_ge
+ 
+   await openAndCloseWindow("about:config", ["about:robots", "about:mozilla"]);
+   extension.sendMessage("check-sessions");
+   recentlyClosed = await extension.awaitMessage("recentlyClosed");
+   // Check for multiple tabs in most recently closed window
+   is(recentlyClosed[0].window.tabs.length, 3, "most recently closed window has the expected number of tabs");
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await openAndCloseWindow();
+   extension.sendMessage("check-sessions");
+   recentlyClosed = await extension.awaitMessage("recentlyClosed");
+   let finalResult = recentlyClosed.filter(onlyNewItemsFilter);
+   checkRecentlyClosed(finalResult, 5, currentWindowId);
+ 
+   isnot(finalResult[0].window, undefined, "first item is a window");
+diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js
+--- a/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js
++++ b/browser/components/extensions/test/browser/browser_ext_sessions_getRecentlyClosed_tabs.js
+@@ -65,17 +65,17 @@ add_task(async function test_sessions_ge
+     tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
+     expectedTabs.push(expectedTabInfo(tab, win));
+     lastAccessedTimes.set(url, tab.lastAccessed);
+   }
+ 
+   await extension.startup();
+ 
+   // Test with a closed tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   extension.sendMessage("check-sessions");
+   let recentlyClosed = await extension.awaitMessage("recentlyClosed");
+   let tabInfo = recentlyClosed[0].tab;
+   let expectedTab = expectedTabs.pop();
+   checkTabInfo(expectedTab, tabInfo);
+   ok(tabInfo.lastAccessed > lastAccessedTimes.get(tabInfo.url),
+      "lastAccessed has been updated");
+diff --git a/browser/components/extensions/test/browser/browser_ext_sessions_restore.js b/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
+--- a/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
++++ b/browser/components/extensions/test/browser/browser_ext_sessions_restore.js
+@@ -111,47 +111,47 @@ add_task(async function test_sessions_re
+   window = windowTracker.getWindow(restored.window.id);
+   await BrowserTestUtils.closeWindow(window);
+   // notificationCount = yield extension.awaitMessage("notificationCount");
+   await assertNotificationCount(5);
+ 
+   // Open and close a tab.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:robots");
+   await TabStateFlusher.flush(tab.linkedBrowser);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await assertNotificationCount(6);
+ 
+   // Restore the most recently closed item.
+   extension.sendMessage("restore");
+   await assertNotificationCount(7);
+   restored = await extension.awaitMessage("restored");
+ 
+   tab = restored.tab;
+   ok(tab, "restore returned a tab");
+   checkLocalTab(tab, "about:robots");
+ 
+   // Close the tab again.
+   let realTab = tabTracker.getTab(tab.id);
+-  await BrowserTestUtils.removeTab(realTab);
++  BrowserTestUtils.removeTab(realTab);
+   await assertNotificationCount(8);
+ 
+   // Restore the tab using the sessionId.
+   extension.sendMessage("check-sessions");
+   recentlyClosed = await extension.awaitMessage("recentlyClosed");
+   extension.sendMessage("restore", recentlyClosed[0].tab.sessionId);
+   await assertNotificationCount(9);
+   restored = await extension.awaitMessage("restored");
+ 
+   tab = restored.tab;
+   ok(tab, "restore returned a tab");
+   checkLocalTab(tab, "about:robots");
+ 
+   // Close the tab again.
+   realTab = tabTracker.getTab(tab.id);
+-  await BrowserTestUtils.removeTab(realTab);
++  BrowserTestUtils.removeTab(realTab);
+   await assertNotificationCount(10);
+ 
+   // Try to restore something with an invalid sessionId.
+   extension.sendMessage("restore-reject");
+   restored = await extension.awaitMessage("restore-rejected");
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js b/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js
+--- a/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js
++++ b/browser/components/extensions/test/browser/browser_ext_sidebarAction_browser_style.js
+@@ -45,17 +45,17 @@ async function testSidebarBrowserStyle(s
+   await extension.startup();
+   await extension.awaitMessage("sidebar-ready");
+ 
+   extension.sendMessage("check-style", sidebarAction, assertMessage);
+   await extension.awaitFinish("sidebar-browser-style");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function test_sidebar_without_setting_browser_style() {
+   await testSidebarBrowserStyle({
+     "default_panel": "panel.html",
+   }, "Expected correct style when browser_style is excluded");
+ });
+ 
+diff --git a/browser/components/extensions/test/browser/browser_ext_slow_script.js b/browser/components/extensions/test/browser/browser_ext_slow_script.js
+--- a/browser/components/extensions/test/browser/browser_ext_slow_script.js
++++ b/browser/components/extensions/test/browser/browser_ext_slow_script.js
+@@ -50,12 +50,12 @@ add_task(async function test_slow_conten
+   let text = document.getAnonymousElementByAttribute(notification, "anonid", "messageText").textContent;
+ 
+   ok(text.includes("\u201cSlow Script Extension\u201d"),
+      "Label is correct");
+ 
+   let stopButton = notification.querySelector("[label='Stop It']");
+   stopButton.click();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js b/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
+--- a/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
++++ b/browser/components/extensions/test/browser/browser_ext_tab_runtimeConnect.js
+@@ -65,10 +65,10 @@ add_task(async function() {
+       `,
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabRuntimeConnect.pass");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_audio.js b/browser/components/extensions/test/browser/browser_ext_tabs_audio.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_audio.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_audio.js
+@@ -191,11 +191,11 @@ add_task(async function() {
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("tab-audio");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_captureTab.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_tabs_captureTab.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_captureTab.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_ext_tabs_captureTab.js
+++++ browser_ext_tabs_captureTab.js
++@@ -116,17 +116,17 @@ async function runTest(options) {
++   });
++ 
++   await extension.startup();
++ 
++   await extension.awaitFinish("captureTab");
++ 
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ add_task(async function testCaptureTab() {
++   await runTest({color: [0, 0, 0], fullZoom: 1});
++ 
++   await runTest({color: [0, 0, 0], fullZoom: 2});
++ 
++   await runTest({color: [0, 0, 0], fullZoom: 0.5});
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js b/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_captureVisibleTab.js
+@@ -116,17 +116,17 @@ async function runTest(options) {
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("captureVisibleTab");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function testCaptureVisibleTab() {
+   await runTest({color: [0, 0, 0], fullZoom: 1});
+ 
+   await runTest({color: [0, 0, 0], fullZoom: 2});
+ 
+   await runTest({color: [0, 0, 0], fullZoom: 0.5});
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_create.js b/browser/components/extensions/test/browser/browser_ext_tabs_create.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_create.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_create.js
+@@ -163,17 +163,17 @@ add_task(async function test_create_opti
+       },
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.create");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_urlbar_focus() {
+   const extension = ExtensionTestUtils.loadExtension({
+     background() {
+       browser.tabs.onUpdated.addListener(function onUpdated(_, info) {
+         if (info.status === "complete") {
+           browser.test.sendMessage("complete");
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_discard.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_tabs_discard.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_discard.js.1442465-4_2.later
+@@ -0,0 +1,17 @@
++--- browser_ext_tabs_discard.js
+++++ browser_ext_tabs_discard.js
++@@ -51,12 +51,12 @@ add_task(async function test_discarded()
++   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
++   let tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
++ 
++   await extension.startup();
++ 
++   await extension.awaitFinish("test-finished");
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(tab1);
++-  await BrowserTestUtils.removeTab(tab2);
+++  BrowserTestUtils.removeTab(tab1);
+++  BrowserTestUtils.removeTab(tab2);
++ });
++ 
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js
+@@ -55,12 +55,12 @@ add_task(async function test_discarded()
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com");
+ 
+   let tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank", {createLazyBrowser: true});
+   SessionStore.setTabState(tab2, JSON.stringify(lazyTabState));
+ 
+   await extension.awaitFinish("test-finished");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js b/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_duplicate.js
+@@ -31,17 +31,17 @@ add_task(async function testDuplicateTab
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.duplicate");
+   await extension.unload();
+ 
+   while (gBrowser.tabs[0].linkedBrowser.currentURI.spec === "http://example.net/") {
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[0]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[0]);
+   }
+ });
+ 
+ add_task(async function testDuplicateTabLazily() {
+   async function background() {
+     let tabLoadComplete = new Promise(resolve => {
+       browser.test.onMessage.addListener((message, tabId, result) => {
+         if (message == "duplicate-tab-done") {
+@@ -136,11 +136,11 @@ add_task(async function testDuplicatePin
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.duplicate.pinned");
+   await extension.unload();
+ 
+   while (gBrowser.tabs[0].linkedBrowser.currentURI.spec === "http://example.net/") {
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[0]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[0]);
+   }
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript.js
+@@ -245,15 +245,15 @@ add_task(async function testExecuteScrip
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("executeScript");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Make sure that we're not holding on to references to closed message
+   // managers.
+   is(countMM(MessageChannel.messageManagers), messageManagersSize, "Message manager count");
+   is(MessageChannel.pendingResponses.size, 0, "Pending response count");
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_bad.js
+@@ -193,18 +193,18 @@ add_task(async function testBadPermissio
+       "page_action": {},
+     },
+     contentSetup: async function() {
+       let [tab] = await browser.tabs.query({active: true, currentWindow: true});
+       await browser.pageAction.show(tab.id);
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
+ });
+ 
+ add_task(async function testMatchDataURI() {
+   // allow top level data: URI navigations, otherwise
+   // window.location.href = data: would be blocked
+   await SpecialPowers.pushPrefEnv({
+     "set": [["security.data_uri.block_toplevel_data_uri_navigations", false]],
+   });
+@@ -274,17 +274,17 @@ add_task(async function testMatchDataURI
+   target.sendMessage("navigate", data);
+ 
+   const url = await scripts.awaitMessage("tab-ready");
+   is(url, data, "Extension tab navigated to a data: URI");
+ 
+   scripts.sendMessage("execute");
+   await scripts.awaitMessage("done");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await scripts.unload();
+   await target.unload();
+ });
+ 
+ add_task(async function testBadURL() {
+   async function background() {
+     let promises = [
+       new Promise(resolve => {
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_good.js
+@@ -235,12 +235,12 @@ add_task(async function testGoodPermissi
+       let item = contextMenu.querySelector("[label=activeTab]");
+ 
+       await EventUtils.synthesizeMouseAtCenter(item, {}, window);
+ 
+       await awaitPopupHidden;
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(forceGC);
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_multiple.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_multiple.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_multiple.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_multiple.js
+@@ -41,10 +41,10 @@ add_task(async function testExecuteScrip
+     },
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("executeScript-multiple");
+ 
+   await extension.unload();
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_no_create.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_no_create.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_no_create.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_no_create.js
+@@ -58,10 +58,10 @@ add_task(async function testExecuteScrip
+   extension.sendMessage(URL);
+   await extension.awaitMessage("open-test-tab");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL, true);
+ 
+   await extension.awaitFinish("executeScript-at-onUpdated");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_executeScript_runAt.js
+@@ -110,10 +110,10 @@ add_task(async function testExecuteScrip
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("executeScript-runAt");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_hide.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide.js.1442465-4_2.later
+@@ -0,0 +1,35 @@
++--- browser_ext_tabs_hide.js
+++++ browser_ext_tabs_hide.js
++@@ -138,17 +138,17 @@ add_task(async function test_tabs_showhi
++     ok(tabs[i].hidden, "tab hidden value is correct");
++     let id = SessionStore.getTabValue(tabs[i], "hiddenBy");
++     is(id, extension.id, "tab hiddenBy value is correct");
++   }
++ 
++   // Test closing the last visible tab, the next tab which is hidden should become
++   // the selectedTab and will be visible.
++   ok(!otherwin.gBrowser.selectedTab.hidden, "selected tab is not hidden");
++-  await BrowserTestUtils.removeTab(otherwin.gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(otherwin.gBrowser.selectedTab);
++   ok(!otherwin.gBrowser.selectedTab.hidden, "tab was unhidden");
++ 
++   // Showall will unhide any remaining hidden tabs.
++   extension.sendMessage("showall");
++   await extension.awaitMessage("shown");
++ 
++   // Check from chrome code that all tabs are visible again.
++   for (let win of BrowserWindowIterator()) {
++@@ -214,11 +214,11 @@ add_task(async function test_tabs_shutdo
++     extension.awaitMessage("ready"),
++     extension.awaitMessage("changeInfo"),
++   ]);
++   Assert.ok(tabs[0].hidden, "Tab is hidden by extension");
++ 
++   await extension.unload();
++ 
++   Assert.ok(!tabs[0].hidden, "Tab is not hidden after unloading extension");
++-  await BrowserTestUtils.removeTab(tabs[0]);
++-  await BrowserTestUtils.removeTab(tabs[1]);
+++  BrowserTestUtils.removeTab(tabs[0]);
+++  BrowserTestUtils.removeTab(tabs[1]);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_hide_update.js.1442465-4_2.later
+@@ -0,0 +1,33 @@
++--- browser_ext_tabs_hide_update.js
+++++ browser_ext_tabs_hide_update.js
++@@ -103,17 +103,17 @@ add_task(async function test_tabs_update
++     },
++     permissions: ["tabs"],
++   };
++   await updateExtension(ID, extdata);
++   Assert.ok(!tab.hidden, "Tab is not hidden hidden after update");
++ 
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ 
++ // Test our update handling.  Currently this means any hidden tabs will be
++ // shown when a tabHide extension is shutdown.  We additionally test the
++ // tabs.onUpdated listener gets called with hidden state changes.
++ add_task(async function test_tabs_disable() {
++   await SpecialPowers.pushPrefEnv({
++@@ -134,10 +134,10 @@ add_task(async function test_tabs_disabl
++   Assert.ok(tab.hidden, "Tab is hidden by extension");
++ 
++   // Test that disable does hide tabs.
++   await disableExtension(ID);
++   Assert.ok(!tab.hidden, "Tab is not hidden hidden after disable");
++ 
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+@@ -108,17 +108,17 @@ add_task(async function testExecuteScrip
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("insertCSS");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Make sure that we're not holding on to references to closed message
+   // managers.
+   is(getMessageManagersSize(MessageChannel.messageManagers), messageManagersSize,
+      "Message manager count");
+   is(MessageChannel.responseManagers.size, responseManagersSize, "Response manager count");
+   is(MessageChannel.pendingResponses.size, 0, "Pending response count");
+ });
+@@ -159,10 +159,10 @@ add_task(async function testInsertCSS_cl
+ 
+   await extension.unload();
+ 
+   const unloadedStyles = await ContentTask.spawn(tab.linkedBrowser, null, getTabContentComputedStyle);
+ 
+   is(unloadedStyles[0], "rgba(0, 0, 0, 0)", "The injected CSS code has been removed as expected");
+   is(unloadedStyles[1], "rgb(0, 0, 0)", "The injected CSS file has been removed as expected");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_lazy.js
+@@ -31,10 +31,10 @@ add_task(async function() {
+ 
+   await extension.startup();
+   await extension.awaitFinish("lazy");
+   await extension.unload();
+ 
+   is(tab.getAttribute("pending"), "true", "The tab is still pending restore");
+   is(tab.linkedBrowser.isConnected, false, "The tab is still lazy");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_move_array.js b/browser/components/extensions/test/browser/browser_ext_tabs_move_array.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_array.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_array.js
+@@ -74,11 +74,11 @@ add_task(async function moveMultiple() {
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.move");
+   await extension.unload();
+ 
+   for (let tab of tabs) {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js b/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window.js
+@@ -32,17 +32,17 @@ add_task(async function() {
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.move.window");
+   await extension.unload();
+ 
+   for (let tab of window.gBrowser.tabs) {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+   await BrowserTestUtils.closeWindow(window1);
+ });
+ 
+ add_task(async function test_currentWindowAfterTabMoved() {
+   const files = {
+     "current.html": "<meta charset=utf-8><script src=current.js></script>",
+     "current.js": function() {
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_move_window_multiple.js b/browser/components/extensions/test/browser/browser_ext_tabs_move_window_multiple.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window_multiple.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window_multiple.js
+@@ -32,12 +32,12 @@ add_task(async function() {
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.move.multiple");
+   await extension.unload();
+ 
+   for (let tab of window.gBrowser.tabs) {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+   await BrowserTestUtils.closeWindow(window1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_move_window_pinned.js b/browser/components/extensions/test/browser/browser_ext_tabs_move_window_pinned.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_move_window_pinned.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_move_window_pinned.js
+@@ -31,12 +31,12 @@ add_task(async function() {
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.move.pin");
+   await extension.unload();
+ 
+   for (let tab of window.gBrowser.tabs) {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+   await BrowserTestUtils.closeWindow(window1);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_opener.js b/browser/components/extensions/test/browser/browser_ext_tabs_opener.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_opener.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_opener.js
+@@ -68,11 +68,11 @@ add_task(async function() {
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("tab-opener");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js b/browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_printPreview.js
+@@ -34,10 +34,10 @@ add_task(async function testPrintPreview
+   isnot(ppToolbar, null, "print preview toolbar created");
+ 
+   is(ppTab, gBrowser.selectedTab, "print preview tab selected");
+   is(ppTab.linkedBrowser.currentURI.spec, "about:printpreview", "print preview browser url correct");
+ 
+   PrintUtils.exitPrintPreview();
+   await BrowserTestUtils.waitForCondition(() => !window.gInPrintPreviewMode);
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.tabs[1]);
++  BrowserTestUtils.removeTab(gBrowser.tabs[1]);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_query.js b/browser/components/extensions/test/browser/browser_ext_tabs_query.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_query.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_query.js
+@@ -44,18 +44,18 @@ add_task(async function() {
+       });
+     },
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("tabs.query");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ 
+   tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/");
+   tab2 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.net/");
+   let tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://test1.example.org/MochiKit/");
+ 
+   // test simple queries
+   extension = ExtensionTestUtils.loadExtension({
+     manifest: {
+@@ -165,19 +165,19 @@ add_task(async function() {
+     extension.sendMessage("check-size");
+     let dims = await extension.awaitMessage("dims");
+     is(dims.width, clientWidth, "tab reports expected width");
+     is(dims.height, clientHeight, "tab reports expected height");
+   }
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab3);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab3);
+   SpecialPowers.clearUserPref(RESOLUTION_PREF);
+ });
+ 
+ add_task(async function testQueryPermissions() {
+   let extension = ExtensionTestUtils.loadExtension({
+     manifest: {
+       "permissions": [],
+     },
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js b/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
+@@ -108,10 +108,10 @@ add_task(async function testExecuteScrip
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("removeCSS");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_saveAsPDF.js b/browser/components/extensions/test/browser/browser_ext_tabs_saveAsPDF.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_saveAsPDF.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_saveAsPDF.js
+@@ -74,17 +74,17 @@ async function testReturnStatus(expected
+   MockFilePicker.cleanup();
+ 
+   if (expectedStatus == "not_saved" || expectedStatus == "not_replaced") {
+     saveFile.permissions = 0o666;
+   }
+ 
+   saveDir.remove(true);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function testSaveAsPDF_saved() {
+   await testReturnStatus("saved");
+ });
+ 
+ add_task(async function testSaveAsPDF_replaced() {
+   await testReturnStatus("replaced");
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_sharingState.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_ext_tabs_sharingState.js
+++++ browser_ext_tabs_sharingState.js
++@@ -64,10 +64,10 @@ add_task(async function test_tabs_mediaI
++   // Test that onUpdated is called after the sharing state is changed from
++   // chrome code.
++   await extension.awaitMessage("ready");
++   gBrowser.setBrowserSharing(tab.linkedBrowser, {});
++ 
++   await extension.awaitFinish("done");
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_update.js b/browser/components/extensions/test/browser/browser_ext_tabs_update.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_update.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_update.js
+@@ -35,11 +35,11 @@ add_task(async function() {
+   });
+ 
+   await Promise.all([extension.startup(), extension.awaitMessage("check")]);
+ 
+   ok(gBrowser.selectedTab == tab2, "correct tab selected");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js b/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_update_url.js
+@@ -58,17 +58,17 @@ async function testTabsUpdateURL(existen
+ 
+   info(`tab.update URL "${tabsUpdateURL}" on tab with URL "${existentTabURL}"`);
+ 
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, existentTabURL);
+ 
+   extension.sendMessage("start", tabsUpdateURL, isErrorExpected);
+   await extension.awaitMessage("done");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+   await extension.unload();
+ }
+ 
+ add_task(async function() {
+   info("Start testing tabs.update on javascript URLs");
+ 
+   let dataURLPage = `data:text/html,
+     <!DOCTYPE html>
+diff --git a/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js b/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
+--- a/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
++++ b/browser/components/extensions/test/browser/browser_ext_tabs_zoom.js
+@@ -212,11 +212,11 @@ add_task(async function() {
+   });
+ 
+   await extension.startup();
+ 
+   await extension.awaitFinish("tab-zoom");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+--- a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
++++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js
+@@ -37,11 +37,11 @@ add_task(async function test_sending_mes
+ 
+   // Simulate opening the newtab open as a user would.
+   BrowserOpenTab();
+ 
+   let url = await extension.awaitMessage("from-newtab-page");
+   ok(url.endsWith(NEWTAB_URI_1),
+      "Newtab url is overriden by the extension.");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js.1442465-4_2.later b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/extensions/test/browser/browser_ext_url_overrides_newtab.js.1442465-4_2.later
+@@ -0,0 +1,158 @@
++--- browser_ext_url_overrides_newtab.js
+++++ browser_ext_url_overrides_newtab.js
++@@ -158,27 +158,27 @@ add_task(async function test_new_tab_ign
++ 
++   // Ensure panel is closed and the setting still isn't set.
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is closed");
++   is(getNotificationSetting(extensionId), null,
++      "The New Tab notification is not set after ignoring the doorhanger");
++ 
++   // Close the first tab and open another new tab.
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   let newTabOpened = waitForNewTab();
++   BrowserOpenTab();
++   await newTabOpened;
++ 
++   // Verify the doorhanger is not shown a second time.
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel doesn't open after ignoring the doorhanger");
++   is(gURLBar.focused, true, "The URL bar is focused with no doorhanger");
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await extension.unload();
++ });
++ 
++ add_task(async function test_new_tab_keep_settings() {
++   await ExtensionSettingsStore.initialize();
++   let notification = getNewTabDoorhanger();
++   let panel = notification.closest("panel");
++   let extensionId = "newtabkeep@mochi.test";
++@@ -223,25 +223,25 @@ add_task(async function test_new_tab_kee
++ 
++   // Ensure panel is closed and setting is updated.
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is closed after click");
++   is(getNotificationSetting(extensionId).value, true,
++      "The New Tab notification is set after keeping the changes");
++ 
++   // Close the first tab and open another new tab.
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   BrowserOpenTab();
++   await extension.awaitMessage("newtab");
++ 
++   // Verify the doorhanger is not shown a second time.
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is not opened after keeping the changes");
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ 
++   let upgradedExtension = ExtensionTestUtils.loadExtension({
++     manifest: Object.assign({}, manifest, {version: "2.0"}),
++     files,
++     useAddonManager: "permanent",
++   });
++ 
++   await upgradedExtension.startup();
++@@ -250,17 +250,17 @@ add_task(async function test_new_tab_kee
++   await upgradedExtension.awaitMessage("newtab");
++ 
++   // Ensure panel is closed and setting is still set.
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is closed after click");
++   is(getNotificationSetting(extensionId).value, true,
++      "The New Tab notification is set after keeping the changes");
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await upgradedExtension.unload();
++   await extension.unload();
++ });
++ 
++ add_task(async function test_new_tab_restore_settings() {
++   await ExtensionSettingsStore.initialize();
++   let notification = getNewTabDoorhanger();
++   let panel = notification.closest("panel");
++@@ -312,17 +312,17 @@ add_task(async function test_new_tab_res
++      "The user has been redirected to about:newtab");
++ 
++   // Wait for the next event tick to make sure the remaining part of the test
++   // is not executed inside tabbrowser's onLocationChange.
++   // See bug 1416153 for more details.
++   await TestUtils.waitForTick();
++ 
++   // Reopen a browser tab and verify that there's no doorhanger.
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   let newTabOpened = waitForNewTab();
++   BrowserOpenTab();
++   await newTabOpened;
++ 
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is not opened after keeping the changes");
++ 
++   // FIXME: We need to enable the add-on so it gets cleared from the
++@@ -334,17 +334,17 @@ add_task(async function test_new_tab_res
++           AddonManager.removeAddonListener(listener);
++           resolve();
++         }
++       },
++     };
++     AddonManager.addAddonListener(listener);
++   });
++   addon.userDisabled = false;
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await addonEnabled;
++   await extension.unload();
++ });
++ 
++ add_task(async function test_new_tab_restore_settings_multiple() {
++   await ExtensionSettingsStore.initialize();
++   let notification = getNewTabDoorhanger();
++   let panel = notification.closest("panel");
++@@ -437,25 +437,25 @@ add_task(async function test_new_tab_res
++      "The user is now on the original New Tab URL since all extensions are disabled");
++ 
++   // Wait for the next event tick to make sure the remaining part of the test
++   // is not executed inside tabbrowser's onLocationChange.
++   // See bug 1416153 for more details.
++   await TestUtils.waitForTick();
++ 
++   // Reopen a browser tab and verify that there's no doorhanger.
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   let newTabOpened = waitForNewTab();
++   BrowserOpenTab();
++   await newTabOpened;
++ 
++   ok(panel.getAttribute("panelopen") != "true",
++      "The notification panel is not opened after keeping the changes");
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ 
++   // FIXME: We need to enable the add-on so it gets cleared from the
++   // ExtensionSettingsStore for now. See bug 1408226.
++   let addonsEnabled = Promise.all([
++     waitForAddonEnabled(addonOne), waitForAddonEnabled(addonTwo)]);
++   addonOne.userDisabled = false;
++   addonTwo.userDisabled = false;
++   await addonsEnabled;
++@@ -504,11 +504,11 @@ add_task(async function dontTemporarilyS
++   let tab = await BrowserTestUtils.openNewForegroundTab({gBrowser, url});
++ 
++   gBrowser.removeProgressListener(wpl);
++   is(gURLBar.value, "", "URL bar value should be empty.");
++   ContentTask.spawn(tab.linkedBrowser, null, function() {
++     is(content.document.body.textContent, "New tab!", "New tab page is loaded.");
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   await extension.unload();
++ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_user_events.js b/browser/components/extensions/test/browser/browser_ext_user_events.js
+--- a/browser/components/extensions/test/browser/browser_ext_user_events.js
++++ b/browser/components/extensions/test/browser/browser_ext_user_events.js
+@@ -61,15 +61,15 @@ add_task(async function testSources() {
+   gBrowser.selectedTab = tab;
+ 
+   let menu = await openContextMenu("body");
+   let items = menu.getElementsByAttribute("label", "test user events");
+   is(items.length, 1, "Found context menu item");
+   EventUtils.synthesizeMouseAtCenter(items[0], {});
+   await check("context menu click");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ 
+   registerCleanupFunction(() => CustomizableUI.reset());
+ });
+ 
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_frameId0.js
+@@ -23,10 +23,10 @@ add_task(async function webNavigation_ge
+ 
+     background: `(${background})(${JSON.stringify(DUMMY_URL)});`,
+   });
+ 
+   await extension.startup();
+   await extension.awaitFinish("frameId checked");
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget.js
+@@ -90,17 +90,17 @@ add_task(async function test_on_created_
+     },
+     expectedWebNavProps: {
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#new-tab-from-targetblank-click`,
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ });
+ 
+ add_task(async function test_on_created_navigation_target_from_mouse_click_subframe() {
+   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+ 
+   const extension = ExtensionTestUtils.loadExtension({
+@@ -166,12 +166,12 @@ add_task(async function test_on_created_
+     },
+     expectedWebNavProps: {
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+       url: `${OPENED_PAGE}#new-tab-from-targetblank-click-subframe`,
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_contextmenu.js
+@@ -84,17 +84,17 @@ add_task(async function test_on_created_
+     },
+     expectedWebNavProps: {
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#new-window-from-context-menu`,
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ });
+ 
+ add_task(async function test_on_created_navigation_target_from_context_menu_subframe() {
+   const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+ 
+   const extension = ExtensionTestUtils.loadExtension({
+@@ -147,12 +147,12 @@ add_task(async function test_on_created_
+     },
+     expectedWebNavProps: {
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+       url: `${OPENED_PAGE}#new-window-from-context-menu-subframe`,
+     },
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_named_window.js
+@@ -86,12 +86,12 @@ add_task(async function test_window_open
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#existent-named-window-open`,
+     },
+   });
+ 
+   assertNoPendingCreatedNavigationTargetData();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_subframe_window_open.js
+@@ -86,17 +86,17 @@ add_task(async function test_window_open
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: expectedSourceTab.sourceTabFrames[1].frameId,
+       url: `${OPENED_PAGE}#new-win-from-window-open-subframe`,
+     },
+   });
+ 
+   assertNoPendingCreatedNavigationTargetData();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ 
+   await extension.unload();
+ });
+ 
+ add_task(async function test_window_open_close_from_browserAction_popup() {
+   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+ 
+   gBrowser.selectedTab = tab1;
+@@ -152,12 +152,12 @@ add_task(async function test_window_open
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#new-tab-from-window-open`,
+     },
+   });
+ 
+   assertNoPendingCreatedNavigationTargetData();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
++++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_onCreatedNavigationTarget_window_open.js
+@@ -86,17 +86,17 @@ add_task(async function test_window_open
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#new-win-from-window-open`,
+     },
+   });
+ 
+   assertNoPendingCreatedNavigationTargetData();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ 
+   await extension.unload();
+ });
+ 
+ add_task(async function test_window_open_close_from_browserAction_popup() {
+   const tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, SOURCE_PAGE);
+ 
+   gBrowser.selectedTab = tab1;
+@@ -152,12 +152,12 @@ add_task(async function test_window_open
+       sourceTabId: expectedSourceTab.sourceTabId,
+       sourceFrameId: 0,
+       url: `${OPENED_PAGE}#new-tab-from-window-open`,
+     },
+   });
+ 
+   assertNoPendingCreatedNavigationTargetData();
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+ 
+   await extension.unload();
+ });
+diff --git a/browser/components/extensions/test/browser/browser_ext_webRequest.js b/browser/components/extensions/test/browser/browser_ext_webRequest.js
+--- a/browser/components/extensions/test/browser/browser_ext_webRequest.js
++++ b/browser/components/extensions/test/browser/browser_ext_webRequest.js
+@@ -90,17 +90,17 @@ add_task(async function test_newTab() {
+       headers,
+     },
+   };
+   extension.sendMessage("set-expected", {expect, ignore: ["favicon.ico"]});
+   await extension.awaitMessage("continue");
+   let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser, dummy + "?newTab");
+ 
+   await extension.awaitMessage("done");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_subframe() {
+   let expect = {
+     "file_dummy.html": {
+       type: "main_frame",
+       headers,
+     },
+diff --git a/browser/components/extensions/test/browser/browser_ext_windows.js b/browser/components/extensions/test/browser/browser_ext_windows.js
+--- a/browser/components/extensions/test/browser/browser_ext_windows.js
++++ b/browser/components/extensions/test/browser/browser_ext_windows.js
+@@ -185,17 +185,17 @@ add_task(async function testWindowTitleP
+   await extension.awaitMessage("grant-activeTab");
+   await clickBrowserAction(extension);
+   extension.sendMessage("title", document.title);
+ 
+   await extension.awaitFinish("window-title-permissions");
+ 
+   await extension.unload();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function testInvalidWindowId() {
+   let extension = ExtensionTestUtils.loadExtension({
+     async background() {
+       await browser.test.assertRejects(
+         // Assuming that this windowId does not exist.
+         browser.windows.get(123456789),
+diff --git a/browser/components/feeds/test/browser/browser_telemetry_checks.js.1442465-4_2.later b/browser/components/feeds/test/browser/browser_telemetry_checks.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/feeds/test/browser/browser_telemetry_checks.js.1442465-4_2.later
+@@ -0,0 +1,15 @@
++--- browser_telemetry_checks.js
+++++ browser_telemetry_checks.js
++@@ -87,11 +87,11 @@ add_task(async function() {
++   Assert.equal(getSnapShot().parent[kLivemarkOpened], 1, "Should count livemark opening");
++ 
++   // And opening an item in the popup:
++   let item = await TestUtils.waitForCondition(
++     () => popup.querySelector("menuitem.bookmark-item"));
++   item.doCommand();
++   Assert.equal(getSnapShot().parent[kLivemarkItemOpened], 1, "Should count livemark item opening");
++   popup.hidePopup();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
+diff --git a/browser/components/migration/tests/browser/browser_undo_notification.js b/browser/components/migration/tests/browser/browser_undo_notification.js
+--- a/browser/components/migration/tests/browser/browser_undo_notification.js
++++ b/browser/components/migration/tests/browser/browser_undo_notification.js
+@@ -21,17 +21,17 @@ add_task(async function autoMigrationUnd
+ 
+   // Disable preloaded activity-stream about:newtab for this test to only run on
+   // the tiles version
+   await SpecialPowers.pushPrefEnv({set: [
+     ["browser.newtabpage.activity-stream.enabled", false],
+     ["browser.newtab.preload", false]
+   ]});
+   // Open and close about:newtab to remove any preloaded activity-stream version
+-  await BrowserTestUtils.removeTab(await BrowserTestUtils.openNewForegroundTab(gBrowser));
++  BrowserTestUtils.removeTab(await BrowserTestUtils.openNewForegroundTab(gBrowser));
+ 
+   for (let url of ["about:newtab", "about:home"]) {
+     undoCalled = false;
+     // Can't use pushPrefEnv because of bug 1323779
+     Services.prefs.setCharPref("browser.migrate.automigrate.browser", "someunknownbrowser");
+     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
+     let browser = tab.linkedBrowser;
+     if (!getNotification(browser)) {
+@@ -40,17 +40,17 @@ add_task(async function autoMigrationUnd
+     }
+ 
+     ok(true, `Got notification for ${url}`);
+     let notification = getNotification(browser);
+     let notificationBox = notification.parentNode;
+     notification.querySelector("button.notification-button-default").click();
+     ok(!undoCalled, "Undo should not be called when clicking the default button");
+     is(notification, notificationBox._closedNotification, "Notification should be closing");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+ 
+     undoCalled = false;
+     Services.prefs.setCharPref("browser.migrate.automigrate.browser", "chrome");
+     tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
+     browser = tab.linkedBrowser;
+     if (!getNotification(browser)) {
+       info(`Notification for ${url} not immediately present, waiting for it.`);
+       await BrowserTestUtils.waitForNotificationBar(gBrowser, browser, kExpectedNotificationId);
+@@ -65,13 +65,13 @@ add_task(async function autoMigrationUnd
+       ["browser.migrate.automigrate.undo-survey-locales", "en-US"],
+     ]});
+     let tabOpenedPromise = BrowserTestUtils.waitForNewTab(gBrowser, "https://example.com/?browser=Google%20Chrome");
+     notification.querySelector("button:not(.notification-button-default)").click();
+     ok(undoCalled, "Undo should be called when clicking the non-default (Don't Keep) button");
+     is(notification, notificationBox._closedNotification, "Notification should be closing");
+     let surveyTab = await tabOpenedPromise;
+     ok(surveyTab, "Should have opened a tab with a survey");
+-    await BrowserTestUtils.removeTab(surveyTab);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(surveyTab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+ 
+diff --git a/browser/components/migration/tests/browser/browser_undo_notification_multiple_dismissal.js b/browser/components/migration/tests/browser/browser_undo_notification_multiple_dismissal.js
+--- a/browser/components/migration/tests/browser/browser_undo_notification_multiple_dismissal.js
++++ b/browser/components/migration/tests/browser/browser_undo_notification_multiple_dismissal.js
+@@ -112,11 +112,11 @@ add_task(async function checkNotificatio
+   info("Waiting for notification to be removed in first (background) tab");
+   await firstTabNotificationRemovedPromise;
+   info("Waiting for bookmark to be removed");
+   await bookmarkRemovedPromise;
+   info("Waiting for prefs to be reset");
+   await prefResetPromise;
+ 
+   info("Removing spare tabs");
+-  await BrowserTestUtils.removeTab(firstTab);
+-  await BrowserTestUtils.removeTab(secondTab);
++  BrowserTestUtils.removeTab(firstTab);
++  BrowserTestUtils.removeTab(secondTab);
+ });
+diff --git a/browser/components/migration/tests/browser/browser_undo_notification_wording.js b/browser/components/migration/tests/browser/browser_undo_notification_wording.js
+--- a/browser/components/migration/tests/browser/browser_undo_notification_wording.js
++++ b/browser/components/migration/tests/browser/browser_undo_notification_wording.js
+@@ -56,12 +56,12 @@ add_task(async function autoMigrationUnd
+         ok(notificationText.includes(localizedImportItem),
+            "Expected notification to contain " + localizedImportItem);
+       } else {
+         ok(!notificationText.includes(localizedImportItem),
+            "Expected notification not to contain " + localizedImportItem);
+       }
+     }
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ });
+ 
+diff --git a/browser/components/newtab/tests/browser/browser_newtab_overrides.js b/browser/components/newtab/tests/browser/browser_newtab_overrides.js
+--- a/browser/components/newtab/tests/browser/browser_newtab_overrides.js
++++ b/browser/components/newtab/tests/browser/browser_newtab_overrides.js
+@@ -78,17 +78,17 @@ add_task(async function override_loads_i
+ 
+     let browser = gBrowser.selectedBrowser;
+     await BrowserTestUtils.browserLoaded(browser);
+ 
+     await ContentTask.spawn(browser, {url: overrideURL}, async function(args) {
+       Assert.equal(content.location.href, args.url.trim(), "Got right URL");
+       Assert.equal(content.document.location.href, args.url.trim(), "Got right URL");
+     });  // jshint ignore:line
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   }
+ });
+ 
+ /*
+  * Tests edge cases when someone overrides the newtabpage with whitespace
+  */
+ add_task(async function override_blank_loads_in_browser() {
+   let overrides = [
+@@ -110,17 +110,17 @@ add_task(async function override_blank_l
+ 
+     let browser = gBrowser.selectedBrowser;
+     await BrowserTestUtils.browserLoaded(browser);
+ 
+     await ContentTask.spawn(browser, {}, async function() {
+       Assert.equal(content.location.href, "about:blank", "Got right URL");
+       Assert.equal(content.document.location.href, "about:blank", "Got right URL");
+     });  // jshint ignore:line
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   }
+ });
+ 
+ function nextChangeNotificationPromise(aNewURL, testMessage) {
+   return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) {  // jshint unused:false
+       Assert.equal(aData, aNewURL, testMessage);
+       return true;
+   });
+diff --git a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+--- a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
++++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+@@ -177,17 +177,17 @@ async function assignCookiesUnderFirstPa
+   // iframe which loads the aURL.
+   let tabInfo = await openTabInFirstParty(aURL, aFirstParty);
+ 
+   // Add cookies into the iframe.
+   await ContentTask.spawn(tabInfo.browser, aCookieValue, async function(value) {
+     content.document.cookie = value;
+   });
+ 
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+ 
+ async function generateCookies(aThirdParty) {
+   // we generate two different cookies for two first party domains.
+   let cookies = [];
+   cookies.push(Math.random().toString());
+   cookies.push(Math.random().toString());
+ 
+@@ -236,17 +236,17 @@ async function doTest(aTestPage, aExpect
+   promiseObserveFavicon = observeFavicon(FIRST_PARTY_TWO, aExpectedCookies[1], secondPageURI);
+ 
+   // Open the tab for the second site.
+   tabInfo = await openTab(TEST_SITE_TWO + aTestPage);
+ 
+   // Waiting until favicon requests are all made.
+   await promiseObserveFavicon;
+ 
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+ 
+ add_task(async function setup() {
+   // Make sure first party isolation is enabled.
+   await SpecialPowers.pushPrefEnv({"set": [
+       ["privacy.firstparty.isolate", true]
+   ]});
+ });
+@@ -324,12 +324,12 @@ add_task(async function test_favicon_cac
+   // go through the network.
+   response = await promiseForFaviconResponse;
+ 
+   // Check that the favicon response has came from the network and it has the
+   // correct first party domain.
+   is(response.topic, "http-on-examine-response", "The favicon image should be loaded through network again.");
+   is(response.firstPartyDomain, FIRST_PARTY_TWO, "We should only observe the network response for the second first party.");
+ 
+-  await BrowserTestUtils.removeTab(tabInfoA.tab);
+-  await BrowserTestUtils.removeTab(tabInfoB.tab);
+-  await BrowserTestUtils.removeTab(tabInfoC.tab);
++  BrowserTestUtils.removeTab(tabInfoA.tab);
++  BrowserTestUtils.removeTab(tabInfoB.tab);
++  BrowserTestUtils.removeTab(tabInfoC.tab);
+ });
+diff --git a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js.1442465-4_2.later b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js.1442465-4_2.later
+@@ -0,0 +1,40 @@
++--- browser_favicon_firstParty.js
+++++ browser_favicon_firstParty.js
++@@ -284,17 +284,17 @@ async function doTestForAllTabsFavicon(a
++   await allTabsPopupShownPromise;
++ 
++   // Close the popup of allTabs and wait until it's done.
++   let allTabsPopupHiddenPromise = BrowserTestUtils.waitForEvent(allTabsBtn, "popuphidden");
++   EventUtils.synthesizeMouseAtCenter(allTabsBtn, {});
++   await allTabsPopupHiddenPromise;
++ 
++   // Close the tab.
++-  await BrowserTestUtils.removeTab(tabInfo.tab);
+++  BrowserTestUtils.removeTab(tabInfo.tab);
++ 
++   faviconURI = aIsThirdParty ? THIRD_PARTY_SITE + FAVICON_URI :
++                                TEST_SITE_TWO + FAVICON_URI;
++ 
++   // Start to observe the event of that favicon has been fully loaded.
++   promiseFaviconLoaded = waitOnFaviconLoaded(faviconURI);
++ 
++   // Open the tab for the second site.
++@@ -317,17 +317,17 @@ async function doTestForAllTabsFavicon(a
++   await allTabsPopupShownPromise;
++ 
++   // Close the popup of allTabs and wait until it's done.
++   allTabsPopupHiddenPromise = BrowserTestUtils.waitForEvent(allTabsBtn, "popuphidden");
++   EventUtils.synthesizeMouseAtCenter(allTabsBtn, {});
++   await allTabsPopupHiddenPromise;
++ 
++   // Close the tab.
++-  await BrowserTestUtils.removeTab(tabInfo.tab);
+++  BrowserTestUtils.removeTab(tabInfo.tab);
++ 
++   // Reset the 'overflow' attribute to make the allTabs button hidden again.
++   tabBrowser.removeAttribute("overflow");
++ }
++ 
++ add_task(async function setup() {
++   // Make sure first party isolation is enabled.
++   await SpecialPowers.pushPrefEnv({"set": [
+diff --git a/browser/components/originattributes/test/browser/browser_windowOpenerRestriction.js b/browser/components/originattributes/test/browser/browser_windowOpenerRestriction.js
+--- a/browser/components/originattributes/test/browser/browser_windowOpenerRestriction.js
++++ b/browser/components/originattributes/test/browser/browser_windowOpenerRestriction.js
+@@ -53,17 +53,17 @@ async function testPref(aIsPrefEnabled) 
+ 
+   // The target page will do the check and show the result through its title.
+   is(targetBrowser.contentTitle, "pass", "The behavior of window.opener is correct.");
+ 
+   // Close Tabs.
+   await ContentTask.spawn(browser, null, async function() {
+     this.openedWindow.close();
+   });
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Reset cookies
+   Services.cookies.removeAll();
+ }
+ 
+ add_task(async function runTests() {
+   let tests = [true, false];
+ 
+diff --git a/browser/components/originattributes/test/browser/head.js b/browser/components/originattributes/test/browser/head.js
+--- a/browser/components/originattributes/test/browser/head.js
++++ b/browser/components/originattributes/test/browser/head.js
+@@ -357,14 +357,14 @@ this.IsolationTestTools = {
+             `isolation ${shouldIsolate ? "on" : "off"} with TabSettingA ` +
+             `${tabSettingA} and tabSettingB ${tabSettingB}` +
+             `, resultA = ${resultA}, resultB = ${resultB}`;
+ 
+           ok(result, msg);
+         }
+ 
+         // Close Tabs.
+-        await BrowserTestUtils.removeTab(tabInfoA.tab);
+-        await BrowserTestUtils.removeTab(tabInfoB.tab);
++        BrowserTestUtils.removeTab(tabInfoA.tab);
++        BrowserTestUtils.removeTab(tabInfoB.tab);
+       }
+     });
+   }
+ };
+diff --git a/browser/components/places/tests/browser/browser_addBookmarkForFrame.js b/browser/components/places/tests/browser/browser_addBookmarkForFrame.js
+--- a/browser/components/places/tests/browser/browser_addBookmarkForFrame.js
++++ b/browser/components/places/tests/browser/browser_addBookmarkForFrame.js
+@@ -21,17 +21,17 @@ async function withAddBookmarkForFrame(t
+   await withBookmarksDialog(true, function() {
+     let frameMenuItem = document.getElementById("frame");
+     frameMenuItem.click();
+ 
+     let bookmarkFrame = document.getElementById("context-bookmarkframe");
+     bookmarkFrame.click();
+   }, taskFn);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function test_open_add_bookmark_for_frame() {
+   info("Test basic opening of the add bookmark for frame dialog.");
+   await withAddBookmarkForFrame(async dialogWin => {
+     let namepicker = dialogWin.document.getElementById("editBMPanel_namePicker");
+     Assert.ok(!namepicker.readOnly, "Name field is writable");
+     Assert.equal(namepicker.value, "Left frame", "Name field is correct.");
+diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js b/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
+--- a/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
++++ b/browser/components/places/tests/browser/browser_bookmarkProperties_bookmarkAllTabs.js
+@@ -7,17 +7,17 @@ const TEST_URLS = [
+ 
+ add_task(async function() {
+   let tabs = [];
+   for (let url of TEST_URLS) {
+     tabs.push(await BrowserTestUtils.openNewForegroundTab(gBrowser, url));
+   }
+   registerCleanupFunction(async function() {
+     for (let tab of tabs) {
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     }
+   });
+ 
+   await withBookmarksDialog(true,
+     function open() {
+       document.getElementById("Browser:BookmarkAllTabs").doCommand();
+     },
+     async dialog => {
+diff --git a/browser/components/places/tests/browser/browser_bookmarkProperties_no_user_actions.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_bookmarkProperties_no_user_actions.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_bookmarkProperties_no_user_actions.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmarkProperties_no_user_actions.js
+++++ browser_bookmarkProperties_no_user_actions.js
++@@ -15,17 +15,17 @@ add_task(async function test_change_titl
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab({
++     gBrowser,
++     opening: TEST_URL,
++     waitForStateStop: true
++   });
++ 
++   registerCleanupFunction(async () => {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++     await PlacesUtils.bookmarks.eraseEverything();
++   });
++ 
++   let bookmarkPanel = document.getElementById("editBookmarkPanel");
++   let shownPromise = promisePopupShown(bookmarkPanel);
++ 
++   let bookmarkStar = BookmarkingUI.star;
++   bookmarkStar.click();
+diff --git a/browser/components/places/tests/browser/browser_bookmark_add_tags.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_bookmark_add_tags.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_bookmark_add_tags.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmark_add_tags.js
+++++ browser_bookmark_add_tags.js
++@@ -29,17 +29,17 @@ add_task(async function test_add_bookmar
++   let tab = await BrowserTestUtils.openNewForegroundTab({
++     gBrowser,
++     opening: TEST_URL,
++     waitForStateStop: true
++   });
++ 
++   // Cleanup.
++   registerCleanupFunction(async function() {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ 
++   let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
++ 
++   // The bookmarks panel is expected to auto-close after this step.
++   await hideBookmarksPanel(async () => {
++     // Click the bookmark star to bookmark the page.
++     await clickBookmarkStar();
+diff --git a/browser/components/places/tests/browser/browser_bookmark_all_tabs.js b/browser/components/places/tests/browser/browser_bookmark_all_tabs.js
+--- a/browser/components/places/tests/browser/browser_bookmark_all_tabs.js
++++ b/browser/components/places/tests/browser/browser_bookmark_all_tabs.js
+@@ -27,11 +27,13 @@ add_task(async function() {
+     BASE_URL + "bookmark_dummy_2.html"
+   ], "Correct URIs are returned");
+ 
+   Assert.deepEqual(URIs.map(URI => URI.title), [
+     "New Tab", "Bookmark Dummy 1", "Bookmark Dummy 2"
+   ], "Correct titles are returned");
+ 
+   registerCleanupFunction(async function() {
+-    await Promise.all(tabs.map(BrowserTestUtils.removeTab));
++    for (let tab of tabs) {
++      BrowserTestUtils.removeTab(tab);
++    }
+   });
+ });
+diff --git a/browser/components/places/tests/browser/browser_bookmark_private_window.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_bookmark_private_window.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_bookmark_private_window.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmark_private_window.js
+++++ browser_bookmark_private_window.js
++@@ -14,17 +14,17 @@ registerCleanupFunction(async () => {
++   await PlacesUtils.bookmarks.eraseEverything();
++ });
++ 
++ add_task(async function test_add_bookmark_from_private_window() {
++   let win = await BrowserTestUtils.openNewBrowserWindow({private: true});
++   let tab = await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
++ 
++   registerCleanupFunction(async () => {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++     await BrowserTestUtils.closeWindow(win);
++   });
++ 
++   // Open the bookmark panel.
++   let bookmarkPanel = win.document.getElementById("editBookmarkPanel");
++   let shownPromise = promisePopupShown(bookmarkPanel);
++   let bookmarkStar = win.BookmarkingUI.star;
++   bookmarkStar.click();
+diff --git a/browser/components/places/tests/browser/browser_bookmark_remove_tags.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_bookmark_remove_tags.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_bookmark_remove_tags.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmark_remove_tags.js
+++++ browser_bookmark_remove_tags.js
++@@ -36,17 +36,17 @@ add_task(async function test_remove_tags
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab({
++     gBrowser,
++     opening: TEST_URL,
++     waitForStateStop: true
++   });
++ 
++   registerCleanupFunction(async () => {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ 
++   let bookmarkPanel = document.getElementById("editBookmarkPanel");
++   let shownPromise = promisePopupShown(bookmarkPanel);
++   let bookmarkStar = BookmarkingUI.star;
++   bookmarkStar.click();
++   await shownPromise;
++ 
+diff --git a/browser/components/places/tests/browser/browser_bookmarklet_windowOpen.js b/browser/components/places/tests/browser/browser_bookmarklet_windowOpen.js
+--- a/browser/components/places/tests/browser/browser_bookmarklet_windowOpen.js
++++ b/browser/components/places/tests/browser/browser_bookmarklet_windowOpen.js
+@@ -50,11 +50,11 @@ add_task(async function openKeywordBookm
+   info("Got tab");
+   let browser = tab.linkedBrowser;
+   if (!browser.currentURI || browser.currentURI.spec != TEST_URL) {
+     info("Waiting for browser load");
+     await BrowserTestUtils.browserLoaded(browser, false, TEST_URL);
+   }
+   is(browser.currentURI && browser.currentURI.spec, TEST_URL, "Tab with expected URL loaded.");
+   info("Waiting to remove tab");
+-  await Promise.all([ BrowserTestUtils.removeTab(tab),
+-                      BrowserTestUtils.removeTab(moztab) ]);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(moztab);
+ });
+diff --git a/browser/components/places/tests/browser/browser_bookmarks_change_title.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_bookmarks_change_title.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_bookmarks_change_title.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_bookmarks_change_title.js
+++++ browser_bookmarks_change_title.js
++@@ -44,17 +44,17 @@ add_task(async function test_change_titl
++ 
++   let tab = await BrowserTestUtils.openNewForegroundTab({
++     gBrowser,
++     opening: TEST_URL,
++     waitForStateStop: true
++   });
++ 
++   registerCleanupFunction(async () => {
++-    await BrowserTestUtils.removeTab(tab);
+++    BrowserTestUtils.removeTab(tab);
++   });
++ 
++   let bookmarkPanel = document.getElementById("editBookmarkPanel");
++   let shownPromise = promisePopupShown(bookmarkPanel);
++ 
++   let bookmarkStar = BookmarkingUI.star;
++   bookmarkStar.click();
++ 
+diff --git a/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js b/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js
+--- a/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js
++++ b/browser/components/places/tests/browser/browser_click_bookmarks_on_toolbar.js
+@@ -28,17 +28,17 @@ function waitForNewTab(url, inBackground
+     if (inBackground) {
+       Assert.notEqual(tab,
+         gBrowser.selectedTab, `The new tab is in the background`);
+     } else {
+       Assert.equal(tab,
+         gBrowser.selectedTab, `The new tab is in the foreground`);
+     }
+ 
+-    return BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ }
+ 
+ add_task(async function setup() {
+   let bookmarks = await Promise.all(TEST_PAGES.map((url, index) => {
+     return PlacesUtils.bookmarks.insert({
+       parentGuid: PlacesUtils.bookmarks.toolbarGuid,
+       title: `Title ${index}`,
+@@ -152,12 +152,12 @@ add_task(async function quickContextMenu
+   await promise;
+ 
+   Assert.ok(!document.getElementById("placesContext_open").disabled,
+             "Commands in context menu are enabled");
+ 
+   promise = BrowserTestUtils.waitForEvent(placesContext, "popuphidden");
+   placesContext.hidePopup();
+   await promise;
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ 
+   await SpecialPowers.popPrefEnv();
+ });
+diff --git a/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js b/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
+--- a/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
++++ b/browser/components/places/tests/browser/browser_library_left_pane_middleclick.js
+@@ -70,17 +70,19 @@ add_task(async function test_open_folder
+     gLibrary.PlacesOrganizer._places.view.treeIndexForNode(bookmarkedNode),
+     0,
+     { button: 1 });
+ 
+   let tabs = await promiseLoaded;
+ 
+   Assert.ok(true, "Expected tabs were loaded");
+ 
+-  await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab)));
++  for (let tab of tabs) {
++    BrowserTestUtils.removeTab(tab);
++  }
+ });
+ 
+ function mouseEventOnCell(aTree, aRowIndex, aColumnIndex, aEventDetails) {
+   var selection = aTree.view.selection;
+   selection.select(aRowIndex);
+   aTree.treeBoxObject.ensureRowIsVisible(aRowIndex);
+   var column = aTree.columns[aColumnIndex];
+ 
+diff --git a/browser/components/places/tests/browser/browser_library_middleclick.js b/browser/components/places/tests/browser/browser_library_middleclick.js
+--- a/browser/components/places/tests/browser/browser_library_middleclick.js
++++ b/browser/components/places/tests/browser/browser_library_middleclick.js
+@@ -181,17 +181,19 @@ async function runTest(test) {
+     BrowserTestUtils.waitForNewTab(gBrowser, uri, false, true)));
+ 
+   mouseEventOnCell(gLibrary.ContentTree.view, 0, 0, { button: 1 });
+ 
+   let tabs = await promiseLoaded;
+ 
+   Assert.ok(true, "Expected tabs were loaded");
+ 
+-  await Promise.all(tabs.map(tab => BrowserTestUtils.removeTab(tab)));
++  for (let tab of tabs) {
++    BrowserTestUtils.removeTab(tab);
++  }
+ 
+   await test.cleanup();
+ }
+ 
+ add_task(async function test_all() {
+   for (let test of gTests) {
+     await runTest(test);
+   }
+diff --git a/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js b/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
+--- a/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
++++ b/browser/components/places/tests/browser/browser_markPageAsFollowedLink.js
+@@ -42,17 +42,17 @@ add_task(async function test() {
+   await ContentTask.spawn(tab.linkedBrowser, {}, async function() {
+     content.frames[0].document.getElementById("clickme").click();
+   });
+ 
+   // Wait for the right frame visit to be registered.
+   info("Waiting right frame visit");
+   await deferredRightFrameVisit.promise;
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ function getTransitionForUrl(url) {
+   return PlacesUtils.withConnectionWrapper("browser_markPageAsFollowedLink", async db => {
+     let rows = await db.execute(`
+       SELECT visit_type
+       FROM moz_historyvisits
+       JOIN moz_places h ON place_id = h.id
+diff --git a/browser/components/places/tests/browser/browser_stayopenmenu.js.1442465-4_2.later b/browser/components/places/tests/browser/browser_stayopenmenu.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/places/tests/browser/browser_stayopenmenu.js.1442465-4_2.later
+@@ -0,0 +1,139 @@
++--- browser_stayopenmenu.js
+++++ browser_stayopenmenu.js
++@@ -6,17 +6,17 @@
++ 
++ async function locateBookmarkAndTestCtrlClick(menupopup) {
++   let testMenuitem = [...menupopup.childNodes].find(node => node.label == "Test1");
++   ok(testMenuitem, "Found test bookmark.");
++   let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(testMenuitem, {accelKey: true});
++   let newTab = await promiseTabOpened;
++   ok(true, "Bookmark ctrl-click opened new tab.");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++   return testMenuitem;
++ }
++ 
++ async function testContextmenu(menuitem) {
++   let doc = menuitem.ownerDocument;
++   let cm = doc.getElementById("placesContext");
++   let promiseEvent = BrowserTestUtils.waitForEvent(cm, "popupshown");
++   EventUtils.synthesizeMouseAtCenter(menuitem, {type: "contextmenu", button: 2});
++@@ -98,28 +98,28 @@ add_task(async function testStayopenBook
++   var menuitem = await locateBookmarkAndTestCtrlClick(BMBpopup);
++   ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
++ 
++   // Test Bookmarks Menu Button stayopen clicks: middle-click.
++   let promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
++   let newTab = await promiseTabOpened;
++   ok(true, "Bookmark middle-click opened new tab.");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++   ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
++ 
++   // Test Bookmarks Menu Button stayopen clicks - 'Open in new tab' on context menu.
++   newTab = await testContextmenu(menuitem);
++   ok(true, "Bookmark contextmenu opened new tab.");
++   ok(BMB.open, "Bookmarks Menu Button's Popup should still be open.");
++   promiseEvent = BrowserTestUtils.waitForEvent(BMBpopup, "popuphidden");
++   BMB.open = false;
++   await promiseEvent;
++   info("Closing menu");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++ 
++   // Test App Menu's Bookmarks Library stayopen clicks.
++   let appMenu = document.getElementById("PanelUI-menu-button");
++   let appMenuPopup = document.getElementById("appMenu-popup");
++   let PopupShownPromise = BrowserTestUtils.waitForEvent(appMenuPopup, "popupshown");
++   appMenu.click();
++   await PopupShownPromise;
++   let libView = document.getElementById("appMenu-libraryView");
++@@ -140,17 +140,17 @@ add_task(async function testStayopenBook
++   var testMenuitem = await locateBookmarkAndTestCtrlClick(menu);
++   ok(appMenu.open, "Menu should remain open.");
++ 
++   // Test App Menu's Bookmarks Library stayopen clicks: middle-click.
++   promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(testMenuitem, {button: 1});
++   newTab = await promiseTabOpened;
++   ok(true, "Bookmark middle-click opened new tab.");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++   ok(PanelView.forNode(BMview).active, "Should still show the bookmarks subview");
++   ok(appMenu.open, "Menu should remain open.");
++ 
++   // Close the App Menu
++   appMenuPopup.hidePopup();
++   ok(!appMenu.open, "The menu should now be closed.");
++ 
++   // Disable the rest of the tests on Mac due to Mac's handling of menus being
++@@ -169,23 +169,23 @@ add_task(async function testStayopenBook
++   menuitem = await locateBookmarkAndTestCtrlClick(BMpopup);
++   ok(BM.open, "Bookmarks Menu's Popup should still be open.");
++ 
++   // Test Bookmarks Menu (menubar) stayopen clicks: middle-click.
++   promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
++   newTab = await promiseTabOpened;
++   ok(true, "Bookmark middle-click opened new tab.");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++   ok(BM.open, "Bookmarks Menu's Popup should still be open.");
++ 
++   // Test Bookmarks Menu (menubar) stayopen clicks: 'Open in new tab' on context menu.
++   newTab = await testContextmenu(menuitem);
++   ok(true, "Bookmark contextmenu opened new tab.");
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++   ok(BM.open, "Bookmarks Menu's Popup should still be open.");
++   promiseEvent = BrowserTestUtils.waitForEvent(BMpopup, "popuphidden");
++   BM.open = false;
++   await promiseEvent;
++ 
++   // Test Bookmarks Toolbar stayopen clicks - Ctrl-click.
++   let BT = document.getElementById("PlacesToolbarItems");
++   let toolbarbutton = BT.firstChild;
++@@ -200,38 +200,38 @@ add_task(async function testStayopenBook
++   promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(menuitem, {ctrlKey: true});
++   newTab = await promiseTabOpened;
++   ok(true, "Bookmark in folder on bookmark's toolbar ctrl-click opened new tab.");
++   ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
++   promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
++   toolbarbutton.open = false;
++   await promiseEvent;
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++ 
++   // Test Bookmarks Toolbar stayopen clicks: middle-click.
++   promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popupshown");
++   EventUtils.synthesizeMouseAtCenter(toolbarbutton, {});
++   await promiseEvent;
++   ok(true, "Bookmarks toolbar folder's popup is open.");
++   promiseTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, null);
++   EventUtils.synthesizeMouseAtCenter(menuitem, {button: 1});
++   newTab = await promiseTabOpened;
++   ok(true, "Bookmark in folder on Bookmarks Toolbar middle-click opened new tab.");
++   ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
++   promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
++   toolbarbutton.open = false;
++   await promiseEvent;
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++ 
++   // Test Bookmarks Toolbar stayopen clicks: 'Open in new tab' on context menu.
++   promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popupshown");
++   EventUtils.synthesizeMouseAtCenter(toolbarbutton, {});
++   await promiseEvent;
++   ok(true, "Bookmarks toolbar folder's popup is open.");
++   newTab = await testContextmenu(menuitem);
++   ok(true, "Bookmark on Bookmarks Toolbar contextmenu opened new tab.");
++   ok(toolbarbutton.open, "Popup of folder on bookmark's toolbar should still be open.");
++   promiseEvent = BrowserTestUtils.waitForEvent(buttonMenupopup, "popuphidden");
++   toolbarbutton.open = false;
++   await promiseEvent;
++-  await BrowserTestUtils.removeTab(newTab);
+++  BrowserTestUtils.removeTab(newTab);
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_bug1020245_openPreferences_to_paneContent.js b/browser/components/preferences/in-content-new/tests/browser_bug1020245_openPreferences_to_paneContent.js
+--- a/browser/components/preferences/in-content-new/tests/browser_bug1020245_openPreferences_to_paneContent.js
++++ b/browser/components/preferences/in-content-new/tests/browser_bug1020245_openPreferences_to_paneContent.js
+@@ -19,17 +19,17 @@ add_task(async function() {
+   is(prefs.selectedPane, "paneGeneral", "General pane is selected when hash is a nonexistant-category");
+   prefs = await openPreferencesViaHash();
+   is(prefs.selectedPane, "paneGeneral", "General pane is selected by default");
+   prefs = await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
+   is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
+   let doc = gBrowser.contentDocument;
+   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
+   ok(doc.querySelector("#locationBarGroup").hidden, "Location Bar prefs should be hidden when only Reports are requested");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test opening Preferences with subcategory on an existing Preferences tab. See bug 1358475.
+ add_task(async function() {
+   let prefs = await openPreferencesViaOpenPreferencesAPI("general", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane is selected by default");
+   let doc = gBrowser.contentDocument;
+   is(doc.location.hash, "#general", "The subcategory should be removed from the URI");
+@@ -37,17 +37,17 @@ add_task(async function() {
+   //   - already opened one about:preferences tab up there and
+   //   - the goal is to test on the existing tab and
+   //   - using `openPreferencesViaOpenPreferencesAPI` would introduce more handling of additional about:blank and unneccessary event
+   openPreferences("privacy-reports");
+   let selectedPane = gBrowser.contentWindow.history.state;
+   is(selectedPane, "panePrivacy", "Privacy pane should be selected");
+   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
+   ok(doc.querySelector("#locationBarGroup").hidden, "Location Bar prefs should be hidden when only Reports are requested");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test opening to a subcategory displays the correct values for preferences
+ add_task(async function() {
+   // Skip if crash reporting isn't enabled since the checkbox will be missing.
+   if (!AppConstants.MOZ_CRASHREPORTER) {
+     return;
+   }
+@@ -58,17 +58,17 @@ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
+ 
+   let doc = gBrowser.contentDocument;
+   ok(
+     doc.querySelector("#automaticallySubmitCrashesBox").checked,
+     "Checkbox for automatically submitting crashes should be checked when the pref is true and only Reports are requested"
+   );
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await SpecialPowers.popPrefEnv();
+ });
+ 
+ add_task(async function() {
+   // Skip if crash reporting isn't enabled since the checkbox will be missing.
+   if (!AppConstants.MOZ_CRASHREPORTER) {
+     return;
+   }
+@@ -79,17 +79,17 @@ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
+ 
+   let doc = gBrowser.contentDocument;
+   ok(
+     !doc.querySelector("#automaticallySubmitCrashesBox").checked,
+     "Checkbox for automatically submitting crashes should not be checked when the pref is false only Reports are requested"
+   );
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await SpecialPowers.popPrefEnv();
+ });
+ 
+ 
+ function openPreferencesViaHash(aPane) {
+   return new Promise(resolve => {
+     gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:preferences" + (aPane ? "#" + aPane : ""));
+     let newTabBrowser = gBrowser.selectedBrowser;
+diff --git a/browser/components/preferences/in-content-new/tests/browser_checkspelling.js b/browser/components/preferences/in-content-new/tests/browser_checkspelling.js
+--- a/browser/components/preferences/in-content-new/tests/browser_checkspelling.js
++++ b/browser/components/preferences/in-content-new/tests/browser_checkspelling.js
+@@ -15,10 +15,10 @@ add_task(async function() {
+ 
+   checkbox.click();
+ 
+   is(checkbox.checked,
+      Services.prefs.getIntPref("layout.spellcheckDefault") == 2,
+      "checkbox should represent pref value after clicking on checkbox");
+   ok(!checkbox.checked, "checkbox should not be checked after clicking on checkbox");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_containers_name_input.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_containers_name_input.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_containers_name_input.js.1442465-4_2.later
+@@ -0,0 +1,18 @@
++--- browser_containers_name_input.js
+++++ browser_containers_name_input.js
++@@ -1,14 +1,14 @@
++ const CONTAINERS_URL = "chrome://browser/content/preferences/containers.xul";
++ 
++ add_task(async function setup() {
++   await openPreferencesViaOpenPreferencesAPI("containers", { leaveOpen: true });
++   registerCleanupFunction(async function() {
++-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   });
++ });
++ 
++ add_task(async function() {
++   async function openDialog() {
++     let doc = gBrowser.selectedBrowser.contentDocument;
++ 
++     let dialogPromise = promiseLoadSubDialog(CONTAINERS_URL);
+diff --git a/browser/components/preferences/in-content-new/tests/browser_cookies_exceptions.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_cookies_exceptions.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_cookies_exceptions.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_cookies_exceptions.js
+++++ browser_cookies_exceptions.js
++@@ -266,17 +266,17 @@ async function runTest(test, observances
++     allow: Ci.nsIPermissionManager.ALLOW_ACTION,
++     deny: Ci.nsIPermissionManager.DENY_ACTION,
++   };
++   let btnApplyChanges = doc.getElementById("btnApplyChanges");
++   let observeAllPromise = createObserveAllPromise(observances);
++ 
++   await test(params, observeAllPromise, () => btnApplyChanges.doCommand());
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ }
++ 
++ function createObserveAllPromise(observances) {
++   return new Promise(resolve => {
++     let permObserver = {
++       observe(aSubject, aTopic, aData) {
++         if (aTopic != "perm-changed")
++           return;
+diff --git a/browser/components/preferences/in-content-new/tests/browser_engines.js b/browser/components/preferences/in-content-new/tests/browser_engines.js
+--- a/browser/components/preferences/in-content-new/tests/browser_engines.js
++++ b/browser/components/preferences/in-content-new/tests/browser_engines.js
+@@ -16,11 +16,11 @@ add_task(async function() {
+   }
+ 
+   // Avoid duplicated keywords
+   tree.view.setCellText(0, tree.columns.getNamedColumn("engineKeyword"), "keyword");
+   tree.view.setCellText(1, tree.columns.getNamedColumn("engineKeyword"), "keyword");
+   let cellKeyword = tree.view.getCellText(1, tree.columns.getNamedColumn("engineKeyword"));
+   isnot(cellKeyword, "keyword", "Do not allow duplicated keywords");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+diff --git a/browser/components/preferences/in-content-new/tests/browser_extension_controlled.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_extension_controlled.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_extension_controlled.js.1442465-4_2.later
+@@ -0,0 +1,127 @@
++--- browser_extension_controlled.js
+++++ browser_extension_controlled.js
++@@ -132,17 +132,17 @@ add_task(async function testExtensionCon
++   let addon = await AddonManager.getAddonByID("@set_homepage");
++   // Enable the extension so we get the UNINSTALL event, which is needed by
++   // ExtensionPreferencesManager to clean up properly.
++   // FIXME: See https://bugzilla.mozilla.org/show_bug.cgi?id=1408226.
++   addon.userDisabled = false;
++   await waitForMessageShown("browserHomePageExtensionContent");
++   // Do the uninstall now that the enable code has been run.
++   addon.uninstall();
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function testPrefLockedHomepage() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   let doc = gBrowser.contentDocument;
++   is(gBrowser.currentURI.spec, "about:preferences#general",
++      "#general should be in the URI for about:preferences");
++ 
++@@ -263,17 +263,17 @@ add_task(async function testPrefLockedHo
++   is(homePageInput.value, "", "The homepage is clear after being unlocked");
++   is(homePageInput.disabled, false, "The homepage is enabled after clearing lock");
++   buttonPrefs.forEach(pref => {
++     is(getButton(pref).disabled, false, `The ${pref} button is enabled when unlocked`);
++   });
++   is(controlledContent.hidden, true,
++      "The extension controlled message is hidden when unlocked with no extension");
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function testExtensionControlledNewTab() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   let doc = gBrowser.contentDocument;
++   is(gBrowser.currentURI.spec, "about:preferences#general",
++      "#general should be in the URI for about:preferences");
++ 
++@@ -310,17 +310,17 @@ add_task(async function testExtensionCon
++   dismissButton.click();
++   await hidden;
++ 
++   // Ensure the New Tab page has been reset and there is no message.
++   ok(!aboutNewTabService.newTabURL.startsWith("moz-extension:"), "new tab page is set back to default");
++   is(controlledContent.hidden, true, "The extension controlled row is shown");
++ 
++   // Cleanup the tab and add-on.
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   let addon = await AddonManager.getAddonByID("@set_newtab");
++   addon.uninstall();
++ });
++ 
++ add_task(async function testExtensionControlledDefaultSearch() {
++   await openPreferencesViaOpenPreferencesAPI("paneSearch", {leaveOpen: true});
++   let doc = gBrowser.contentDocument;
++   let extensionId = "@set_default_search";
++@@ -406,34 +406,34 @@ add_task(async function testExtensionCon
++   // Verify the extension is updated and search engine didn't change.
++   is(addon.version, "2.0", "The updated addon has the expected version");
++   is(controlledContent.hidden, true, "The extension controlled row is hidden after update");
++   is(initialEngine, Services.search.currentEngine,
++      "default search engine is still the initial engine after update");
++ 
++   await originalExtension.unload();
++   await updatedExtension.unload();
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function testExtensionControlledHomepageUninstalledAddon() {
++   async function checkHomepageEnabled() {
++     await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++     let doc = gBrowser.contentDocument;
++     is(gBrowser.currentURI.spec, "about:preferences#general",
++       "#general should be in the URI for about:preferences");
++     let controlledContent = doc.getElementById("browserHomePageExtensionContent");
++ 
++     // The homepage is enabled.
++     let homepageInut = doc.getElementById("browserHomePage");
++     is(homepageInut.disabled, false, "The homepage input is enabled");
++     is(homepageInut.value, "", "The homepage input is empty");
++     is(controlledContent.hidden, true, "The extension controlled row is hidden");
++ 
++-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   }
++ 
++   await ExtensionSettingsStore.initialize();
++ 
++   // Verify the setting isn't reported as controlled and the inputs are enabled.
++   is(ExtensionSettingsStore.getSetting("prefs", "homepage_override"), null,
++      "The homepage_override is not set");
++   await checkHomepageEnabled();
++@@ -607,17 +607,17 @@ add_task(async function testExtensionCon
++ 
++   // Enable the extension so we get the UNINSTALL event, which is needed by
++   // ExtensionPreferencesManager to clean up properly.
++   // TODO: BUG 1408226
++   await reEnableExtension(addon);
++ 
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function testExtensionControlledProxyConfig() {
++   const proxySvc = Ci.nsIProtocolProxyService;
++   const PROXY_DEFAULT = proxySvc.PROXYCONFIG_SYSTEM;
++   const EXTENSION_ID = "@set_proxy";
++   const CONTROLLED_SECTION_ID = "proxyExtensionContent";
++   const CONTROLLED_BUTTON_ID = "disableProxyExtension";
++@@ -809,10 +809,10 @@ add_task(async function testExtensionCon
++ 
++   // Enable the extension so we get the UNINSTALL event, which is needed by
++   // ExtensionPreferencesManager to clean up properly.
++   // TODO: BUG 1408226
++   await reEnableExtension(addon);
++ 
++   await extension.unload();
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_fluent.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_fluent.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_fluent.js.1442465-4_2.later
+@@ -0,0 +1,14 @@
++--- browser_fluent.js
+++++ browser_fluent.js
++@@ -39,10 +39,10 @@ add_task(async function() {
++ 
++   Assert.deepEqual(msg, {
++     value: null,
++     attrs: [
++       {name: "label", value: elem.getAttribute("label")}
++     ]
++   });
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_layersacceleration.js b/browser/components/preferences/in-content-new/tests/browser_layersacceleration.js
+--- a/browser/components/preferences/in-content-new/tests/browser_layersacceleration.js
++++ b/browser/components/preferences/in-content-new/tests/browser_layersacceleration.js
+@@ -13,10 +13,10 @@ add_task(async function() {
+      Services.prefs.getBoolPref("layers.acceleration.disabled"),
+      "checkbox should represent inverted pref value before clicking on checkbox");
+ 
+   checkbox.click();
+ 
+   is(!checkbox.checked,
+      Services.prefs.getBoolPref("layers.acceleration.disabled"),
+      "checkbox should represent inverted pref value after clicking on checkbox");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_masterpassword.js b/browser/components/preferences/in-content-new/tests/browser_masterpassword.js
+--- a/browser/components/preferences/in-content-new/tests/browser_masterpassword.js
++++ b/browser/components/preferences/in-content-new/tests/browser_masterpassword.js
+@@ -45,10 +45,10 @@ add_task(async function() {
+   dialogURL = "";
+   checkbox.click();
+   is(dialogURL,
+      "chrome://mozapps/content/preferences/removemp.xul",
+      "clicking on the checkbox to uncheck master password should show the removal dialog");
+   ok(button.disabled, "master password button should now be disabled");
+   ok(!checkbox.checked, "master password checkbox should now be unchecked");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_performance.js b/browser/components/preferences/in-content-new/tests/browser_performance.js
+--- a/browser/components/preferences/in-content-new/tests/browser_performance.js
++++ b/browser/components/preferences/in-content-new/tests/browser_performance.js
+@@ -66,17 +66,17 @@ add_task(async function() {
+   contentProcessCount.value = DEFAULT_PROCESS_COUNT;
+   contentProcessCount.doCommand();
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT, "pref value should be default value");
+   is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT, "selected item should be default one");
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be still shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+   let doc = gBrowser.contentDocument;
+   let useRecommendedPerformanceSettings = doc.querySelector("#useRecommendedPerformanceSettings");
+@@ -91,34 +91,34 @@ add_task(async function() {
+   useRecommendedPerformanceSettings.click();
+ 
+   is(Services.prefs.getBoolPref("browser.preferences.defaultPerformanceSettings.enabled"), true,
+     "pref value should be true before clicking on checkbox");
+   ok(useRecommendedPerformanceSettings.checked, "checkbox should be checked before clicking on checkbox");
+   is(performanceSettings.hidden, true, "performance settings section should be still shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+   let doc = gBrowser.contentDocument;
+   let performanceSettings = doc.querySelector("#performanceSettings");
+ 
+   is(performanceSettings.hidden, true, "performance settings section should not be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.setIntPref("dom.ipc.processCount", 7);
+ 
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+@@ -127,17 +127,17 @@ add_task(async function() {
+   let performanceSettings = doc.querySelector("#performanceSettings");
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   let contentProcessCount = doc.querySelector("#contentProcessCount");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), 7, "pref value should be 7");
+   is(contentProcessCount.selectedItem.value, 7, "selected item should be 7");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.setBoolPref("layers.acceleration.disabled", true);
+ 
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+@@ -147,10 +147,10 @@ add_task(async function() {
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   let allowHWAccel = doc.querySelector("#allowHWAccel");
+   is(Services.prefs.getBoolPref("layers.acceleration.disabled"), true,
+     "pref value is false");
+   ok(!allowHWAccel.checked, "checkbox should not be checked");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_performance_e10srollout.js b/browser/components/preferences/in-content-new/tests/browser_performance_e10srollout.js
+--- a/browser/components/preferences/in-content-new/tests/browser_performance_e10srollout.js
++++ b/browser/components/preferences/in-content-new/tests/browser_performance_e10srollout.js
+@@ -33,17 +33,17 @@ add_task(async function() {
+      "pref value should be false after clicking on checkbox");
+   ok(!useRecommendedPerformanceSettings.checked, "checkbox should not be checked after clicking on checkbox");
+ 
+   let contentProcessCount = doc.querySelector("#contentProcessCount");
+   is(contentProcessCount.disabled, false, "process count control should be enabled");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT + 1, "e10s rollout value should be default value");
+   is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT + 1, "selected item should be the default one");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+   Services.prefs.clearUserPref("dom.ipc.processCount.web");
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+@@ -57,17 +57,17 @@ add_task(async function() {
+   let performanceSettings = doc.querySelector("#performanceSettings");
+   is(performanceSettings.hidden, false, "performance settings section is shown");
+ 
+   let contentProcessCount = doc.querySelector("#contentProcessCount");
+   is(contentProcessCount.disabled, false, "process count control should be enabled");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT, "default value should be the current value");
+   is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT, "selected item should be the default one");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+   Services.prefs.clearUserPref("dom.ipc.processCount.web");
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.setIntPref("dom.ipc.processCount", DEFAULT_PROCESS_COUNT + 2);
+@@ -89,14 +89,14 @@ add_task(async function() {
+   let useRecommendedPerformanceSettings = doc.querySelector("#useRecommendedPerformanceSettings");
+   useRecommendedPerformanceSettings.click();
+ 
+   is(Services.prefs.getBoolPref("browser.preferences.defaultPerformanceSettings.enabled"), true,
+     "pref value should be true after clicking on checkbox");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT,
+     "process count should be default value");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+   Services.prefs.clearUserPref("dom.ipc.processCount.web");
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_performance_non_e10s.js b/browser/components/preferences/in-content-new/tests/browser_performance_non_e10s.js
+--- a/browser/components/preferences/in-content-new/tests/browser_performance_non_e10s.js
++++ b/browser/components/preferences/in-content-new/tests/browser_performance_non_e10s.js
+@@ -54,34 +54,34 @@ add_task(async function() {
+   allowHWAccelPref = Services.prefs.getBoolPref("layers.acceleration.disabled");
+   is(allowHWAccelPref, DEFAULT_HW_ACCEL_PREF,
+     "pref value should be the default value after clicking on checkbox");
+   is(allowHWAccel.checked, !allowHWAccelPref, "checkbox should show the invert of the current value");
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be still shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+   let doc = gBrowser.contentDocument;
+   let performanceSettings = doc.querySelector("#performanceSettings");
+ 
+   is(performanceSettings.hidden, true, "performance settings section should not be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.setBoolPref("layers.acceleration.disabled", true);
+ 
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+@@ -91,10 +91,10 @@ add_task(async function() {
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   let allowHWAccel = doc.querySelector("#allowHWAccel");
+   is(Services.prefs.getBoolPref("layers.acceleration.disabled"), true,
+     "pref value is false");
+   ok(!allowHWAccel.checked, "checkbox should not be checked");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_privacypane.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_privacypane.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_privacypane.js.1442465-4_2.later
+@@ -0,0 +1,34 @@
++--- browser_privacypane.js
+++++ browser_privacypane.js
++@@ -14,17 +14,17 @@ add_task(async function testBrowserError
++   await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
++ 
++   let doc = gBrowser.contentDocument;
++   ok(
++     doc.querySelector("#collectBrowserErrorsBox").checked,
++     "Checkbox for collecting browser errors should be checked when the pref is true"
++   );
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await SpecialPowers.popPrefEnv();
++ });
++ 
++ // Test that the Learn More link is set to the correct, formatted URL from a
++ // pref value
++ add_task(async function testBrowserErrorLearnMore() {
++   // Skip if non-Nightly since the checkbox will be missing.
++   if (!AppConstants.NIGHTLY_BUILD) {
++@@ -38,11 +38,11 @@ add_task(async function testBrowserError
++ 
++   let doc = gBrowser.contentDocument;
++   is(
++     doc.querySelector("#collectBrowserErrorsLearnMore").href,
++     `https://example.com/${Services.appinfo.name}/`,
++     "Learn More link for browser error collection should have an href set by a pref"
++   );
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   await SpecialPowers.popPrefEnv();
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_1.js
+@@ -8,19 +8,19 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Set Home Page" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Set Home Page", "startupGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Languages" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Choose languages", "languagesGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_2.js
+@@ -11,19 +11,19 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Saved Logins" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("sites are stored", "passwordsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Exceptions - Tracking Protection" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("disabled Tracking Protection", "trackingGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_3.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_3.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_3.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_3.js
+@@ -10,19 +10,19 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Allowed Sites - Add-ons Installation" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("allowed to install add-ons", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Certificate Manager" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("identify these certificate authorities", "certSelection");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_4.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_4.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_4.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_4.js
+@@ -8,19 +8,19 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Update History" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("updates have been installed", "updateApp");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Location Permissions" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("set location permissions", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_5.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_5.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_5.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_5.js
+@@ -10,28 +10,28 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Fonts" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Text Encoding", "fontsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Colors" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Link Colors", "fontsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Exceptions - Saved Logins" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("sites will not be saved", "passwordsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_6.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_6.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_6.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_6.js
+@@ -11,19 +11,19 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Block Lists" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("block Web elements", "trackingGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Allowed Sites - Pop-ups" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("open pop-up windows", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_7.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_7.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_7.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_7.js
+@@ -12,28 +12,28 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Device Manager" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Security Modules and Devices", "certSelection");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Connection Settings" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("Use system proxy settings", "connectionGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Settings - Site Data" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("store site data on your computer", "siteDataGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_8.js b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_8.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_8.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_8.js
+@@ -10,28 +10,28 @@ add_task(async function() {
+ });
+ 
+ /**
+  * Test for searching for the "Camera Permissions" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("set camera permissions", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Microphone Permissions" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("set microphone permissions", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for searching for the "Notification Permissions" subdialog.
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   await evaluateSearchResults("set notifications permissions", "permissionsGroup");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_site_data.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_site_data.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_search_subdialogs_within_preferences_site_data.js.1442465-4_2.later
+@@ -0,0 +1,35 @@
++--- browser_search_subdialogs_within_preferences_site_data.js
+++++ browser_search_subdialogs_within_preferences_site_data.js
++@@ -8,28 +8,28 @@ add_task(async function() {
++ });
++ 
++ /**
++  * Test for searching for the "Settings - Site Data" subdialog.
++  */
++ add_task(async function() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   await evaluateSearchResults("cookies", ["siteDataGroup", "historyGroup"]);
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   await evaluateSearchResults("site data", ["siteDataGroup", "historyGroup"]);
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   await evaluateSearchResults("cache", ["siteDataGroup", "historyGroup"]);
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function() {
++   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
++   await evaluateSearchResults("third-party", "siteDataGroup");
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_1.js
+@@ -9,17 +9,17 @@ requestLongerTimeout(6);
+  * Tests to see if search bar is being hidden when pref is turned off
+  */
+ add_task(async function() {
+   await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", false]]});
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   let searchInput = gBrowser.contentDocument.querySelectorAll("#searchInput");
+   is(searchInput.length, 1, "There should only be one element name searchInput querySelectorAll");
+   is_element_hidden(searchInput[0], "Search box should be hidden");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await SpecialPowers.popPrefEnv();
+ });
+ 
+ /**
+  * Enabling searching functionality. Will display search bar from this testcase forward.
+  */
+ add_task(async function() {
+   await SpecialPowers.pushPrefEnv({"set": [["browser.preferences.search", true]]});
+@@ -27,17 +27,17 @@ add_task(async function() {
+ 
+ /**
+  * Tests to see if search bar is being shown when pref is turned on
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+   is_element_visible(searchInput, "Search box should be shown");
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for "Search Result" panel.
+  * After it runs a search, it tests if the "Search Results" panel is the only selected category.
+  * The search is then cleared, it then tests if the "General" panel is the only selected category.
+  */
+ add_task(async function() {
+@@ -75,17 +75,17 @@ add_task(async function() {
+     let child = categoriesList.children[i]
+     if (child.id == "category-general") {
+       is(child.selected, true, "General panel should be selected");
+     } else if (child.id) {
+       is(child.selected, false, "No other panel should be selected");
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for "password" case. When we search "password", it should show the "passwordGroup"
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+ 
+@@ -147,17 +147,17 @@ add_task(async function() {
+     || child.id == "browsingCategory"
+     || child.id == "networkProxyCategory") {
+       is_element_visible(child, "Should be in general tab");
+     } else if (child.id) {
+       is_element_hidden(child, "Should not be in general tab");
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for if nothing is found
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+ 
+@@ -185,17 +185,17 @@ add_task(async function() {
+   let count = query.length;
+   while (count--) {
+     EventUtils.sendKey("BACK_SPACE");
+   }
+   await searchCompletedPromise;
+ 
+   is_element_hidden(noResultsEl, "Should not be in search results");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for if we go back to general tab after search case
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("privacy", {leaveOpen: true});
+   let generalPane = gBrowser.contentDocument.getElementById("generalCategory");
+@@ -221,17 +221,17 @@ add_task(async function() {
+   while (count--) {
+     EventUtils.sendKey("BACK_SPACE");
+   }
+   await searchCompletedPromise;
+ 
+   // Checks if back to normal
+   is_element_visible(generalPane, "Should be in generalPane");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ /**
+  * Test for if we go to another tab after searching
+  */
+ add_task(async function() {
+   await openPreferencesViaOpenPreferencesAPI("paneGeneral", {leaveOpen: true});
+   let searchInput = gBrowser.contentDocument.getElementById("searchInput");
+@@ -253,10 +253,10 @@ add_task(async function() {
+     let child = categoriesList.children[i]
+     if (child.id == "category-privacy") {
+       is(child.selected, true, "Privacy panel should be selected");
+     } else if (child.id) {
+       is(child.selected, false, "No other panel should be selected");
+     }
+   }
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_2.js b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_2.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_2.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_2.js
+@@ -58,10 +58,10 @@ add_task(async function() {
+   searchCompletedPromise = BrowserTestUtils.waitForEvent(
+       gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == query);
+   EventUtils.sendString(query);
+   await searchCompletedPromise;
+ 
+   let noResultsEl = gBrowser.contentDocument.querySelector(".no-results-message");
+   is_element_visible(noResultsEl, "Should be reporting no results");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_command.js b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_command.js
+--- a/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_command.js
++++ b/browser/components/preferences/in-content-new/tests/browser_search_within_preferences_command.js
+@@ -27,10 +27,10 @@ add_task(async function() {
+       gBrowser.contentWindow, "PreferencesSearchCompleted", evt => evt.detail == "");
+   searchInput.value = "";
+   searchInput.doCommand();
+   await searchCompletedPromise;
+ 
+   // Checks if back to normal
+   is_element_visible(generalPane, "Should be in generalPane");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/browser_security-1.js b/browser/components/preferences/in-content-new/tests/browser_security-1.js
+--- a/browser/components/preferences/in-content-new/tests/browser_security-1.js
++++ b/browser/components/preferences/in-content-new/tests/browser_security-1.js
+@@ -17,17 +17,17 @@ registerCleanupFunction(function() {
+ 
+ // This test only opens the Preferences once, and then reloads the page
+ // each time that it wants to test various preference combinations. We
+ // only use one tab (instead of opening/closing for each test) for all
+ // to help improve test times on debug builds.
+ add_task(async function setup() {
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   });
+ });
+ 
+ // test the safebrowsing preference
+ add_task(async function() {
+   async function checkPrefSwitch(val1, val2) {
+     Services.prefs.setBoolPref("browser.safebrowsing.phishing.enabled", val1);
+     Services.prefs.setBoolPref("browser.safebrowsing.malware.enabled", val2);
+diff --git a/browser/components/preferences/in-content-new/tests/browser_security-2.js b/browser/components/preferences/in-content-new/tests/browser_security-2.js
+--- a/browser/components/preferences/in-content-new/tests/browser_security-2.js
++++ b/browser/components/preferences/in-content-new/tests/browser_security-2.js
+@@ -17,17 +17,17 @@ registerCleanupFunction(function() {
+ 
+ // This test only opens the Preferences once, and then reloads the page
+ // each time that it wants to test various preference combinations. We
+ // only use one tab (instead of opening/closing for each test) for all
+ // to help improve test times on debug builds.
+ add_task(async function setup() {
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   });
+ });
+ 
+ // test the download protection preference
+ add_task(async function() {
+   async function checkPrefSwitch(val) {
+     Services.prefs.setBoolPref("browser.safebrowsing.downloads.enabled", val);
+ 
+diff --git a/browser/components/preferences/in-content-new/tests/browser_spotlight.js.1442465-4_2.later b/browser/components/preferences/in-content-new/tests/browser_spotlight.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/preferences/in-content-new/tests/browser_spotlight.js.1442465-4_2.later
+@@ -0,0 +1,43 @@
++--- browser_spotlight.js
+++++ browser_spotlight.js
++@@ -3,37 +3,37 @@ add_task(async function test_reports_sec
++   let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-reports", {leaveOpen: true});
++   is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
++   let doc = gBrowser.contentDocument;
++   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
++   await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
++     "Wait for the reports section is spotlighted.");
++   is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "reports",
++     "The reports section is spotlighted.");
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function test_address_autofill_section() {
++   let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-address-autofill", {leaveOpen: true});
++   is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
++   let doc = gBrowser.contentDocument;
++   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
++   await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
++     "Wait for the ddress-autofill section is spotlighted.");
++   is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "address-autofill",
++     "The ddress-autofill section is spotlighted.");
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
++ 
++ add_task(async function test_credit_card_autofill_section() {
++   if (!Services.prefs.getBoolPref("extensions.formautofill.creditCards.available")) {
++     return;
++   }
++   let prefs = await openPreferencesViaOpenPreferencesAPI("privacy-credit-card-autofill", {leaveOpen: true});
++   is(prefs.selectedPane, "panePrivacy", "Privacy pane is selected by default");
++   let doc = gBrowser.contentDocument;
++   is(doc.location.hash, "#privacy", "The subcategory should be removed from the URI");
++   await TestUtils.waitForCondition(() => doc.querySelector(".spotlight"),
++     "Wait for the credit-card-autofill section is spotlighted.");
++   is(doc.querySelector(".spotlight").getAttribute("data-subcategory"), "credit-card-autofill",
++     "The credit-card-autofill section is spotlighted.");
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ });
+diff --git a/browser/components/preferences/in-content-new/tests/privacypane_tests_perwindow.js b/browser/components/preferences/in-content-new/tests/privacypane_tests_perwindow.js
+--- a/browser/components/preferences/in-content-new/tests/privacypane_tests_perwindow.js
++++ b/browser/components/preferences/in-content-new/tests/privacypane_tests_perwindow.js
+@@ -4,17 +4,17 @@
+ async function runTestOnPrivacyPrefPane(testFunc) {
+   info("runTestOnPrivacyPrefPane entered");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences", true, true);
+   let browser = tab.linkedBrowser;
+   info("loaded about:preferences");
+   browser.contentWindow.gotoPref("panePrivacy");
+   info("viewing privacy pane, executing testFunc");
+   testFunc(browser.contentWindow);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function controlChanged(element) {
+   element.doCommand();
+ }
+ 
+ // We can only test the panes that don't trigger a preference update
+ function test_pane_visibility(win) {
+diff --git a/browser/components/preferences/in-content-new/tests/siteData/browser_clearSiteData.js b/browser/components/preferences/in-content-new/tests/siteData/browser_clearSiteData.js
+--- a/browser/components/preferences/in-content-new/tests/siteData/browser_clearSiteData.js
++++ b/browser/components/preferences/in-content-new/tests/siteData/browser_clearSiteData.js
+@@ -6,26 +6,26 @@
+ ChromeUtils.import("resource:///modules/SitePermissions.jsm");
+ 
+ async function testClearData(clearSiteData, clearCache) {
+   let quotaURI = Services.io.newURI(TEST_QUOTA_USAGE_ORIGIN);
+   SitePermissions.set(quotaURI, "persistent-storage", SitePermissions.ALLOW);
+ 
+   // Open a test site which saves into appcache.
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Fill indexedDB with test data.
+   // Don't wait for the page to load, to register the content event handler as quickly as possible.
+   // If this test goes intermittent, we might have to tell the page to wait longer before
+   // firing the event.
+   BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL, false);
+   await BrowserTestUtils.waitForContentEvent(
+     gBrowser.selectedBrowser, "test-indexedDB-done", false, null, true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Register some service workers.
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await promiseServiceWorkerRegisteredFor(TEST_SERVICE_WORKER_URL);
+ 
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+ 
+   // Test the initial states.
+@@ -141,17 +141,17 @@ async function testClearData(clearSiteDa
+         () => sizeLabel.textContent != opts.initialSizeLabelValue, "Site data size label should have updated.");
+     });
+   }
+ 
+   let desiredPermissionState = clearSiteData ? SitePermissions.UNKNOWN : SitePermissions.ALLOW;
+   let permission = SitePermissions.get(quotaURI, "persistent-storage");
+   is(permission.state, desiredPermissionState, "Should have the correct permission state.");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await SiteDataManager.removeAll();
+ }
+ 
+ // Test opening the "Clear All Data" dialog and cancelling.
+ add_task(async function() {
+   await testClearData(false, false);
+ });
+ 
+diff --git a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData.js b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData.js
+--- a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData.js
++++ b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData.js
+@@ -9,23 +9,23 @@ function getPersistentStoragePermStatus(
+   return Services.perms.testExactPermissionFromPrincipal(principal, "persistent-storage");
+ }
+ 
+ // Test listing site using quota usage or site using appcache
+ // This is currently disabled because of bug 1414751.
+ add_task(async function() {
+   // Open a test site which would save into appcache
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Open a test site which would save into quota manager
+   BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
+   await BrowserTestUtils.waitForContentEvent(
+     gBrowser.selectedBrowser, "test-indexedDB-done", false, null, true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   let updatedPromise = promiseSiteDataManagerSitesUpdated();
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   await updatedPromise;
+   await openSiteDataSettingsDialog();
+   let dialog = content.gSubDialog._topDialog;
+   let dialogFrame = dialog._frame;
+   let frameDoc = dialogFrame.contentDocument;
+@@ -44,17 +44,17 @@ add_task(async function() {
+   await new Promise(resolve => {
+     let principal = Services.scriptSecurityManager
+                             .createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
+     let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
+     request.callback = resolve;
+   });
+ 
+   await SiteDataManager.removeAll();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }).skip(); // Bug 1414751
+ 
+ // Test buttons are disabled and loading message shown while updating sites
+ add_task(async function() {
+   let updatedPromise = promiseSiteDataManagerSitesUpdated();
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   await updatedPromise;
+   let cacheSize = await SiteDataManager.getCacheSize();
+@@ -90,17 +90,17 @@ add_task(async function() {
+   await SiteDataManager.getTotalUsage()
+                        .then(usage => {
+                          actual = totalSiteDataSizeLabel.textContent;
+                          expected = prefStrBundle.getFormattedString(
+                            "totalSiteDataSize1", DownloadUtils.convertByteUnits(usage + cacheSize));
+                           is(actual, expected, "Should show the right total site data size");
+                        });
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test clearing service wroker through the "Clear All" button
+ add_task(async function() {
+   // Register a test service
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   // Test the initial states
+@@ -111,17 +111,17 @@ add_task(async function() {
+   let acceptPromise = promiseAlertDialogOpen("accept");
+   mockSiteDataManager.register(SiteDataManager);
+   mockSiteDataManager.fakeSites = [
+   let updatePromise = promiseSiteDataManagerSitesUpdated();
+   clearBtn.doCommand();
+   await acceptPromise;
+   await updatePromise;
+   await promiseServiceWorkersCleared();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test clearing service wroker through the settings panel
+ add_task(async function() {
+   // Register a test service worker
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   // Test the initial states
+@@ -144,17 +144,17 @@ add_task(async function() {
+     } else {
+       ok(false, `Should have one site of ${host}`);
+     }
+   });
+   await acceptRemovePromise;
+   await updatePromise;
+   await promiseServiceWorkersCleared();
+   await SiteDataManager.removeAll();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test showing and removing sites with cookies.
+ add_task(async function() {
+   // Add some test cookies.
+   let uri = Services.io.newURI("https://example.com");
+   let uri2 = Services.io.newURI("https://example.org");
+   Services.cookies.add(uri.host, uri.path, "test1", "1",
+@@ -252,11 +252,11 @@ add_task(async function() {
+ 
+   ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+     let frameDoc = content.gSubDialog._topDialog._frame.contentDocument;
+ 
+     let siteItems = frameDoc.getElementsByTagName("richlistitem");
+     is(siteItems.length, 0, "Should list no sites with cookies");
+   });
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+diff --git a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData2.js b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData2.js
+--- a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData2.js
++++ b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData2.js
+@@ -89,17 +89,17 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptPromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSiteDataSettingsDialog();
+   assertAllSitesNotListed(win);
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function removeAllSitesOneByOne() {
+     frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
+     let removeBtn = frameDoc.getElementById("removeSelected");
+     let sitesList = frameDoc.getElementById("sitesList");
+     let sites = sitesList.getElementsByTagName("richlistitem");
+     for (let i = sites.length - 1; i >= 0; --i) {
+       sites[i].click();
+@@ -200,17 +200,17 @@ add_task(async function() {
+   assertSitesListed(doc, fakeHosts.slice(2));
+   saveBtn.doCommand();
+   await removeDialogOpenPromise;
+   await settingsDialogClosePromise;
+   await openSiteDataSettingsDialog();
+   assertSitesListed(doc, fakeHosts.slice(2));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function removeSelectedSite(hosts) {
+     frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
+     let removeBtn = frameDoc.getElementById("removeSelected");
+     is(removeBtn.disabled, true, "Should start with disabled removeSelected button");
+     let sitesList = frameDoc.getElementById("sitesList");
+     hosts.forEach(host => {
+       let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+@@ -277,17 +277,17 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptRemovePromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSiteDataSettingsDialog();
+   assertSitesListed(doc, fakeHosts.filter(host => !host.includes("xyz")));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test dynamically clearing all site data
+ add_task(async function() {
+   mockSiteDataManager.register(SiteDataManager, [
+     {
+       usage: 1024,
+       origin: "https://account.xyz.com",
+@@ -334,10 +334,10 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptRemovePromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSiteDataSettingsDialog();
+   assertAllSitesNotListed(win);
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData3.js b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData3.js
+--- a/browser/components/preferences/in-content-new/tests/siteData/browser_siteData3.js
++++ b/browser/components/preferences/in-content-new/tests/siteData/browser_siteData3.js
+@@ -35,17 +35,17 @@ add_task(async function() {
+   let updatePromise = promiseSiteDataManagerSitesUpdated();
+   let doc = gBrowser.selectedBrowser.contentDocument;
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   await updatePromise;
+   await openSiteDataSettingsDialog();
+   assertSitesListed(doc, fakeHosts.filter(host => host != "shopping.xyz.com"));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test grouping and listing sites across scheme, port and origin attributes by host
+ add_task(async function() {
+   const quotaUsage = 1024;
+   mockSiteDataManager.register(SiteDataManager, [
+     {
+       usage: quotaUsage,
+@@ -92,17 +92,17 @@ add_task(async function() {
+   is(columns[1].value, "5", "Should group cookies across scheme, port and origin attributes");
+ 
+   let prefStrBundle = frameDoc.getElementById("bundlePreferences");
+   expected = prefStrBundle.getFormattedString("siteUsagePersistent",
+     DownloadUtils.convertByteUnits(quotaUsage * mockSiteDataManager.fakeSites.length));
+   is(columns[2].value, expected, "Should sum up usages across scheme, port, origin attributes and persistent status");
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test sorting
+ add_task(async function() {
+   mockSiteDataManager.register(SiteDataManager, [
+     {
+       usage: 1024,
+       origin: "https://account.xyz.com",
+@@ -153,17 +153,17 @@ add_task(async function() {
+ 
+   // Test sorting on the cookies column
+   cookiesCol.click();
+   assertSortByCookies("ascending");
+   cookiesCol.click();
+   assertSortByCookies("descending");
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function assertSortByBaseDomain(order) {
+     let siteItems = sitesList.getElementsByTagName("richlistitem");
+     for (let i = 0; i < siteItems.length - 1; ++i) {
+       let aHost = siteItems[i].getAttribute("host");
+       let bHost = siteItems[i + 1].getAttribute("host");
+       let a = findSiteByHost(aHost);
+       let b = findSiteByHost(bHost);
+@@ -251,17 +251,17 @@ add_task(async function() {
+ 
+   // Test sorting on the date column
+   lastAccessedCol.click();
+   assertSortByDate("ascending");
+   lastAccessedCol.click();
+   assertSortByDate("descending");
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function assertSortByDate(order) {
+     let siteItems = sitesList.getElementsByTagName("richlistitem");
+     for (let i = 0; i < siteItems.length - 1; ++i) {
+       let aHost = siteItems[i].getAttribute("host");
+       let bHost = siteItems[i + 1].getAttribute("host");
+       let a = findSiteByHost(aHost);
+       let b = findSiteByHost(bHost);
+diff --git a/browser/components/preferences/in-content-new/tests/siteData/head.js b/browser/components/preferences/in-content-new/tests/siteData/head.js
+--- a/browser/components/preferences/in-content-new/tests/siteData/head.js
++++ b/browser/components/preferences/in-content-new/tests/siteData/head.js
+@@ -235,17 +235,17 @@ function promiseCookiesCleared() {
+ }
+ 
+ async function loadServiceWorkerTestPage(url) {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+   await BrowserTestUtils.waitForCondition(() => {
+     return ContentTask.spawn(tab.linkedBrowser, {}, () =>
+       content.document.body.getAttribute("data-test-service-worker-registered") === "true");
+   }, `Fail to load service worker test ${url}`);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function promiseServiceWorkersCleared() {
+   return BrowserTestUtils.waitForCondition(() => {
+     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+     if (serviceWorkers.length == 0) {
+       ok(true, "Cleared all service workers");
+       return true;
+diff --git a/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js b/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
+--- a/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
++++ b/browser/components/preferences/in-content/tests/browser_homepages_filter_aboutpreferences.js
+@@ -10,11 +10,11 @@ add_task(async function() {
+   let useCurrent = doc.getElementById("useCurrent");
+   useCurrent.click();
+ 
+   is(gBrowser.tabs.length, 3, "Three tabs should be open");
+   is(Services.prefs.getCharPref("browser.startup.homepage"), "about:blank|about:home",
+      "about:blank and about:home should be the only homepages set");
+ 
+   Services.prefs.setCharPref("browser.startup.homepage", oldHomepagePref);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content/tests/browser_performance.js b/browser/components/preferences/in-content/tests/browser_performance.js
+--- a/browser/components/preferences/in-content/tests/browser_performance.js
++++ b/browser/components/preferences/in-content/tests/browser_performance.js
+@@ -147,10 +147,10 @@ add_task(async function() {
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   let allowHWAccel = doc.querySelector("#allowHWAccel");
+   is(Services.prefs.getBoolPref("layers.acceleration.disabled"), true,
+     "pref value is false");
+   ok(!allowHWAccel.checked, "checkbox should not be checked");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js b/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js
+--- a/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js
++++ b/browser/components/preferences/in-content/tests/browser_performance_e10srollout.js
+@@ -32,17 +32,17 @@ add_task(async function testPrefsAreDefa
+      "pref value should be false after clicking on checkbox");
+   ok(!useRecommendedPerformanceSettings.checked, "checkbox should not be checked after clicking on checkbox");
+ 
+   let contentProcessCount = doc.querySelector("#contentProcessCount");
+   is(contentProcessCount.disabled, false, "process count control should be enabled");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT, "default pref should be default value");
+   is(contentProcessCount.selectedItem.value, DEFAULT_PROCESS_COUNT, "selected item should be the default one");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+ });
+ 
+ add_task(async function testPrefsSetByUser() {
+   Services.prefs.setIntPref("dom.ipc.processCount", DEFAULT_PROCESS_COUNT + 2);
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
+@@ -62,13 +62,13 @@ add_task(async function testPrefsSetByUs
+   let useRecommendedPerformanceSettings = doc.querySelector("#useRecommendedPerformanceSettings");
+   useRecommendedPerformanceSettings.click();
+ 
+   is(Services.prefs.getBoolPref("browser.preferences.defaultPerformanceSettings.enabled"), true,
+     "pref value should be true after clicking on checkbox");
+   is(Services.prefs.getIntPref("dom.ipc.processCount"), DEFAULT_PROCESS_COUNT,
+     "process count should be default value");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   Services.prefs.clearUserPref("dom.ipc.processCount");
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+ });
+diff --git a/browser/components/preferences/in-content/tests/browser_performance_non_e10s.js b/browser/components/preferences/in-content/tests/browser_performance_non_e10s.js
+--- a/browser/components/preferences/in-content/tests/browser_performance_non_e10s.js
++++ b/browser/components/preferences/in-content/tests/browser_performance_non_e10s.js
+@@ -54,34 +54,34 @@ add_task(async function() {
+   allowHWAccelPref = Services.prefs.getBoolPref("layers.acceleration.disabled");
+   is(allowHWAccelPref, DEFAULT_HW_ACCEL_PREF,
+     "pref value should be the default value after clicking on checkbox");
+   is(allowHWAccel.checked, !allowHWAccelPref, "checkbox should show the invert of the current value");
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be still shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+   let doc = gBrowser.contentDocument;
+   let performanceSettings = doc.querySelector("#performanceSettings");
+ 
+   is(performanceSettings.hidden, true, "performance settings section should not be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", false);
+ 
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ add_task(async function() {
+   Services.prefs.setBoolPref("layers.acceleration.disabled", true);
+ 
+   let prefs = await openPreferencesViaOpenPreferencesAPI("paneGeneral", null, {leaveOpen: true});
+   is(prefs.selectedPane, "paneGeneral", "General pane was selected");
+ 
+@@ -91,10 +91,10 @@ add_task(async function() {
+   is(performanceSettings.hidden, false, "performance settings section should be shown");
+ 
+   let allowHWAccel = doc.querySelector("#allowHWAccel");
+   is(Services.prefs.getBoolPref("layers.acceleration.disabled"), true,
+     "pref value is false");
+   ok(!allowHWAccel.checked, "checkbox should not be checked");
+ 
+   Services.prefs.setBoolPref("browser.preferences.defaultPerformanceSettings.enabled", true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+--- a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
++++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+@@ -4,17 +4,17 @@
+ async function runTestOnPrivacyPrefPane(testFunc) {
+   info("runTestOnPrivacyPrefPane entered");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences", true, true);
+   let browser = tab.linkedBrowser;
+   info("loaded about:preferences");
+   browser.contentWindow.gotoPref("panePrivacy");
+   info("viewing privacy pane, executing testFunc");
+   testFunc(browser.contentWindow);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function controlChanged(element) {
+   element.doCommand();
+ }
+ 
+ // We can only test the panes that don't trigger a preference update
+ function test_pane_visibility(win) {
+diff --git a/browser/components/preferences/in-content/tests/siteData/browser_clearSiteData.js b/browser/components/preferences/in-content/tests/siteData/browser_clearSiteData.js
+--- a/browser/components/preferences/in-content/tests/siteData/browser_clearSiteData.js
++++ b/browser/components/preferences/in-content/tests/siteData/browser_clearSiteData.js
+@@ -6,26 +6,26 @@
+ ChromeUtils.import("resource:///modules/SitePermissions.jsm");
+ 
+ async function testClearData(clearSiteData, clearCache) {
+   let quotaURI = Services.io.newURI(TEST_QUOTA_USAGE_ORIGIN);
+   SitePermissions.set(quotaURI, "persistent-storage", SitePermissions.ALLOW);
+ 
+   // Open a test site which saves into appcache.
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Fill indexedDB with test data.
+   // Don't wait for the page to load, to register the content event handler as quickly as possible.
+   // If this test goes intermittent, we might have to tell the page to wait longer before
+   // firing the event.
+   BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL, false);
+   await BrowserTestUtils.waitForContentEvent(
+     gBrowser.selectedBrowser, "test-indexedDB-done", false, null, true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Register some service workers.
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await promiseServiceWorkerRegisteredFor(TEST_SERVICE_WORKER_URL);
+ 
+   await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+ 
+   // Test the initial states.
+@@ -141,17 +141,17 @@ async function testClearData(clearSiteDa
+         () => sizeLabel.textContent != opts.initialSizeLabelValue, "Site data size label should have updated.");
+     });
+   }
+ 
+   let desiredPermissionState = clearSiteData ? SitePermissions.UNKNOWN : SitePermissions.ALLOW;
+   let permission = SitePermissions.get(quotaURI, "persistent-storage");
+   is(permission.state, desiredPermissionState, "Should have the correct permission state.");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await SiteDataManager.removeAll();
+ }
+ 
+ // Test opening the "Clear All Data" dialog and cancelling.
+ add_task(async function() {
+   await testClearData(false, false);
+ });
+ 
+diff --git a/browser/components/preferences/in-content/tests/siteData/browser_siteData.js b/browser/components/preferences/in-content/tests/siteData/browser_siteData.js
+--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData.js
++++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData.js
+@@ -9,23 +9,23 @@ function getPersistentStoragePermStatus(
+   return Services.perms.testExactPermissionFromPrincipal(principal, "persistent-storage");
+ }
+ 
+ // Test listing site using quota usage or site using appcache
+ // This is currently disabled because of bug 1414751.
+ add_task(async function() {
+   // Open a test site which would save into appcache
+   await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_OFFLINE_URL);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   // Open a test site which would save into quota manager
+   BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_QUOTA_USAGE_URL);
+   await BrowserTestUtils.waitForContentEvent(
+     gBrowser.selectedBrowser, "test-indexedDB-done", false, null, true);
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   let updatedPromise = promiseSitesUpdated();
+   await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+   await updatedPromise;
+   await openSettingsDialog();
+   let dialogFrame = content.gSubDialog._topDialog._frame;
+   let frameDoc = dialogFrame.contentDocument;
+ 
+@@ -43,17 +43,17 @@ add_task(async function() {
+   await new Promise(resolve => {
+     let principal = Services.scriptSecurityManager
+                             .createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
+     let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
+     request.callback = resolve;
+   });
+ 
+   await SiteDataManager.removeAll();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }).skip(); // Bug 1414751
+ 
+ // Test buttons are disabled and loading message shown while updating sites
+ add_task(async function() {
+   let updatedPromise = promiseSitesUpdated();
+   await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+   await updatedPromise;
+   let cacheSize = await SiteDataManager.getCacheSize();
+@@ -89,17 +89,17 @@ add_task(async function() {
+   await SiteDataManager.getTotalUsage()
+                        .then(usage => {
+                           actual = totalSiteDataSizeLabel.textContent;
+                           expected = prefStrBundle.getFormattedString(
+                            "totalSiteDataSize1", DownloadUtils.convertByteUnits(usage + cacheSize));
+                           is(actual, expected, "Should show the right total site data size");
+                        });
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test clearing service wroker through the "Clear All" button
+ add_task(async function() {
+   // Register a test service
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await openPreferencesViaOpenPreferencesAPI("privacy", { leaveOpen: true });
+   // Test the initial states
+@@ -108,17 +108,17 @@ add_task(async function() {
+   let doc = gBrowser.selectedBrowser.contentDocument;
+   let clearBtn = doc.getElementById("clearSiteDataButton");
+   let acceptPromise = promiseAlertDialogOpen("accept");
+   let updatePromise = promiseSiteDataManagerSitesUpdated();
+   clearBtn.doCommand();
+   await acceptPromise;
+   await updatePromise;
+   await promiseServiceWorkersCleared();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test clearing service wroker through the settings panel
+ add_task(async function() {
+   // Register a test service worker
+   await loadServiceWorkerTestPage(TEST_SERVICE_WORKER_URL);
+   await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+   // Test the initial states
+@@ -142,17 +142,17 @@ add_task(async function() {
+     } else {
+       ok(false, `Should have one site of ${host}`);
+     }
+   });
+   await acceptRemovePromise;
+   await updatePromise;
+   await promiseServiceWorkersCleared();
+   await SiteDataManager.removeAll();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test showing and removing sites with cookies.
+ add_task(async function() {
+   // Add some test cookies.
+   let uri = Services.io.newURI("https://example.com");
+   let uri2 = Services.io.newURI("https://example.org");
+   Services.cookies.add(uri.host, uri.path, "test1", "1",
+@@ -250,10 +250,10 @@ add_task(async function() {
+ 
+   ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
+     let frameDoc = content.gSubDialog._topDialog._frame.contentDocument;
+ 
+     let siteItems = frameDoc.getElementsByTagName("richlistitem");
+     is(siteItems.length, 0, "Should list no sites with cookies");
+   });
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content/tests/siteData/browser_siteData2.js b/browser/components/preferences/in-content/tests/siteData/browser_siteData2.js
+--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData2.js
++++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData2.js
+@@ -89,17 +89,17 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptPromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSettingsDialog();
+   assertAllSitesNotListed(win);
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function removeAllSitesOneByOne() {
+     frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
+     let removeBtn = frameDoc.getElementById("removeSelected");
+     let sitesList = frameDoc.getElementById("sitesList");
+     let sites = sitesList.getElementsByTagName("richlistitem");
+     for (let i = sites.length - 1; i >= 0; --i) {
+       sites[i].click();
+@@ -200,17 +200,17 @@ add_task(async function() {
+   assertSitesListed(doc, fakeHosts.slice(2));
+   saveBtn.doCommand();
+   await removeDialogOpenPromise;
+   await settingsDialogClosePromise;
+   await openSettingsDialog();
+   assertSitesListed(doc, fakeHosts.slice(2));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function removeSelectedSite(hosts) {
+     frameDoc = win.gSubDialog._topDialog._frame.contentDocument;
+     let removeBtn = frameDoc.getElementById("removeSelected");
+     is(removeBtn.disabled, true, "Should start with disabled removeSelected button");
+     let sitesList = frameDoc.getElementById("sitesList");
+     hosts.forEach(host => {
+       let site = sitesList.querySelector(`richlistitem[host="${host}"]`);
+@@ -277,17 +277,17 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptRemovePromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSettingsDialog();
+   assertSitesListed(doc, fakeHosts.filter(host => !host.includes("xyz")));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test dynamically clearing all site data
+ add_task(async function() {
+   mockSiteDataManager.register([
+     {
+       usage: 1024,
+       origin: "https://account.xyz.com",
+@@ -334,10 +334,10 @@ add_task(async function() {
+   saveBtn.doCommand();
+   await acceptRemovePromise;
+   await settingsDialogClosePromise;
+   await updatePromise;
+   await openSettingsDialog();
+   assertAllSitesNotListed(win);
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/browser/components/preferences/in-content/tests/siteData/browser_siteData3.js b/browser/components/preferences/in-content/tests/siteData/browser_siteData3.js
+--- a/browser/components/preferences/in-content/tests/siteData/browser_siteData3.js
++++ b/browser/components/preferences/in-content/tests/siteData/browser_siteData3.js
+@@ -44,17 +44,17 @@ add_task(async function() {
+   searchBox.doCommand();
+   assertSitesListed(doc, fakeHosts.filter(host => host.includes("bar")));
+ 
+   searchBox.value = "";
+   searchBox.doCommand();
+   assertSitesListed(doc, fakeHosts);
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test not displaying sites which store 0 byte and don't have persistent storage.
+ add_task(async function() {
+   mockSiteDataManager.register([
+     {
+       usage: 0,
+       origin: "https://account.xyz.com",
+@@ -87,17 +87,17 @@ add_task(async function() {
+   let updatePromise = promiseSitesUpdated();
+   let doc = gBrowser.selectedBrowser.contentDocument;
+   await openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+   await updatePromise;
+   await openSettingsDialog();
+   assertSitesListed(doc, fakeHosts.filter(host => host != "shopping.xyz.com"));
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+ // Test sorting
+ add_task(async function() {
+   mockSiteDataManager.register([
+     {
+       usage: 1024,
+       origin: "https://account.xyz.com",
+@@ -148,17 +148,17 @@ add_task(async function() {
+ 
+   // Test sorting on the cookies column
+   cookiesCol.click();
+   assertSortByCookies("ascending");
+   cookiesCol.click();
+   assertSortByCookies("descending");
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function assertSortByBaseDomain(order) {
+     let siteItems = sitesList.getElementsByTagName("richlistitem");
+     for (let i = 0; i < siteItems.length - 1; ++i) {
+       let aHost = siteItems[i].getAttribute("host");
+       let bHost = siteItems[i + 1].getAttribute("host");
+       let a = findSiteByHost(aHost);
+       let b = findSiteByHost(bHost);
+@@ -246,17 +246,17 @@ add_task(async function() {
+ 
+   // Test sorting on the date column
+   lastAccessedCol.click();
+   assertSortByDate("ascending");
+   lastAccessedCol.click();
+   assertSortByDate("descending");
+ 
+   await mockSiteDataManager.unregister();
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+   function assertSortByDate(order) {
+     let siteItems = sitesList.getElementsByTagName("richlistitem");
+     for (let i = 0; i < siteItems.length - 1; ++i) {
+       let aHost = siteItems[i].getAttribute("host");
+       let bHost = siteItems[i + 1].getAttribute("host");
+       let a = findSiteByHost(aHost);
+       let b = findSiteByHost(bHost);
+diff --git a/browser/components/preferences/in-content/tests/siteData/head.js b/browser/components/preferences/in-content/tests/siteData/head.js
+--- a/browser/components/preferences/in-content/tests/siteData/head.js
++++ b/browser/components/preferences/in-content/tests/siteData/head.js
+@@ -238,17 +238,17 @@ function promiseCookiesCleared() {
+ }
+ 
+ async function loadServiceWorkerTestPage(url) {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
+   await BrowserTestUtils.waitForCondition(() => {
+     return ContentTask.spawn(tab.linkedBrowser, {}, () =>
+       content.document.body.getAttribute("data-test-service-worker-registered") === "true");
+   }, `Fail to load service worker test ${url}`);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function promiseServiceWorkersCleared() {
+   return BrowserTestUtils.waitForCondition(() => {
+     let serviceWorkers = serviceWorkerManager.getAllRegistrations();
+     if (serviceWorkers.length == 0) {
+       ok(true, "Cleared all service workers");
+       return true;
+diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
++++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_about.js
+@@ -18,17 +18,17 @@ async function openAboutPrivateBrowsing(
+  */
+ async function testLinkOpensTab({ win, tab, elementId, expectedUrl }) {
+   let newTabPromise = BrowserTestUtils.waitForNewTab(win.gBrowser, expectedUrl);
+   await ContentTask.spawn(tab, elementId, async function(elemId) {
+     content.document.getElementById(elemId).click();
+   });
+   let newTab = await newTabPromise;
+   ok(true, `Clicking ${elementId} opened ${expectedUrl} in a new tab.`);
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ }
+ 
+ /**
+  * Clicks the given link and checks this opens the given URI in the same tab.
+  *
+  * This function does not return to the previous page.
+  */
+ async function testLinkOpensUrl({ win, tab, elementId, expectedUrl }) {
+diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
+--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
++++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_newtab_from_popup.js
+@@ -54,12 +54,12 @@ add_task(async function test_private_pop
+   await BrowserTestUtils.synthesizeMouseAtCenter("#second", {}, popupBrowser);
+   let newPrivTab = await newTabPromise;
+ 
+   // Ensure that the newly created tab's browser is private.
+   ok(PrivateBrowsingUtils.isBrowserPrivate(newPrivTab.linkedBrowser),
+      "Newly opened tab should be private.");
+ 
+   // Clean up
+-  await BrowserTestUtils.removeTab(newPrivTab);
++  BrowserTestUtils.removeTab(newPrivTab);
+   await BrowserTestUtils.closeWindow(popupWin);
+   await BrowserTestUtils.closeWindow(privWin);
+ });
+diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
+--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
++++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
+@@ -13,17 +13,17 @@ add_task(async function test() {
+   const TITLE_2 = "Title 2";
+ 
+   await PlacesUtils.history.clear();
+ 
+   let promiseTitleChanged = PlacesTestUtils.waitForNotification(
+     "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+   registerCleanupFunction(async () => {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+   info("Wait for a title change notification.");
+   await promiseTitleChanged;
+   await BrowserTestUtils.waitForCondition(async function() {
+     let entry = await PlacesUtils.history.fetch(TEST_URL);
+     return entry && entry.title == TITLE_1;
+   }, "The title matches the original title after first visit");
+ 
+diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
+--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
++++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoom.js
+@@ -25,13 +25,13 @@ add_task(async function test() {
+   await BrowserTestUtils.switchTab(win.gBrowser, tabMozilla);
+ 
+   // make sure the zoom level has not changed
+   is(win.ZoomManager.zoom, mozillaZoom,
+      "Entering private browsing should not reset the zoom on a tab");
+ 
+   // cleanup
+   win.FullZoom.reset();
+-  await Promise.all([ BrowserTestUtils.removeTab(tabMozilla),
+-                      BrowserTestUtils.removeTab(tabAbout) ]);
++  BrowserTestUtils.removeTab(tabMozilla);
++  BrowserTestUtils.removeTab(tabAbout);
+ 
+   await BrowserTestUtils.closeWindow(win);
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_block_mozAddonManager.js b/browser/components/resistfingerprinting/test/browser/browser_block_mozAddonManager.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_block_mozAddonManager.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_block_mozAddonManager.js
+@@ -24,12 +24,12 @@ add_task(async function test() {
+         is(content.navigator.mozAddonManager, undefined,
+           "The navigator.mozAddonManager should not exist when the pref is on.");
+       } else {
+         ok(content.navigator.mozAddonManager,
+           "The navigator.mozAddonManager should exist when the pref is off.");
+       }
+     });
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     await SpecialPowers.popPrefEnv();
+   }
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js b/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_bug1369357_site_specific_zoom_level.js
+@@ -20,12 +20,12 @@ add_task(async function() {
+     ]
+   });
+ 
+   tab3 = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
+   tab3Zoom = ZoomManager.getZoomForBrowser(tab3.linkedBrowser);
+ 
+   isnot(tab3Zoom, tab1Zoom, "privacy.resistFingerprinting is true, site-specific zoom level should be disabled");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab3);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab3);
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_navigator.js b/browser/components/resistfingerprinting/test/browser/browser_navigator.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_navigator.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_navigator.js
+@@ -74,17 +74,17 @@ async function testNavigator() {
+   is(result.hardwareConcurrency, SPOOFED_HW_CONCURRENCY, "Navigator.hardwareConcurrency is correctly spoofed.");
+ 
+   is(result.appCodeName, CONST_APPCODENAME, "Navigator.appCodeName reports correct constant value.");
+   is(result.product, CONST_PRODUCT, "Navigator.product reports correct constant value.");
+   is(result.productSub, CONST_PRODUCTSUB, "Navigator.productSub reports correct constant value.");
+   is(result.vendor, CONST_VENDOR, "Navigator.vendor reports correct constant value.");
+   is(result.vendorSub, CONST_VENDORSUB, "Navigator.vendorSub reports correct constant value.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ async function testWorkerNavigator() {
+   // Open a tab to collect result from worker.
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+   let result = await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+@@ -105,17 +105,17 @@ async function testWorkerNavigator() {
+   is(result.appVersion, SPOOFED_APPVERSION[AppConstants.platform], "Navigator.appVersion is correctly spoofed.");
+   is(result.platform, SPOOFED_PLATFORM[AppConstants.platform], "Navigator.platform is correctly spoofed.");
+   is(result.userAgent, spoofedUserAgent, "Navigator.userAgent is correctly spoofed.");
+   is(result.hardwareConcurrency, SPOOFED_HW_CONCURRENCY, "Navigator.hardwareConcurrency is correctly spoofed.");
+ 
+   is(result.appCodeName, CONST_APPCODENAME, "Navigator.appCodeName reports correct constant value.");
+   is(result.product, CONST_PRODUCT, "Navigator.product reports correct constant value.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function setup() {
+   await SpecialPowers.pushPrefEnv({"set":
+     [["privacy.resistFingerprinting", true]]
+   });
+ 
+   let appVersion = parseInt(Services.appinfo.version);
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_netInfo.js b/browser/components/resistfingerprinting/test/browser/browser_netInfo.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_netInfo.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_netInfo.js
+@@ -13,17 +13,17 @@ async function testWindow() {
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+     ok("connection" in content.navigator, "navigator.connection should exist");
+ 
+     is(content.navigator.connection.type, "unknown", "The connection type is spoofed correctly");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ async function testWorker() {
+   // Open a tab to test network information in a worker.
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+@@ -40,17 +40,17 @@ async function testWorker() {
+           ok(false, "Unknown message type");
+           resolve();
+         }
+       };
+       worker.postMessage({type: "runTests"});
+     });
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function runTest() {
+   await SpecialPowers.pushPrefEnv({"set":
+     [
+       ["privacy.resistFingerprinting", true],
+       ["dom.netinfo.enabled",          true]
+     ]
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_performanceAPI.js
+@@ -112,17 +112,17 @@ add_task(async function runRPTests() {
+     is(content.performance.getEntriesByName("Test", "mark").length, 0, "For resistFingerprinting, there should be no entries for performance.getEntriesByName()");
+ 
+   };
+ 
+   await setupTest(tab, true, true, 100, runTests);
+   await setupTest(tab, true, false, 13, runTests);
+   await setupTest(tab, true, false, .13, runTests);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // ================================================================================================
+ // ================================================================================================
+ add_task(async function runRTPTests() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+@@ -160,17 +160,17 @@ add_task(async function runRTPTests() {
+     content.performance.clearMeasures();
+     content.performance.clearResourceTimings();
+   };
+ 
+   await setupTest(tab, false, true, 100, runTests);
+   await setupTest(tab, false, true, 13, runTests);
+   await setupTest(tab, false, true, .13, runTests);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ // ================================================================================================
+ // ================================================================================================
+ let runWorkerTest = async function(data) {
+   let expectedPrecision = data.precision;
+   let workerCall = data.workerCall;
+     await new Promise(resolve => {
+@@ -194,21 +194,21 @@ let runWorkerTest = async function(data)
+ add_task(async function runRPTestsForWorker() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+   await setupTest(tab, true, true, 100, runWorkerTest, "runRPTests");
+   await setupTest(tab, true, false, 13, runWorkerTest, "runRPTests");
+   await setupTest(tab, true, true, .13, runWorkerTest, "runRPTests");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   });
+ 
+ add_task(async function runRTPTestsForWorker() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_dummy.html");
+ 
+   await setupTest(tab, false, true, 100, runWorkerTest, "runRTPTests");
+   await setupTest(tab, false, true, 13, runWorkerTest, "runRTPTests");
+   await setupTest(tab, false, true, .13, runWorkerTest, "runRTPTests");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_roundedWindow_newWindow.js
+@@ -39,11 +39,11 @@ add_task(async function test_new_window(
+         "The screen.height has a correct rounded value");
+       is(content.innerWidth, input.gMaxAvailWidth,
+         "The window.innerWidth has a correct rounded value");
+       is(content.innerHeight, input.gMaxAvailHeight,
+         "The window.innerHeight has a correct rounded value");
+     }
+   );
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await BrowserTestUtils.closeWindow(win);
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js b/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_spoofing_keyboard_event.js
+@@ -745,17 +745,17 @@ add_task(async function runTestsForEngli
+       result: { key: "\u00DF", code: "", charCode: 223, keyCode: 0,
+                 location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD, altKey: false, shiftKey: false,
+                 ctrlKey: false, altGraphKey: false }
+     }
+   );
+ 
+   gBrowser.removeEventListener("keypress", eventConsumer, true);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function runTestForSuppressModifierKeys() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, TEST_PATH + "file_keyBoardEvent.sjs?language=en-US");
+ 
+   // Prevent Alt key to trigger the menu item.
+   gBrowser.addEventListener("keydown", eventConsumer, true);
+@@ -805,11 +805,11 @@ add_task(async function runTestForSuppre
+       BrowserTestUtils.synthesizeKey("x", {}, tab.linkedBrowser);
+ 
+       await testPromise;
+     }
+   }
+ 
+   gBrowser.removeEventListener("keydown", eventConsumer, true);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/browser/components/resistfingerprinting/test/browser/browser_timezone.js b/browser/components/resistfingerprinting/test/browser/browser_timezone.js
+--- a/browser/components/resistfingerprinting/test/browser/browser_timezone.js
++++ b/browser/components/resistfingerprinting/test/browser/browser_timezone.js
+@@ -30,10 +30,10 @@ add_task(async function test_timezone() 
+       is(dateObj.getDate(), dateObj.getUTCDate(), "The month reports in UTC timezone.");
+       is(dateObj.getDay(), dateObj.getUTCDay(), "The day reports in UTC timezone.");
+       is(dateObj.getHours(), dateObj.getUTCHours(), "The hours reports in UTC timezone.");
+       is(dateObj.getTimezoneOffset(), 0, "The difference with UTC timezone is 0.");
+ 
+     }
+   );
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/resistfingerprinting/test/browser/head.js b/browser/components/resistfingerprinting/test/browser/head.js
+--- a/browser/components/resistfingerprinting/test/browser/head.js
++++ b/browser/components/resistfingerprinting/test/browser/head.js
+@@ -29,17 +29,17 @@ async function calcMaximumAvailSize(aChr
+       return result;
+     });
+ 
+     // Calculate the maximum available window size which is depending on the
+     // available screen space.
+     chromeUIWidth = win.outerWidth - contentSize.width;
+     chromeUIHeight = win.outerHeight - contentSize.height;
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     await BrowserTestUtils.closeWindow(win);
+   } else {
+     chromeUIWidth = aChromeWidth;
+     chromeUIHeight = aChromeHeight;
+   }
+ 
+   let availWidth = window.screen.availWidth;
+   let availHeight = window.screen.availHeight;
+@@ -86,17 +86,17 @@ async function calcPopUpWindowChromeUISi
+       chromeHeight: win.outerHeight - win.innerHeight
+     };
+ 
+     win.close();
+ 
+     return res;
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   return result;
+ }
+ 
+ async function testWindowOpen(aBrowser, aSettingWidth, aSettingHeight,
+                          aTargetWidth, aTargetHeight, aTestOuter,
+                          aMaxAvailWidth, aMaxAvailHeight, aPopupChromeUIWidth,
+                          aPopupChromeUIHeight) {
+@@ -278,17 +278,17 @@ class RoundedWindowTest {
+     // Open a tab to test.
+     this.tab = await BrowserTestUtils.openNewForegroundTab(
+       gBrowser, this.TEST_PATH + "file_dummy.html");
+ 
+     for (let test of this.testCases) {
+       await this.doTest(test, testOuter);
+     }
+ 
+-    await BrowserTestUtils.removeTab(this.tab);
++    BrowserTestUtils.removeTab(this.tab);
+   }
+ 
+   async doTest() {
+     throw new Error("RoundedWindowTest.doTest must be overridden.");
+   }
+ }
+ 
+ class WindowSettingTest extends RoundedWindowTest {
+diff --git a/browser/components/search/test/browser_google_behavior.js b/browser/components/search/test/browser_google_behavior.js
+--- a/browser/components/search/test/browser_google_behavior.js
++++ b/browser/components/search/test/browser_google_behavior.js
+@@ -167,10 +167,10 @@ async function testSearchEngine(engineDe
+     await test.run(tab);
+ 
+     let receivedURI = await stateChangePromise;
+ 
+     Assert.equal(receivedURI, test.searchURL);
+   }
+ 
+   engine.alias = undefined;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+diff --git a/browser/components/search/test/browser_oneOffContextMenu.js b/browser/components/search/test/browser_oneOffContextMenu.js
+--- a/browser/components/search/test/browser_oneOffContextMenu.js
++++ b/browser/components/search/test/browser_oneOffContextMenu.js
+@@ -68,13 +68,13 @@ async function doTest() {
+   EventUtils.synthesizeKey("KEY_Escape");
+   await promise;
+ 
+   // Check the loaded tab.
+   Assert.equal(tab.linkedBrowser.currentURI.spec,
+                "http://mochi.test:8888/browser/browser/components/search/test/",
+                "Expected search tab should have loaded");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Move the cursor out of the panel area to avoid messing with other tests.
+   await EventUtils.synthesizeNativeMouseMove(searchbar);
+ }
+diff --git a/browser/components/search/test/browser_searchEngine_behaviors.js b/browser/components/search/test/browser_searchEngine_behaviors.js
+--- a/browser/components/search/test/browser_searchEngine_behaviors.js
++++ b/browser/components/search/test/browser_searchEngine_behaviors.js
+@@ -207,10 +207,10 @@ async function testSearchEngine(engineDe
+     await test.run(tab);
+ 
+     let receivedURI = await stateChangePromise;
+ 
+     Assert.equal(receivedURI, test.searchURL);
+   }
+ 
+   engine.alias = undefined;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+diff --git a/browser/components/sessionstore/test/browser_480893.js b/browser/components/sessionstore/test/browser_480893.js
+--- a/browser/components/sessionstore/test/browser_480893.js
++++ b/browser/components/sessionstore/test/browser_480893.js
+@@ -38,10 +38,10 @@ add_task(async function() {
+   doc = browser.contentDocument;
+ 
+   // Click on the "Close" button after about:sessionrestore is loaded.
+   doc.getElementById("errorCancel").click();
+   await BrowserTestUtils.browserLoaded(browser);
+ 
+   is(browser.currentURI.spec, homepage, "loaded page is the homepage");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/sessionstore/test/browser_579879.js b/browser/components/sessionstore/test/browser_579879.js
+--- a/browser/components/sessionstore/test/browser_579879.js
++++ b/browser/components/sessionstore/test/browser_579879.js
+@@ -10,11 +10,11 @@ add_task(async function() {
+ 
+   is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 is at the first position");
+   await promiseRemoveTabAndSessionState(tab1);
+ 
+   tab1 = undoCloseTab();
+   ok(tab1.pinned, "pinned tab 1 has been restored as a pinned tab");
+   is(Array.indexOf(gBrowser.tabs, tab1), 0, "pinned tab 1 has been restored to the first position");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/browser/components/sessionstore/test/browser_615394-SSWindowState_events_undoCloseTab.js b/browser/components/sessionstore/test/browser_615394-SSWindowState_events_undoCloseTab.js
+--- a/browser/components/sessionstore/test/browser_615394-SSWindowState_events_undoCloseTab.js
++++ b/browser/components/sessionstore/test/browser_615394-SSWindowState_events_undoCloseTab.js
+@@ -48,10 +48,10 @@ add_task(async function test_undoCloseTa
+   Assert.equal(busyEventCount, 1);
+   Assert.equal(readyEventCount, 1);
+   Assert.equal(ss.getTabValue(reopenedTab, "baz"), "qux");
+   Assert.equal(reopenedTab.linkedBrowser.currentURI.spec, "about:rights");
+ 
+   window.removeEventListener("SSWindowStateBusy", onSSWindowStateBusy);
+   window.removeEventListener("SSWindowStateReady", onSSWindowStateReady);
+ 
+-  await BrowserTestUtils.removeTab(reopenedTab);
++  BrowserTestUtils.removeTab(reopenedTab);
+ });
+diff --git a/browser/components/sessionstore/test/browser_background_tab_crash.js b/browser/components/sessionstore/test/browser_background_tab_crash.js
+--- a/browser/components/sessionstore/test/browser_background_tab_crash.js
++++ b/browser/components/sessionstore/test/browser_background_tab_crash.js
+@@ -56,18 +56,18 @@ async function setupBackgroundTabs(testF
+                remoteBrowser2.frameLoader.childID,
+                "Both remote browsers should share the same content process.");
+ 
+   // Now switch back to the non-remote browser...
+   await BrowserTestUtils.switchTab(gBrowser, initialTab);
+ 
+   await testFn([tab1, tab2]);
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ }
+ 
+ /**
+  * Takes some set of background tabs that are assumed to all belong to
+  * the same content process, and crashes them.
+  *
+  * @param tabs (Array(<xul:tab>))
+  *        The tabs to crash.
+diff --git a/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js b/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js
+--- a/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js
++++ b/browser/components/sessionstore/test/browser_docshell_uuid_consistency.js
+@@ -18,18 +18,18 @@ add_task(async function duplicateTab() {
+   await ContentTask.spawn(tab2.linkedBrowser, null, function() {
+     let docshell = content.window.QueryInterface(Ci.nsIInterfaceRequestor)
+                                  .getInterface(Ci.nsIWebNavigation)
+                                  .QueryInterface(Ci.nsIDocShell);
+     let shEntry = docshell.sessionHistory.getEntryAtIndex(0, false);
+     is(shEntry.docshellID.toString(), docshell.historyID.toString());
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // Second test - open a tab and navigate across processes, which triggers sessionrestore to persist history.
+ add_task(async function contentToChromeNavigate() {
+   const TEST_URL = "data:text/html,foo";
+   let tab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ 
+@@ -56,10 +56,10 @@ add_task(async function contentToChromeN
+ 
+   // 'cause we're in the chrome process, we can just directly poke at the shistory.
+   let sh = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
+ 
+   is(sh.count, 2);
+   is(sh.getEntryAtIndex(0, false).docshellID.toString(), docShell.historyID.toString());
+   is(sh.getEntryAtIndex(1, false).docshellID.toString(), docShell.historyID.toString());
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/sessionstore/test/browser_duplicate_history.js b/browser/components/sessionstore/test/browser_duplicate_history.js
+--- a/browser/components/sessionstore/test/browser_duplicate_history.js
++++ b/browser/components/sessionstore/test/browser_duplicate_history.js
+@@ -12,11 +12,12 @@ add_task(async function() {
+     let before = TabStateCache.get(aBrowser);
+ 
+     let newTab = SessionStore.duplicateTab(window, tab);
+     await BrowserTestUtils.browserLoaded(newTab.linkedBrowser);
+     let after = TabStateCache.get(newTab.linkedBrowser);
+ 
+     isnot(before.history.entries, after.history.entries,
+           "The entry objects should not be shared");
+-    await BrowserTestUtils.removeTab(newTab);
++
++    BrowserTestUtils.removeTab(newTab);
+   });
+ });
+diff --git a/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js b/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
+--- a/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
++++ b/browser/components/sessionstore/test/browser_multiple_navigateAndRestore.js
+@@ -27,10 +27,10 @@ add_task(async function() {
+   is(browser.currentURI.spec, PAGE_2, "Should have PAGE_2 as the browser currentURI");
+ 
+   await ContentTask.spawn(browser, PAGE_2, async function(expectedURL) {
+     docShell.QueryInterface(Ci.nsIWebNavigation);
+     Assert.equal(docShell.currentURI.spec, expectedURL,
+        "Content should have PAGE_2 as the browser currentURI");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/browser/components/sessionstore/test/browser_newtab_userTypedValue.js b/browser/components/sessionstore/test/browser_newtab_userTypedValue.js
+--- a/browser/components/sessionstore/test/browser_newtab_userTypedValue.js
++++ b/browser/components/sessionstore/test/browser_newtab_userTypedValue.js
+@@ -35,17 +35,17 @@ add_task(async function() {
+   await TabStateFlusher.flush(win.gBrowser.selectedBrowser);
+ 
+   is(win.gURLBar.value, "", "URL bar should be empty");
+   tab = win.gBrowser.selectedTab;
+   is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null");
+   state = JSON.parse(SessionStore.getTabState(tab));
+   ok(!state.userTypedValue, "userTypedValue should be undefined on the tab's state");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   for (let url of gInitialPages) {
+     if (url == BROWSER_NEW_TAB_URL) {
+       continue; // We tested about:newtab using BrowserOpenTab() above.
+     }
+     info("Testing " + url + " - " + new Date());
+     await BrowserTestUtils.openNewForegroundTab(win.gBrowser, url);
+     await BrowserTestUtils.closeWindow(win);
+@@ -62,15 +62,15 @@ add_task(async function() {
+ 
+     is(win.gURLBar.value, "", "URL bar should be empty");
+     tab = win.gBrowser.selectedTab;
+     is(tab.linkedBrowser.userTypedValue, null, "userTypedValue should be null");
+     state = JSON.parse(SessionStore.getTabState(tab));
+     ok(!state.userTypedValue, "userTypedValue should be undefined on the tab's state");
+ 
+     info("Removing tab - " + new Date());
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     info("Finished removing tab - " + new Date());
+   }
+   info("Removing window - " + new Date());
+   await BrowserTestUtils.closeWindow(win);
+   info("Finished removing window - " + new Date());
+ });
+diff --git a/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js b/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
+--- a/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
++++ b/browser/components/sessionstore/test/browser_parentProcessRestoreHash.js
+@@ -84,12 +84,12 @@ add_task(async function() {
+   ok(!tab.linkedBrowser.isRemoteBrowser, "Browser should no longer be remote");
+ 
+   is(gURLBar.textValue, TESTURL, "URL bar visible value should be correct.");
+   is(gURLBar.value, TESTURL, "URL bar value should be correct.");
+   is(gURLBar.getAttribute("pageproxystate"), "valid", "URL bar is in valid page proxy state");
+ 
+   ok(!tab.linkedBrowser.userTypedValue, "No userTypedValue should be on the browser.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   gBrowser.removeProgressListener(wpl);
+   TestAboutPage.unregister();
+ });
+diff --git a/browser/components/sessionstore/test/browser_revive_crashed_bg_tabs.js b/browser/components/sessionstore/test/browser_revive_crashed_bg_tabs.js
+--- a/browser/components/sessionstore/test/browser_revive_crashed_bg_tabs.js
++++ b/browser/components/sessionstore/test/browser_revive_crashed_bg_tabs.js
+@@ -46,11 +46,11 @@ add_task(async function test_revive_bg_t
+ 
+   // Now select newTab2 to make sure it restores.
+   let newTab2Restored = promiseTabRestored(newTab2);
+   gBrowser.selectedTab = newTab2;
+   await newTab2Restored;
+ 
+   ok(browser2.isRemoteBrowser, "Restored browser should be remote");
+ 
+-  await BrowserTestUtils.removeTab(newTab1);
+-  await BrowserTestUtils.removeTab(newTab2);
++  BrowserTestUtils.removeTab(newTab1);
++  BrowserTestUtils.removeTab(newTab2);
+ });
+diff --git a/browser/components/sessionstore/test/browser_unrestored_crashedTabs.js b/browser/components/sessionstore/test/browser_unrestored_crashedTabs.js
+--- a/browser/components/sessionstore/test/browser_unrestored_crashedTabs.js
++++ b/browser/components/sessionstore/test/browser_unrestored_crashedTabs.js
+@@ -59,11 +59,11 @@ add_task(async function test() {
+     ok(!originalTab.isRemoteBrowser, "Should not be remote");
+ 
+     // We'd better be able to restore it still.
+     gBrowser.selectedTab = originalTab;
+     SessionStore.reviveCrashedTab(originalTab);
+     await promiseTabRestored(originalTab);
+ 
+     // Clean up.
+-    await BrowserTestUtils.removeTab(unrestoredTab);
++    BrowserTestUtils.removeTab(unrestoredTab);
+   });
+ });
+diff --git a/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js b/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js
+--- a/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js
++++ b/browser/extensions/formautofill/test/browser/browser_autocomplete_footer.js
+@@ -29,17 +29,17 @@ add_task(async function test_click_on_fo
+     const optionButton = itemsBox.querySelector(".autocomplete-richlistitem:last-child")._optionButton;
+     const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
+     // Wait for dropdown animation finished to continue mouse synthesizing.
+     await sleep(1000);
+     await EventUtils.synthesizeMouseAtCenter(optionButton, {});
+     info(`expecting tab: about:preferences#privacy opened`);
+     const prefTab = await prefTabPromise;
+     info(`expecting tab: about:preferences#privacy removed`);
+-    await BrowserTestUtils.removeTab(prefTab);
++    BrowserTestUtils.removeTab(prefTab);
+     ok(true, "Tab: preferences#privacy was successfully opened by clicking on the footer");
+ 
+     await closePopup(browser);
+   });
+ });
+ 
+ add_task(async function test_press_enter_on_footer() {
+   await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
+@@ -51,17 +51,17 @@ add_task(async function test_press_enter
+     const prefTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, PRIVACY_PREF_URL);
+     for (let i = 0; i < listItemElems.length; i++) {
+       await BrowserTestUtils.synthesizeKey("VK_DOWN", {}, browser);
+     }
+     await BrowserTestUtils.synthesizeKey("VK_RETURN", {}, browser);
+     info(`expecting tab: about:preferences#privacy opened`);
+     const prefTab = await prefTabPromise;
+     info(`expecting tab: about:preferences#privacy removed`);
+-    await BrowserTestUtils.removeTab(prefTab);
++    BrowserTestUtils.removeTab(prefTab);
+     ok(true, "Tab: preferences#privacy was successfully opened by pressing enter on the footer");
+ 
+     await closePopup(browser);
+   });
+ });
+ 
+ add_task(async function test_phishing_warning_single_category() {
+   await BrowserTestUtils.withNewTab({gBrowser, url: URL}, async function(browser) {
+diff --git a/browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js b/browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js
+--- a/browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js
++++ b/browser/extensions/formautofill/test/browser/browser_first_time_use_doorhanger.js
+@@ -30,17 +30,17 @@ add_task(async function test_first_time_
+ 
+       await promiseShown;
+       let cb = getDoorhangerCheckbox();
+       ok(cb.hidden, "Sync checkbox should be hidden");
+       // Open the panel via main button
+       await clickDoorhangerButton(MAIN_BUTTON);
+       let tab = await tabPromise;
+       ok(tab, "Privacy panel opened");
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     }
+   );
+ 
+   addresses = await getAddresses();
+   is(addresses.length, 1, "Address saved");
+   let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
+   is(ftuPref, false, "First time use flag is false");
+ });
+@@ -109,15 +109,15 @@ add_task(async function test_first_time_
+       is(cb.checked, false, "Checkbox state should match addresses sync state");
+       cb.click();
+       is(SpecialPowers.getBoolPref(SYNC_ADDRESSES_PREF), true,
+          "addresses sync should be enabled after checked");
+       // Open the panel via main button
+       await clickDoorhangerButton(MAIN_BUTTON);
+       let tab = await tabPromise;
+       ok(tab, "Privacy panel opened");
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     }
+   );
+ 
+   let ftuPref = SpecialPowers.getBoolPref(FTU_PREF);
+   is(ftuPref, false, "First time use flag is false");
+ });
+diff --git a/browser/modules/test/browser/browser_PageActions.js b/browser/modules/test/browser/browser_PageActions.js
+--- a/browser/modules/test/browser/browser_PageActions.js
++++ b/browser/modules/test/browser/browser_PageActions.js
+@@ -10,17 +10,17 @@ add_task(async function init() {
+   // The page action urlbar button, and therefore the panel, is only shown when
+   // the current tab is actionable -- i.e., a normal web page.  about:blank is
+   // not, so open a new tab first thing, and close it when this test is done.
+   let tab = await BrowserTestUtils.openNewForegroundTab({
+     gBrowser,
+     url: "http://example.com/",
+   });
+   registerCleanupFunction(async () => {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+ 
+ 
+ // Tests a simple non-built-in action without an iframe or subview.  Also
+ // thoroughly checks most of the action's properties, methods, and DOM nodes, so
+ // it's not necessary to do that in general in other test tasks.
+ add_task(async function simple() {
+diff --git a/browser/modules/test/browser/browser_PageActions.js.1442465-4_2.later b/browser/modules/test/browser/browser_PageActions.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/modules/test/browser/browser_PageActions.js.1442465-4_2.later
+@@ -0,0 +1,40 @@
++--- browser_PageActions.js
+++++ browser_PageActions.js
++@@ -1312,17 +1312,17 @@ add_task(async function contextMenu() {
++   Assert.equal(menuItems[2].label, "Manage Extension\u2026",
++                "'Manage' item is present");
++   contextMenuPromise = promisePanelHidden("pageActionContextMenu");
++   let aboutAddonsPromise =
++     BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
++   EventUtils.synthesizeMouseAtCenter(menuItems[2], {});
++   let values = await Promise.all([aboutAddonsPromise, contextMenuPromise]);
++   let aboutAddonsTab = values[0];
++-  await BrowserTestUtils.removeTab(aboutAddonsTab);
+++  BrowserTestUtils.removeTab(aboutAddonsTab);
++ 
++   // Open the context menu on the action's urlbar button.
++   let urlbarButton = BrowserPageActions.urlbarButtonNodeForActionID(action.id);
++   contextMenuPromise = promisePanelShown("pageActionContextMenu");
++   EventUtils.synthesizeMouseAtCenter(urlbarButton, {
++     type: "contextmenu",
++     button: 2,
++   });
++@@ -1398,17 +1398,17 @@ add_task(async function contextMenu() {
++   Assert.equal(menuItems[2].label, "Manage Extension\u2026",
++                "'Manage' item is present");
++   contextMenuPromise = promisePanelHidden("pageActionContextMenu");
++   aboutAddonsPromise =
++     BrowserTestUtils.waitForNewTab(gBrowser, "about:addons");
++   EventUtils.synthesizeMouseAtCenter(menuItems[2], {});
++   values = await Promise.all([aboutAddonsPromise, contextMenuPromise]);
++   aboutAddonsTab = values[0];
++-  await BrowserTestUtils.removeTab(aboutAddonsTab);
+++  BrowserTestUtils.removeTab(aboutAddonsTab);
++ 
++   // Done, clean up.
++   action.remove();
++ 
++   // urlbar tests that run after this one can break if the mouse is left over
++   // the area where the urlbar popup appears, which seems to happen due to the
++   // above synthesized mouse events.  Move it over the urlbar.
++   EventUtils.synthesizeMouseAtCenter(gURLBar, { type: "mousemove" });
+diff --git a/browser/modules/test/browser/browser_SitePermissions.js b/browser/modules/test/browser/browser_SitePermissions.js
+--- a/browser/modules/test/browser/browser_SitePermissions.js
++++ b/browser/modules/test/browser/browser_SitePermissions.js
+@@ -84,11 +84,11 @@ add_task(async function testGetAllPermis
+ 
+   SitePermissions.remove(uri, "cookie");
+   SitePermissions.remove(uri, "popup");
+   SitePermissions.remove(uri, "geo");
+   SitePermissions.remove(uri, "shortcuts");
+ 
+   Services.prefs.clearUserPref("permissions.default.shortcuts");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+ 
+diff --git a/browser/modules/test/browser/browser_urlBar_zoom.js.1442465-4_2.later b/browser/modules/test/browser/browser_urlBar_zoom.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/modules/test/browser/browser_urlBar_zoom.js.1442465-4_2.later
+@@ -0,0 +1,21 @@
++--- browser_urlBar_zoom.js
+++++ browser_urlBar_zoom.js
++@@ -30,17 +30,17 @@ async function testZoomButtonAppearsAndD
++   let zoomResetPromise = BrowserTestUtils.waitForEvent(window, zoomEventType);
++   zoomResetButton.click();
++   await zoomResetPromise;
++   pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
++   expectedZoomLevel = 100;
++   is(pageZoomLevel, expectedZoomLevel, "Clicking zoom button successfully resets browser zoom to 100%");
++   is(zoomResetButton.hidden, true, "Zoom reset button returns to being hidden");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ add_task(async function() {
++   await testZoomButtonAppearsAndDisappearsBasedOnZoomChanges("FullZoomChange");
++   await SpecialPowers.pushPrefEnv({"set": [["browser.zoom.full", false]]});
++   await testZoomButtonAppearsAndDisappearsBasedOnZoomChanges("TextZoomChange");
++   await SpecialPowers.pushPrefEnv({"set": [["browser.zoom.full", true]]});
++ });
+diff --git a/browser/modules/test/browser/formValidation/browser_validation_invisible.js.1442465-4_2.later b/browser/modules/test/browser/formValidation/browser_validation_invisible.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/browser/modules/test/browser/formValidation/browser_validation_invisible.js.1442465-4_2.later
+@@ -0,0 +1,31 @@
++--- browser_validation_invisible.js
+++++ browser_validation_invisible.js
++@@ -24,26 +24,26 @@ add_task(async function test_display_non
++   incrementTest();
++   let testPage =
++     "data:text/html;charset=utf-8," +
++     '<form target="t"><input type="url"  placeholder="url" value="http://" style="display: none;"><input id="s" type="button" value="check"></form>';
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
++   await BrowserTestUtils.synthesizeMouse("#s", 0, 0, {}, gBrowser.selectedBrowser);
++ 
++   checkPopupHide();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ /**
++  * In this test, we check that no popup appears if the element visibility is hidden.
++  */
++ add_task(async function test_visibility_hidden() {
++   incrementTest();
++   let testPage =
++     "data:text/html;charset=utf-8," +
++     '<form target="t"><input type="url"  placeholder="url" value="http://" style="visibility: hidden;"><input id="s" type="button" value="check"></form>';
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, testPage);
++   await BrowserTestUtils.synthesizeMouse("#s", 0, 0, {}, gBrowser.selectedBrowser);
++ 
++   checkPopupHide();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
+diff --git a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm
+--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm
++++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/PermissionPrompts.jsm
+@@ -115,17 +115,17 @@ var PermissionPrompts = {
+     },
+   },
+ };
+ 
+ async function closeLastTab() {
+   if (!lastTab) {
+     return;
+   }
+-  await BrowserTestUtils.removeTab(lastTab);
++  BrowserTestUtils.removeTab(lastTab);
+   lastTab = null;
+ }
+ 
+ async function clickOn(selector) {
+   let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ 
+   // Save the tab so we can close it later.
+   lastTab = await BrowserTestUtils.openNewForegroundTab(browserWindow.gBrowser, URL);
+diff --git a/devtools/client/jsonview/test/browser_jsonview_serviceworker.js b/devtools/client/jsonview/test/browser_jsonview_serviceworker.js
+--- a/devtools/client/jsonview/test/browser_jsonview_serviceworker.js
++++ b/devtools/client/jsonview/test/browser_jsonview_serviceworker.js
+@@ -60,14 +60,14 @@ add_task(async function() {
+   await clickJsonNode(".jsonPanelBox .treeTable .treeLabel");
+   is(await countRows(), 1, "There must be one row");
+ 
+   await ContentTask.spawn(browser, { script: SW, scope: TEST_JSON_URL }, async opts => {
+     let reg = await content.navigator.serviceWorker.getRegistration(opts.scope);
+     await reg.unregister();
+   });
+ 
+-  await BrowserTestUtils.removeTab(swTab);
++  BrowserTestUtils.removeTab(swTab);
+ });
+ 
+ function countRows() {
+   return getElementCount(".jsonPanelBox .treeTable .treeRow");
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js
+@@ -49,10 +49,10 @@ async function waitForMessageAndViewSour
+ 
+   let locationNode = msg.querySelector(".message-location .frame-link-source");
+   ok(locationNode, "Message location link element found");
+ 
+   let onTabOpen = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+   locationNode.click();
+   let newTab = await onTabOpen;
+   ok(true, "The view source tab was opened in response to clicking the link");
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ }
+diff --git a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_clickable_urls.js b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_clickable_urls.js
+--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_clickable_urls.js
++++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_webconsole_clickable_urls.js
+@@ -27,10 +27,10 @@ add_task(async function() {
+ 
+   info("Clicking on the link");
+   link.click();
+ 
+   const newTab = await onTabLoaded;
+   // We only need to check that newTab is truthy since
+   // BrowserTestUtils.waitForNewTab checks the URL.
+   ok(newTab, "The expected tab was opened.");
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ });
+diff --git a/docshell/test/browser/browser_click_link_within_view_source.js b/docshell/test/browser/browser_click_link_within_view_source.js
+--- a/docshell/test/browser/browser_click_link_within_view_source.js
++++ b/docshell/test/browser/browser_click_link_within_view_source.js
+@@ -50,11 +50,11 @@ add_task(async function test_click_link_
+ 
+     await loadPromise;
+ 
+     tabSpec = gBrowser.selectedBrowser.currentURI.spec;
+     info("loading: " + tabSpec);
+     ok(tabSpec.startsWith("view-source:file://") && tabSpec.endsWith(DUMMY_FILE),
+        "loading view-source of html succeeded");
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+diff --git a/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js
+--- a/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js
++++ b/docshell/test/browser/browser_history_triggeringprincipal_viewsource.js
+@@ -40,11 +40,11 @@ add_task(async function() {
+     loadPromise = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, VIEW_SRC_URI);
+     let backItem = backCtxtMenu.getElementsByAttribute("id", "context-back")[0];
+     backItem.click();
+     backCtxtMenu.hidePopup();
+     await loadPromise;
+     is(gBrowser.selectedBrowser.currentURI.spec, VIEW_SRC_URI,
+       "clicking .back() to view-source of html succeeded");
+ 
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ });
+diff --git a/docshell/test/browser/browser_uriFixupAlternateRedirects.js b/docshell/test/browser/browser_uriFixupAlternateRedirects.js
+--- a/docshell/test/browser/browser_uriFixupAlternateRedirects.js
++++ b/docshell/test/browser/browser_uriFixupAlternateRedirects.js
+@@ -20,10 +20,10 @@ add_task(async function() {
+   ok(contentURL.startsWith(errorURI), "Should be on an error page");
+ 
+   const contentPrincipal = tab.linkedBrowser.contentPrincipal;
+   ok(contentPrincipal.URI.spec.startsWith(errorURI), "Principal should be for the error page");
+ 
+   originalURL = new URL(originalURL);
+   is(originalURL.host, "example", "Should be an error for http://example, not http://www.example.com/");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/base/test/browser_aboutnewtab_process_selection.js.1442465-4_2.later b/dom/base/test/browser_aboutnewtab_process_selection.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/dom/base/test/browser_aboutnewtab_process_selection.js.1442465-4_2.later
+@@ -0,0 +1,37 @@
++--- browser_aboutnewtab_process_selection.js
+++++ browser_aboutnewtab_process_selection.js
++@@ -64,18 +64,18 @@ add_task(async function(){
++   await ContentTask.spawn(tab1.linkedBrowser, null, async function() {
++     const TEST_URL = "http://www.example.com/browser/dom/base/test/dummy.html";
++     content.location.href = TEST_URL;
++   });
++   await BrowserTestUtils.browserLoaded(tab1.linkedBrowser, false, TEST_URL);
++   is(ppmm.childCount, originalChildCount + 2,
++      "Navigating away from the preloaded browser (child side) should create a new content process.")
++ 
++-  await BrowserTestUtils.removeTab(tab1);
++-  await BrowserTestUtils.removeTab(tab2);
+++  BrowserTestUtils.removeTab(tab1);
+++  BrowserTestUtils.removeTab(tab2);
++ 
++   // Make sure the preload browser does not keep any of the new processes alive.
++   gBrowser.removePreloadedBrowser();
++ 
++   // Since we kept alive all the processes, we can shut down the ones that do
++   // not host any tabs reliably.
++   ppmm.releaseCachedProcesses();
++ });
++@@ -97,12 +97,12 @@ add_task(async function preloaded_state_
++   is(preloadedTabState, PRELOADED_STATE, "The preloaded browser has the correct attribute");
++ 
++   // Navigate away and check that the attribute has been removed altogether
++   gBrowser.selectedBrowser.loadURI(TEST_URL);
++   let navigatedTabHasState = gBrowser.selectedBrowser.hasAttribute("preloadedState");
++   ok(!navigatedTabHasState, "Correctly removed the preloadState attribute when navigating away");
++ 
++   // Remove tabs and preloaded browsers
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++   gBrowser.removePreloadedBrowser();
++ });
++ 
+diff --git a/dom/base/test/browser_bug1303838.js b/dom/base/test/browser_bug1303838.js
+--- a/dom/base/test/browser_bug1303838.js
++++ b/dom/base/test/browser_bug1303838.js
+@@ -98,18 +98,18 @@ async function testLinkClick(withFrame, 
+ 
+   if (!loadDivertedInBackground) {
+     await BrowserTestUtils.switchTab(gBrowser, tab);
+   }
+   await clickLink(withFrame, "#frame-link-3", tab.linkedBrowser, testTab.linkedBrowser, !loadDivertedInBackground);
+   is(gBrowser.tabs.length, 3, "check tabs.length");
+   is(gBrowser.selectedTab, loadDivertedInBackground ? tab : testTab, "check selectedTab");
+ 
+-  await BrowserTestUtils.removeTab(testTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(testTab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function clickLink(isFrame, linkId, browser, testBrowser, awaitTabSwitch = false, locationChangeNum = 1) {
+   let promises = [];
+   if (awaitTabSwitch) {
+     promises.push(waitForTabSwitch(gBrowser));
+   }
+   promises.push(testBrowser ?
+diff --git a/dom/base/test/browser_bug902350.js b/dom/base/test/browser_bug902350.js
+--- a/dom/base/test/browser_bug902350.js
++++ b/dom/base/test/browser_bug902350.js
+@@ -35,10 +35,10 @@ add_task(async function mixed_content_bl
+   // Navigating to insecure domain through target='_top' should succeed.
+   await insecureLoadPromise;
+ 
+   // The link click should not invoke the Mixed Content Blocker.
+   let {gIdentityHandler} = testBrowser.ownerGlobal;
+   ok (!gIdentityHandler._identityBox.classList.contains("mixedActiveBlocked"),
+       "Mixed Content Doorhanger did not appear when trying to navigate top");
+ 
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ });
+diff --git a/dom/base/test/browser_inputStream_structuredClone.js b/dom/base/test/browser_inputStream_structuredClone.js
+--- a/dom/base/test/browser_inputStream_structuredClone.js
++++ b/dom/base/test/browser_inputStream_structuredClone.js
+@@ -34,17 +34,17 @@ async function runTest(input, url) {
+ 
+     return dataBack;
+   });
+ 
+   ok(dataBack.check, "The inputStream is a nsIInputStream also on content.");
+   ok(data.inputStream instanceof Ci.nsIInputStream, "The original object was an inputStream");
+   ok(dataBack.inputStream instanceof Ci.nsIInputStream, "We have an inputStream back from the content.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function test() {
+   let a = "a";
+   for (let i = 0; i < 25; ++i) {
+     a+=a;
+   }
+ 
+diff --git a/dom/base/test/browser_timeout_throttling_with_audio_playback.js b/dom/base/test/browser_timeout_throttling_with_audio_playback.js
+--- a/dom/base/test/browser_timeout_throttling_with_audio_playback.js
++++ b/dom/base/test/browser_timeout_throttling_with_audio_playback.js
+@@ -42,17 +42,17 @@ async function runTest(url) {
+         let after = new Date();
+         resolve(after - before);
+       }, delay);
+     });
+   });
+   ok(timeout <= kMinTimeoutBackground, `Got the correct timeout (${timeout})`);
+ 
+   // All done.
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ }
+ 
+ add_task(async function setup() {
+   await SpecialPowers.pushPrefEnv({"set": [
+     ["dom.min_background_timeout_value", kMinTimeoutBackground],
+   ]});
+ });
+ 
+diff --git a/dom/broadcastchannel/tests/browser_private_browsing.js b/dom/broadcastchannel/tests/browser_private_browsing.js
+--- a/dom/broadcastchannel/tests/browser_private_browsing.js
++++ b/dom/broadcastchannel/tests/browser_private_browsing.js
+@@ -59,14 +59,14 @@ add_task(async function() {
+   });
+ 
+   var what1 = await p1;
+   ok(what1, 'hello world from private browsing', 'No messages received from the other window.');
+ 
+   var what2 = await p2;
+   ok(what1, 'hello world from non private browsing', 'No messages received from the other window.');
+ 
+-  await BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab1);
+   await BrowserTestUtils.closeWindow(win1);
+ 
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab2);
+   await BrowserTestUtils.closeWindow(win2);
+ });
+diff --git a/dom/file/ipc/tests/browser_ipcBlob.js b/dom/file/ipc/tests/browser_ipcBlob.js
+--- a/dom/file/ipc/tests/browser_ipcBlob.js
++++ b/dom/file/ipc/tests/browser_ipcBlob.js
+@@ -28,18 +28,18 @@ add_task(async function test_CtoPtoC_big
+       fr.onloadend = function() {
+         resolve(fr.result == new Array(1024*1024).join('123456789ABCDEF'));
+       }
+     });
+   });
+ 
+   ok(status, "CtoPtoC-big: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // Less than 1mb memory blob childA-parent-childB.
+ add_task(async function test_CtoPtoC_small() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+   let browser1 = gBrowser.getBrowserForTab(tab1);
+ 
+   let blob = await ContentTask.spawn(browser1, null, function() {
+@@ -60,18 +60,18 @@ add_task(async function test_CtoPtoC_sma
+       fr.onloadend = function() {
+         resolve(fr.result == "hello world!");
+       }
+     });
+   });
+ 
+   ok(status, "CtoPtoC-small: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // More than 1mb memory blob childA-parent-childB: BroadcastChannel
+ add_task(async function test_CtoPtoC_bc_big() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+   let browser1 = gBrowser.getBrowserForTab(tab1);
+ 
+   await ContentTask.spawn(browser1, null, function() {
+@@ -96,18 +96,18 @@ add_task(async function test_CtoPtoC_bc_
+       }
+ 
+       bc.postMessage("GO!");
+     });
+   });
+ 
+   ok(status, "CtoPtoC-broadcastChannel-big: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // Less than 1mb memory blob childA-parent-childB: BroadcastChannel
+ add_task(async function test_CtoPtoC_bc_small() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+   let browser1 = gBrowser.getBrowserForTab(tab1);
+ 
+   await ContentTask.spawn(browser1, null, function() {
+@@ -132,18 +132,18 @@ add_task(async function test_CtoPtoC_bc_
+       }
+ 
+       bc.postMessage("GO!");
+     });
+   });
+ 
+   ok(status, "CtoPtoC-broadcastChannel-small: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // blob URL childA-parent-childB
+ add_task(async function test_CtoPtoC_bc_small() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+   let browser1 = gBrowser.getBrowserForTab(tab1);
+ 
+   let blobURL = await ContentTask.spawn(browser1, null, function() {
+@@ -162,18 +162,18 @@ add_task(async function test_CtoPtoC_bc_
+       }
+ 
+       xhr.send();
+     });
+   });
+ 
+   ok(status, "CtoPtoC-blobURL: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+ 
+ // Multipart Blob childA-parent-childB.
+ add_task(async function test_CtoPtoC_multipart() {
+   let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, BASE_URI);
+   let browser1 = gBrowser.getBrowserForTab(tab1);
+ 
+   let blob = await ContentTask.spawn(browser1, null, function() {
+@@ -195,11 +195,11 @@ add_task(async function test_CtoPtoC_mul
+       fr.onloadend = function() {
+         resolve(fr.result == "hello world!");
+       }
+     });
+   });
+ 
+   ok(status, "CtoPtoC-multipart: Data match!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/dom/file/ipc/tests/browser_ipcBlob_temporary.js b/dom/file/ipc/tests/browser_ipcBlob_temporary.js
+--- a/dom/file/ipc/tests/browser_ipcBlob_temporary.js
++++ b/dom/file/ipc/tests/browser_ipcBlob_temporary.js
+@@ -104,11 +104,11 @@ add_task(async function test() {
+   ok(status, "All good for tab1!");
+ 
+   status = await ContentTask.spawn(browser2, null, function() {
+     return content.window.testPromise;
+   });
+ 
+   ok(status, "All good for tab2!");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/dom/security/test/cors/browser_CORS-console-warnings.js b/dom/security/test/cors/browser_CORS-console-warnings.js
+--- a/dom/security/test/cors/browser_CORS-console-warnings.js
++++ b/dom/security/test/cors/browser_CORS-console-warnings.js
+@@ -22,21 +22,21 @@ function on_new_message(new_messages) {
+     let text = elem.textContent;
+     if (text.match('Cross-Origin Request Blocked:')) {
+       ok(true, "message is: " + text);
+       messages_seen++;
+     }
+   }
+ }
+ 
+-function* do_cleanup() {
++async function do_cleanup() {
+   if (webconsole) {
+     webconsole.ui.off("new-messages", on_new_message);
+   }
+-  yield unsetCookiePref();
++  await unsetCookiePref();
+ }
+ 
+ /**
+  * Set e10s related preferences in the test environment.
+  * @return {Promise} promise that resolves when preferences are set.
+  */
+ function setCookiePref() {
+   return new Promise(resolve =>
+@@ -54,40 +54,40 @@ function setCookiePref() {
+  */
+ function unsetCookiePref() {
+   return new Promise(resolve => {
+     SpecialPowers.popPrefEnv(resolve);
+   });
+ }
+ 
+ //jscs:disable
+-add_task(function*() {
++add_task(async function() {
+   //jscs:enable
+   // A longer timeout is necessary for this test than the plain mochitests
+   // due to opening a new tab with the web console.
+   requestLongerTimeout(4);
+   registerCleanupFunction(do_cleanup);
+-  yield setCookiePref();
++  await setCookiePref();
+ 
+   let test_uri = "http://mochi.test:8888/browser/dom/security/test/cors/file_cors_logging_test.html";
+ 
+-  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
++  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+ 
+-  let toolbox = yield openToolboxForTab(tab, "webconsole");
++  let toolbox = await openToolboxForTab(tab, "webconsole");
+   ok(toolbox, "Got toolbox");
+   let hud = toolbox.getCurrentPanel().hud;
+   ok(hud, "Got hud");
+ 
+   if (!webconsole) {
+     registerCleanupFunction(do_cleanup);
+     hud.ui.on("new-messages", on_new_message);
+     webconsole = hud;
+   }
+ 
+   BrowserTestUtils.loadURI(gBrowser, test_uri);
+ 
+-  yield BrowserTestUtils.waitForLocationChange(gBrowser, test_uri+"#finished");
++  await BrowserTestUtils.waitForLocationChange(gBrowser, test_uri+"#finished");
+ 
+   // Different OS combinations
+   ok(messages_seen > 0, "Saw " + messages_seen + " messages.");
+ 
+-  yield BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/security/test/general/browser_test_toplevel_data_navigations.js b/dom/security/test/general/browser_test_toplevel_data_navigations.js
+--- a/dom/security/test/general/browser_test_toplevel_data_navigations.js
++++ b/dom/security/test/general/browser_test_toplevel_data_navigations.js
+@@ -22,33 +22,33 @@ add_task(async function test_nav_data_ur
+ });
+ 
+ add_task(async function test_nav_data_uri_redirect() {
+   await SpecialPowers.pushPrefEnv({
+     "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]],
+   });
+   let tab = BrowserTestUtils.addTab(gBrowser, kRedirectURI);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+   // wait to make sure data: URI did not load before checking that it got blocked
+   await new Promise(resolve => setTimeout(resolve, 500));
+   await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     is(content.document.body.innerHTML, "",
+        "data: URI navigation after server redirect should be blocked");
+   });
+ });
+ 
+ add_task(async function test_nav_data_uri_meta_redirect() {
+   await SpecialPowers.pushPrefEnv({
+     "set": [["security.data_uri.block_toplevel_data_uri_navigations", true]],
+   });
+   let tab = BrowserTestUtils.addTab(gBrowser, kMetaRedirectURI);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+   // wait to make sure data: URI did not load before checking that it got blocked
+   await new Promise(resolve => setTimeout(resolve, 500));
+   await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     is(content.document.body.innerHTML, "",
+        "data: URI navigation after meta redirect should be blocked");
+   });
+ });
+diff --git a/dom/tests/browser/browser_autofocus_background.js b/dom/tests/browser/browser_autofocus_background.js
+--- a/dom/tests/browser/browser_autofocus_background.js
++++ b/dom/tests/browser/browser_autofocus_background.js
+@@ -26,12 +26,12 @@ add_task(async function() {
+        "The background tab's focused element should be " + testingList[i].tagName);
+   }
+ 
+   is(document.activeElement, tabs[0].linkedBrowser,
+      "The background tab's focused element should not cause the tab to be focused");
+ 
+   // Cleaning up.
+   for (let i = 1; i < tabs.length; i++) {
+-    await BrowserTestUtils.removeTab(tabs[i]);
++    BrowserTestUtils.removeTab(tabs[i]);
+   }
+ });
+ 
+diff --git a/dom/tests/browser/browser_beforeunload_between_chrome_content.js b/dom/tests/browser/browser_beforeunload_between_chrome_content.js
+--- a/dom/tests/browser/browser_beforeunload_between_chrome_content.js
++++ b/dom/tests/browser/browser_beforeunload_between_chrome_content.js
+@@ -75,17 +75,17 @@ add_task(async function() {
+   let dialogShown2 = awaitAndCloseBeforeUnloadDialog(false);
+   gBrowser.goForward();
+   await Promise.all([
+     dialogShown2,
+     BrowserTestUtils.browserLoaded(browser)
+   ]);
+   is(beforeUnloadCount, 2, "Should have received two beforeunload events.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Test navigation from a chrome page to a content page. Also check that only
+  * one beforeunload event is fired.
+  */
+ add_task(async function() {
+   let beforeUnloadCount = 0;
+@@ -136,10 +136,10 @@ add_task(async function() {
+   let dialogShown2 = awaitAndCloseBeforeUnloadDialog(false);
+   gBrowser.goForward();
+   await Promise.all([
+     dialogShown2,
+     BrowserTestUtils.browserLoaded(browser)
+   ]);
+   is(beforeUnloadCount, 2, "Should have received two beforeunload events.");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js b/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js
+--- a/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js
++++ b/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js
+@@ -19,10 +19,10 @@ add_task(async function testDismissHange
+   info("Clicked outside the Geolocation panel to dismiss it");
+ 
+   let hasLocation = await ContentTask.spawn(gBrowser.selectedBrowser, {}, async function() {
+     return content.document.body.innerHTML.includes("location...");
+   });
+ 
+   ok(hasLocation, "Location is not shared");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ });
+diff --git a/dom/tests/browser/browser_bug1236512.js b/dom/tests/browser/browser_bug1236512.js
+--- a/dom/tests/browser/browser_bug1236512.js
++++ b/dom/tests/browser/browser_bug1236512.js
+@@ -62,17 +62,17 @@ add_task(async function() {
+        "window");
+   await new Promise(resolve => waitForFocus(resolve, window));
+   await waitContentVisibilityChange(true /* isHidden */, browserTest);
+ 
+   info("test window should still report 'hidden' since it is still fully covered " +
+        "by another window");
+   let tab = BrowserTestUtils.addTab(browserTest);
+   await BrowserTestUtils.switchTab(browserTest, tab);
+-  await BrowserTestUtils.removeTab(browserTest.selectedTab);
++  BrowserTestUtils.removeTab(browserTest.selectedTab);
+   await testContentVisibilityState(true /* isHidden */, browserTest);
+ 
+   info("test window should report 'visible' if it is not fully covered by " +
+        "another window");
+   await new Promise(resolve => waitForFocus(resolve, winTest));
+   await waitContentVisibilityChange(false /* isHidden */, browserTest);
+ 
+   info("closing test window");
+diff --git a/dom/tests/browser/browser_localStorage_e10s.js b/dom/tests/browser/browser_localStorage_e10s.js
+--- a/dom/tests/browser/browser_localStorage_e10s.js
++++ b/dom/tests/browser/browser_localStorage_e10s.js
+@@ -60,17 +60,17 @@ async function openTestTabInOwnProcess(n
+   return knownTab;
+ }
+ 
+ /**
+  * Close all the tabs we opened.
+  */
+ async function cleanupTabs(knownTabs) {
+   for (let knownTab of knownTabs.byName.values()) {
+-    await BrowserTestUtils.removeTab(knownTab.tab);
++    BrowserTestUtils.removeTab(knownTab.tab);
+     knownTab.cleanup();
+   }
+   knownTabs.cleanup();
+ }
+ 
+ /**
+  * Wait for a LocalStorage flush to occur.  This notification can occur as a
+  * result of any of:
+diff --git a/dom/tests/browser/browser_localStorage_privatestorageevent.js b/dom/tests/browser/browser_localStorage_privatestorageevent.js
+--- a/dom/tests/browser/browser_localStorage_privatestorageevent.js
++++ b/dom/tests/browser/browser_localStorage_privatestorageevent.js
+@@ -61,14 +61,14 @@ add_task(async function() {
+   });
+ 
+   let privSaw = await ContentTask.spawn(privBrowser, null, function(opts) {
+     return content.window.gotStorageEvent;
+   });
+ 
+   ok(!privSaw, "privWin shouldn't be able to see pubWin's storage events");
+ 
+-  await BrowserTestUtils.removeTab(privTab);
++  BrowserTestUtils.removeTab(privTab);
+   await BrowserTestUtils.closeWindow(privWin);
+ 
+-  await BrowserTestUtils.removeTab(pubTab);
++  BrowserTestUtils.removeTab(pubTab);
+   await BrowserTestUtils.closeWindow(pubWin);
+ });
+diff --git a/dom/tests/browser/browser_noopener.js b/dom/tests/browser/browser_noopener.js
+--- a/dom/tests/browser/browser_noopener.js
++++ b/dom/tests/browser/browser_noopener.js
+@@ -72,18 +72,18 @@ async function doTests(private, containe
+       is(content.window.name, test.name, "Name should match for " + testid);
+       if (test.opener) {
+         ok(content.window.opener, "Opener should have been set for " + testid);
+       } else {
+         ok(!content.window.opener, "Opener should not have been set for " + testid);
+       }
+     });
+ 
+-    await BrowserTestUtils.removeTab(tab);
+-    await BrowserTestUtils.removeTab(originalTab);
++    BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(originalTab);
+   }
+ 
+   window.close();
+ }
+ 
+ async function doAllTests() {
+   // Non-private window
+   await doTests(false, false);
+diff --git a/dom/tests/browser/browser_test_focus_after_modal_state.js b/dom/tests/browser/browser_test_focus_after_modal_state.js
+--- a/dom/tests/browser/browser_test_focus_after_modal_state.js
++++ b/dom/tests/browser/browser_test_focus_after_modal_state.js
+@@ -60,10 +60,10 @@ add_task(async function() {
+   await Promise.all([waitForBlur, waitForFocus]);
+   await ContentTask.spawn(browser, null, async function() {
+     is(content.document.activeElement, content.document.getElementById("edit"),
+        "Focus should be back on iframe element");
+   });
+   is(lastMessageReceived, "Test:FocusReceived",
+      "Should receive blur and then focus event");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/tests/browser/browser_test_new_window_from_content.js b/dom/tests/browser/browser_test_new_window_from_content.js
+--- a/dom/tests/browser/browser_test_new_window_from_content.js
++++ b/dom/tests/browser/browser_test_new_window_from_content.js
+@@ -125,17 +125,17 @@ function prepareForResult(aBrowser, aExp
+         await BrowserTestUtils.closeWindow(newWin);
+       })();
+       break;
+     case kNewTab:
+       return (async function() {
+         let newTab = await BrowserTestUtils.waitForNewTab(gBrowser);
+         is(newTab.linkedBrowser.currentURI.spec, expectedSpec,
+            "Should be at dummy.html");
+-        await BrowserTestUtils.removeTab(newTab);
++        BrowserTestUtils.removeTab(newTab);
+       })();
+       break;
+     default:
+       ok(false, "prepareForResult can't handle an expectation of " + aExpectation)
+       return;
+   }
+ 
+   return deferred.promise;
+diff --git a/dom/tests/browser/browser_test_toolbars_visibility.js b/dom/tests/browser/browser_test_toolbars_visibility.js
+--- a/dom/tests/browser/browser_test_toolbars_visibility.js
++++ b/dom/tests/browser/browser_test_toolbars_visibility.js
+@@ -130,17 +130,17 @@ add_task(async function() {
+     await BrowserTestUtils.synthesizeMouseAtCenter("#winOpenDefault", {}, browser);
+     let tab = await newTabPromise;
+ 
+     // Check that all toolbars are visible
+     let toolbars = await getToolbarsFromBrowserContent(gBrowser.selectedBrowser);
+     testDefaultToolbars(toolbars);
+ 
+     // Cleanup
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+ 
+     // Now let's open a window with toolbars hidden
+     let winPromise = BrowserTestUtils.waitForNewWindow();
+     await BrowserTestUtils.synthesizeMouseAtCenter("#winOpenNonDefault", {}, browser);
+     let popupWindow = await winPromise;
+ 
+     let popupBrowser = popupWindow.gBrowser.selectedBrowser;
+     await BrowserTestUtils.browserLoaded(popupBrowser);
+diff --git a/dom/u2f/tests/browser/browser_abort_visibility.js b/dom/u2f/tests/browser/browser_abort_visibility.js
+--- a/dom/u2f/tests/browser/browser_abort_visibility.js
++++ b/dom/u2f/tests/browser/browser_abort_visibility.js
+@@ -86,16 +86,16 @@ add_task(async function test_abort() {
+   await startGetAssertionRequest(tab_get);
+   await assertStatus(tab_get, "pending");
+ 
+   // Switch back to the first tab, the get() request is aborted.
+   await BrowserTestUtils.switchTab(gBrowser, tab_create);
+   await waitForStatus(tab_get, "aborted");
+ 
+   // Close tabs.
+-  await BrowserTestUtils.removeTab(tab_create);
+-  await BrowserTestUtils.removeTab(tab_get);
++  BrowserTestUtils.removeTab(tab_create);
++  BrowserTestUtils.removeTab(tab_get);
+ 
+   // Cleanup.
+   Services.prefs.clearUserPref("security.webauth.u2f");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
+ });
+diff --git a/dom/u2f/tests/browser/browser_appid_localhost.js b/dom/u2f/tests/browser/browser_appid_localhost.js
+--- a/dom/u2f/tests/browser/browser_appid_localhost.js
++++ b/dom/u2f/tests/browser/browser_appid_localhost.js
+@@ -64,15 +64,15 @@ add_task(async function () {
+   });
+ 
+   // Test: Correct TLD
+   await promiseU2FRegister(tab, "https://localhost:443/appId").then(res => {
+     is(res.errorCode, 0, "https://localhost:443/appId should work.");
+   });
+ 
+   // Close tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Cleanup.
+   Services.prefs.clearUserPref("security.webauth.u2f");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
+ });
+diff --git a/dom/webauthn/tests/browser/browser_abort_visibility.js b/dom/webauthn/tests/browser/browser_abort_visibility.js
+--- a/dom/webauthn/tests/browser/browser_abort_visibility.js
++++ b/dom/webauthn/tests/browser/browser_abort_visibility.js
+@@ -102,18 +102,18 @@ add_task(async function test_switch_tab(
+   await startGetAssertionRequest(tab_get);
+   await assertStatus(tab_get, "pending");
+ 
+   // Switch back to the first tab, the get() request is aborted.
+   await BrowserTestUtils.switchTab(gBrowser, tab_create);
+   await waitForStatus(tab_get, "aborted");
+ 
+   // Close tabs.
+-  await BrowserTestUtils.removeTab(tab_create);
+-  await BrowserTestUtils.removeTab(tab_get);
++  BrowserTestUtils.removeTab(tab_create);
++  BrowserTestUtils.removeTab(tab_get);
+ });
+ 
+ add_task(async function test_new_window_make() {
+   // Create a new tab for the MakeCredential() request.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+ 
+   // Start a MakeCredential request.
+   await startMakeCredentialRequest(tab);
+@@ -137,17 +137,17 @@ add_task(async function test_new_window_
+   await assertStatus(tab, "pending");
+ 
+   // Open a new window. The tab will lose focus.
+   let win = await BrowserTestUtils.openNewBrowserWindow();
+   await waitForStatus(tab, "aborted");
+   await BrowserTestUtils.closeWindow(win);
+ 
+   // Close tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_minimize_make() {
+   let env = Cc["@mozilla.org/process/environment;1"]
+               .getService(Ci.nsIEnvironment);
+   // Minimizing windows doesn't supported in headless mode.
+   if (env.get("MOZ_HEADLESS")) {
+     return;
+@@ -163,17 +163,17 @@ add_task(async function test_minimize_ma
+   // Minimize the window.
+   window.minimize();
+   await waitForStatus(tab, "aborted");
+ 
+   // Restore the window.
+   await new Promise(resolve => SimpleTest.waitForFocus(resolve, window));
+ 
+   // Close tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function test_minimize_get() {
+   let env = Cc["@mozilla.org/process/environment;1"]
+               .getService(Ci.nsIEnvironment);
+   // Minimizing windows doesn't supported in headless mode.
+   if (env.get("MOZ_HEADLESS")) {
+     return;
+@@ -189,10 +189,10 @@ add_task(async function test_minimize_ge
+   // Minimize the window.
+   window.minimize();
+   await waitForStatus(tab, "aborted");
+ 
+   // Restore the window.
+   await new Promise(resolve => SimpleTest.waitForFocus(resolve, window));
+ 
+   // Close tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/webauthn/tests/browser/browser_fido_appid_extension.js b/dom/webauthn/tests/browser/browser_fido_appid_extension.js
+--- a/dom/webauthn/tests/browser/browser_fido_appid_extension.js
++++ b/dom/webauthn/tests/browser/browser_fido_appid_extension.js
+@@ -137,17 +137,17 @@ add_task(async function test_appid() {
+       let rpIdHashSign = authenticatorData.slice(0, 32);
+       ok(memcmp(rpIdHash, rpIdHashSign), "rpIdHash is correct");
+ 
+       let clientData = JSON.parse(buffer2string(clientDataJSON));
+       is(clientData.clientExtensions.appid, appid, "appid extension sent");
+     });
+ 
+   // Close tab.
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(function test_cleanup() {
+   Services.prefs.clearUserPref("security.webauth.u2f");
+   Services.prefs.clearUserPref("security.webauth.webauthn");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_softtoken");
+   Services.prefs.clearUserPref("security.webauth.webauthn_enable_usbtoken");
+ });
+diff --git a/dom/workers/test/serviceworkers/browser_download_canceled.js b/dom/workers/test/serviceworkers/browser_download_canceled.js
+--- a/dom/workers/test/serviceworkers/browser_download_canceled.js
++++ b/dom/workers/test/serviceworkers/browser_download_canceled.js
+@@ -137,11 +137,11 @@ add_task(async function interruptedDownl
+ 
+   // Cleanup
+   await ContentTask.spawn(
+     tab.linkedBrowser,
+     null,
+     function() {
+       return content.wrappedJSObject.registration.unregister();
+     });
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   await clearDownloads();
+ });
+diff --git a/dom/workers/test/serviceworkers/browser_force_refresh.js b/dom/workers/test/serviceworkers/browser_force_refresh.js
+--- a/dom/workers/test/serviceworkers/browser_force_refresh.js
++++ b/dom/workers/test/serviceworkers/browser_force_refresh.js
+@@ -45,17 +45,17 @@ function test() {
+     async function done() {
+       tab.linkedBrowser.messageManager.removeMessageListener("test:event", eventHandler);
+ 
+       await ContentTask.spawn(tabBrowser, null, async function() {
+         const swr = await content.navigator.serviceWorker.getRegistration();
+         await swr.unregister();
+       });
+ 
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+       executeSoon(finish);
+     }
+ 
+     var maxCacheLoadCount = 3;
+     var cachedLoadCount = 0;
+     var baseLoadCount = 0;
+ 
+     function eventHandler(msg) {
+diff --git a/dom/workers/test/serviceworkers/browser_storage_permission.js.1442465-4_2.later b/dom/workers/test/serviceworkers/browser_storage_permission.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/dom/workers/test/serviceworkers/browser_storage_permission.js.1442465-4_2.later
+@@ -0,0 +1,145 @@
++--- browser_storage_permission.js
+++++ browser_storage_permission.js
++@@ -34,51 +34,51 @@ add_task(async function setup() {
++             worker.removeEventListener("statechange", onStateChange);
++             resolve();
++           }
++         });
++       });
++     }
++   );
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function test_allow_permission() {
++   Services.perms.add(Services.io.newURI(PAGE_URI), "cookie",
++                      Ci.nsICookiePermission.ACCESS_ALLOW);
++ 
++   let tab = BrowserTestUtils.addTab(gBrowser, SCOPE);
++   let browser = gBrowser.getBrowserForTab(tab);
++   await BrowserTestUtils.browserLoaded(browser);
++ 
++   let controller = await ContentTask.spawn(browser, null, async function() {
++     return content.navigator.serviceWorker.controller;
++   });
++ 
++   ok(!!controller, "page should be controlled with storage allowed");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function test_deny_permission() {
++   Services.perms.add(Services.io.newURI(PAGE_URI), "cookie",
++                      Ci.nsICookiePermission.ACCESS_DENY);
++ 
++   let tab = BrowserTestUtils.addTab(gBrowser, SCOPE);
++   let browser = gBrowser.getBrowserForTab(tab);
++   await BrowserTestUtils.browserLoaded(browser);
++ 
++   let controller = await ContentTask.spawn(browser, null, async function() {
++     return content.navigator.serviceWorker.controller;
++   });
++ 
++   is(controller, null, "page should be not controlled with storage denied");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   Services.perms.remove(Services.io.newURI(PAGE_URI), "cookie");
++ });
++ 
++ add_task(async function test_session_permission() {
++   Services.perms.add(Services.io.newURI(PAGE_URI), "cookie",
++                      Ci.nsICookiePermission.ACCESS_SESSION);
++ 
++   let tab = BrowserTestUtils.addTab(gBrowser, SCOPE);
++@@ -86,17 +86,17 @@ add_task(async function test_session_per
++   await BrowserTestUtils.browserLoaded(browser);
++ 
++   let controller = await ContentTask.spawn(browser, null, async function() {
++     return content.navigator.serviceWorker.controller;
++   });
++ 
++   is(controller, null, "page should be not controlled with session storage");
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++   Services.perms.remove(Services.io.newURI(PAGE_URI), "cookie");
++ });
++ 
++ // Test to verify an about:blank iframe successfully inherits the
++ // parent's controller when storage is blocked between opening the
++ // parent page and creating the iframe.
++ add_task(async function test_block_storage_before_blank_iframe() {
++   Services.perms.remove(Services.io.newURI(PAGE_URI), "cookie");
++@@ -129,17 +129,17 @@ add_task(async function test_block_stora
++     content.document.body.appendChild(f);
++     await new Promise(resolve => f.onload = resolve);
++     return !!f.contentWindow.navigator.serviceWorker.controller;
++   });
++ 
++   ok(!!controller3, "page should be controlled with storage allowed");
++ 
++   await SpecialPowers.popPrefEnv();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ // Test to verify a blob URL iframe successfully inherits the
++ // parent's controller when storage is blocked between opening the
++ // parent page and creating the iframe.
++ add_task(async function test_block_storage_before_blob_iframe() {
++   Services.perms.remove(Services.io.newURI(PAGE_URI), "cookie");
++ 
++@@ -177,17 +177,17 @@ add_task(async function test_block_stora
++     content.document.body.appendChild(f);
++     await new Promise(resolve => f.onload = resolve);
++     return !!f.contentWindow.navigator.serviceWorker.controller;
++   });
++ 
++   ok(!!controller3, "page should be controlled with storage allowed");
++ 
++   await SpecialPowers.popPrefEnv();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ // Test to verify a blob worker script does not hit our service
++ // worker storage assertions when storage is blocked between opening
++ // the parent page and creating the worker.  Note, we cannot
++ // explicitly check if the worker is controlled since we don't expose
++ // WorkerNavigator.serviceWorkers.controller yet.
++ add_task(async function test_block_storage_before_blob_worker() {
++@@ -229,17 +229,17 @@ add_task(async function test_block_stora
++     return await new Promise(resolve => {
++       w.onmessage = e => resolve(e.data);
++     });
++   });
++ 
++   ok(scriptURL2.startsWith("blob:"), "blob URL worker should run");
++ 
++   await SpecialPowers.popPrefEnv();
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function cleanup() {
++   Services.perms.remove(Services.io.newURI(PAGE_URI), "cookie");
++ 
++   let tab = BrowserTestUtils.addTab(gBrowser, PAGE_URI);
++   let browser = gBrowser.getBrowserForTab(tab);
++   await BrowserTestUtils.browserLoaded(browser);
++@@ -257,10 +257,10 @@ add_task(async function cleanup() {
++         if (worker.state === "redundant") {
++           worker.removeEventListener("statechange", onStateChange);
++           resolve();
++         }
++       });
++     });
++   });
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/dom/workers/test/serviceworkers/browser_unregister_with_containers.js.1442465-4_2.later b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/dom/workers/test/serviceworkers/browser_unregister_with_containers.js.1442465-4_2.later
+@@ -0,0 +1,62 @@
++--- browser_unregister_with_containers.js
+++++ browser_unregister_with_containers.js
++@@ -84,53 +84,53 @@ add_task(async function test() {
++   await doRegister(containerBrowser1);
++   await doRegister(containerBrowser2);
++ 
++   await checkUncontrolled(containerBrowser1);
++   await checkUncontrolled(containerBrowser2);
++ 
++   // Close the tabs we used to register the service workers.  These are not
++   // controlled.
++-  await BrowserTestUtils.removeTab(containerTab1);
++-  await BrowserTestUtils.removeTab(containerTab2);
+++  BrowserTestUtils.removeTab(containerTab1);
+++  BrowserTestUtils.removeTab(containerTab2);
++ 
++   // Open a controlled tab in each container.
++   containerTab1 = BrowserTestUtils.addTab(gBrowser, SCOPE, { userContextId: 1 });
++   containerBrowser1 = gBrowser.getBrowserForTab(containerTab1);
++   await BrowserTestUtils.browserLoaded(containerBrowser1);
++ 
++   containerTab2 = BrowserTestUtils.addTab(gBrowser, SCOPE, { userContextId: 2 });
++   containerBrowser2 = gBrowser.getBrowserForTab(containerTab2);
++   await BrowserTestUtils.browserLoaded(containerBrowser2);
++ 
++   await checkControlled(containerBrowser1);
++   await checkControlled(containerBrowser2);
++ 
++   // Remove the first container's controlled tab
++-  await BrowserTestUtils.removeTab(containerTab1);
+++  BrowserTestUtils.removeTab(containerTab1);
++ 
++   // Create a new uncontrolled tab for the first container and use it to
++   // unregister the service worker.
++   containerTab1 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 1 });
++   containerBrowser1 = gBrowser.getBrowserForTab(containerTab1);
++   await BrowserTestUtils.browserLoaded(containerBrowser1);
++   await doUnregister(containerBrowser1);
++ 
++   await checkUncontrolled(containerBrowser1);
++   await checkControlled(containerBrowser2);
++ 
++   // Remove the second container's controlled tab
++-  await BrowserTestUtils.removeTab(containerTab2);
+++  BrowserTestUtils.removeTab(containerTab2);
++ 
++   // Create a new uncontrolled tab for the second container and use it to
++   // unregister the service worker.
++   containerTab2 = BrowserTestUtils.addTab(gBrowser, PAGE_URI, { userContextId: 2 });
++   containerBrowser2 = gBrowser.getBrowserForTab(containerTab2);
++   await BrowserTestUtils.browserLoaded(containerBrowser2);
++   await doUnregister(containerBrowser2);
++ 
++   await checkUncontrolled(containerBrowser1);
++   await checkUncontrolled(containerBrowser2);
++ 
++   // Close the two tabs we used to unregister the service worker.
++-  await BrowserTestUtils.removeTab(containerTab1);
++-  await BrowserTestUtils.removeTab(containerTab2);
+++  BrowserTestUtils.removeTab(containerTab1);
+++  BrowserTestUtils.removeTab(containerTab2);
++ });
+diff --git a/dom/workers/test/serviceworkers/browser_userContextId_openWindow.js b/dom/workers/test/serviceworkers/browser_userContextId_openWindow.js
+--- a/dom/workers/test/serviceworkers/browser_userContextId_openWindow.js
++++ b/dom/workers/test/serviceworkers/browser_userContextId_openWindow.js
+@@ -121,11 +121,11 @@ add_task(async function test() {
+     })
+     .then(() => {
+       return uci;
+     });
+   });
+ 
+   is(uci, USER_CONTEXT_ID, "Tab runs with UCI " + USER_CONTEXT_ID);
+ 
+-  await BrowserTestUtils.removeTab(newTab);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/dom/workers/test/serviceworkers/isolated/multi-e10s-update/browser_multie10s_update.js b/dom/workers/test/serviceworkers/isolated/multi-e10s-update/browser_multie10s_update.js
+--- a/dom/workers/test/serviceworkers/isolated/multi-e10s-update/browser_multie10s_update.js
++++ b/dom/workers/test/serviceworkers/isolated/multi-e10s-update/browser_multie10s_update.js
+@@ -123,11 +123,11 @@ add_task(async function test_update() {
+     // accesss content without using wrappedJSObject.
+     await content.registration.unregister();
+     const { count } =
+       await content.fetch(url + "?get-and-clear-count").then(r => r.json());
+     return count;
+   });
+   is(count, 2, "SW should have been fetched only twice");
+ 
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/dom/xhr/tests/browser_xhr_onchange_leak.js b/dom/xhr/tests/browser_xhr_onchange_leak.js
+--- a/dom/xhr/tests/browser_xhr_onchange_leak.js
++++ b/dom/xhr/tests/browser_xhr_onchange_leak.js
+@@ -14,10 +14,10 @@ add_task(async function test() {
+   let done = await ContentTask.spawn(browser,{}, async function(browser){
+     let doc = content.document;
+     let promise = ContentTaskUtils.waitForEvent(this, "DOMContentLoaded", true);
+     content.location = "about:home";
+     await promise;
+     return true;
+   });
+   is(done, true, "need to check something");
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+ });
+diff --git a/extensions/cookie/test/browser_permmgr_viewsrc.js b/extensions/cookie/test/browser_permmgr_viewsrc.js
+--- a/extensions/cookie/test/browser_permmgr_viewsrc.js
++++ b/extensions/cookie/test/browser_permmgr_viewsrc.js
+@@ -10,10 +10,10 @@ add_task(async function() {
+                                                         /* waitForLoad */ true,
+                                                         /* waitForStateStop */ false,
+                                                         /* forceNewProcess */ true);
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+     is(Services.perms.testPermission(Services.io.newURI("http://example.com"),
+                                      "viewsourceTestingPerm"),
+        Services.perms.ALLOW_ACTION);
+   });
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/extensions/cookie/test/browser_test_favicon.js b/extensions/cookie/test/browser_test_favicon.js
+--- a/extensions/cookie/test/browser_test_favicon.js
++++ b/extensions/cookie/test/browser_test_favicon.js
+@@ -9,14 +9,14 @@ add_task(async function() {
+   let promise = TestUtils.topicObserved("cookie-rejected", subject => {
+     let uri = subject.QueryInterface(Ci.nsIURI);
+     return uri.spec == iconUrl;
+   });
+ 
+   // Kick off a page load that will load the favicon.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+   registerCleanupFunction(async function() {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   });
+ 
+   await promise;
+   ok(true, "foreign favicon cookie was blocked");
+ });
+diff --git a/js/xpconnect/tests/browser/browser_dead_object.js b/js/xpconnect/tests/browser/browser_dead_object.js
+--- a/js/xpconnect/tests/browser/browser_dead_object.js
++++ b/js/xpconnect/tests/browser/browser_dead_object.js
+@@ -17,10 +17,10 @@ add_task(async function test() {
+       let id = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
+       return id == args.innerWindowId;
+     });
+     content.location = "about:home";
+     await promise;
+     return Cu.isDeadWrapper(doc);
+   });
+   is(contentDocDead, true, "wrapper is dead");
+-  await BrowserTestUtils.removeTab(newTab); 
++  BrowserTestUtils.removeTab(newTab); 
+ });
+diff --git a/layout/base/tests/browser_bug617076.js b/layout/base/tests/browser_bug617076.js
+--- a/layout/base/tests/browser_bug617076.js
++++ b/layout/base/tests/browser_bug617076.js
+@@ -35,12 +35,12 @@ add_task(async function test() {
+     await BrowserTestUtils.synthesizeMouse("#test-button", 1, 1, { type: "mouseover" }, browser);
+     await BrowserTestUtils.synthesizeMouse("#test-button", 2, 6, { type: "mousemove" }, browser);
+     await BrowserTestUtils.synthesizeMouse("#test-button", 2, 4, { type: "mousemove" }, browser);
+   } finally {
+     EventUtils.disableNonTestMouseEvents(false);
+   }
+ 
+   // cleanup
+-  await BrowserTestUtils.removeTab(testTab);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(testTab);
++  BrowserTestUtils.removeTab(tab2);
+   ok(true, "pass if no assertions");
+ });
+diff --git a/netwerk/test/browser/browser_about_cache.js b/netwerk/test/browser/browser_about_cache.js
+--- a/netwerk/test/browser/browser_about_cache.js
++++ b/netwerk/test/browser/browser_about_cache.js
+@@ -4,17 +4,17 @@
+  * Open a dummy page, then open about:cache and verify the opened page shows up in the cache.
+  */
+ add_task(async function() {
+   const kRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/",
+                                                     "https://example.com/");
+   const kTestPage = kRoot + "dummy.html";
+   // Open the dummy page to get it cached.
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage, true);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:cache", true);
+   let expectedPageCheck = function(uri) {
+     info("Saw load for " + uri);
+     // Can't easily use searchParms and new URL() because it's an about: URI...
+     return uri.startsWith("about:cache?") && uri.includes("storage=disk");
+   };
+   let diskPageLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser, false, expectedPageCheck);
+@@ -62,10 +62,10 @@ add_task(async function() {
+     is(principalURI && principalURI.spec, content.document.location.href, "Principal matches location");
+     let channel = content.document.docShell.currentDocumentChannel;
+     principalURI = channel.loadInfo.triggeringPrincipal.URI;
+     is(principalURI && principalURI.spec, triggeringURISpec, "Triggering principal matches previous location");
+     ok(!channel.loadInfo.loadingPrincipal, "Loading principal should be null.");
+     ok(content.document.querySelectorAll("th").length,
+        "Should have several table headers with data.");
+   });
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/parser/htmlparser/tests/mochitest/browser_viewsource.js b/parser/htmlparser/tests/mochitest/browser_viewsource.js
+--- a/parser/htmlparser/tests/mochitest/browser_viewsource.js
++++ b/parser/htmlparser/tests/mochitest/browser_viewsource.js
+@@ -12,11 +12,11 @@ add_task(async function() {
+   });
+ 
+   let viewSourceContentPromise = ContentTask.spawn(viewSourceTab.linkedBrowser, null, async function() {
+     return content.document.body.textContent;
+   });
+ 
+   let results = await Promise.all([viewSourceContentPromise, xhrPromise]);
+   is(results[0], results[1], "Sources should match");
+-  await BrowserTestUtils.removeTab(viewSourceTab);
++  BrowserTestUtils.removeTab(viewSourceTab);
+ });
+ 
+diff --git a/services/fxaccounts/tests/browser/browser_device_connected.js b/services/fxaccounts/tests/browser/browser_device_connected.js
+--- a/services/fxaccounts/tests/browser/browser_device_connected.js
++++ b/services/fxaccounts/tests/browser/browser_device_connected.js
+@@ -31,17 +31,17 @@ async function testDeviceConnected(devic
+ 
+   Services.obs.notifyObservers(null, "fxaccounts:device_connected", deviceName);
+ 
+   let tab = await waitForTabPromise;
+   Assert.ok("Tab successfully opened");
+ 
+   Assert.equal(tab.linkedBrowser.currentURI.spec, DEVICES_URL);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function() {
+   expectedBody = accountsBundle.formatStringFromName("deviceConnectedBody", ["My phone"], 1);
+   await testDeviceConnected("My phone");
+ });
+ 
+ add_task(async function() {
+diff --git a/services/fxaccounts/tests/browser/browser_verify_login.js b/services/fxaccounts/tests/browser/browser_verify_login.js
+--- a/services/fxaccounts/tests/browser/browser_verify_login.js
++++ b/services/fxaccounts/tests/browser/browser_verify_login.js
+@@ -19,10 +19,10 @@ add_task(async function() {
+ 
+   let waitForTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+ 
+   Services.obs.notifyObservers(null, "fxaccounts:verify_login", JSON.stringify(payload.data));
+ 
+   let tab = await waitForTabPromise;
+   Assert.ok("Tab successfully opened");
+   Assert.equal(tab.linkedBrowser.currentURI.spec, payload.data.url);
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/testing/mochitest/tests/browser/browser_BrowserTestUtils.js b/testing/mochitest/tests/browser/browser_BrowserTestUtils.js
+--- a/testing/mochitest/tests/browser/browser_BrowserTestUtils.js
++++ b/testing/mochitest/tests/browser/browser_BrowserTestUtils.js
+@@ -50,17 +50,17 @@ add_task(async function() {
+ 
+ add_task(async function() {
+   await BrowserTestUtils.registerAboutPage(
+     registerCleanupFunction, "about-pages-are-cool",
+     getRootDirectory(gTestPath) + "dummy.html", 0);
+   let tab = await BrowserTestUtils.openNewForegroundTab(
+     gBrowser, "about:about-pages-are-cool", true);
+   ok(tab, "Successfully created an about: page and loaded it.");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+   try {
+     await BrowserTestUtils.unregisterAboutPage("about-pages-are-cool");
+     ok(true, "Successfully unregistered the about page.");
+   } catch (ex) {
+     ok(false, "Should not throw unregistering a known about: page");
+   }
+   await BrowserTestUtils.unregisterAboutPage("random-other-about-page").then(() => {
+     ok(false, "Should not have succeeded unregistering an unknown about: page.");
+diff --git a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
+--- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
++++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
+@@ -282,27 +282,27 @@ add_task(async function test_close_tab()
+       } while (true);
+ 
+       if (mode == "close") {
+         info(`Waiting for close`);
+         await promiseClosed;
+       } else {
+         info(`Waiting for reload`);
+         await promiseReloaded;
+-        await BrowserTestUtils.removeTab(tab);
++        BrowserTestUtils.removeTab(tab);
+       }
+     }
+   }
+ });
+ 
+ add_task(async function cleanup() {
+   // Cleanup
+   info("Cleaning up");
+   await promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null);
+ 
+   info("Closing tabs");
+   for (let tab of gBrowser.tabs) {
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ 
+   info("Done");
+   gBrowser.selectedTab = null;
+ });
+diff --git a/toolkit/components/passwordmgr/test/browser/browser_autocomplete_insecure_warning.js b/toolkit/components/passwordmgr/test/browser/browser_autocomplete_insecure_warning.js
+--- a/toolkit/components/passwordmgr/test/browser/browser_autocomplete_insecure_warning.js
++++ b/toolkit/components/passwordmgr/test/browser/browser_autocomplete_insecure_warning.js
+@@ -31,11 +31,11 @@ add_task(async function test_clickInsecu
+ 
+     await BrowserTestUtils.waitForCondition(() => !warningItem.collapsed, "Wait for warning to show");
+ 
+     info("Clicking on warning");
+     let supportTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, EXPECTED_SUPPORT_URL);
+     EventUtils.synthesizeMouseAtCenter(warningItem, {});
+     let supportTab = await supportTabPromise;
+     ok(supportTab, "Support tab opened");
+-    await BrowserTestUtils.removeTab(supportTab);
++    BrowserTestUtils.removeTab(supportTab);
+   });
+ });
+diff --git a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
+--- a/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
++++ b/toolkit/components/passwordmgr/test/browser/browser_capture_doorhanger_window_open.js
+@@ -97,17 +97,17 @@ add_task(async function test_saveChromeV
+   let url = "subtst_notifications_11.html?notifyu2|notifyp2||";
+   let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+   await withTestTabUntilStorageChange(url, async function() {
+     await notifShownPromise;
+     let popup = getCaptureDoorhanger("password-save");
+     ok(popup, "got notification popup");
+     await checkDoorhangerUsernamePassword("notifyu2", "notifyp2");
+     clickDoorhangerButton(popup, REMEMBER_BUTTON);
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   });
+ 
+   // Check result of clicking Remember
+   let logins = Services.logins.getAllLogins();
+   is(logins.length, 1, "Should only have 1 login now");
+   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
+   is(login.username, "notifyu2", "Check the username used on the new entry");
+   is(login.password, "notifyp2", "Check the password used on the new entry");
+@@ -118,17 +118,17 @@ add_task(async function test_changeChrom
+   let url = "subtst_notifications_11.html?notifyu2|pass2||";
+   let notifShownPromise = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown");
+   await withTestTabUntilStorageChange(url, async function() {
+     await notifShownPromise;
+     let popup = getCaptureDoorhanger("password-change");
+     ok(popup, "got notification popup");
+     await checkDoorhangerUsernamePassword("notifyu2", "pass2");
+     clickDoorhangerButton(popup, CHANGE_BUTTON);
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   });
+ 
+   // Check to make sure we updated the password, timestamps and use count for
+   // the login being changed with this form.
+   let logins = Services.logins.getAllLogins();
+   is(logins.length, 1, "Should have 1 login");
+   let login = logins[0].QueryInterface(Ci.nsILoginMetaInfo);
+   is(login.username, "notifyu2", "Check the username");
+diff --git a/toolkit/components/passwordmgr/test/browser/browser_hasInsecureLoginForms_streamConverter.js b/toolkit/components/passwordmgr/test/browser/browser_hasInsecureLoginForms_streamConverter.js
+--- a/toolkit/components/passwordmgr/test/browser/browser_hasInsecureLoginForms_streamConverter.js
++++ b/toolkit/components/passwordmgr/test/browser/browser_hasInsecureLoginForms_streamConverter.js
+@@ -89,14 +89,14 @@ add_task(async function test_streamConve
+     BrowserTestUtils.switchTab(gBrowser, tab),
+     BrowserTestUtils.browserLoaded(browser),
+     // One event is triggered by pageshow and one by DOMFormHasPassword.
+     waitForInsecureLoginFormsStateChange(browser, 2),
+   ]);
+ 
+   Assert.ok(!LoginManagerParent.hasInsecureLoginForms(browser));
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   await ContentTask.spawn(originalBrowser, null, async function() {
+     this.cleanupFunction();
+   });
+ });
+diff --git a/toolkit/components/places/tests/browser/browser_bug399606.js b/toolkit/components/places/tests/browser/browser_bug399606.js
+--- a/toolkit/components/places/tests/browser/browser_bug399606.js
++++ b/toolkit/components/places/tests/browser/browser_bug399606.js
+@@ -39,17 +39,17 @@ add_task(async function() {
+     historyObserver.expectedURI = Services.io.newURI(uri);
+     let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+     PlacesUtils.history.addObserver(historyObserver);
+     gBrowser.loadURI(uri);
+     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
+     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
+     await BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false, uri);
+     PlacesUtils.history.removeObserver(historyObserver);
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ 
+   for (let uri of URIS) {
+     await promiseLoadedThreeTimes(uri);
+     is(historyObserver.count, 1,
+       "onVisit has been received right number of times for " + uri);
+   }
+ });
+diff --git a/toolkit/components/places/tests/browser/browser_bug680727.js b/toolkit/components/places/tests/browser/browser_bug680727.js
+--- a/toolkit/components/places/tests/browser/browser_bug680727.js
++++ b/toolkit/components/places/tests/browser/browser_bug680727.js
+@@ -98,10 +98,10 @@ function reloadListener() {
+ function reloadAsyncListener(aURI, aIsVisited) {
+   ok(kUniqueURI.equals(aURI) && aIsVisited, "We have visited the URI.");
+   PlacesTestUtils.clearHistory().then(finish);
+ }
+ 
+ registerCleanupFunction(async function() {
+   Services.prefs.setIntPref("network.proxy.type", proxyPrefValue);
+   Services.io.offline = false;
+-  await BrowserTestUtils.removeTab(ourTab);
++  BrowserTestUtils.removeTab(ourTab);
+ });
+diff --git a/toolkit/components/places/tests/browser/browser_multi_redirect_frecency.js.1442465-4_2.later b/toolkit/components/places/tests/browser/browser_multi_redirect_frecency.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/places/tests/browser/browser_multi_redirect_frecency.js.1442465-4_2.later
+@@ -0,0 +1,52 @@
++--- browser_multi_redirect_frecency.js
+++++ browser_multi_redirect_frecency.js
++@@ -57,17 +57,17 @@ add_task(async function test_multiple_re
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_1, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_2, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function redirect_check_second_typed_visit() {
++   // A second visit with a typed url.
++   PlacesUtils.history.markPageAsTyped(REDIRECT_URI);
++ 
++   redirectSourceFrecency += REDIRECT_SOURCE_VISIT_BONUS;
++   // TODO Bug 487813 - This should be TYPED_VISIT_BONUS, however as we don't
++@@ -91,17 +91,17 @@ add_task(async function redirect_check_s
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_1, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_2, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ 
++ add_task(async function redirect_check_subsequent_link_visit() {
++   // Another visit, but this time as a visited url.
++   redirectSourceFrecency += REDIRECT_SOURCE_VISIT_BONUS;
++   // TODO Bug 487813 - This should be TYPED_VISIT_BONUS, however as we don't
++   // currently track redirects across multiple redirects, we fallback to the
++@@ -124,10 +124,10 @@ add_task(async function redirect_check_s
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_1, redirectSourceFrecency, 1);
++   await check_uri(INTERMEDIATE_URI_2, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/toolkit/components/places/tests/browser/browser_onvisit_title_null_for_navigation.js b/toolkit/components/places/tests/browser/browser_onvisit_title_null_for_navigation.js
+--- a/toolkit/components/places/tests/browser/browser_onvisit_title_null_for_navigation.js
++++ b/toolkit/components/places/tests/browser/browser_onvisit_title_null_for_navigation.js
+@@ -19,10 +19,10 @@ add_task(async function checkTitleNotifi
+           resolve();
+         }
+       },
+     };
+     PlacesUtils.history.addObserver(obs);
+   });
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, EXPECTED_URL.spec);
+   await promiseTitleChanged;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/components/places/tests/browser/browser_redirect.js.1442465-4_2.later b/toolkit/components/places/tests/browser/browser_redirect.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/toolkit/components/places/tests/browser/browser_redirect.js.1442465-4_2.later
+@@ -0,0 +1,52 @@
++--- browser_redirect.js
+++++ browser_redirect.js
++@@ -52,17 +52,17 @@ add_task(async function redirect_check_n
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
++   info("Waiting for onVisits");
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function redirect_check_second_typed_visit() {
++   // A second visit with a typed url.
++   PlacesUtils.history.markPageAsTyped(REDIRECT_URI);
++ 
++   redirectSourceFrecency += REDIRECT_SOURCE_VISIT_BONUS;
++   redirectTargetFrecency += TYPED_VISIT_BONUS;
++@@ -81,17 +81,17 @@ add_task(async function redirect_check_s
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
++   info("Waiting for onVisits");
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
++ 
++ add_task(async function redirect_check_subsequent_link_visit() {
++   // Another visit, but this time as a visited url.
++   redirectSourceFrecency += REDIRECT_SOURCE_VISIT_BONUS;
++   redirectTargetFrecency += LINK_VISIT_BONUS;
++   let redirectNotified = false;
++ 
++@@ -108,10 +108,10 @@ add_task(async function redirect_check_s
++   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, REDIRECT_URI.spec);
++   info("Waiting for onVisits");
++   await visitedPromise;
++   ok(redirectNotified, "The redirect should have been notified");
++ 
++   await check_uri(REDIRECT_URI, redirectSourceFrecency, 1);
++   await check_uri(TARGET_URI, redirectTargetFrecency, 0);
++ 
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ });
+diff --git a/toolkit/components/places/tests/browser/browser_visited_notfound.js b/toolkit/components/places/tests/browser/browser_visited_notfound.js
+--- a/toolkit/components/places/tests/browser/browser_visited_notfound.js
++++ b/toolkit/components/places/tests/browser/browser_visited_notfound.js
+@@ -1,17 +1,17 @@
+ add_task(async function test() {
+   const TEST_URL = "http://mochi.test:8888/notFoundPage.html";
+   // Ensure that decay frecency doesn't kick in during tests (as a result
+   // of idle-daily).
+   Services.prefs.setCharPref("places.frecency.decayRate", "1.0");
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
+   registerCleanupFunction(async function() {
+     Services.prefs.clearUserPref("places.frecency.decayRate");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+     await PlacesTestUtils.clearHistory();
+   });
+ 
+   // First add a visit to the page, this will ensure that later we skip
+   // updating the frecency for a newly not-found page.
+   await PlacesTestUtils.addVisits({ uri: TEST_URL });
+   let frecency = await PlacesTestUtils.fieldInDB(TEST_URL, "frecency");
+   is(frecency, 100, "Check initial frecency");
+diff --git a/toolkit/components/printing/tests/browser_page_change_print_original.js b/toolkit/components/printing/tests/browser_page_change_print_original.js
+--- a/toolkit/components/printing/tests/browser_page_change_print_original.js
++++ b/toolkit/components/printing/tests/browser_page_change_print_original.js
+@@ -54,10 +54,10 @@ add_task(async function pp_after_orienta
+ 
+   // Check that the other tab is definitely showing the new page:
+   await ContentTask.spawn(browserToPrint, null, async function() {
+     is(content.document.body.textContent.trim(), "REPLACED PAGE!", "Original page should have changed.");
+   });
+ 
+   PrintUtils.exitPrintPreview();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/components/printing/tests/browser_preview_switch_print_selected.js b/toolkit/components/printing/tests/browser_preview_switch_print_selected.js
+--- a/toolkit/components/printing/tests/browser_preview_switch_print_selected.js
++++ b/toolkit/components/printing/tests/browser_preview_switch_print_selected.js
+@@ -95,10 +95,10 @@ add_task(async function switch_print_pre
+   // Assert that we are selecting default print preview browser, and not simplified one
+   is(gBrowser.selectedTab.linkedBrowser, defaultPPBrowser,
+      "Should have default print preview browser selected");
+   isnot(gBrowser.selectedTab.linkedBrowser, simplifiedPPBrowser,
+         "Should not have simplified print preview browser selected");
+ 
+   PrintUtils.exitPrintPreview();
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/components/tooltiptext/tests/browser_input_file_tooltips.js b/toolkit/components/tooltiptext/tests/browser_input_file_tooltips.js
+--- a/toolkit/components/tooltiptext/tests/browser_input_file_tooltips.js
++++ b/toolkit/components/tooltiptext/tests/browser_input_file_tooltips.js
+@@ -97,17 +97,17 @@ async function do_test(test) {
+   info("Second mouse move");
+   await EventUtils.synthesizeAndWaitNativeMouseMove(tab.linkedBrowser, 70, 5);
+   info("Waiting for tooltip to open");
+   let tooltip = await awaitTooltipOpen;
+ 
+   is(tooltip.getAttribute("label"), test.result, "tooltip label should match expectation");
+ 
+   info("Closing tab");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ function createTempFile() {
+   let file = FileUtils.getDir("TmpD", [], false);
+   file.append("testfile_bug1251809");
+   file.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0o644);
+   return file;
+ }
+diff --git a/toolkit/components/viewsource/test/browser/browser_open_docgroup.js b/toolkit/components/viewsource/test/browser/browser_open_docgroup.js
+--- a/toolkit/components/viewsource/test/browser/browser_open_docgroup.js
++++ b/toolkit/components/viewsource/test/browser/browser_open_docgroup.js
+@@ -32,17 +32,17 @@ add_task(async function test_view_source
+     let sourceBrowser = sourceTab.linkedBrowser;
+     await waitForSourceLoaded(sourceBrowser);
+ 
+     await ContentTask.spawn(sourceBrowser, null, async function() {
+       Assert.equal(content.document.body.id, "viewsource",
+                    "View source mode enabled");
+     });
+ 
+-    await BrowserTestUtils.removeTab(sourceTab);
++    BrowserTestUtils.removeTab(sourceTab);
+   });
+ 
+   await SpecialPowers.popPrefEnv();
+ });
+ 
+ /**
+  * Tests that we can open View Source in a window.
+  */
+diff --git a/toolkit/content/tests/browser/browser_audioCompeting.js b/toolkit/content/tests/browser/browser_audioCompeting.js
+--- a/toolkit/content/tests/browser/browser_audioCompeting.js
++++ b/toolkit/content/tests/browser/browser_audioCompeting.js
+@@ -73,28 +73,28 @@ add_task(async function cross_tabs_audio
+   await ContentTask.spawn(tab2.linkedBrowser, null,
+                           audio_should_keep_playing_even_go_to_background);
+ 
+   info("- play audio from background tab 1 -");
+   await ContentTask.spawn(tab1.linkedBrowser, null,
+                           play_audio_from_invisible_tab);
+ 
+   info("- remove tabs -");
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab3);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab3);
+ });
+ 
+ add_task(async function within_one_tab_audio_competing() {
+   info("- open tab and play audio1 -");
+   let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+                                                         "about:blank");
+   tab.linkedBrowser.loadURI(PAGE);
+   await waitForTabPlayingEvent(tab, true);
+ 
+   info("- play audio2 in the same tab -");
+   await ContentTask.spawn(tab.linkedBrowser, null,
+                           play_non_autoplay_audio);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+diff --git a/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js b/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
+--- a/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
++++ b/toolkit/content/tests/browser/browser_autoplay_policy_iframe_hierarchy.js
+@@ -139,17 +139,17 @@ async function test_permission_propagati
+         }
+       }
+     }
+     await ContentTask.spawn(tab.linkedBrowser,
+                             [layerIdx, testName, layersNum],
+                             playing_video_may_success);
+ 
+     info("- remove tab -");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+   }
+ }
+ 
+ add_task(async function start_test() {
+   info("- setup test preference -");
+   await setup_test_preference();
+   requestLongerTimeout(2);
+ 
+diff --git a/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js b/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
+--- a/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
++++ b/toolkit/content/tests/browser/browser_autoplay_policy_play_twice.js
+@@ -31,17 +31,17 @@ async function allow_play_for_played_vid
+   try {
+     await video.play();
+     ok(true, "success to resolve play promise");
+   } catch (e) {
+     ok(false, "promise should not be rejected");
+   }
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ }
+ 
+ add_task(async function start_test() {
+   await setup_test_preference(true);
+   await allow_play_for_played_video();
+ 
+   await setup_test_preference(false);
+   await allow_play_for_played_video();
+diff --git a/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js.1442465-4_2.later b/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/toolkit/content/tests/browser/browser_autoplay_policy_user_gestures.js.1442465-4_2.later
+@@ -0,0 +1,40 @@
++--- browser_autoplay_policy_user_gestures.js
+++++ browser_autoplay_policy_user_gestures.js
++@@ -66,17 +66,17 @@ async function test_play_without_user_ge
++     info("- call play() without user activation -");
++     await video.play().catch(function() {
++       ok(video.paused, "video can't start play without user input.");
++     });
++   }
++   await ContentTask.spawn(tab.linkedBrowser, null, play_video);
++ 
++   info("- remove tab -");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ async function test_play_with_user_gesture(gesture) {
++   info("- open new tab -");
++   let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
++                                                         "about:blank");
++   tab.linkedBrowser.loadURI(VIDEO_PAGE);
++   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
++@@ -94,17 +94,17 @@ async function test_play_with_user_gestu
++     } catch (e) {
++       ok(!gesture.isActivationGesture, "user gesture can not activate the page");
++       ok(video.paused, "video can not start playing.");
++     }
++   }
++   await ContentTask.spawn(tab.linkedBrowser, gesture, play_video);
++ 
++   info("- remove tab -");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ }
++ 
++ add_task(async function start_test() {
++   info("- setup test preference -");
++   await setup_test_preference();
++ 
++   info("- test play when page doesn't be activated -");
++   await test_play_without_user_gesture();
+diff --git a/toolkit/content/tests/browser/browser_block_autoplay_media.js b/toolkit/content/tests/browser/browser_block_autoplay_media.js
+--- a/toolkit/content/tests/browser/browser_block_autoplay_media.js
++++ b/toolkit/content/tests/browser/browser_block_autoplay_media.js
+@@ -60,12 +60,12 @@ add_task(async function block_autoplay_m
+                           check_audio_suspended);
+ 
+   info("- should still block media from tab2 -");
+   await waitForTabPlayingEvent(tab2, false);
+   await ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
+                           check_audio_suspended);
+ 
+   info("- remove tabs -");
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
+-  await BrowserTestUtils.removeTab(tab3);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab3);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js b/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
+--- a/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
++++ b/toolkit/content/tests/browser/browser_block_autoplay_media_pausedAfterPlay.js
+@@ -73,11 +73,11 @@ add_task(async function block_autoplay_m
+   await ContentTask.spawn(tab2.linkedBrowser, false,
+                           check_audio_pause_state);
+ 
+   info("- check tab2's media suspend type -");
+   await ContentTask.spawn(tab2.linkedBrowser, SuspendedType.NONE_SUSPENDED,
+                           check_audio_suspended);
+ 
+   info("- remove tabs -");
+-  await BrowserTestUtils.removeTab(tab1);
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab1);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js b/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
+--- a/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
++++ b/toolkit/content/tests/browser/browser_block_autoplay_playAfterTabVisible.js
+@@ -75,10 +75,10 @@ add_task(async function media_should_be_
+ 
+   info("- audio should be playing -");
+   await ContentTask.spawn(tab.linkedBrowser, false,
+                           check_audio_pause_state);
+   await ContentTask.spawn(tab.linkedBrowser, SuspendedType.NONE_SUSPENDED,
+                           check_audio_suspended);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_multipleMedia.js b/toolkit/content/tests/browser/browser_block_multipleMedia.js
+--- a/toolkit/content/tests/browser/browser_block_multipleMedia.js
++++ b/toolkit/content/tests/browser/browser_block_multipleMedia.js
+@@ -112,10 +112,10 @@ add_task(async function block_multiple_m
+   await BrowserTestUtils.switchTab(window.gBrowser, tab);
+ 
+   info("- tab should be resumed -");
+   await waitForTabPlayingEvent(tab, true);
+   await ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
+                                    check_all_audio_suspended);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_notInTreeAudio.js b/toolkit/content/tests/browser/browser_block_notInTreeAudio.js
+--- a/toolkit/content/tests/browser/browser_block_notInTreeAudio.js
++++ b/toolkit/content/tests/browser/browser_block_notInTreeAudio.js
+@@ -79,10 +79,10 @@ add_task(async function block_not_in_tre
+ 
+   info("- tab should be resumed -");
+   await waitForTabBlockEvent(tab, false);
+ 
+   info("- tab should be audible -");
+   await waitForTabPlayingEvent(tab, true);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js b/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
+--- a/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
++++ b/toolkit/content/tests/browser/browser_block_playMediaInMuteTab.js
+@@ -105,10 +105,10 @@ add_task(async function unblock_icon_sho
+                           check_audio_suspended);
+   await ContentTask.spawn(tab.linkedBrowser, true /* mute */,
+                           check_audio_volume_and_mute);
+ 
+   info("- tab should not display unblocking icon -");
+   await waitForTabBlockEvent(tab, false);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js b/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
+--- a/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
++++ b/toolkit/content/tests/browser/browser_block_silentAudioTrack_media.js
+@@ -54,17 +54,17 @@ add_task(async function unblock_icon_sho
+ 
+   info("- should not display unblocking icon -");
+   await waitForTabBlockEvent(tab, false);
+ 
+   info("- should not display sound indicator icon -");
+   await waitForTabPlayingEvent(tab, false);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function should_not_show_sound_indicator_after_resume_tab() {
+   info("- open new background tab -");
+   let tab = window.gBrowser.addTab("about:blank");
+   tab.linkedBrowser.loadURI(PAGE);
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+ 
+@@ -84,10 +84,10 @@ add_task(async function should_not_show_
+ 
+   info("- should not display unblocking icon -");
+   await waitForTabBlockEvent(tab, false);
+ 
+   info("- should not display sound indicator icon -");
+   await waitForTabPlayingEvent(tab, false);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_block_webAudio.js b/toolkit/content/tests/browser/browser_block_webAudio.js
+--- a/toolkit/content/tests/browser/browser_block_webAudio.js
++++ b/toolkit/content/tests/browser/browser_block_webAudio.js
+@@ -28,10 +28,10 @@ add_task(async function block_web_audio(
+ 
+   info("- tab should be resumed -");
+   await waitForTabBlockEvent(tab, false);
+ 
+   info("- tab should be audible -");
+   await waitForTabPlayingEvent(tab, true);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_bug1198465.js b/toolkit/content/tests/browser/browser_bug1198465.js
+--- a/toolkit/content/tests/browser/browser_bug1198465.js
++++ b/toolkit/content/tests/browser/browser_bug1198465.js
+@@ -66,10 +66,10 @@ add_task(async function() {
+   is(findBar._findField.value, "ab", "ab kept instead of prefill value");
+ 
+   EventUtils.sendChar("c", window);
+   is(findBar._findField.value, "abc", "c is appended after ab");
+ 
+   // Clear the findField value to make the test  run successfully
+   // for multiple runs in the same browser session.
+   findBar._findField.value = "";
+-  await BrowserTestUtils.removeTab(aTab);
++  BrowserTestUtils.removeTab(aTab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_bug451286.js b/toolkit/content/tests/browser/browser_bug451286.js
+--- a/toolkit/content/tests/browser/browser_bug451286.js
++++ b/toolkit/content/tests/browser/browser_bug451286.js
+@@ -96,17 +96,17 @@ add_task(async function() {
+   // Test 1: Were the matches in iframe correctly highlighted?
+   let res = compareSnapshots(findSnapshot, manualSnapshot, true);
+   ok(res[0], "Matches found in iframe correctly highlighted");
+ 
+   // Test 2: Were the matches in iframe correctly unhighlighted?
+   res = compareSnapshots(noHighlightSnapshot, unhighlightSnapshot, true);
+   ok(res[0], "Highlighting in iframe correctly removed");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ function toggleHighlightAndWait(shouldHighlight) {
+   return new Promise((resolve) => {
+     let listener = {
+       onFindResult() {},
+       onHighlightFinished() {
+         gFindBar.browser.finder.removeResultListener(listener);
+diff --git a/toolkit/content/tests/browser/browser_bug594509.js b/toolkit/content/tests/browser/browser_bug594509.js
+--- a/toolkit/content/tests/browser/browser_bug594509.js
++++ b/toolkit/content/tests/browser/browser_bug594509.js
+@@ -1,9 +1,9 @@
+ add_task(async function() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:rights");
+ 
+   await ContentTask.spawn(tab.linkedBrowser, null, async function() {
+     Assert.ok(content.document.getElementById("your-rights"), "about:rights content loaded");
+   });
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_charsetMenu_swapBrowsers.js b/toolkit/content/tests/browser/browser_charsetMenu_swapBrowsers.js
+--- a/toolkit/content/tests/browser/browser_charsetMenu_swapBrowsers.js
++++ b/toolkit/content/tests/browser/browser_charsetMenu_swapBrowsers.js
+@@ -19,10 +19,10 @@ add_task(async function test() {
+   let swapped = BrowserTestUtils.waitForEvent(tab2.linkedBrowser, "SwapDocShells");
+ 
+   // NB: Closes tab1.
+   gBrowser.swapBrowsersAndCloseOther(tab2, tab1);
+   await swapped;
+ 
+   ok(charsetMenuEnabled(), "should have a charset after the swap");
+ 
+-  await BrowserTestUtils.removeTab(tab2);
++  BrowserTestUtils.removeTab(tab2);
+ });
+diff --git a/toolkit/content/tests/browser/browser_f7_caret_browsing.js b/toolkit/content/tests/browser/browser_f7_caret_browsing.js
+--- a/toolkit/content/tests/browser/browser_f7_caret_browsing.js
++++ b/toolkit/content/tests/browser/browser_f7_caret_browsing.js
+@@ -138,17 +138,17 @@ add_task(async function checkTogglingCar
+   await waitForFocusOnInput(tab.linkedBrowser);
+ 
+   ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
+ 
+   Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+   Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+   Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function toggleCheckboxNoCaretBrowsing() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kURL);
+   await focusInput(tab.linkedBrowser);
+ 
+   let promiseGotKey = promiseCaretPromptOpened();
+   hitF7();
+@@ -174,17 +174,17 @@ add_task(async function toggleCheckboxNo
+ 
+   syncToggleCaretNoDialog(false);
+   ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be disabled.");
+ 
+   Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+   Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+   Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ 
+ add_task(async function toggleCheckboxWantCaretBrowsing() {
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kURL);
+   await focusInput(tab.linkedBrowser);
+ 
+   let promiseGotKey = promiseCaretPromptOpened();
+@@ -212,14 +212,14 @@ add_task(async function toggleCheckboxWa
+   syncToggleCaretNoDialog(false);
+   syncToggleCaretNoDialog(true);
+   syncToggleCaretNoDialog(false);
+ 
+   Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
+   Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
+   Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ 
+ 
+ 
+diff --git a/toolkit/content/tests/browser/browser_findbar.js b/toolkit/content/tests/browser/browser_findbar.js
+--- a/toolkit/content/tests/browser/browser_findbar.js
++++ b/toolkit/content/tests/browser/browser_findbar.js
+@@ -149,17 +149,17 @@ add_task(async function test_reinitializ
+   // Findbar should keep operating normally after remoteness change.
+   await promiseFindFinished("z", false);
+   is(findbar._findStatusDesc.textContent, findbar._notFoundStr,
+      "Findbar status text should be 'Phrase not found'");
+ 
+   await promiseFindFinished("s", false);
+   ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ /**
+  * Ensure that the initial typed characters aren't lost immediately after
+  * opening the find bar.
+  */
+ add_task(async function() {
+   // This test only makes sence in e10s evironment.
+@@ -190,17 +190,17 @@ add_task(async function() {
+ 
+   is(findBar._findField.value, initialValue, "still has initial find query");
+ 
+   await Promise.all(promises);
+   is(document.activeElement, findBar._findField.inputField,
+     "findbar is now focused");
+   is(findBar._findField.value, "abc", "abc fully entered as find query");
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ function promiseFindFinished(searchText, highlightOn) {
+   return new Promise(resolve => {
+ 
+     let findbar = gBrowser.getFindBar();
+     findbar.startFind(findbar.FIND_NORMAL);
+     let highlightElement = findbar.getElement("highlight");
+diff --git a/toolkit/content/tests/browser/browser_label_textlink.js b/toolkit/content/tests/browser/browser_label_textlink.js
+--- a/toolkit/content/tests/browser/browser_label_textlink.js
++++ b/toolkit/content/tests/browser/browser_label_textlink.js
+@@ -11,28 +11,28 @@ add_task(async function() {
+       doc.documentElement.append(label);
+     });
+ 
+     // Test that click will open tab in foreground.
+     let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
+     await BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test", {}, browser);
+     let newTab = await awaitNewTab;
+     is(newTab.linkedBrowser, gBrowser.selectedBrowser, "selected tab should be example page");
+-    await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ 
+     // Test that ctrl+shift+click/meta+shift+click will open tab in background.
+     awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
+     await BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test",
+       {ctrlKey: true, metaKey: true, shiftKey: true},
+       browser);
+     await awaitNewTab;
+     is(gBrowser.selectedBrowser, browser, "selected tab should be original tab");
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+ 
+     // Middle-clicking should open tab in foreground.
+     awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
+     await BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test",
+       {button: 1}, browser);
+     newTab = await awaitNewTab;
+     is(newTab.linkedBrowser, gBrowser.selectedBrowser, "selected tab should be example page");
+-    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
++    BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
+   });
+ });
+diff --git a/toolkit/content/tests/browser/browser_mute_webAudio.js b/toolkit/content/tests/browser/browser_mute_webAudio.js
+--- a/toolkit/content/tests/browser/browser_mute_webAudio.js
++++ b/toolkit/content/tests/browser/browser_mute_webAudio.js
+@@ -66,10 +66,10 @@ add_task(async function mute_web_audio()
+   ok(tab.linkedBrowser.audioMuted, "Audio should be muted now");
+   await click_icon(tab);
+   ok(!tab.linkedBrowser.audioMuted, "Audio should be unmuted now");
+ 
+   info("- tab should be audible -");
+   await waitForTabPlayingEvent(tab, true);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js b/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
+--- a/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
++++ b/toolkit/content/tests/browser/browser_resume_bkg_video_on_tab_hover.js
+@@ -135,11 +135,11 @@ add_task(async function resume_and_suspe
+   info("- video's owner tab goes to background again, should suspend video -");
+   promise = get_video_decoding_suspend_promise(browser);
+   let blankTab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+                                                              "about:blank");
+   await promise;
+   await check_should_send_unselected_tab_hover_msg(browser);
+ 
+   info("- remove tabs -");
+-  await BrowserTestUtils.removeTab(tab);
+-  await BrowserTestUtils.removeTab(blankTab);
++  BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(blankTab);
+ });
+diff --git a/toolkit/content/tests/browser/browser_sound_indicator_silent_video.js b/toolkit/content/tests/browser/browser_sound_indicator_silent_video.js
+--- a/toolkit/content/tests/browser/browser_sound_indicator_silent_video.js
++++ b/toolkit/content/tests/browser/browser_sound_indicator_silent_video.js
+@@ -37,17 +37,17 @@ add_task(async function should_not_show_
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   await ContentTask.spawn(tab.linkedBrowser, true /* playing */,
+                           check_audio_playing_state);
+ 
+   info("- tab should not have sound indicator after playing silent video -");
+   await waitForTabPlayingEvent(tab, false);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function should_not_show_sound_indicator_for_almost_silent_video() {
+   info("- open new foreground tab -");
+   let tab = await BrowserTestUtils.openNewForegroundTab(window.gBrowser,
+                                                         "about:blank");
+ 
+   info("- tab should not have sound indicator before playing almost silent video -");
+@@ -58,10 +58,10 @@ add_task(async function should_not_show_
+   await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
+   await ContentTask.spawn(tab.linkedBrowser, true /* playing */,
+                           check_audio_playing_state);
+ 
+   info("- tab should not have sound indicator after playing almost silent video -");
+   await waitForTabPlayingEvent(tab, false);
+ 
+   info("- remove tab -");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+diff --git a/toolkit/content/tests/browser/head.js b/toolkit/content/tests/browser/head.js
+--- a/toolkit/content/tests/browser/head.js
++++ b/toolkit/content/tests/browser/head.js
+@@ -162,17 +162,17 @@ class DateTimeTestHelper {
+     if (!this.panel.hidden) {
+       let pickerClosePromise = new Promise(resolve => {
+         this.panel.addEventListener("popuphidden", resolve, {once: true});
+       });
+       this.panel.hidePopup();
+       this.panel.closePicker();
+       await pickerClosePromise;
+     }
+-    await BrowserTestUtils.removeTab(this.tab);
++    BrowserTestUtils.removeTab(this.tab);
+     this.tab = null;
+   }
+ 
+   /**
+    * Clean up after tests. Remove the frame to prevent leak.
+    */
+   cleanup() {
+     this.frame.remove();
+diff --git a/toolkit/modules/tests/browser/browser_FinderHighlighter.js b/toolkit/modules/tests/browser/browser_FinderHighlighter.js
+--- a/toolkit/modules/tests/browser/browser_FinderHighlighter.js
++++ b/toolkit/modules/tests/browser/browser_FinderHighlighter.js
+@@ -435,17 +435,17 @@ add_task(async function testHideOnLocati
+   promise = promiseTestHighlighterOutput(browser, word, {
+     rectCount: 0,
+     insertCalls: [0, 0],
+     removeCalls: [1, 2]
+   });
+   await BrowserTestUtils.loadURI(browser, url);
+   await promise;
+ 
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ });
+ 
+ add_task(async function testHideOnClear() {
+   let url = kFixtureBaseURL + "file_FinderSample.html";
+   await BrowserTestUtils.withNewTab(url, async function(browser) {
+     let findbar = gBrowser.getFindBar();
+     await promiseOpenFindbar(findbar);
+ 
+diff --git a/toolkit/modules/tests/browser/browser_WebRequest_ancestors.js b/toolkit/modules/tests/browser/browser_WebRequest_ancestors.js
+--- a/toolkit/modules/tests/browser/browser_WebRequest_ancestors.js
++++ b/toolkit/modules/tests/browser/browser_WebRequest_ancestors.js
+@@ -13,17 +13,17 @@ add_task(async function test_ancestors_e
+     deferred.resolve();
+   }
+ 
+   // Filter on a path to ensure the root favicon request doesn't confuse the test.
+   WebRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: new MatchPatternSet(["http://mochi.test/test/*"])}, ["blocking"]);
+ 
+   let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://mochi.test:8888/test/");
+   await deferred.promise;
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   WebRequest.onBeforeRequest.removeListener(onBeforeRequest);
+ });
+ 
+ add_task(async function test_ancestors_null() {
+   let deferred = PromiseUtils.defer();
+   function onBeforeRequest(details) {
+     info(`onBeforeRequest ${details.url}`);
+diff --git a/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js b/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js
+--- a/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js
++++ b/toolkit/mozapps/extensions/test/browser/browser_file_xpi_no_process_switch.js
+@@ -80,17 +80,17 @@ async function testOpenedAndDraggedXPI(a
+            "move");
+   is(effect, "move", "Drag should be accepted");
+   let [newTab, newTabInstallNotification] = await promiseTabAndNotification;
+   await promiseNotification;
+   if (gBrowser.selectedTab != newTab) {
+     await BrowserTestUtils.switchTab(gBrowser, newTab);
+   }
+   await newTabInstallNotification;
+-  await BrowserTestUtils.removeTab(newTab);
++  BrowserTestUtils.removeTab(newTab);
+   await CheckBrowserInPid(aBrowser, browserPid,
+                           "Check that browser has not switched process.");
+ }
+ 
+ // Test for bug 1175267.
+ add_task(async function() {
+   await SpecialPowers.pushPrefEnv({
+     set: [["xpinstall.customConfirmationUI", true]]
+diff --git a/toolkit/mozapps/extensions/test/browser/browser_getmorethemes.js b/toolkit/mozapps/extensions/test/browser/browser_getmorethemes.js
+--- a/toolkit/mozapps/extensions/test/browser/browser_getmorethemes.js
++++ b/toolkit/mozapps/extensions/test/browser/browser_getmorethemes.js
+@@ -18,17 +18,17 @@ add_task(async function getthemes_link_v
+ 
+   info("Clicking button to get more themes");
+   let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, MAIN_URL);
+   EventUtils.synthesizeMouseAtCenter(button, { }, aManager);
+   await awaitNewTab;
+ 
+   is(gBrowser.currentURI.spec, Services.urlFormatter.formatURLPref(PREF_GETTHEMESURL), "Theme discovery URL should match");
+ 
+-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
+   await close_manager(aManager);
+ });
+ 
+ add_task(async function getthemes_link_hidden_on_appearance_when_pref_cleared() {
+   await SpecialPowers.pushPrefEnv({set: [[PREF_GETTHEMESURL, ""]]});
+ 
+   let aManager = await open_manager("addons://list/extension");
+   info("Testing theme discovery information");
+diff --git a/toolkit/mozapps/extensions/test/browser/browser_legacy_themes.js.1442465-4_2.later b/toolkit/mozapps/extensions/test/browser/browser_legacy_themes.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/toolkit/mozapps/extensions/test/browser/browser_legacy_themes.js.1442465-4_2.later
+@@ -0,0 +1,16 @@
++--- browser_legacy_themes.js
+++++ browser_legacy_themes.js
++@@ -68,12 +68,12 @@ add_task(async function() {
++   // In automation, app.support.baseURL points to a page on localhost.
++   // The actual page is 404 in the test but that doesn't matter here,
++   // just the fact that we load the right URL.
++   let url = Services.prefs.getStringPref("app.support.baseURL") + "complete-themes";
++   let tabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url);
++   button.click();
++   let tab = await tabPromise;
++   ok(true, "Find a replacement button opened SUMO page");
++-  await BrowserTestUtils.removeTab(tab);
+++  BrowserTestUtils.removeTab(tab);
++ 
++   await close_manager(mgrWin);
++ });
+diff --git a/toolkit/mozapps/extensions/test/browser/browser_webext_options_addon_reload.js.1442465-4_2.later b/toolkit/mozapps/extensions/test/browser/browser_webext_options_addon_reload.js.1442465-4_2.later
+new file mode 100644
+--- /dev/null
++++ b/toolkit/mozapps/extensions/test/browser/browser_webext_options_addon_reload.js.1442465-4_2.later
+@@ -0,0 +1,16 @@
++--- browser_webext_options_addon_reload.js
+++++ browser_webext_options_addon_reload.js
++@@ -94,12 +94,12 @@ add_task(async function test_options_on_
++     info("Wait the new options_ui page XUL browser to be created");
++     await onceOptionsReloaded;
++ 
++     let optionsBrowsers = aboutAddonsDocument.querySelectorAll("#addon-options");
++ 
++     Assert.equal(optionsBrowsers.length, 1, "Got a single XUL browser for the addon options_ui page");
++   }
++ 
++-  await BrowserTestUtils.removeTab(gBrowser.selectedTab);
+++  BrowserTestUtils.removeTab(gBrowser.selectedTab);
++ 
++   await extension.unload();
++ });
+diff --git a/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js b/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js
+--- a/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js
++++ b/uriloader/exthandler/tests/mochitest/browser_auto_close_window.js
+@@ -117,17 +117,17 @@ add_task(async function nested_window_op
+ 
+     await ContentTask.spawn(nestedBrowser, null, function() {
+       ok(content.opener, "this window has an opener");
+     });
+ 
+     await testNewTab(nestedBrowser);
+ 
+     isnot(secondTab.linkedBrowser, null, "the page that triggered the download is still open");
+-    await BrowserTestUtils.removeTab(secondTab);
++    BrowserTestUtils.removeTab(secondTab);
+   });
+ });
+ 
+ add_task(async function cleanup() {
+   // Unregister our factory from XPCOM and restore the original CID.
+   registrar.unregisterFactory(MOCK_HELPERAPP_DIALOG_CID, mockHelperAppService);
+   registrar.registerFactory(HELPERAPP_DIALOG_CID, "",
+                             HELPERAPP_DIALOG_CONTRACT_ID, null);
+diff --git a/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js b/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
+--- a/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
++++ b/uriloader/exthandler/tests/mochitest/browser_web_protocol_handlers.js
+@@ -43,17 +43,17 @@ add_task(async function() {
+ 
+   let promiseTabOpened =
+     BrowserTestUtils.waitForNewTab(gBrowser, expectedURL);
+   await BrowserTestUtils.synthesizeMouseAtCenter(link, {button: 1}, browser);
+   let tab = await promiseTabOpened;
+   gBrowser.selectedTab = tab;
+   is(gURLBar.value, expectedURL,
+      "the expected URL is displayed in the location bar");
+-  await BrowserTestUtils.removeTab(tab);
++  BrowserTestUtils.removeTab(tab);
+ 
+   // Shift-click the testprotocol link and check the new window.
+   let newWindowPromise = BrowserTestUtils.waitForNewWindow();
+   await BrowserTestUtils.synthesizeMouseAtCenter(link, {shiftKey: true},
+                                                  browser);
+   let win = await newWindowPromise;
+   await BrowserTestUtils.browserLoaded(win.gBrowser.selectedBrowser);
+   await BrowserTestUtils.waitForCondition(() => win.gBrowser.currentURI.spec == expectedURL);

+ 303 - 0
frg/work-js/mozilla-release/patches/1442465-5-61a1.patch

@@ -0,0 +1,303 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1521425809 -32400
+# Node ID d82a41dc01ab4a335e5c07ed6fc0a426be2ddf24
+# Parent  b821d8f3425e85dfae1ebc43773f4d61e20e6ecd
+Bug 1442465 - Part 5: Workaround for some BrowserTestUtils.removeTab cases. r=dao
+
+diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
++++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+@@ -81,17 +81,20 @@ var gTests = [
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+     is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+     is(webrtcUI.getActiveStreams(true).length, 1, "1 active video stream");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+ 
+     info("removing the second tab");
+-    await BrowserTestUtils.removeTab(tab);
++    // FIXME: This should wait for indicator update instead (bug 1444007).
++    let sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
++    BrowserTestUtils.removeTab(tab);
++    await sessionStorePromise;
+ 
+     // Check that we still show the sharing indicators for the first tab's stream.
+     await promiseWaitForCondition(() => !webrtcUI.showCameraIndicator);
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
+     ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+     is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+@@ -184,17 +187,20 @@ var gTests = [
+ 
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
+     is(webrtcUI.getActiveStreams(true).length, 2, "2 active camera streams");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+ 
+     info("removing the second tab");
+-    await BrowserTestUtils.removeTab(tab);
++    // FIXME: This should wait for indicator update instead (bug 1444007).
++    let sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
++    BrowserTestUtils.removeTab(tab);
++    await sessionStorePromise;
+ 
+     // Check that we still show the sharing indicators for the first tab's stream.
+     await promiseWaitForCondition(() => webrtcUI.showCameraIndicator);
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
+     is(webrtcUI.getActiveStreams(true).length, 1, "1 active camera stream");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+@@ -299,17 +305,17 @@ var gTests = [
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showScreenSharingIndicator, "webrtcUI wants the screen sharing indicator shown");
+     ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
+     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
+     is(webrtcUI.getActiveStreams(false, false, true).length, 2, "2 active desktop sharing streams");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+ 
+     info("removing the second tab");
+-    await BrowserTestUtils.removeTab(tab);
++    BrowserTestUtils.removeTab(tab);
+ 
+     // When both tabs use the same content process, the frame script for the
+     // first tab receives observer notifications for things happening in the
+     // second tab, so let's clear the observer call counts before we cleanup
+     // in the first tab.
+     await ignoreObserversCalled();
+ 
+     // Close the first tab's stream and verify that all indicators are removed.
+diff --git a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+--- a/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
++++ b/browser/components/originattributes/test/browser/browser_favicon_firstParty.js
+@@ -224,18 +224,20 @@ async function doTest(aTestPage, aExpect
+   let tabInfo = await openTab(TEST_SITE_ONE + aTestPage);
+ 
+   // Waiting until favicon requests are all made.
+   await promiseObserveFavicon;
+ 
+   // Waiting until favicon loaded.
+   await promiseFaviconLoaded;
+ 
+-  // Close the tab.
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
++  // FIXME: We need to wait for the next event tick here to avoid observing
++  //        the previous tab info in the next step (bug 1446725).
++  await new Promise(executeSoon);
+ 
+   // Start to observe the favicon requests earlier in case we miss it.
+   promiseObserveFavicon = observeFavicon(FIRST_PARTY_TWO, aExpectedCookies[1], secondPageURI);
+ 
+   // Open the tab for the second site.
+   tabInfo = await openTab(TEST_SITE_TWO + aTestPage);
+ 
+   // Waiting until favicon requests are all made.
+diff --git a/browser/components/originattributes/test/browser/browser_favicon_userContextId.js b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
+--- a/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
++++ b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js
+@@ -154,18 +154,18 @@ async function generateCookies(aHost) {
+   await ContentTask.spawn(tabInfoA.browser, cookies[0], async function(value) {
+     content.document.cookie = value;
+   });
+ 
+   await ContentTask.spawn(tabInfoB.browser, cookies[1], async function(value) {
+     content.document.cookie = value;
+   });
+ 
+-  await BrowserTestUtils.removeTab(tabInfoA.tab);
+-  await BrowserTestUtils.removeTab(tabInfoB.tab);
++  BrowserTestUtils.removeTab(tabInfoA.tab);
++  BrowserTestUtils.removeTab(tabInfoB.tab);
+ 
+   return cookies;
+ }
+ 
+ async function doTest(aTestPage, aFaviconHost, aFaviconURL) {
+   let cookies = await generateCookies(aFaviconHost);
+   let pageURI = makeURI(aTestPage);
+ 
+@@ -182,28 +182,31 @@ async function doTest(aTestPage, aFavico
+   let tabInfo = await openTabInUserContext(aTestPage, USER_CONTEXT_ID_PERSONAL);
+ 
+   // Waiting for favicon requests are all made.
+   await observer.promise;
+   // Waiting for favicon loaded.
+   await promiseWaitOnFaviconLoaded;
+ 
+   // Close the tab.
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
++  // FIXME: We need to wait for the next event tick here to avoid observing
++  //        the previous tab info in the next step (bug 1446725).
++  await new Promise(executeSoon);
+ 
+   // Reset the observer for observing requests for the work container.
+   observer.reset(USER_CONTEXT_ID_WORK, cookies[1], pageURI, aFaviconURL);
+   tabInfo = await openTabInUserContext(aTestPage, USER_CONTEXT_ID_WORK);
+ 
+   // Waiting for favicon requests are all made.
+   await observer.promise;
+ 
+   Services.obs.removeObserver(observer, "http-on-modify-request");
+ 
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+ 
+ add_task(async function setup() {
+   // Make sure userContext is enabled.
+   await SpecialPowers.pushPrefEnv({"set": [
+       ["privacy.userContext.enabled", true]
+   ]});
+ });
+diff --git a/browser/components/originattributes/test/browser/browser_favicon_userContextId.js.1442465-5.later b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js.1442465-5.later
+new file mode 100644
+--- /dev/null
++++ b/browser/components/originattributes/test/browser/browser_favicon_userContextId.js.1442465-5.later
+@@ -0,0 +1,43 @@
++--- browser_favicon_userContextId.js
+++++ browser_favicon_userContextId.js
++@@ -246,17 +249,20 @@ async function doTestForAllTabsFavicon(a
++   EventUtils.synthesizeMouseAtCenter(allTabsBtn, {});
++   await allTabsPopupHiddenPromise;
++ 
++   // Remove the observer for not receiving the favicon requests for opening a tab
++   // since we want to focus on the favicon of allTabs menu here.
++   Services.obs.removeObserver(observer, "http-on-modify-request");
++ 
++   // Close the tab.
++-  await BrowserTestUtils.removeTab(tabInfo.tab);
+++  BrowserTestUtils.removeTab(tabInfo.tab);
+++  // FIXME: We need to wait for the next event tick here to avoid observing
+++  //        the previous tab info in the next step (bug 1446725).
+++  await new Promise(executeSoon);
++ 
++   // Open the tab under the work container and wait until the favicon is loaded.
++   promiseWaitOnFaviconLoaded = waitOnFaviconLoaded(aFaviconURL);
++   tabInfo = await openTabInUserContext(aTestPage, USER_CONTEXT_ID_WORK);
++   await promiseWaitOnFaviconLoaded;
++ 
++   // Clear the image cache again.
++   clearAllImageCaches();
++@@ -276,17 +282,17 @@ async function doTestForAllTabsFavicon(a
++   // Close the popup of allTabs and wait until it's done.
++   allTabsPopupHiddenPromise = BrowserTestUtils.waitForEvent(allTabsBtn, "popuphidden");
++   EventUtils.synthesizeMouseAtCenter(allTabsBtn, {});
++   await allTabsPopupHiddenPromise;
++ 
++   Services.obs.removeObserver(observer, "http-on-modify-request");
++ 
++   // Close the tab.
++-  await BrowserTestUtils.removeTab(tabInfo.tab);
+++  BrowserTestUtils.removeTab(tabInfo.tab);
++ 
++   // Reset the 'overflow' attribute to make the allTabs button hidden again.
++   tabBrowser.removeAttribute("overflow");
++ }
++ 
++ add_task(async function setup() {
++   // Make sure userContext is enabled.
++   await SpecialPowers.pushPrefEnv({"set": [
+diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
+--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
++++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_favicon.js
+@@ -164,17 +164,17 @@ function waitOnFaviconLoaded(aFaviconURL
+ 
+ async function assignCookies(aBrowser, aURL, aCookieValue) {
+   let tabInfo = await openTab(aBrowser, aURL);
+ 
+   await ContentTask.spawn(tabInfo.browser, aCookieValue, async function(value) {
+     content.document.cookie = value;
+   });
+ 
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
+ }
+ 
+ async function openTab(aBrowser, aURL) {
+   let tab = aBrowser.addTab(aURL);
+ 
+   // Select tab and make sure its browser is focused.
+   aBrowser.selectedTab = tab;
+   tab.ownerGlobal.focus();
+@@ -223,29 +223,32 @@ add_task(async function test_favicon_pri
+ 
+   // Open a tab for the private window.
+   let tabInfo = await openTab(privateWindow.gBrowser, TEST_PAGE);
+ 
+   // Waiting until favicon requests are all made.
+   await promiseObserveFavicon;
+ 
+   // Close the tab.
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
++  // FIXME: We need to wait for the next event tick here to avoid observing
++  //        the previous tab info in the next step (bug 1446725).
++  await new Promise(executeSoon);
+ 
+   // Add the observer earlier in case we don't capture events in time.
+   promiseObserveFavicon = observeFavicon(false, cookies[1], pageURI);
+ 
+   // Open a tab for the non-private window.
+   tabInfo = await openTab(gBrowser, TEST_PAGE);
+ 
+   // Waiting until favicon requests are all made.
+   await promiseObserveFavicon;
+ 
+   // Close the tab.
+-  await BrowserTestUtils.removeTab(tabInfo.tab);
++  BrowserTestUtils.removeTab(tabInfo.tab);
+   await BrowserTestUtils.closeWindow(privateWindow);
+ });
+ 
+ add_task(async function test_favicon_cache_privateBrowsing() {
+   // Clear all image cahces and network cache before running the test.
+   clearAllImageCaches();
+ 
+   Services.cache2.clear();
+@@ -276,12 +279,12 @@ add_task(async function test_favicon_cac
+ 
+   // Wait for the favicon response of the private tab.
+   response = await waitOnFaviconResponse(FAVICON_CACHE_URI);
+ 
+   // Make sure the favicon is loaded through the network and its privateBrowsingId is correct.
+   is(response.topic, "http-on-examine-response", "The favicon image should be loaded through the network again.");
+   is(response.privateBrowsingId, 1, "We should observe the network response for the private tab.");
+ 
+-  await BrowserTestUtils.removeTab(tabInfoPrivate.tab);
+-  await BrowserTestUtils.removeTab(tabInfoNonPrivate.tab);
++  BrowserTestUtils.removeTab(tabInfoPrivate.tab);
++  BrowserTestUtils.removeTab(tabInfoNonPrivate.tab);
+   await BrowserTestUtils.closeWindow(privateWindow);
+ });
+diff --git a/dom/base/test/browser_messagemanager_loadprocessscript.js b/dom/base/test/browser_messagemanager_loadprocessscript.js
+--- a/dom/base/test/browser_messagemanager_loadprocessscript.js
++++ b/dom/base/test/browser_messagemanager_loadprocessscript.js
+@@ -81,17 +81,21 @@ add_task(async function(){
+   await SpecialPowers.pushPrefEnv({"set": [["dom.ipc.keepProcessesAlive.web", 5]]})
+ 
+   let tabs = [];
+   for (let i = 0; i < 3; i++) {
+     tabs[i] = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
+   }
+ 
+   for (let i = 0; i < 3; i++) {
+-    await BrowserTestUtils.removeTab(tabs[i]);
++    // FIXME: This should wait for the tab removal gets reflected to the
++    //        process count (bug 1446726).
++    let sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tabs[i]);
++    BrowserTestUtils.removeTab(tabs[i]);
++    await sessionStorePromise;
+   }
+ 
+   ppmm.releaseCachedProcesses();
+   checkBaseProcessCount("Should get back to the base number of processes at this point");
+ })
+ 
+ // Test that loading a process script loads in all existing processes
+ add_task(async function() {

+ 88 - 0
frg/work-js/mozilla-release/patches/1442465-6-61a1.patch

@@ -0,0 +1,88 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1521425809 -32400
+# Node ID 9659a30c4bb04e36661f5ac3d44a8d0b73173708
+# Parent  4ac0c4267f08ad4089c23816301666748b92e714
+Bug 1442465 - Part 6: Stop returning a Promise from BrowserTestUtils.removeTab. r=dao
+
+diff --git a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+--- a/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
++++ b/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm
+@@ -103,17 +103,17 @@ var BrowserTestUtils = {
+     let originalWindow = tab.ownerGlobal;
+     let result = await taskFn(tab.linkedBrowser);
+     let finalWindow = tab.ownerGlobal;
+     if (originalWindow == finalWindow && !tab.closing && tab.linkedBrowser) {
+       // taskFn may resolve within a tick after opening a new tab.
+       // We shouldn't remove the newly opened tab in the same tick.
+       // Wait for the next tick here.
+       await TestUtils.waitForTick();
+-      await BrowserTestUtils.removeTab(tab);
++      BrowserTestUtils.removeTab(tab);
+     } else {
+       Services.console.logStringMessage(
+         "BrowserTestUtils.withNewTab: Tab was already closed before " +
+         "removeTab would have been called");
+     }
+     return Promise.resolve(result);
+   },
+ 
+@@ -982,55 +982,26 @@ var BrowserTestUtils = {
+    *  return value are the same as synthesizeMouse.
+    */
+   synthesizeMouseAtPoint(offsetX, offsetY, event, browser)
+   {
+     return BrowserTestUtils.synthesizeMouse(null, offsetX, offsetY, event, browser);
+   },
+ 
+   /**
+-   * Removes the given tab from its parent tabbrowser and
+-   * waits until its final message has reached the parent.
++   * Removes the given tab from its parent tabbrowser.
++   * This method doesn't SessionStore etc.
+    *
+    * @param (tab) tab
+    *        The tab to remove.
+    * @param (Object) options
+    *        Extra options to pass to tabbrowser's removeTab method.
+-   * @returns (Promise)
+-   * @resolves When the tab is removed. Does not get passed a value.
+    */
+   removeTab(tab, options = {}) {
+-    let tabRemoved = BrowserTestUtils.tabRemoved(tab);
+-    if (!tab.closing) {
+-      tab.ownerGlobal.gBrowser.removeTab(tab, options);
+-    }
+-    return tabRemoved;
+-  },
+-
+-  /**
+-   * Returns a Promise that resolves once a tab has been removed.
+-   *
+-   * @param (tab) tab
+-   *        The tab that will be removed.
+-   * @returns (Promise)
+-   * @resolves When the tab is removed. Does not get passed a value.
+-   */
+-  tabRemoved(tab) {
+-    return new Promise(resolve => {
+-      let {messageManager: mm, frameLoader} = tab.linkedBrowser;
+-      // FIXME! We shouldn't use "SessionStore:update" to know the tab was
+-      // removed.  It will be processed before other "SessionStore:update"
+-      // listeners hasn't been processed.
+-      mm.addMessageListener("SessionStore:update", function onMessage(msg) {
+-        if (msg.targetFrameLoader == frameLoader && msg.data.isFinal) {
+-          mm.removeMessageListener("SessionStore:update", onMessage);
+-          TestUtils.executeSoon(() => resolve());
+-        }
+-      }, true);
+-    });
++    tab.ownerGlobal.gBrowser.removeTab(tab, options);
+   },
+ 
+   /**
+    * Returns a Promise that resolves once the tab starts closing.
+    *
+    * @param (tab) tab
+    *        The tab that will be removed.
+    * @returns (Promise)

+ 30 - 0
frg/work-js/mozilla-release/patches/1442465-7-61a1.patch

@@ -0,0 +1,30 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1521430846 -32400
+# Node ID b09ae094c0542194c212657923be635011387518
+# Parent  3b8a284e9420a68a76eb372120b31204bbbe689b
+Bug 1442465 - followup: Wait for session store update in browser/components/migration/tests/browser/browser_undo_notification.js. r=me
+
+diff --git a/browser/components/migration/tests/browser/browser_undo_notification.js b/browser/components/migration/tests/browser/browser_undo_notification.js
+--- a/browser/components/migration/tests/browser/browser_undo_notification.js
++++ b/browser/components/migration/tests/browser/browser_undo_notification.js
+@@ -40,17 +40,19 @@ add_task(async function autoMigrationUnd
+     }
+ 
+     ok(true, `Got notification for ${url}`);
+     let notification = getNotification(browser);
+     let notificationBox = notification.parentNode;
+     notification.querySelector("button.notification-button-default").click();
+     ok(!undoCalled, "Undo should not be called when clicking the default button");
+     is(notification, notificationBox._closedNotification, "Notification should be closing");
++    let sessionUpdatePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
+     BrowserTestUtils.removeTab(tab);
++    await sessionUpdatePromise;
+ 
+     undoCalled = false;
+     Services.prefs.setCharPref("browser.migrate.automigrate.browser", "chrome");
+     tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url, false);
+     browser = tab.linkedBrowser;
+     if (!getNotification(browser)) {
+       info(`Notification for ${url} not immediately present, waiting for it.`);
+       await BrowserTestUtils.waitForNotificationBar(gBrowser, browser, kExpectedNotificationId);

+ 2 - 2
frg/work-js/mozilla-release/patches/1443429-2-66a1.patch

@@ -2,7 +2,7 @@
 # User Jean-Yves Avenard <jyavenard@mozilla.com>
 # Date 1544731500 0
 # Node ID dd14a663cea2b64a5ef1ac702bf8e01ace067fc2
-# Parent  39ab7f1028744ba6bc26c63760cb4ed37f117710
+# Parent  2e1e2af29d1eb83a7e32927feb044de9530248a5
 Bug 1443429 - P2. Fix constness. r=bholley
 
 And minor C++ cleanup to keep the static analyser happy.
@@ -71,7 +71,7 @@ diff --git a/xpcom/base/CycleCollectedJSContext.cpp b/xpcom/base/CycleCollectedJ
  CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
  {
    MOZ_ASSERT(mJSContext);
-   mStableStateEvents.AppendElement(Move(aRunnable));
+   mStableStateEvents.AppendElement(std::move(aRunnable));
 diff --git a/xpcom/base/CycleCollectedJSContext.h b/xpcom/base/CycleCollectedJSContext.h
 --- a/xpcom/base/CycleCollectedJSContext.h
 +++ b/xpcom/base/CycleCollectedJSContext.h

+ 484 - 0
frg/work-js/mozilla-release/patches/1443846-1-61a1.patch

@@ -0,0 +1,484 @@
+# HG changeset patch
+# User Razvan Caliman <rcaliman@mozilla.com>
+# Date 1520784336 -3600
+# Node ID 342a85114d4c93b3722dd231ae768e2ccbcb111e
+# Parent  ef80329bc6aa2eab5839538fbb1de128a16a2ba8
+Bug 1443846 - Part 1: Add swatch to mark rule as selected and toggle font inspector panel. r=pbro
+
+To use, toggle pref to true: devtools.inspector.fonteditor.enabled
+
+- Introduces the ability for tools to mark one or more rules as "selected". Store rule references for tools to know here to map changes.
+- On mouse hover over any rule, show the font editor swatch.
+- On click on the font editor swatch, toggle the rule as "selected" and toggle the font inspector panel.
+
+MozReview-Commit-ID: 3eTzEOXkApl
+
+diff --git a/devtools/client/inspector/rules/rules.js b/devtools/client/inspector/rules/rules.js
+--- a/devtools/client/inspector/rules/rules.js
++++ b/devtools/client/inspector/rules/rules.js
+@@ -35,16 +35,17 @@ const {debounce} = require("devtools/sha
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ const clipboardHelper = require("devtools/shared/platform/clipboard");
+ const AutocompletePopup = require("devtools/client/shared/autocomplete-popup");
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles";
+ const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit";
++const PREF_FONT_EDITOR = "devtools.inspector.fonteditor.enabled";
+ const FILTER_CHANGED_TIMEOUT = 150;
+ 
+ // This is used to parse user input when filtering.
+ const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/;
+ // This is used to parse the filter search value to see if the filter
+ // should be strict or not
+ const FILTER_STRICT_RE = /\s*`(.*?)`\s*$/;
+ const INSET_POINT_TYPES = ["top", "right", "bottom", "left"];
+@@ -95,35 +96,41 @@ const INSET_POINT_TYPES = ["top", "right
+  * @param {Object} store
+  *        The CSS rule view can use this object to store metadata
+  *        that might outlast the rule view, particularly the current
+  *        set of disabled properties.
+  * @param {PageStyleFront} pageStyle
+  *        The PageStyleFront for communicating with the remote server.
+  */
+ function CssRuleView(inspector, document, store, pageStyle) {
++  EventEmitter.decorate(this);
++
+   this.inspector = inspector;
+   this.highlighters = inspector.highlighters;
+   this.styleDocument = document;
+   this.styleWindow = this.styleDocument.defaultView;
+   this.store = store || {};
++  // References to rules marked by various editors where they intend to write changes.
++  // @see selectRule(), unselectRule()
++  this.selectedRules = new Map();
+   this.pageStyle = pageStyle;
+ 
+   // Allow tests to override debouncing behavior, as this can cause intermittents.
+   this.debounce = debounce;
+ 
+   this.cssProperties = getCssProperties(inspector.toolbox);
+ 
+   this._outputParser = new OutputParser(document, this.cssProperties);
+ 
+   this._onAddRule = this._onAddRule.bind(this);
+   this._onContextMenu = this._onContextMenu.bind(this);
+   this._onCopy = this._onCopy.bind(this);
+   this._onFilterStyles = this._onFilterStyles.bind(this);
+   this._onClearSearch = this._onClearSearch.bind(this);
++  this._onRuleSelected = this._onRuleSelected.bind(this);
+   this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this);
+   this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this);
+   this._onToggleClassPanel = this._onToggleClassPanel.bind(this);
+ 
+   let doc = this.styleDocument;
+   this.element = doc.getElementById("ruleview-container-focusable");
+   this.addRuleButton = doc.getElementById("ruleview-add-rule-button");
+   this.searchField = doc.getElementById("ruleview-searchbox");
+@@ -149,27 +156,29 @@ function CssRuleView(inspector, document
+   this.addRuleButton.addEventListener("click", this._onAddRule);
+   this.searchField.addEventListener("input", this._onFilterStyles);
+   this.searchClearButton.addEventListener("click", this._onClearSearch);
+   this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel);
+   this.classToggle.addEventListener("click", this._onToggleClassPanel);
+   this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass);
+   this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass);
+   this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass);
++  this.on("ruleview-rule-selected", this._onRuleSelected);
+ 
+   this._handlePrefChange = this._handlePrefChange.bind(this);
+   this._handleUAStylePrefChange = this._handleUAStylePrefChange.bind(this);
+   this._handleDefaultColorUnitPrefChange =
+     this._handleDefaultColorUnitPrefChange.bind(this);
+ 
+   this._prefObserver = new PrefObserver("devtools.");
+   this._prefObserver.on(PREF_UA_STYLES, this._handleUAStylePrefChange);
+   this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handleDefaultColorUnitPrefChange);
+ 
+   this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES);
++  this.showFontEditor = Services.prefs.getBoolPref(PREF_FONT_EDITOR);
+ 
+   // The popup will be attached to the toolbox document.
+   this.popup = new AutocompletePopup(inspector._toolbox.doc, {
+     autoSelect: true,
+     theme: "auto"
+   });
+ 
+   this._showEmpty();
+@@ -177,18 +186,16 @@ function CssRuleView(inspector, document
+   this._contextmenu = new StyleInspectorMenu(this, { isRuleView: true });
+ 
+   // Add the tooltips and highlighters to the view
+   this.tooltips = new TooltipsOverlay(this);
+ 
+   this.highlighters.addToView(this);
+ 
+   this.classListPreviewer = new ClassListPreviewer(this.inspector, this.classPanel);
+-
+-  EventEmitter.decorate(this);
+ }
+ 
+ CssRuleView.prototype = {
+   // The element that we're inspecting.
+   _viewedElement: null,
+ 
+   // Used for cancelling timeouts in the style filter.
+   _filterChangedTimeout: null,
+@@ -715,29 +722,31 @@ CssRuleView.prototype = {
+     if (this._contextmenu) {
+       this._contextmenu.destroy();
+       this._contextmenu = null;
+     }
+ 
+     this.tooltips.destroy();
+     this.highlighters.removeFromView(this);
+     this.classListPreviewer.destroy();
++    this.unselectAllRules();
+ 
+     // Remove bound listeners
+     this.shortcuts.destroy();
+     this.element.removeEventListener("copy", this._onCopy);
+     this.element.removeEventListener("contextmenu", this._onContextMenu);
+     this.addRuleButton.removeEventListener("click", this._onAddRule);
+     this.searchField.removeEventListener("input", this._onFilterStyles);
+     this.searchClearButton.removeEventListener("click", this._onClearSearch);
+     this.pseudoClassToggle.removeEventListener("click", this._onTogglePseudoClassPanel);
+     this.classToggle.removeEventListener("click", this._onToggleClassPanel);
+     this.hoverCheckbox.removeEventListener("click", this._onTogglePseudoClass);
+     this.activeCheckbox.removeEventListener("click", this._onTogglePseudoClass);
+     this.focusCheckbox.removeEventListener("click", this._onTogglePseudoClass);
++    this.off("ruleview-rule-selected", this._onRuleSelected);
+ 
+     this.searchField = null;
+     this.searchClearButton = null;
+     this.pseudoClassPanel = null;
+     this.pseudoClassToggle = null;
+     this.classPanel = null;
+     this.classToggle = null;
+     this.hoverCheckbox = null;
+@@ -794,16 +803,17 @@ CssRuleView.prototype = {
+       this.popup.hidePopup();
+     }
+ 
+     this.clear(false);
+     this._viewedElement = element;
+ 
+     this.clearPseudoClassPanel();
+     this.refreshAddRuleButtonState();
++    this.unselectAllRules();
+ 
+     if (!this._viewedElement) {
+       this._stopSelectingElement();
+       this._clearRules();
+       this._showEmpty();
+       this.refreshPseudoClassPanel();
+       return promise.resolve(undefined);
+     }
+@@ -1189,16 +1199,127 @@ CssRuleView.prototype = {
+         isHighlighted = true;
+       }
+     }
+ 
+     return isHighlighted;
+   },
+ 
+   /**
++  * Mark a rule as selected for the given editor id.
++  *
++  * Editing tools can mark one or more rules as selected for themselves so they have
++  * a reference of where to make changes, like add / remove properties.
++  * Each editor has an identifier string (aka editorId) which is used as a key in a map
++  * that holds references to Rule objects.
++  *
++  * Many editors may operate at the same time (ex: Font Editor and Shape Path Editor) so
++  * there are multiple possible selected rules at any given time. A rule can be selected
++  * by different editors at the same time, with each editor operating independently on it.
++  *
++  * @param {Rule} rule
++  *        Rule object for which to hold a reference.
++  * @param {String} editorId
++  *        Key to use for collecting references to selected rules.
++  * @param {Boolean} [unselectOthers=true]
++  *        Optional. Default: `true`. If true, unselect all other rules that were
++  *        selected for the given editor. Ensures only one rule at a time is selected for
++  *        a particular editor. Set to `false` if an editor may operate on multiple rules
++  *        at a time.
++  */
++  selectRule(rule, editorId, unselectOthers = true) {
++    const rules = this.getSelectedRules(editorId);
++    if (!rules.includes(rule)) {
++      this.selectedRules.set(editorId, [...rules, rule]);
++    }
++
++    // Mark other rules for this editorId as unselected.
++    if (unselectOthers) {
++      rules
++        .filter(item => item !== rule)
++        .map(item => this.unselectRule(item, editorId));
++    }
++
++    this.emit("ruleview-rule-selected", {editorId, rule});
++  },
++
++  /**
++   * Unmark a rule as selected for the given editor id.
++   *
++   * @param {Rule} rule
++   *        Rule object for which to remove the reference.
++   * @param {String} editorId
++   *        Key for which to mark the given rule as selected.
++   */
++  unselectRule(rule, editorId) {
++    const rules = this.selectedRules.get(editorId);
++    if (!Array.isArray(rules)) {
++      return;
++    }
++
++    let index = rules.findIndex(item => item === rule);
++    if (index === -1) {
++      return;
++    }
++
++    rules.splice(index, 1);
++    this.selectedRules.set(editorId, rules);
++    this.emit("ruleview-rule-unselected", {editorId, rule});
++  },
++
++  /**
++  * Unmark all selected rules for all editors. If an editor id is provided, unmark all
++  * selected rules just for that editor leaving others untouched.
++  *
++  * @param {String} editorId
++  *        Optional editor id for which to restrict unselect operation.
++  */
++  unselectAllRules(editorId) {
++    for (let [id, rules] of this.selectedRules) {
++      // If we're supposed to unselect rules from just one editorId but it did not match,
++      // skip this iteration.
++      if (editorId && id !== editorId) {
++        continue;
++      }
++      rules.map(rule => this.unselectRule(rule, id));
++    }
++  },
++
++  /**
++   * Return an array of selected rules for the given editor id.
++   * If no rules match, return an empty arrary;
++   *
++   * @param {String} editorId
++   *        Editor id for which to return selected rules.
++   * @return {Array}
++   */
++  getSelectedRules(editorId) {
++    const rules = this.selectedRules.get(editorId);
++    return Array.isArray(rules) ? rules : [];
++  },
++
++  /**
++   * Called when a rule from the Rule view was marked as selected for an editor.
++   * Handle the event and show panels relevant for the given editor id.
++   *
++   * @param {Object} eventData
++   *        Data payload for the event. Contains:
++   *        - {String} editorId - id of the editor for which the rule was selected
++   *        - {Rule} rule - reference to rule that was selected
++   */
++  _onRuleSelected(eventData) {
++    const { editorId } = eventData;
++    switch (editorId) {
++      case "fonteditor":
++        this.inspector.sidebar.show("fontinspector");
++        break;
++    }
++  },
++
++  /**
+    * Highlights the rule selector that matches the filter search value and
+    * returns a boolean indicating whether or not the selector was highlighted.
+    *
+    * @param  {Rule} rule
+    *         The Rule object.
+    * @return {Boolean} true if the rule selector was highlighted,
+    *         false otherwise.
+    */
+diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js
+--- a/devtools/client/inspector/rules/views/rule-editor.js
++++ b/devtools/client/inspector/rules/views/rule-editor.js
+@@ -60,37 +60,45 @@ function RuleEditor(ruleView, rule) {
+   this.sourceMapURLService = this.toolbox.sourceMapURLService;
+   this.rule = rule;
+ 
+   this.isEditable = !rule.isSystem;
+   // Flag that blocks updates of the selector and properties when it is
+   // being edited
+   this.isEditing = false;
+ 
++  this._onFontSwatchClick = this._onFontSwatchClick.bind(this);
+   this._onNewProperty = this._onNewProperty.bind(this);
+   this._newPropertyDestroy = this._newPropertyDestroy.bind(this);
+   this._onSelectorDone = this._onSelectorDone.bind(this);
+   this._locationChanged = this._locationChanged.bind(this);
+   this.updateSourceLink = this.updateSourceLink.bind(this);
+   this._onToolChanged = this._onToolChanged.bind(this);
+   this._updateLocation = this._updateLocation.bind(this);
+   this._onSourceClick = this._onSourceClick.bind(this);
++  this._onRuleUnselected = this._onRuleUnselected.bind(this);
+ 
+   this.rule.domRule.on("location-changed", this._locationChanged);
+   this.toolbox.on("tool-registered", this._onToolChanged);
+   this.toolbox.on("tool-unregistered", this._onToolChanged);
++  this.ruleView.on("ruleview-rule-unselected", this._onRuleUnselected);
+ 
+   this._create();
+ }
+ 
+ RuleEditor.prototype = {
+   destroy: function() {
+     this.rule.domRule.off("location-changed");
+     this.toolbox.off("tool-registered", this._onToolChanged);
+     this.toolbox.off("tool-unregistered", this._onToolChanged);
++    this.ruleView.off("ruleview-rule-unselected", this._onRuleUnselected);
++
++    if (this.fontSwatch) {
++      this.fontSwatch.removeEventListener("click", this._onFontSwatchClick);
++    }
+ 
+     let url = null;
+     if (this.rule.sheet) {
+       url = this.rule.sheet.href || this.rule.sheet.nodeHref;
+     }
+     if (url && !this.rule.isSystem && this.rule.domRule.type !== ELEMENT_STYLE) {
+       // Only get the original source link if the rule isn't a system
+       // rule and if it isn't an inline rule.
+@@ -230,16 +238,64 @@ RuleEditor.prototype = {
+         this.doc.defaultView.focus();
+       });
+ 
+       // Create a property editor when the close brace is clicked.
+       editableItem({ element: this.closeBrace }, () => {
+         this.newProperty();
+       });
+     }
++
++    // Create the font editor toggle icon visible on hover.
++    if (this.ruleView.showFontEditor) {
++      this.fontSwatch = createChild(this.element, "div", {
++        class: "ruleview-fontswatch"
++      });
++
++      // TODO: replace with tool icon and use this as visually hidden a11y text.
++      this.fontSwatch.textContent = "Aa";
++      this.fontSwatch.addEventListener("click", this._onFontSwatchClick);
++    }
++  },
++
++  /**
++   * Handler for clicks on font swatch icon.
++   * Toggles the selected state of the the current rule for the font editor.
++   *
++   * @param {MouseEvent} e
++   *        Mouse click event.
++   */
++  _onFontSwatchClick: function(e) {
++    const editorId = "fonteditor";
++    const isActive = e.target.classList.toggle("active");
++
++    if (isActive) {
++      this.ruleView.selectRule(this.rule, editorId);
++    } else {
++      this.ruleView.unselectRule(this.rule, editorId);
++    }
++  },
++
++  /**
++   * Called when a rule was released from being selected for an editor.
++   * A rule may be released by: toggling a swatch icon, an action from an editor
++   * (ex: close), selecting a different node in the markup view, etc.
++   *
++   * @param {Object} eventData
++   *        Data payload for the event. Contains:
++   *        - {String} editorId - id of the editor for which the rule was released
++   *        - {Rule} rule - reference to rule that was released
++   */
++  _onRuleUnselected: function(eventData) {
++    const { rule, editorId } = eventData;
++
++    // If no longer selected for the font editor, toggle the swatch icon.
++    if (editorId === "fonteditor" && rule == this.rule) {
++      this.fontSwatch.classList.remove("active");
++    }
+   },
+ 
+   /**
+    * Called when a tool is registered or unregistered.
+    */
+   _onToolChanged: function() {
+     // When the source editor is registered, update the source links
+     // to be clickable; and if it is unregistered, update the links to
+diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js
+--- a/devtools/client/preferences/devtools.js
++++ b/devtools/client/preferences/devtools.js
+@@ -66,16 +66,18 @@ pref("devtools.inspector.shapesHighlight
+ // Enable the Changes View
+ pref("devtools.changesview.enabled", false);
+ // Enable the Events View
+ pref("devtools.eventsview.enabled", false);
+ // Enable the Flexbox Inspector panel
+ pref("devtools.flexboxinspector.enabled", false);
+ // Enable the new Animation Inspector
+ pref("devtools.new-animationinspector.enabled", false);
++// Enable the Variable Fonts editor
++pref("devtools.inspector.fonteditor.enabled", false);
+ 
+ // Grid highlighter preferences
+ pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
+ pref("devtools.gridinspector.gridOutlineMaxRows", 50);
+ pref("devtools.gridinspector.showGridAreas", false);
+ pref("devtools.gridinspector.showGridLineNumbers", false);
+ pref("devtools.gridinspector.showInfiniteLines", false);
+ 
+diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css
+--- a/devtools/client/themes/rules.css
++++ b/devtools/client/themes/rules.css
+@@ -512,16 +512,39 @@
+   background-size: 1em;
+ }
+ 
+ .ruleview-angleswatch {
+   background: url("chrome://devtools/skin/images/angle-swatch.svg");
+   background-size: 1em;
+ }
+ 
++.ruleview-rule:not(:hover) .ruleview-fontswatch:not(.active) {
++  visibility: hidden;
++}
++
++.ruleview-fontswatch {
++  background-color: var(--grey-40);
++  background-size: 1em;
++  color: white;
++  cursor: pointer;
++
++  font-size: .8em;
++  position: absolute;
++  right: 2em;
++  bottom: .5em;
++  padding: .1em .2em;
++
++  -moz-user-select: none;
++}
++
++.ruleview-fontswatch.active {
++  background-color: var(--blue-50);
++}
++
+ .ruleview-shapeswatch {
+   background: url("chrome://devtools/skin/images/tool-shadereditor.svg");
+   -moz-context-properties: fill;
+   fill: var(--rule-shape-toggle-color);
+   border-radius: 0;
+   background-size: 1em;
+   box-shadow: none;
+ }

+ 600 - 0
frg/work-js/mozilla-release/patches/1443846-2-61a1.patch

@@ -0,0 +1,600 @@
+# HG changeset patch
+# User Razvan Caliman <rcaliman@mozilla.com>
+# Date 1520972152 -3600
+# Node ID 6795fe2cb8d5237dc3dc24bbcbfbc8617aecbddf
+# Parent  2302c0c672a294bebf64d799d1412c8d043ea54e
+Bug 1443846 - Part 2: Add skeleton for font editor panel. r=gl
+
+- Implement basic React component & Redux store and actions for font editor.
+- Move font overview rendering from FontsApp into its own component: FontOverview. FontsApp remains just a wrapper for FontEditor and FontOverview.
+- Listen to rule selection events to toggle the display of the font editor and font overview panels.
+
+MozReview-Commit-ID: 496LHPqpnKL
+
+diff --git a/devtools/client/inspector/fonts/actions/font-editor.js b/devtools/client/inspector/fonts/actions/font-editor.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/fonts/actions/font-editor.js
+@@ -0,0 +1,21 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const {
++  UPDATE_EDITOR_VISIBILITY,
++} = require("./index");
++
++module.exports = {
++
++  toggleFontEditor(isVisible, selector = "") {
++    return {
++      type: UPDATE_EDITOR_VISIBILITY,
++      isVisible,
++      selector,
++    };
++  },
++
++};
+diff --git a/devtools/client/inspector/fonts/actions/index.js b/devtools/client/inspector/fonts/actions/index.js
+--- a/devtools/client/inspector/fonts/actions/index.js
++++ b/devtools/client/inspector/fonts/actions/index.js
+@@ -3,15 +3,18 @@
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ const { createEnum } = require("devtools/client/shared/enum");
+ 
+ createEnum([
+ 
++  // Toggle the visibiltiy of the font editor
++  "UPDATE_EDITOR_VISIBILITY",
++
+   // Update the list of fonts.
+   "UPDATE_FONTS",
+ 
+   // Update the preview text.
+   "UPDATE_PREVIEW_TEXT",
+ 
+ ], module.exports);
+diff --git a/devtools/client/inspector/fonts/actions/moz.build b/devtools/client/inspector/fonts/actions/moz.build
+--- a/devtools/client/inspector/fonts/actions/moz.build
++++ b/devtools/client/inspector/fonts/actions/moz.build
+@@ -1,11 +1,12 @@
+ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+ # vim: set filetype=python:
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ DevToolsModules(
++    'font-editor.js',
+     'font-options.js',
+     'fonts.js',
+     'index.js',
+ )
+diff --git a/devtools/client/inspector/fonts/components/FontEditor.js b/devtools/client/inspector/fonts/components/FontEditor.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/fonts/components/FontEditor.js
+@@ -0,0 +1,32 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { PureComponent } = require("devtools/client/shared/vendor/react");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++
++const Types = require("../types");
++
++class FontEditor extends PureComponent {
++  static get propTypes() {
++    return {
++      fontEditor: PropTypes.shape(Types.fontEditor).isRequired,
++    };
++  }
++
++  render() {
++    const { selector } = this.props.fontEditor;
++
++    return dom.div(
++      {
++        className: "theme-sidebar inspector-tabpanel",
++        id: "sidebar-panel-fonteditor"
++      }, `Placeholder for Font Editor panel for selector: ${selector}`
++    );
++  }
++}
++
++module.exports = FontEditor;
+diff --git a/devtools/client/inspector/fonts/components/FontOverview.js b/devtools/client/inspector/fonts/components/FontOverview.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/fonts/components/FontOverview.js
+@@ -0,0 +1,88 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
++const dom = require("devtools/client/shared/vendor/react-dom-factories");
++const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
++
++const Accordion = createFactory(require("devtools/client/inspector/layout/components/Accordion"));
++const FontList = createFactory(require("./FontList"));
++
++const { getStr } = require("../utils/l10n");
++const Types = require("../types");
++
++class FontOverview extends PureComponent {
++  static get propTypes() {
++    return {
++      fontData: PropTypes.shape(Types.fontData).isRequired,
++      fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
++      onPreviewFonts: PropTypes.func.isRequired,
++    };
++  }
++
++  renderElementFonts() {
++    let {
++      fontData,
++      fontOptions,
++      onPreviewFonts,
++    } = this.props;
++    let { fonts } = fontData;
++
++    return fonts.length ?
++      FontList({
++        fonts,
++        fontOptions,
++        onPreviewFonts
++      })
++      :
++      dom.div(
++        {
++          className: "devtools-sidepanel-no-result"
++        },
++        getStr("fontinspector.noFontsOnSelectedElement")
++      );
++  }
++
++  renderOtherFonts() {
++    let {
++      fontData,
++      onPreviewFonts,
++      fontOptions,
++    } = this.props;
++    let { otherFonts } = fontData;
++
++    if (!otherFonts.length) {
++      return null;
++    }
++
++    return Accordion({
++      items: [
++        {
++          header: getStr("fontinspector.otherFontsInPageHeader"),
++          component: FontList,
++          componentProps: {
++            fontOptions,
++            fonts: otherFonts,
++            onPreviewFonts
++          },
++          opened: false
++        }
++      ]
++    });
++  }
++
++  render() {
++    return dom.div(
++      {
++        id: "font-container",
++      },
++      this.renderElementFonts(),
++      this.renderOtherFonts()
++    );
++  }
++}
++
++module.exports = FontOverview;
+diff --git a/devtools/client/inspector/fonts/components/FontsApp.js b/devtools/client/inspector/fonts/components/FontsApp.js
+--- a/devtools/client/inspector/fonts/components/FontsApp.js
++++ b/devtools/client/inspector/fonts/components/FontsApp.js
+@@ -4,92 +4,51 @@
+ 
+ "use strict";
+ 
+ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ const { connect } = require("devtools/client/shared/vendor/react-redux");
+ 
+-const Accordion = createFactory(require("devtools/client/inspector/layout/components/Accordion"));
+-const FontList = createFactory(require("./FontList"));
++const FontEditor = createFactory(require("./FontEditor"));
++const FontOverview = createFactory(require("./FontOverview"));
+ 
+-const { getStr } = require("../utils/l10n");
+ const Types = require("../types");
+ 
+ class FontsApp extends PureComponent {
+   static get propTypes() {
+     return {
+       fontData: PropTypes.shape(Types.fontData).isRequired,
++      fontEditor: PropTypes.shape(Types.fontEditor).isRequired,
+       fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
+       onPreviewFonts: PropTypes.func.isRequired,
+     };
+   }
+ 
+-  renderElementFonts() {
+-    let {
+-      fontData,
+-      fontOptions,
+-      onPreviewFonts,
+-    } = this.props;
+-    let { fonts } = fontData;
+-
+-    return fonts.length ?
+-      FontList({
+-        fonts,
+-        fontOptions,
+-        onPreviewFonts
+-      })
+-      :
+-      dom.div(
+-        {
+-          className: "devtools-sidepanel-no-result"
+-        },
+-        getStr("fontinspector.noFontsOnSelectedElement")
+-      );
+-  }
+-
+-  renderOtherFonts() {
+-    let {
++  render() {
++    const {
+       fontData,
+-      onPreviewFonts,
++      fontEditor,
+       fontOptions,
++      onPreviewFonts
+     } = this.props;
+-    let { otherFonts } = fontData;
+-
+-    if (!otherFonts.length) {
+-      return null;
+-    }
+ 
+-    return Accordion({
+-      items: [
+-        {
+-          header: getStr("fontinspector.otherFontsInPageHeader"),
+-          component: FontList,
+-          componentProps: {
+-            fontOptions,
+-            fonts: otherFonts,
+-            onPreviewFonts
+-          },
+-          opened: false
+-        }
+-      ]
+-    });
+-  }
+-
+-  render() {
+     return dom.div(
+       {
+         className: "theme-sidebar inspector-tabpanel",
+         id: "sidebar-panel-fontinspector"
+       },
+-      dom.div(
+-        {
+-          id: "font-container"
+-        },
+-        this.renderElementFonts(),
+-        this.renderOtherFonts()
+-      )
++      fontEditor.isVisible ?
++        FontEditor({
++          fontEditor,
++        })
++        :
++        FontOverview({
++          fontData,
++          fontOptions,
++          onPreviewFonts,
++        })
+     );
+   }
+ }
+ 
+ module.exports = connect(state => state)(FontsApp);
+diff --git a/devtools/client/inspector/fonts/components/moz.build b/devtools/client/inspector/fonts/components/moz.build
+--- a/devtools/client/inspector/fonts/components/moz.build
++++ b/devtools/client/inspector/fonts/components/moz.build
+@@ -1,12 +1,14 @@
+ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+ # vim: set filetype=python:
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ DevToolsModules(
+     'Font.js',
++    'FontEditor.js',
+     'FontList.js',
++    'FontOverview.js',
+     'FontPreview.js',
+     'FontsApp.js',
+ )
+diff --git a/devtools/client/inspector/fonts/fonts.js b/devtools/client/inspector/fonts/fonts.js
+--- a/devtools/client/inspector/fonts/fonts.js
++++ b/devtools/client/inspector/fonts/fonts.js
+@@ -14,28 +14,34 @@ const { Provider } = require("devtools/c
+ const FontsApp = createFactory(require("./components/FontsApp"));
+ 
+ const { LocalizationHelper } = require("devtools/shared/l10n");
+ const INSPECTOR_L10N =
+   new LocalizationHelper("devtools/client/locales/inspector.properties");
+ 
+ const { updateFonts } = require("./actions/fonts");
+ const { updatePreviewText } = require("./actions/font-options");
++const { toggleFontEditor } = require("./actions/font-editor");
++
++const FONT_EDITOR_ID = "fonteditor";
+ 
+ class FontInspector {
+   constructor(inspector, window) {
+     this.document = window.document;
+     this.inspector = inspector;
+     this.pageStyle = this.inspector.pageStyle;
++    this.ruleView = this.inspector.getPanel("ruleview").view;
++    this.selectedRule = null;
+     this.store = this.inspector.store;
+ 
+     this.update = this.update.bind(this);
+-
+     this.onNewNode = this.onNewNode.bind(this);
+     this.onPreviewFonts = this.onPreviewFonts.bind(this);
++    this.onRuleSelected = this.onRuleSelected.bind(this);
++    this.onRuleUnselected = this.onRuleUnselected.bind(this);
+     this.onThemeChanged = this.onThemeChanged.bind(this);
+ 
+     this.init();
+   }
+ 
+   init() {
+     if (!this.inspector) {
+       return;
+@@ -52,16 +58,18 @@ class FontInspector {
+       title: INSPECTOR_L10N.getStr("inspector.sidebar.fontInspectorTitle"),
+     }, fontsApp);
+ 
+     // Expose the provider to let inspector.js use it in setupSidebar.
+     this.provider = provider;
+ 
+     this.inspector.selection.on("new-node-front", this.onNewNode);
+     this.inspector.sidebar.on("fontinspector-selected", this.onNewNode);
++    this.ruleView.on("ruleview-rule-selected", this.onRuleSelected);
++    this.ruleView.on("ruleview-rule-unselected", this.onRuleUnselected);
+ 
+     // Listen for theme changes as the color of the previews depend on the theme
+     gDevTools.on("theme-switched", this.onThemeChanged);
+ 
+     this.store.dispatch(updatePreviewText(""));
+     this.update(false, "");
+   }
+ 
+@@ -84,21 +92,25 @@ class FontInspector {
+ 
+   /**
+    * Destruction function called when the inspector is destroyed. Removes event listeners
+    * and cleans up references.
+    */
+   destroy() {
+     this.inspector.selection.off("new-node-front", this.onNewNode);
+     this.inspector.sidebar.off("fontinspector-selected", this.onNewNode);
++    this.ruleView.off("ruleview-rule-selected", this.onRuleSelected);
++    this.ruleView.off("ruleview-rule-unselected", this.onRuleUnselected);
+     gDevTools.off("theme-switched", this.onThemeChanged);
+ 
+     this.document = null;
+     this.inspector = null;
+     this.pageStyle = null;
++    this.ruleView = null;
++    this.selectedRule = null;
+     this.store = null;
+   }
+ 
+   async getFontsForNode(node, options) {
+     // In case we've been destroyed in the meantime
+     if (!this.document) {
+       return [];
+     }
+@@ -146,16 +158,55 @@ class FontInspector {
+    * Handler for change in preview input.
+    */
+   onPreviewFonts(value) {
+     this.store.dispatch(updatePreviewText(value));
+     this.update();
+   }
+ 
+   /**
++   * Handler for "ruleview-rule-selected" event emitted from the rule view when a rule is
++   * marked as selected for an editor.
++   * If selected for the font editor, hold a reference to the rule so we know where to
++   * put property changes coming from the font editor and show the font editor panel.
++   *
++   * @param {Object} eventData
++   *        Data payload for the event. Contains:
++   *        - {String} editorId - id of the editor for which the rule was selected
++   *        - {Rule} rule - reference to rule that was selected
++   */
++  onRuleSelected(eventData) {
++    const { editorId, rule } = eventData;
++    if (editorId === FONT_EDITOR_ID) {
++      const selector = rule.matchedSelectors[0];
++      this.selectedRule = rule;
++      this.store.dispatch(toggleFontEditor(true, selector));
++    }
++  }
++
++  /**
++   * Handler for "ruleview-rule-unselected" event emitted from the rule view when a rule
++   * was released from being selected for an editor.
++   * If previously selected for the font editor, release the reference to the rule and
++   * hide the font editor panel.
++   *
++   * @param {Object} eventData
++   *        Data payload for the event. Contains:
++   *        - {String} editorId - id of the editor for which the rule was released
++   *        - {Rule} rule - reference to rule that was released
++   */
++  onRuleUnselected(eventData) {
++    const { editorId, rule } = eventData;
++    if (editorId === FONT_EDITOR_ID && rule == this.selectedRule) {
++      this.selectedRule = null;
++      this.store.dispatch(toggleFontEditor(false));
++    }
++  }
++
++  /**
+    * Handler for the "theme-switched" event.
+    */
+   onThemeChanged(frame) {
+     if (frame === this.document.defaultView) {
+       this.update();
+     }
+   }
+ 
+diff --git a/devtools/client/inspector/fonts/reducers/font-editor.js b/devtools/client/inspector/fonts/reducers/font-editor.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/fonts/reducers/font-editor.js
+@@ -0,0 +1,32 @@
++/* This Source Code Form is subject to the terms of the Mozilla Public
++ * License, v. 2.0. If a copy of the MPL was not distributed with this
++ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
++
++"use strict";
++
++const {
++  UPDATE_EDITOR_VISIBILITY,
++} = require("../actions/index");
++
++const INITIAL_STATE = {
++  // Whether or not the font editor is visible.
++  isVisible: false,
++  // Selector text of the rule where font properties will be written.
++  selector: "",
++};
++
++let reducers = {
++
++  [UPDATE_EDITOR_VISIBILITY](state, { isVisible, selector }) {
++    return { ...state, isVisible, selector };
++  },
++
++};
++
++module.exports = function(state = INITIAL_STATE, action) {
++  let reducer = reducers[action.type];
++  if (!reducer) {
++    return state;
++  }
++  return reducer(state, action);
++};
+diff --git a/devtools/client/inspector/fonts/reducers/moz.build b/devtools/client/inspector/fonts/reducers/moz.build
+--- a/devtools/client/inspector/fonts/reducers/moz.build
++++ b/devtools/client/inspector/fonts/reducers/moz.build
+@@ -1,10 +1,11 @@
+ # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+ # vim: set filetype=python:
+ # This Source Code Form is subject to the terms of the Mozilla Public
+ # License, v. 2.0. If a copy of the MPL was not distributed with this
+ # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ 
+ DevToolsModules(
++    'font-editor.js',
+     'font-options.js',
+     'fonts.js',
+ )
+diff --git a/devtools/client/inspector/fonts/types.js b/devtools/client/inspector/fonts/types.js
+--- a/devtools/client/inspector/fonts/types.js
++++ b/devtools/client/inspector/fonts/types.js
+@@ -74,16 +74,27 @@ const font = exports.font = {
+   variationInstances: PropTypes.arrayOf(PropTypes.shape(fontVariationInstance))
+ };
+ 
+ exports.fontOptions = {
+   // The current preview text
+   previewText: PropTypes.string,
+ };
+ 
++exports.fontEditor = {
++  // Font currently being edited
++  font: PropTypes.shape(font),
++
++  // Whether or not the font editor is visible
++  isVisible: PropTypes.bool,
++
++  // Selector text of the rule where font properties will be written
++  selector: PropTypes.string,
++};
++
+ /**
+  * Font data.
+  */
+ exports.fontData = {
+   // The fonts used in the current element.
+   fonts: PropTypes.arrayOf(PropTypes.shape(font)),
+ 
+   // Fonts used elsewhere.
+diff --git a/devtools/client/inspector/reducers.js b/devtools/client/inspector/reducers.js
+--- a/devtools/client/inspector/reducers.js
++++ b/devtools/client/inspector/reducers.js
+@@ -10,10 +10,11 @@
+ exports.animations = require("devtools/client/inspector/animation/reducers/animations");
+ exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
+ exports.changes = require("devtools/client/inspector/changes/reducers/changes");
+ exports.events = require("devtools/client/inspector/events/reducers/events");
+ exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
+ exports.flexbox = require("devtools/client/inspector/flexbox/reducers/flexbox");
+ exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");
+ exports.fontData = require("devtools/client/inspector/fonts/reducers/fonts");
++exports.fontEditor = require("devtools/client/inspector/fonts/reducers/font-editor");
+ exports.grids = require("devtools/client/inspector/grids/reducers/grids");
+ exports.highlighterSettings = require("devtools/client/inspector/grids/reducers/highlighter-settings");
+diff --git a/devtools/client/themes/fonts.css b/devtools/client/themes/fonts.css
+--- a/devtools/client/themes/fonts.css
++++ b/devtools/client/themes/fonts.css
+@@ -5,16 +5,20 @@
+ #sidebar-panel-fontinspector {
+   margin: 0;
+   display: flex;
+   flex-direction: column;
+   width: 100%;
+   height: 100%;
+ }
+ 
++#sidebar-panel-fonteditor {
++  padding: 1em;
++}
++
+ #font-container {
+   overflow: auto;
+   flex: auto;
+ }
+ 
+ .fonts-list {
+   padding: 0;
+   margin: 0;

+ 111 - 0
frg/work-js/mozilla-release/patches/1443846-3-61a1.patch

@@ -0,0 +1,111 @@
+# HG changeset patch
+# User Razvan Caliman <rcaliman@mozilla.com>
+# Date 1521216399 -3600
+# Node ID 5299fa36891d2aad1f0a64e5941d068373e2f300
+# Parent  d772daaf03ab04e29e8674762cc9fb8f023ff52b
+Bug 1443846 - Part 3: Add SVG swatch icon for font editor toggle. r=pbro
+
+MozReview-Commit-ID: Akx2PjdOVgO
+
+diff --git a/devtools/client/inspector/rules/views/rule-editor.js b/devtools/client/inspector/rules/views/rule-editor.js
+--- a/devtools/client/inspector/rules/views/rule-editor.js
++++ b/devtools/client/inspector/rules/views/rule-editor.js
+@@ -245,18 +245,16 @@ RuleEditor.prototype = {
+     }
+ 
+     // Create the font editor toggle icon visible on hover.
+     if (this.ruleView.showFontEditor) {
+       this.fontSwatch = createChild(this.element, "div", {
+         class: "ruleview-fontswatch"
+       });
+ 
+-      // TODO: replace with tool icon and use this as visually hidden a11y text.
+-      this.fontSwatch.textContent = "Aa";
+       this.fontSwatch.addEventListener("click", this._onFontSwatchClick);
+     }
+   },
+ 
+   /**
+    * Handler for clicks on font swatch icon.
+    * Toggles the selected state of the the current rule for the font editor.
+    *
+diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn
+--- a/devtools/client/jar.mn
++++ b/devtools/client/jar.mn
+@@ -113,16 +113,17 @@ devtools.jar:
+     skin/firebug-theme.css (themes/firebug-theme.css)
+     skin/toolbars.css (themes/toolbars.css)
+     skin/toolbox.css (themes/toolbox.css)
+     skin/tooltips.css (themes/tooltips.css)
+     skin/images/add.svg (themes/images/add.svg)
+     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
+     skin/images/filters.svg (themes/images/filters.svg)
+     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
++    skin/images/font-swatch.svg (themes/images/font-swatch.svg)
+     skin/images/grid.svg (themes/images/grid.svg)
+     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
+     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
+     skin/images/controls.png (themes/images/controls.png)
+     skin/images/controls@2x.png (themes/images/controls@2x.png)
+     skin/images/copy.svg (themes/images/copy.svg)
+     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
+     skin/images/performance-details-waterfall.svg (themes/images/performance-details-waterfall.svg)
+diff --git a/devtools/client/themes/images/font-swatch.svg b/devtools/client/themes/images/font-swatch.svg
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/themes/images/font-swatch.svg
+@@ -0,0 +1,6 @@
++<!-- This Source Code Form is subject to the terms of the Mozilla Public
++   - License, v. 2.0. If a copy of the MPL was not distributed with this
++   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
++<svg viewBox="0 0 16 11" version="1.1" xmlns="http://www.w3.org/2000/svg">
++  <path fill-rule="evenodd" fill="context-fill" d="M2,0 L14,0 C15.1045695,-2.02906125e-16 16,0.8954305 16,2 L16,9 C16,10.1045695 15.1045695,11 14,11 L2,11 C0.8954305,11 1.3527075e-16,10.1045695 0,9 L0,2 C-1.3527075e-16,0.8954305 0.8954305,2.02906125e-16 2,0 Z M6.84277344,8.70001221 L8.27978516,8.70001221 L6.078125,2.35870361 L4.51806641,2.35870361 L2.31640625,8.70001221 L3.64794922,8.70001221 L4.13574219,7.18829346 L6.36376953,7.18829346 L6.84277344,8.70001221 Z M5.21679688,3.67706299 L4.42138672,6.16436768 L6.08251953,6.16436768 L5.29589844,3.67706299 L5.21679688,3.67706299 Z M10.4946289,8.77471924 C9.56298828,8.77471924 8.90820312,8.20343018 8.90820312,7.31573486 C8.90820312,6.43682861 9.58056641,5.92706299 10.784668,5.85675049 L11.9580078,5.78643799 L11.9580078,5.39093018 C11.9580078,4.9866333 11.6679688,4.75811768 11.140625,4.75811768 C10.6791992,4.75811768 10.371582,4.9163208 10.2661133,5.20635986 L9.08837891,5.20635986 C9.171875,4.30987549 9.99365234,3.74737549 11.2109375,3.74737549 C12.4941406,3.74737549 13.2192383,4.3538208 13.2192383,5.39093018 L13.2192383,8.70001221 L11.9799805,8.70001221 L11.9799805,8.06719971 L11.9008789,8.06719971 C11.6503906,8.51104736 11.1230469,8.77471924 10.4946289,8.77471924 Z M10.9165039,7.81231689 C11.5097656,7.81231689 11.9580078,7.43438721 11.9580078,6.94219971 L11.9580078,6.58624268 L10.9780273,6.64776611 C10.4287109,6.68731689 10.1606445,6.8850708 10.1606445,7.23223877 C10.1606445,7.59259033 10.4726562,7.81231689 10.9165039,7.81231689 Z"></path>
++</svg>
+diff --git a/devtools/client/themes/rules.css b/devtools/client/themes/rules.css
+--- a/devtools/client/themes/rules.css
++++ b/devtools/client/themes/rules.css
+@@ -517,32 +517,33 @@
+   background-size: 1em;
+ }
+ 
+ .ruleview-rule:not(:hover) .ruleview-fontswatch:not(.active) {
+   visibility: hidden;
+ }
+ 
+ .ruleview-fontswatch {
+-  background-color: var(--grey-40);
+-  background-size: 1em;
+-  color: white;
+-  cursor: pointer;
++  position: absolute;
++  width: 1.45em;
++  height: 1em;
++  right: 1.5em;
++  bottom: 0.5em;
++  font-size: 1em;
+ 
+-  font-size: .8em;
+-  position: absolute;
+-  right: 2em;
+-  bottom: .5em;
+-  padding: .1em .2em;
+-
++  background: url("chrome://devtools/skin/images/font-swatch.svg");
++  -moz-context-properties: fill;
++  fill: var(--grey-40);
++  background-size: contain;
++  cursor: pointer;
+   -moz-user-select: none;
+ }
+ 
+ .ruleview-fontswatch.active {
+-  background-color: var(--blue-50);
++  fill: var(--blue-50);
+ }
+ 
+ .ruleview-shapeswatch {
+   background: url("chrome://devtools/skin/images/tool-shadereditor.svg");
+   -moz-context-properties: fill;
+   fill: var(--rule-shape-toggle-color);
+   border-radius: 0;
+   background-size: 1em;

+ 44 - 0
frg/work-js/mozilla-release/patches/1444007-61a1.patch

@@ -0,0 +1,44 @@
+# HG changeset patch
+# User Dão Gottwald <dao@mozilla.com>
+# Date 1521716784 -3600
+# Node ID c6b39a06d68001398d36c05136b9b44614630702
+# Parent  6f8c56e15084b75426337daf65c8086f4103531a
+Bug 1444007 - Remove race condition in browser_devices_get_user_media_multi_process.js. r=arai
+
+MozReview-Commit-ID: 8cvuI8a984p
+
+diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
++++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+@@ -187,27 +187,26 @@ var gTests = [
+ 
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
+     is(webrtcUI.getActiveStreams(true).length, 2, "2 active camera streams");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+ 
+     info("removing the second tab");
+-    // FIXME: This should wait for indicator update instead (bug 1444007).
+-    let sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
+     BrowserTestUtils.removeTab(tab);
+-    await sessionStorePromise;
+ 
+     // Check that we still show the sharing indicators for the first tab's stream.
+-    await promiseWaitForCondition(() => webrtcUI.showCameraIndicator);
++     await Promise.all([
++      TestUtils.waitForCondition(() => webrtcUI.showCameraIndicator),
++      TestUtils.waitForCondition(() => webrtcUI.getActiveStreams(true).length == 1),
++    ]);
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(!webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator hidden");
+-    is(webrtcUI.getActiveStreams(true).length, 1, "1 active camera stream");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+ 
+     await checkSharingUI({video: true});
+ 
+     // When both tabs use the same content process, the frame script for the
+     // first tab receives observer notifications for things happening in the
+     // second tab, so let's clear the observer call counts before we cleanup
+     // in the first tab.

File diff suppressed because it is too large
+ 7998 - 0
frg/work-js/mozilla-release/patches/1444033-61a1.patch


+ 451 - 0
frg/work-js/mozilla-release/patches/1444327-61a1.patch

@@ -0,0 +1,451 @@
+# HG changeset patch
+# User Patrick Brosset <pbrosset@mozilla.com>
+# Date 1521040615 -3600
+# Node ID d57cfd8c5c3928da25d7270ad33a06ccd501580e
+# Parent  25173961e2d623c451b18193adcabef8ab715621
+Bug 1444327 - Bring back ability to see and copy font URLs; r=jdescottes
+
+In the fonts panel UI prior to Firefox 60, remote font URLs used to be
+displayed in full in a text input field. It made it easy to copy them.
+With the redesign that happened in 60 (bug 1437548 and 1442001), getting
+the URL became harder. The URL isn't visible anymore easily. There's a link
+that can be clicked to load the URL in the browser, or it can also be copied
+from the @font-face CSS rule code section. But that's harder.
+
+This change adds the beginning of the URL back (with an ellipsis) and a
+simple button that copies the link.
+
+Note that the new test failed intermittently on non e10s (took too long).
+This was because of a react middleware which was logging all actions, which,
+in non-e10s, ended up logging StyleRuleActors, which got serialized and caused
+way too much logs to be printed, slowing the test down. So the test was
+disabled on non-e10s.
+
+MozReview-Commit-ID: 2oSMoWKYhTk
+
+diff --git a/devtools/client/inspector/fonts/components/Font.js b/devtools/client/inspector/fonts/components/Font.js
+--- a/devtools/client/inspector/fonts/components/Font.js
++++ b/devtools/client/inspector/fonts/components/Font.js
+@@ -5,16 +5,18 @@
+ "use strict";
+ 
+ const { createFactory, PureComponent } = require("devtools/client/shared/vendor/react");
+ const dom = require("devtools/client/shared/vendor/react-dom-factories");
+ const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
+ 
+ const FontPreview = createFactory(require("./FontPreview"));
+ 
++loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clipboard");
++
+ const { getStr } = require("../utils/l10n");
+ const Types = require("../types");
+ 
+ class Font extends PureComponent {
+   static get propTypes() {
+     return {
+       font: PropTypes.shape(Types.font).isRequired,
+       fontOptions: PropTypes.shape(Types.fontOptions).isRequired,
+@@ -25,28 +27,33 @@ class Font extends PureComponent {
+   constructor(props) {
+     super(props);
+ 
+     this.state = {
+       isFontFaceRuleExpanded: false,
+     };
+ 
+     this.onFontFaceRuleToggle = this.onFontFaceRuleToggle.bind(this);
++    this.onCopyURL = this.onCopyURL.bind(this);
+   }
+ 
+   componentWillReceiveProps(newProps) {
+     if (this.props.font.name === newProps.font.name) {
+       return;
+     }
+ 
+     this.setState({
+       isFontFaceRuleExpanded: false,
+     });
+   }
+ 
++  onCopyURL() {
++    clipboardHelper.copyString(this.props.font.URI);
++  }
++
+   onFontFaceRuleToggle(event) {
+     this.setState({
+       isFontFaceRuleExpanded: !this.state.isFontFaceRuleExpanded
+     });
+     event.stopPropagation();
+   }
+ 
+   renderFontCSSCode(rule, ruleText) {
+@@ -76,37 +83,43 @@ class Font extends PureComponent {
+             className: "font-css-code-expander",
+             onClick: this.onFontFaceRuleToggle,
+           }
+         ),
+       trailing
+     );
+   }
+ 
+-  renderFontTypeAndURL(url, format) {
++  renderFontOrigin(url) {
+     if (!url) {
+       return dom.p(
+         {
+-          className: "font-format-url"
++          className: "font-origin system"
+         },
+         getStr("fontinspector.system")
+       );
+     }
+ 
+     return dom.p(
+       {
+-        className: "font-format-url"
++        className: "font-origin remote",
+       },
+-      getStr("fontinspector.remote"),
+-      dom.a(
++      dom.span(
+         {
+-          className: "font-url",
+-          href: url
++          className: "url",
++          title: url
+         },
+-        format
++        url
++      ),
++      dom.button(
++        {
++          className: "copy-icon",
++          onClick: this.onCopyURL,
++          title: getStr("fontinspector.copyURL"),
++        }
+       )
+     );
+   }
+ 
+   renderFontName(name) {
+     return dom.h1(
+       {
+         className: "font-name"
+@@ -134,29 +147,28 @@ class Font extends PureComponent {
+       font,
+       fontOptions,
+       onPreviewFonts,
+     } = this.props;
+ 
+     let { previewText } = fontOptions;
+ 
+     let {
+-      format,
+       name,
+       previewUrl,
+       rule,
+       ruleText,
+       URI,
+     } = font;
+ 
+     return dom.li(
+       {
+         className: "font",
+       },
+       this.renderFontName(name),
+       FontPreview({ previewText, previewUrl, onPreviewFonts }),
+-      this.renderFontTypeAndURL(URI, format),
++      this.renderFontOrigin(URI),
+       this.renderFontCSSCode(rule, ruleText)
+     );
+   }
+ }
+ 
+ module.exports = Font;
+diff --git a/devtools/client/inspector/fonts/test/browser.ini b/devtools/client/inspector/fonts/test/browser.ini
+--- a/devtools/client/inspector/fonts/test/browser.ini
++++ b/devtools/client/inspector/fonts/test/browser.ini
+@@ -10,13 +10,16 @@ support-files =
+   !/devtools/client/commandline/test/helpers.js
+   !/devtools/client/inspector/test/head.js
+   !/devtools/client/inspector/test/shared-head.js
+   !/devtools/client/shared/test/shared-head.js
+   !/devtools/client/shared/test/test-actor.js
+   !/devtools/client/shared/test/test-actor-registry.js
+ 
+ [browser_fontinspector.js]
++[browser_fontinspector_copy-URL.js]
++skip-if = !e10s # too slow on !e10s, logging fully serialized actors (Bug 1446595)
++subsuite = clipboard
+ [browser_fontinspector_edit-previews.js]
+ [browser_fontinspector_expand-css-code.js]
+ [browser_fontinspector_other-fonts.js]
+ [browser_fontinspector_text-node.js]
+ [browser_fontinspector_theme-change.js]
+diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector.js b/devtools/client/inspector/fonts/test/browser_fontinspector.js
+--- a/devtools/client/inspector/fonts/test/browser_fontinspector.js
++++ b/devtools/client/inspector/fonts/test/browser_fontinspector.js
+@@ -5,73 +5,59 @@
+ 
+ requestLongerTimeout(2);
+ 
+ const TEST_URI = URL_ROOT + "browser_fontinspector.html";
+ const FONTS = [{
+   name: "Ostrich Sans Medium",
+   remote: true,
+   url: URL_ROOT + "ostrich-regular.ttf",
+-  format: "truetype",
+   cssName: "bar"
+ }, {
+   name: "Ostrich Sans Black",
+   remote: true,
+   url: URL_ROOT + "ostrich-black.ttf",
+-  format: "",
+   cssName: "bar"
+ }, {
+   name: "Ostrich Sans Black",
+   remote: true,
+   url: URL_ROOT + "ostrich-black.ttf",
+-  format: "",
+   cssName: "bar"
+ }, {
+   name: "Ostrich Sans Medium",
+   remote: true,
+   url: URL_ROOT + "ostrich-regular.ttf",
+-  format: "",
+   cssName: "barnormal"
+ }];
+ 
+ add_task(function* () {
+   let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
+   ok(!!view, "Font inspector document is alive.");
+ 
+   let viewDoc = view.document;
+ 
+   yield testBodyFonts(inspector, viewDoc);
+   yield testDivFonts(inspector, viewDoc);
+ });
+ 
+ function isRemote(fontLi) {
+-  return fontLi.querySelectorAll(".font-format-url a").length === 1;
+-}
+-
+-function getFormat(fontLi) {
+-  let link = fontLi.querySelector(".font-format-url a");
+-  if (!link) {
+-    return null;
+-  }
+-
+-  return link.textContent;
++  return fontLi.querySelector(".font-origin").classList.contains("remote");
+ }
+ 
+ function* testBodyFonts(inspector, viewDoc) {
+   let lis = getUsedFontsEls(viewDoc);
+   is(lis.length, 5, "Found 5 fonts");
+ 
+   for (let i = 0; i < FONTS.length; i++) {
+     let li = lis[i];
+     let font = FONTS[i];
+ 
+-    is(getName(li), font.name, "font " + i + " right font name");
+-    is(isRemote(li), font.remote, "font " + i + " remote value correct");
+-    is(li.querySelector(".font-url").href, font.url, "font " + i + " url correct");
+-    is(getFormat(li), font.format, "font " + i + " format correct");
++    is(getName(li), font.name, `font ${i} right font name`);
++    is(isRemote(li), font.remote, `font ${i} remote value correct`);
++    is(li.querySelector(".font-origin").textContent, font.url, `font ${i} url correct`);
+   }
+ 
+   // test that the bold and regular fonts have different previews
+   let regSrc = lis[0].querySelector(".font-preview").src;
+   let boldSrc = lis[1].querySelector(".font-preview").src;
+   isnot(regSrc, boldSrc, "preview for bold font is different from regular");
+ 
+   // test system font
+diff --git a/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js b/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/inspector/fonts/test/browser_fontinspector_copy-URL.js
+@@ -0,0 +1,25 @@
++/* vim: set ts=2 et sw=2 tw=80: */
++/* Any copyright is dedicated to the Public Domain.
++   http://creativecommons.org/publicdomain/zero/1.0/ */
++
++"use strict";
++
++// Test that an icon appears next to web font URLs, and that clicking it copies the URL
++// to the clipboard thanks to it.
++
++const TEST_URI = URL_ROOT + "browser_fontinspector.html";
++
++add_task(async function() {
++  let { view } = await openFontInspectorForURL(TEST_URI);
++  let viewDoc = view.document;
++
++  let fontEl = getUsedFontsEls(viewDoc)[0];
++  let linkEl = fontEl.querySelector(".font-origin");
++  let iconEl = linkEl.querySelector(".copy-icon");
++
++  ok(iconEl, "The icon is displayed");
++  is(iconEl.getAttribute("title"), "Copy URL", "This is the right icon");
++
++  info("Clicking the button and waiting for the clipboard to receive the URL");
++  await waitForClipboardPromise(() => iconEl.click(), linkEl.textContent);
++});
+diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn
+--- a/devtools/client/jar.mn
++++ b/devtools/client/jar.mn
+@@ -118,16 +118,17 @@ devtools.jar:
+     skin/images/breadcrumbs-divider.svg (themes/images/breadcrumbs-divider.svg)
+     skin/images/filters.svg (themes/images/filters.svg)
+     skin/images/filter-swatch.svg (themes/images/filter-swatch.svg)
+     skin/images/grid.svg (themes/images/grid.svg)
+     skin/images/angle-swatch.svg (themes/images/angle-swatch.svg)
+     skin/images/pseudo-class.svg (themes/images/pseudo-class.svg)
+     skin/images/controls.png (themes/images/controls.png)
+     skin/images/controls@2x.png (themes/images/controls@2x.png)
++    skin/images/copy.svg (themes/images/copy.svg)
+     skin/images/animation-fast-track.svg (themes/images/animation-fast-track.svg)
+     skin/images/performance-details-waterfall.svg (themes/images/performance-details-waterfall.svg)
+     skin/images/performance-details-call-tree.svg (themes/images/performance-details-call-tree.svg)
+     skin/images/performance-details-flamegraph.svg (themes/images/performance-details-flamegraph.svg)
+     skin/breadcrumbs.css (themes/breadcrumbs.css)
+     skin/chart.css (themes/chart.css)
+     skin/widgets.css (themes/widgets.css)
+     skin/images/power.svg (themes/images/power.svg)
+diff --git a/devtools/client/locales/en-US/font-inspector.properties b/devtools/client/locales/en-US/font-inspector.properties
+--- a/devtools/client/locales/en-US/font-inspector.properties
++++ b/devtools/client/locales/en-US/font-inspector.properties
+@@ -4,24 +4,25 @@
+ 
+ # LOCALIZATION NOTE This file contains the Font Inspector strings.
+ # The Font Inspector is a panel accessible in the Inspector sidebar.
+ 
+ # LOCALIZATION NOTE (fontinspector.system) This label indicates that the font is a local
+ # system font.
+ fontinspector.system=system
+ 
+-# LOCALIZATION NOTE (fontinspector.remote) This label indicates that the font is a remote
+-# font.
+-fontinspector.remote=remote
+-
+ # LOCALIZATION NOTE (fontinspector.noFontsOnSelectedElement): This label is shown when
+ # no fonts found on the selected element.
+ fontinspector.noFontsOnSelectedElement=No fonts were found for the current element.
+ 
+ # LOCALIZATION NOTE (fontinspector.otherFontsInPageHeader): This is the text for the
+ # header of a collapsible section containing other fonts used in the page.
+ fontinspector.otherFontsInPageHeader=Other fonts in page
+ 
+ # LOCALIZATION NOTE (fontinspector.editPreview): This is the text that appears in a
+ # tooltip on hover of a font preview string. Clicking on the string opens a text input
+ # where users can type to change the preview text.
+ fontinspector.editPreview=Click to edit preview
++
++# LOCALIZATION NOTE (fontinspector.copyURL): This is the text that appears in a tooltip
++# displayed when the user hovers over the copy icon next to the font URL.
++# Clicking the copy icon copies the full font URL to the user's clipboard
++fontinspector.copyURL=Copy URL
+diff --git a/devtools/client/themes/fonts.css b/devtools/client/themes/fonts.css
+--- a/devtools/client/themes/fonts.css
++++ b/devtools/client/themes/fonts.css
+@@ -21,16 +21,17 @@
+   list-style: none;
+ }
+ 
+ .font {
+   border: 1px solid var(--theme-splitter-color);
+   border-width: 0 1px 1px 0;
+   display: grid;
+   grid-template-columns: 1fr auto;
++  grid-column-gap: 10px;
+   padding: 10px 20px;
+ }
+ 
+ #font-container .theme-twisty {
+   display: inline-block;
+   cursor: pointer;
+   vertical-align: bottom;
+ }
+@@ -103,40 +104,49 @@
+   color: var(--theme-body-color-inactive);
+   border-radius: 3px;
+   border-style: solid;
+   border-width: 1px;
+   text-align: center;
+   vertical-align: middle;
+ }
+ 
+-.font-format-url {
+-  text-transform: capitalize;
++.font-origin {
+   margin-top: .2em;
+   color: var(--grey-50);
++  justify-self: start;
++}
++
++.font-origin.system {
++  text-transform: capitalize;
++}
++
++.font-origin.remote {
++  display: grid;
++  grid-template-columns: 1fr 20px;
+ }
+ 
+-.font-url {
+-  margin-inline-start: .5em;
+-  text-decoration: underline;
+-  color: var(--theme-highlight-blue);
+-  background: transparent;
+-  border: none;
+-  cursor: pointer;
++.font-origin.remote .url {
++  text-overflow: ellipsis;
++  overflow: hidden;
++  white-space: nowrap;
+ }
+ 
+-.font-url::after {
+-  content: "";
+-  display: inline-block;
+-  height: 13px;
+-  width: 13px;
+-  margin: -.3rem .15rem 0 0.25rem;
+-  vertical-align: middle;
+-  background-image: url(chrome://devtools-shim/content/aboutdevtools/images/external-link.svg);
+-  background-repeat: no-repeat;
+-  background-size: 13px 13px;
++.font-origin .copy-icon {
++  border: 0;
++  padding: 0;
++  position: relative;
++  cursor: pointer;
++  width: 12px;
++  height: 12px;
++  place-self: center;
++
++  background: url(chrome://devtools/skin/images/copy.svg) no-repeat;
++  background-size: 12px;
++  background-position-x: -1px;
+   -moz-context-properties: fill;
+-  fill: var(--blue-60);
++  fill: var(--theme-toolbar-color);
++
+ }
+ 
+ #font-container .devtools-sidepanel-no-result + .accordion {
+   border-block-start: 1px solid var(--theme-splitter-color);
+ }
+diff --git a/devtools/client/themes/images/copy.svg b/devtools/client/themes/images/copy.svg
+new file mode 100644
+--- /dev/null
++++ b/devtools/client/themes/images/copy.svg
+@@ -0,0 +1,6 @@
++<!-- This Source Code Form is subject to the terms of the Mozilla Public
++   - License, v. 2.0. If a copy of the MPL was not distributed with this
++   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
++<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="context-fill #0b0b0b">
++  <path d="M5.70001221,11.125 L5.70001221,4.625 L7.66001225,4.625 L7.66001225,6.94642857 C7.66001225,7.20271429 7.87953225,7.41071429 8.15001225,7.41071429 L10.6000123,7.41071429 L10.6000123,11.125 L5.70001221,11.125 Z M4.7,5.475 L4.7,7.275 L2,7.275 L2,0.975 L3.8,0.975 L3.8,3.225 C3.8,3.4734 4.0016,3.675 4.25,3.675 L6.5,3.675 C5.5064,3.675 4.7,4.4814 4.7,5.475 Z M6.84002447,3.00050006 L4.65002441,3.00050006 L4.65002441,0.8105 L6.84002447,3.00050006 Z M10.5,6.6000061 L8.5,6.6000061 L8.5,4.6000061 L10.5,6.6000061 Z M11.28025,6.21975 L9.03025,3.96975 C8.89,3.82875 8.6995,3.75 8.5,3.75 L7.75,3.75 L7.75,3 C7.75,2.80125 7.67125,2.61 7.53025,2.46975 L5.28025,0.21975 C5.14,0.07875 4.94875,0 4.75,0 L2.5,0 C1.672,0 1,0.672 1,1.5 L1,6.75 C1,7.578 1.672,8.25 2.5,8.25 L4.75,8.25 L4.75,10.5 C4.75,11.328 5.422,12 6.25,12 L10,12 C10.828,12 11.5,11.328 11.5,10.5 L11.5,6.75 C11.5,6.55125 11.42125,6.36 11.28025,6.21975 Z"></path>
++</svg>

+ 87 - 0
frg/work-js/mozilla-release/patches/1445153-61a1.patch

@@ -0,0 +1,87 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1520927494 -3600
+# Node ID 86919fbab7882bacdf7d680b89b6a4658c1b6ca1
+# Parent  9443a3fd6642b99ef9d9cd4cd02f297db5e3392c
+Bug 1445153 - Remove remaining references to computedview in boxmodel component;r=pbro
+
+MozReview-Commit-ID: CxX09ZQ9jhk
+
+diff --git a/devtools/client/inspector/boxmodel/box-model.js b/devtools/client/inspector/boxmodel/box-model.js
+--- a/devtools/client/inspector/boxmodel/box-model.js
++++ b/devtools/client/inspector/boxmodel/box-model.js
+@@ -76,23 +76,22 @@ BoxModel.prototype = {
+       onHideBoxModelHighlighter: this.onHideBoxModelHighlighter,
+       onShowBoxModelEditor: this.onShowBoxModelEditor,
+       onShowBoxModelHighlighter: this.onShowBoxModelHighlighter,
+       onToggleGeometryEditor: this.onToggleGeometryEditor,
+     };
+   },
+ 
+   /**
+-   * Returns true if the computed or layout panel is visible, and false otherwise.
++   * Returns true if the layout panel is visible, and false otherwise.
+    */
+   isPanelVisible() {
+     return this.inspector.toolbox && this.inspector.sidebar &&
+            this.inspector.toolbox.currentToolId === "inspector" &&
+-           (this.inspector.sidebar.getCurrentTabID() === "layoutview" ||
+-            this.inspector.sidebar.getCurrentTabID() === "computedview");
++           (this.inspector.sidebar.getCurrentTabID() === "layoutview");
+   },
+ 
+   /**
+    * Returns true if the layout panel is visible and the current element is valid to
+    * be displayed in the view.
+    */
+   isPanelVisibleAndNodeValid() {
+     return this.isPanelVisible() &&
+diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js
+--- a/devtools/client/inspector/inspector.js
++++ b/devtools/client/inspector/inspector.js
+@@ -664,17 +664,17 @@ Inspector.prototype = {
+         panel = new ComputedViewTool(this, this.panelWin);
+         break;
+       case "ruleview":
+         const {RuleViewTool} = require("devtools/client/inspector/rules/rules");
+         panel = new RuleViewTool(this, this.panelWin);
+         break;
+       case "boxmodel":
+         // box-model isn't a panel on its own, it used to, now it is being used by
+-        // computed view and layout which retrieves an instance via getPanel.
++        // the layout view which retrieves an instance via getPanel.
+         const BoxModel = require("devtools/client/inspector/boxmodel/box-model");
+         panel = new BoxModel(this, this.panelWin);
+         break;
+       default:
+         // This is a custom panel or a non lazy-loaded one.
+         return null;
+     }
+     this._panels.set(id, panel);
+diff --git a/devtools/client/inspector/test/shared-head.js b/devtools/client/inspector/test/shared-head.js
+--- a/devtools/client/inspector/test/shared-head.js
++++ b/devtools/client/inspector/test/shared-head.js
+@@ -49,21 +49,19 @@ var openInspector = Task.async(function*
+  */
+ var openInspectorSidebarTab = Task.async(function* (id) {
+   let {toolbox, inspector, testActor} = yield openInspector();
+ 
+   info("Selecting the " + id + " sidebar");
+ 
+   let onSidebarSelect = inspector.sidebar.once("select");
+   if (id === "layoutview") {
+-    // The layout and computed views should wait until the box-model widget is ready.
++    // The layout view should wait until the box-model and grid-panel are ready.
+     let onBoxModelViewReady = inspector.once("boxmodel-view-updated");
+-    // The layout view also needs to wait for the grid panel to be fully updated.
+-    let onGridPanelReady = id === "layoutview" ?
+-      inspector.once("grid-panel-updated") : Promise.resolve();
++    let onGridPanelReady = inspector.once("grid-panel-updated");
+     inspector.sidebar.select(id);
+     yield onBoxModelViewReady;
+     yield onGridPanelReady;
+   } else {
+     inspector.sidebar.select(id);
+   }
+   yield onSidebarSelect;
+ 

+ 0 - 0
frg/work-js/mozilla-release/patches/1445181-1-61a1-patch → frg/work-js/mozilla-release/patches/1445181-1-61a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1445181-2-61a1-patch → frg/work-js/mozilla-release/patches/1445181-2-61a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1445181-3-61a1-patch → frg/work-js/mozilla-release/patches/1445181-3-61a1.patch


+ 0 - 0
frg/work-js/mozilla-release/patches/1445196-61a1-patch → frg/work-js/mozilla-release/patches/1445196-61a1.patch


+ 74 - 0
frg/work-js/mozilla-release/patches/1445776-61a1.patch

@@ -0,0 +1,74 @@
+# HG changeset patch
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1521064927 18000
+# Node ID e4df3b09cb1af2fdbd5ec86c4f31141141441f6c
+# Parent  ae2853a3f75495b3cdec6c3a474672160b8f1317
+Bug 1445776 - Add development restart shortcut to Browser Console. r=bgrins
+
+Adds the browser restart shortcut (Cmd / Ctrl + Alt + R) to the Browser Console
+window.  Only enabled for local development builds.
+
+MozReview-Commit-ID: 2oTT55TYCx6
+
+diff --git a/devtools/client/shared/key-shortcuts.js b/devtools/client/shared/key-shortcuts.js
+--- a/devtools/client/shared/key-shortcuts.js
++++ b/devtools/client/shared/key-shortcuts.js
+@@ -130,18 +130,26 @@ KeyShortcuts.parseElectronKey = function
+ 
+   // Plus is a special case. It's a character key and shouldn't be matched
+   // against a keycode as it is only accessible via Shift/Capslock
+   if (key === "Plus") {
+     key = "+";
+   }
+ 
+   if (typeof key === "string" && key.length === 1) {
+-    // Match any single character
+-    shortcut.key = key.toLowerCase();
++    if (shortcut.alt) {
++      // When Alt is involved, some platforms (macOS) give different printable characters
++      // for the `key` value, like `®` for the key `R`.  In this case, prefer matching by
++      // `keyCode` instead.
++      shortcut.keyCode = KeyCodes[`DOM_VK_${key.toUpperCase()}`];
++      shortcut.keyCodeString = key;
++    } else {
++      // Match any single character
++      shortcut.key = key.toLowerCase();
++    }
+   } else if (key in ElectronKeysMapping) {
+     // Maps the others manually to DOM API DOM_VK_*
+     key = ElectronKeysMapping[key];
+     shortcut.keyCode = KeyCodes[key];
+     // Used only to stringify the shortcut
+     shortcut.keyCodeString = key;
+     shortcut.key = key;
+   } else {
+diff --git a/devtools/client/webconsole/new-webconsole.js b/devtools/client/webconsole/new-webconsole.js
+--- a/devtools/client/webconsole/new-webconsole.js
++++ b/devtools/client/webconsole/new-webconsole.js
+@@ -248,16 +248,26 @@ NewWebConsoleFrame.prototype = {
+ 
+     shortcuts.on(clearShortcut, () => this.jsterm.clearOutput(true));
+ 
+     if (this.isBrowserConsole) {
+       shortcuts.on(l10n.getStr("webconsole.close.key"),
+                    this.window.top.close.bind(this.window.top));
+ 
+       ZoomKeys.register(this.window);
++
++      if (!system.constants.MOZILLA_OFFICIAL) {
++        // In local builds, inject the "quick restart" shortcut.
++        // This script expects to have Services on the global and we haven't yet imported
++        // it into the window, so assign it.
++        this.window.Services = Services;
++        Services.scriptloader.loadSubScript(
++          "chrome://browser/content/browser-development-helpers.js", this.window);
++        shortcuts.on("CmdOrCtrl+Alt+R", this.window.DevelopmentHelpers.quickRestart);
++      }
+     } else if (Services.prefs.getBoolPref(PREF_SIDEBAR_ENABLED)) {
+       shortcuts.on("Esc", event => {
+         if (!this.jsterm.autocompletePopup || !this.jsterm.autocompletePopup.isOpen) {
+           this.newConsoleOutput.dispatchSidebarClose();
+         }
+       });
+     }
+   },

+ 382 - 0
frg/work-js/mozilla-release/patches/1446064-61a1.patch

@@ -0,0 +1,382 @@
+# HG changeset patch
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1521137054 18000
+# Node ID 115ca10fde0e8b7cfb0d34087a030959e0e5b18b
+# Parent  89e912db179ccd6f9ba0755d85710d489f7090a6
+Bug 1446064 - Clean up webconsole-connection-proxy.js. r=nchevobbe
+
+MozReview-Commit-ID: LWRuJQJgkrm
+
+diff --git a/.eslintignore b/.eslintignore
+--- a/.eslintignore
++++ b/.eslintignore
+@@ -134,17 +134,16 @@ devtools/client/storage/test/*.html
+ !devtools/client/storage/test/storage-overflow.html
+ !devtools/client/storage/test/storage-search.html
+ !devtools/client/storage/test/storage-unsecured-iframe.html
+ !devtools/client/storage/test/storage-unsecured-iframe-usercontextid.html
+ devtools/client/webaudioeditor/**
+ devtools/client/webconsole/net/**
+ !devtools/client/webconsole/new-console-output/test/mochitest/**
+ devtools/client/webconsole/test/**
+-devtools/client/webconsole/webconsole-connection-proxy.js
+ devtools/client/webconsole/webconsole.js
+ devtools/client/webide/**
+ !devtools/client/webide/components/webideCli.js
+ devtools/server/tests/browser/storage-*.html
+ !devtools/server/tests/browser/storage-unsecured-iframe.html
+ devtools/server/tests/browser/stylesheets-nested-iframes.html
+ devtools/server/tests/unit/xpcshell_debugging_script.js
+ devtools/client/shared/webpack/shims/test/test_clipboard.html
+diff --git a/devtools/client/webconsole/webconsole-connection-proxy.js b/devtools/client/webconsole/webconsole-connection-proxy.js
+--- a/devtools/client/webconsole/webconsole-connection-proxy.js
++++ b/devtools/client/webconsole/webconsole-connection-proxy.js
+@@ -1,17 +1,14 @@
+-/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+-const {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ 
+ const l10n = require("devtools/client/webconsole/webconsole-l10n");
+ 
+ const PREF_CONNECTION_TIMEOUT = "devtools.debugger.remote-timeout";
+ // Web Console connection proxy
+ 
+@@ -110,17 +107,17 @@ WebConsoleConnectionProxy.prototype = {
+ 
+   /**
+    * Initialize a debugger client and connect it to the debugger server.
+    *
+    * @return object
+    *         A promise object that is resolved/rejected based on the success of
+    *         the connection initialization.
+    */
+-  connect: function () {
++  connect: function() {
+     if (this._connectDefer) {
+       return this._connectDefer.promise;
+     }
+ 
+     this._connectDefer = defer();
+ 
+     let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT);
+     this._connectTimer = setTimeout(this._connectionTimeout, timeout);
+@@ -157,30 +154,30 @@ WebConsoleConnectionProxy.prototype = {
+ 
+     return connPromise;
+   },
+ 
+   /**
+    * Connection timeout handler.
+    * @private
+    */
+-  _connectionTimeout: function () {
++  _connectionTimeout: function() {
+     let error = {
+       error: "timeout",
+       message: l10n.getStr("connectionTimeout"),
+     };
+ 
+     this._connectDefer.reject(error);
+   },
+ 
+   /**
+    * Attach to the Web Console actor.
+    * @private
+    */
+-  _attachConsole: function () {
++  _attachConsole: function() {
+     let listeners = ["PageError", "ConsoleAPI", "NetworkActivity",
+                      "FileActivity"];
+     // Enable the forwarding of console messages to the parent process
+     // when we open the Browser Console or Toolbox.
+     if (this.target.chrome && !this.target.isAddon) {
+       listeners.push("ContentProcessMessages");
+     }
+     this.client.attachConsole(this._consoleActor, listeners,
+@@ -192,17 +189,17 @@ WebConsoleConnectionProxy.prototype = {
+    *
+    * @private
+    * @param object response
+    *        The JSON response object received from the server.
+    * @param object webConsoleClient
+    *        The WebConsoleClient instance for the attached console, for the
+    *        specific tab we work with.
+    */
+-  _onAttachConsole: function (response, webConsoleClient) {
++  _onAttachConsole: function(response, webConsoleClient) {
+     if (response.error) {
+       console.error("attachConsole failed: " + response.error + " " +
+                     response.message);
+       this._connectDefer.reject(response);
+       return;
+     }
+ 
+     this.webConsoleClient = webConsoleClient;
+@@ -220,46 +217,46 @@ WebConsoleConnectionProxy.prototype = {
+     this.webConsoleClient.getCachedMessages(msgs, this._onCachedMessages);
+ 
+     this.webConsoleFrame._onUpdateListeners();
+   },
+ 
+   /**
+    * Dispatch a message add on the new frontend and emit an event for tests.
+    */
+-  dispatchMessageAdd: function (packet) {
++  dispatchMessageAdd: function(packet) {
+     this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
+   },
+ 
+   /**
+    * Batched dispatch of messages.
+    */
+-  dispatchMessagesAdd: function (packets) {
++  dispatchMessagesAdd: function(packets) {
+     this.webConsoleFrame.newConsoleOutput.dispatchMessagesAdd(packets);
+   },
+ 
+   /**
+    * Dispatch a message event on the new frontend and emit an event for tests.
+    */
+-  dispatchMessageUpdate: function (networkInfo, response) {
++  dispatchMessageUpdate: function(networkInfo, response) {
+     this.webConsoleFrame.newConsoleOutput.dispatchMessageUpdate(networkInfo, response);
+   },
+ 
+-  dispatchRequestUpdate: function (id, data) {
++  dispatchRequestUpdate: function(id, data) {
+     this.webConsoleFrame.newConsoleOutput.dispatchRequestUpdate(id, data);
+   },
+ 
+   /**
+    * The "cachedMessages" response handler.
+    *
+    * @private
+    * @param object response
+    *        The JSON response object received from the server.
+    */
+-  _onCachedMessages: function (response) {
++  _onCachedMessages: function(response) {
+     if (response.error) {
+       console.error("Web Console getCachedMessages error: " + response.error +
+                     " " + response.message);
+       this._connectDefer.reject(response);
+       return;
+     }
+ 
+     if (!this._connectTimer) {
+@@ -290,17 +287,17 @@ WebConsoleConnectionProxy.prototype = {
+    * for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onPageError: function (type, packet) {
++  _onPageError: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       this.dispatchMessageAdd(packet);
+     } else {
+       this.webConsoleFrame.handlePageError(packet.pageError);
+     }
+@@ -310,17 +307,17 @@ WebConsoleConnectionProxy.prototype = {
+    * for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onLogMessage: function (type, packet) {
++  _onLogMessage: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       this.dispatchMessageAdd(packet);
+     } else {
+       this.webConsoleFrame.handleLogMessage(packet);
+     }
+@@ -330,17 +327,17 @@ WebConsoleConnectionProxy.prototype = {
+    * the UI for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onConsoleAPICall: function (type, packet) {
++  _onConsoleAPICall: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       this.dispatchMessageAdd(packet);
+     } else {
+       this.webConsoleFrame.handleConsoleAPICall(packet.message);
+     }
+@@ -350,17 +347,17 @@ WebConsoleConnectionProxy.prototype = {
+    * the UI for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object networkInfo
+    *        The network request information.
+    */
+-  _onNetworkEvent: function (type, networkInfo) {
++  _onNetworkEvent: function(type, networkInfo) {
+     if (!this.webConsoleFrame) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       this.dispatchMessageAdd(networkInfo);
+     } else {
+       this.webConsoleFrame.handleNetworkEvent(networkInfo);
+     }
+@@ -370,17 +367,17 @@ WebConsoleConnectionProxy.prototype = {
+    * the UI for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object response
+    *        The update response received from the server.
+    */
+-  _onNetworkEventUpdate: function (type, response) {
++  _onNetworkEventUpdate: function(type, response) {
+     if (!this.webConsoleFrame) {
+       return;
+     }
+     let { packet, networkInfo } = response;
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       this.dispatchMessageUpdate(networkInfo, response);
+     } else {
+       this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet);
+@@ -391,27 +388,27 @@ WebConsoleConnectionProxy.prototype = {
+    * the UI for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onFileActivity: function (type, packet) {
++  _onFileActivity: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       // TODO: Implement for new console
+     } else {
+       this.webConsoleFrame.handleFileActivity(packet.uri);
+     }
+   },
+-  _onReflowActivity: function (type, packet) {
++  _onReflowActivity: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       // TODO: Implement for new console
+     } else {
+       this.webConsoleFrame.handleReflowActivity(packet);
+     }
+@@ -421,17 +418,17 @@ WebConsoleConnectionProxy.prototype = {
+    * the UI for displaying.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onServerLogCall: function (type, packet) {
++  _onServerLogCall: function(type, packet) {
+     if (!this.webConsoleFrame || packet.from != this._consoleActor) {
+       return;
+     }
+     if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
+       // TODO: Implement for new console
+     } else {
+       this.webConsoleFrame.handleConsoleAPICall(packet.message);
+     }
+@@ -441,59 +438,59 @@ WebConsoleConnectionProxy.prototype = {
+    * received the Web Console UI is cleared.
+    *
+    * @private
+    * @param string type
+    *        Message type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onLastPrivateContextExited: function (type, packet) {
++  _onLastPrivateContextExited: function(type, packet) {
+     if (this.webConsoleFrame && packet.from == this._consoleActor) {
+       this.webConsoleFrame.jsterm.clearPrivateMessages();
+     }
+   },
+ 
+   /**
+    * The "will-navigate" and "navigate" event handlers. We redirect any message
+    * to the UI for displaying.
+    *
+    * @private
+    * @param string event
+    *        Event type.
+    * @param object packet
+    *        The message received from the server.
+    */
+-  _onTabNavigated: function (event, packet) {
++  _onTabNavigated: function(event, packet) {
+     if (!this.webConsoleFrame) {
+       return;
+     }
+ 
+     this.webConsoleFrame.handleTabNavigated(event, packet);
+   },
+ 
+   /**
+    * Release an object actor.
+    *
+    * @param string actor
+    *        The actor ID to send the request to.
+    */
+-  releaseActor: function (actor) {
++  releaseActor: function(actor) {
+     if (this.client) {
+       this.client.release(actor);
+     }
+   },
+ 
+   /**
+    * Disconnect the Web Console from the remote server.
+    *
+    * @return object
+    *         A promise object that is resolved when disconnect completes.
+    */
+-  disconnect: function () {
++  disconnect: function() {
+     if (this._disconnecter) {
+       return this._disconnecter.promise;
+     }
+ 
+     this._disconnecter = defer();
+ 
+     if (!this.client) {
+       this._disconnecter.resolve(null);

+ 310 - 0
frg/work-js/mozilla-release/patches/1446941-61a1.patch

@@ -0,0 +1,310 @@
+# HG changeset patch
+# User Nicolas Chevobbe <nchevobbe@mozilla.com>
+# Date 1521459340 -3600
+# Node ID 97f72a6b16c63cf2c2eead4eecb1d36a29a99731
+# Parent  6be0f74b6b6be6b726b2fea5ffea4d18d55a7c73
+Bug 1446941 - Remove old-event-emitter usage from styleeditor; r=gl.
+
+MozReview-Commit-ID: D8szZGCwb9i
+
+diff --git a/devtools/client/inspector/rules/test/browser_rules_original-source-link.js b/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
+--- a/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
++++ b/devtools/client/inspector/rules/test/browser_rules_original-source-link.js
+@@ -47,17 +47,17 @@ function* testClickingLink(toolbox, view
+   link.scrollIntoView();
+   link.click();
+   yield onStyleEditorReady;
+ }
+ 
+ function checkDisplayedStylesheet(toolbox) {
+   let panel = toolbox.getCurrentPanel();
+   return new Promise((resolve, reject) => {
+-    panel.UI.on("editor-selected", (event, editor) => {
++    panel.UI.on("editor-selected", editor => {
+       // The style editor selects the first sheet at first load before
+       // selecting the desired sheet.
+       if (editor.styleSheet.href.endsWith("scss")) {
+         info("Original source editor selected");
+         editor.getSourceEditor().then(editorSelected)
+           .then(resolve, reject);
+       }
+     });
+diff --git a/devtools/client/inspector/rules/test/browser_rules_original-source-link2.js b/devtools/client/inspector/rules/test/browser_rules_original-source-link2.js
+--- a/devtools/client/inspector/rules/test/browser_rules_original-source-link2.js
++++ b/devtools/client/inspector/rules/test/browser_rules_original-source-link2.js
+@@ -47,17 +47,17 @@ function* testClickingLink(toolbox, view
+   link.scrollIntoView();
+   link.click();
+   yield onStyleEditorReady;
+ }
+ 
+ function checkDisplayedStylesheet(toolbox) {
+   let panel = toolbox.getCurrentPanel();
+   return new Promise((resolve, reject) => {
+-    panel.UI.on("editor-selected", (event, editor) => {
++    panel.UI.on("editor-selected", editor => {
+       // The style editor selects the first sheet at first load before
+       // selecting the desired sheet.
+       if (editor.styleSheet.href.endsWith("scss")) {
+         info("Original source editor selected");
+         editor.getSourceEditor().then(editorSelected)
+           .then(resolve, reject);
+       }
+     });
+diff --git a/devtools/client/inspector/test/head.js b/devtools/client/inspector/test/head.js
+--- a/devtools/client/inspector/test/head.js
++++ b/devtools/client/inspector/test/head.js
+@@ -577,17 +577,17 @@ function waitForStyleEditor(toolbox, hre
+ 
+   return new Promise(resolve => {
+     toolbox.once("styleeditor-selected").then(() => {
+       let panel = toolbox.getCurrentPanel();
+       ok(panel && panel.UI, "Styleeditor panel switched to front");
+ 
+       // A helper that resolves the promise once it receives an editor that
+       // matches the expected href. Returns false if the editor was not correct.
+-      let gotEditor = (event, editor) => {
++      let gotEditor = editor => {
+         let currentHref = editor.styleSheet.href;
+         if (!href || (href && currentHref.endsWith(href))) {
+           info("Stylesheet editor selected");
+           panel.UI.off("editor-selected", gotEditor);
+ 
+           editor.getSourceEditor().then(sourceEditor => {
+             info("Stylesheet editor fully loaded");
+             resolve(sourceEditor);
+@@ -598,17 +598,17 @@ function waitForStyleEditor(toolbox, hre
+ 
+         info("The editor was incorrect. Waiting for editor-selected event.");
+         return false;
+       };
+ 
+       // The expected editor may already be selected. Check the if the currently
+       // selected editor is the expected one and if not wait for an
+       // editor-selected event.
+-      if (!gotEditor("styleeditor-selected", panel.UI.selectedEditor)) {
++      if (!gotEditor(panel.UI.selectedEditor)) {
+         // The expected editor is not selected (yet). Wait for it.
+         panel.UI.on("editor-selected", gotEditor);
+       }
+     });
+   });
+ }
+ 
+ /**
+diff --git a/devtools/client/styleeditor/StyleEditorUI.jsm b/devtools/client/styleeditor/StyleEditorUI.jsm
+--- a/devtools/client/styleeditor/StyleEditorUI.jsm
++++ b/devtools/client/styleeditor/StyleEditorUI.jsm
+@@ -6,17 +6,17 @@
+ "use strict";
+ 
+ this.EXPORTED_SYMBOLS = ["StyleEditorUI"];
+ 
+ const {loader, require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const Services = require("Services");
+ const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
+ const {OS} = require("resource://gre/modules/osfile.jsm");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ const {
+   getString,
+   text,
+   wire,
+   showFilePicker,
+ } = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+ const {SplitView} = require("resource://devtools/client/shared/SplitView.jsm");
+@@ -429,22 +429,20 @@ StyleEditorUI.prototype = {
+     };
+ 
+     showFilePicker(file, false, parentWindow, onFileSelected);
+   },
+ 
+   /**
+    * Forward any error from a stylesheet.
+    *
+-   * @param  {string} event
+-   *         Event name
+    * @param  {data} data
+    *         The event data
+    */
+-  _onError: function(event, data) {
++  _onError: function(data) {
+     this.emit("error", data);
+   },
+ 
+   /**
+    * Toggle the original sources pref.
+    */
+   _toggleOrigSources: function() {
+     let isEnabled = Services.prefs.getBoolPref(PREF_ORIG_SOURCES);
+@@ -501,17 +499,17 @@ StyleEditorUI.prototype = {
+    * @param {StyleSheetEditor}  editor
+    *        The editor to remove.
+    */
+   _removeStyleSheetEditor: function(editor) {
+     if (editor.summary) {
+       this._view.removeItem(editor.summary);
+     } else {
+       let self = this;
+-      this.on("editor-added", function onAdd(event, added) {
++      this.on("editor-added", function onAdd(added) {
+         if (editor == added) {
+           self.off("editor-added", onAdd);
+           self._view.removeItem(editor.summary);
+         }
+       });
+     }
+ 
+     editor.destroy();
+@@ -741,34 +739,34 @@ StyleEditorUI.prototype = {
+   getEditorSummary: function(editor) {
+     let self = this;
+ 
+     if (editor.summary) {
+       return Promise.resolve(editor.summary);
+     }
+ 
+     return new Promise(resolve => {
+-      this.on("editor-added", function onAdd(e, selected) {
++      this.on("editor-added", function onAdd(selected) {
+         if (selected == editor) {
+           self.off("editor-added", onAdd);
+           resolve(editor.summary);
+         }
+       });
+     });
+   },
+ 
+   getEditorDetails: function(editor) {
+     let self = this;
+ 
+     if (editor.details) {
+       return Promise.resolve(editor.details);
+     }
+ 
+     return new Promise(resolve => {
+-      this.on("editor-added", function onAdd(e, selected) {
++      this.on("editor-added", function onAdd(selected) {
+         if (selected == editor) {
+           self.off("editor-added", onAdd);
+           resolve(editor.details);
+         }
+       });
+     });
+   },
+ 
+diff --git a/devtools/client/styleeditor/StyleSheetEditor.jsm b/devtools/client/styleeditor/StyleSheetEditor.jsm
+--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
++++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
+@@ -8,17 +8,17 @@
+ this.EXPORTED_SYMBOLS = ["StyleSheetEditor"];
+ 
+ const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const Editor = require("devtools/client/sourceeditor/editor");
+ const promise = require("promise");
+ const {shortSource, prettifyCSS} = require("devtools/shared/inspector/css-logic");
+ const {console} = require("resource://gre/modules/Console.jsm");
+ const Services = require("Services");
+-const EventEmitter = require("devtools/shared/old-event-emitter");
++const EventEmitter = require("devtools/shared/event-emitter");
+ const {FileUtils} = require("resource://gre/modules/FileUtils.jsm");
+ const {NetUtil} = require("resource://gre/modules/NetUtil.jsm");
+ const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm", {});
+ const {
+   getString,
+   showFilePicker,
+ } = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+ 
+@@ -397,21 +397,19 @@ StyleSheetEditor.prototype = {
+    */
+   _onMediaRuleMatchesChange: function() {
+     this.emit("media-rules-changed", this.mediaRules);
+   },
+ 
+   /**
+    * Forward error event from stylesheet.
+    *
+-   * @param  {string} event
+-   *         Event type
+-   * @param  {string} errorCode
++   * @param  {Object} data: The parameters to customize the error message
+    */
+-  _onError: function(event, data) {
++  _onError: function(data) {
+     this.emit("error", data);
+   },
+ 
+   /**
+    * Create source editor and load state into it.
+    * @param  {DOMElement} inputElement
+    *         Element to load source editor in
+    * @param  {CssProperties} cssProperties
+diff --git a/devtools/client/styleeditor/styleeditor-panel.js b/devtools/client/styleeditor/styleeditor-panel.js
+--- a/devtools/client/styleeditor/styleeditor-panel.js
++++ b/devtools/client/styleeditor/styleeditor-panel.js
+@@ -2,17 +2,17 @@
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+ "use strict";
+ 
+ var Services = require("Services");
+ var promise = require("promise");
+ var {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
+-var EventEmitter = require("devtools/shared/old-event-emitter");
++var EventEmitter = require("devtools/shared/event-emitter");
+ 
+ var {StyleEditorUI} = require("resource://devtools/client/styleeditor/StyleEditorUI.jsm");
+ var {getString} = require("resource://devtools/client/styleeditor/StyleEditorUtil.jsm");
+ var {initCssProperties} = require("devtools/shared/fronts/css-properties");
+ 
+ var StyleEditorPanel = function StyleEditorPanel(panelWin, toolbox) {
+   EventEmitter.decorate(this);
+ 
+@@ -62,22 +62,20 @@ StyleEditorPanel.prototype = {
+ 
+     return this;
+   },
+ 
+   /**
+    * Show an error message from the style editor in the toolbox
+    * notification box.
+    *
+-   * @param  {string} event
+-   *         Type of event
+    * @param  {string} data
+    *         The parameters to customize the error message
+    */
+-  _showError: function(event, data) {
++  _showError: function(data) {
+     if (!this._toolbox) {
+       // could get an async error after we've been destroyed
+       return;
+     }
+ 
+     let errorMessage = getString(data.key);
+     if (data.append) {
+       errorMessage += " " + data.append;
+diff --git a/devtools/client/styleeditor/test/browser_styleeditor_new.js b/devtools/client/styleeditor/test/browser_styleeditor_new.js
+--- a/devtools/client/styleeditor/test/browser_styleeditor_new.js
++++ b/devtools/client/styleeditor/test/browser_styleeditor_new.js
+@@ -24,17 +24,17 @@ add_task(async function() {
+ 
+   testUpdated(editor, originalHref);
+ });
+ 
+ function createNew(ui, panelWindow) {
+   info("Creating a new stylesheet now");
+ 
+   return new Promise(resolve => {
+-    ui.once("editor-added", (ev, editor) => {
++    ui.once("editor-added", editor => {
+       editor.getSourceEditor().then(resolve);
+     });
+ 
+     waitForFocus(function() {
+       // create a new style sheet
+       let newButton = panelWindow.document
+         .querySelector(".style-editor-newButton");
+       ok(newButton, "'new' button exists");

+ 73 - 0
frg/work-js/mozilla-release/patches/1447154-1-61a1.patch

@@ -0,0 +1,73 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1521494349 -3600
+# Node ID d5f46438881b8110515c568a67b92697f029812f
+# Parent  ad5169bb3e13eeb06d710a6a23b283e515e9c674
+Bug 1447154 - Remove unused getOSCPU method from devtools/shared/system;r=jryans
+
+MozReview-Commit-ID: JyzCszMF1UF
+
+diff --git a/devtools/shared/system.js b/devtools/shared/system.js
+--- a/devtools/shared/system.js
++++ b/devtools/shared/system.js
+@@ -242,49 +242,16 @@ function getScreenDimensions() {
+   if (width.value > 2880 || height.value > 1800) {
+     return 12;
+   }
+ 
+   // Other dimension such as a VM.
+   return 11;
+ }
+ 
+-/**
+- * Function for fetching OS CPU and returning
+- * an enum for Telemetry.
+- */
+-function getOSCPU() {
+-  if (oscpu.includes("NT 5.1") || oscpu.includes("NT 5.2")) {
+-    return 0;
+-  }
+-  if (oscpu.includes("NT 6.0")) {
+-    return 1;
+-  }
+-  if (oscpu.includes("NT 6.1")) {
+-    return 2;
+-  }
+-  if (oscpu.includes("NT 6.2")) {
+-    return 3;
+-  }
+-  if (oscpu.includes("NT 6.3")) {
+-    return 4;
+-  }
+-  if (oscpu.includes("OS X")) {
+-    return 5;
+-  }
+-  if (oscpu.includes("Linux")) {
+-    return 6;
+-  }
+-  if (oscpu.includes("NT 10.")) {
+-    return 7;
+-  }
+-  // Other OS.
+-  return 12;
+-}
+-
+ function getSetting(name) {
+   let deferred = defer();
+ 
+   if ("@mozilla.org/settingsService;1" in Cc) {
+     let settingsService;
+ 
+     // TODO bug 1205797, make this work in child processes.
+     try {
+@@ -302,10 +269,9 @@ function getSetting(name) {
+     deferred.reject(new Error("No settings service"));
+   }
+   return deferred.promise;
+ }
+ 
+ exports.getSystemInfo = getSystemInfo;
+ exports.getSetting = getSetting;
+ exports.getScreenDimensions = getScreenDimensions;
+-exports.getOSCPU = getOSCPU;
+ exports.constants = AppConstants;

+ 402 - 0
frg/work-js/mozilla-release/patches/1447154-2-61a1.patch

@@ -0,0 +1,402 @@
+# HG changeset patch
+# User Julian Descottes <jdescottes@mozilla.com>
+# Date 1521494300 -3600
+# Node ID 0500c3c587140e787272da07346380c98245c5e2
+# Parent  23bc2d4e8241bb0156b35c31abc61e3df8f5c08d
+Bug 1447154 - Use AppConstants.jsm instead of devtools/shared/system to get constants;r=jryans,nchevobbe
+
+devtools/shared/system is a complex module retrieving details device/platform
+information. Modules that only need to get AppConstants should not pull down
+such a complex dependency.
+
+MozReview-Commit-ID: 2FmCO8nBSpP
+
+diff --git a/devtools/client/framework/ToolboxProcess.jsm b/devtools/client/framework/ToolboxProcess.jsm
+--- a/devtools/client/framework/ToolboxProcess.jsm
++++ b/devtools/client/framework/ToolboxProcess.jsm
+@@ -9,25 +9,25 @@
+ const DBG_XUL = "chrome://devtools/content/framework/toolbox-process-window.xul";
+ const CHROME_DEBUGGER_PROFILE_NAME = "chrome_debugger_profile";
+ 
+ const { console } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
+ const { require, DevToolsLoader } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+ const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
+ 
+ ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
++ChromeUtils.defineModuleGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
++
+ XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
+   return require("devtools/client/shared/telemetry");
+ });
+ XPCOMUtils.defineLazyGetter(this, "EventEmitter", function() {
+   return require("devtools/shared/event-emitter");
+ });
+-XPCOMUtils.defineLazyGetter(this, "system", function() {
+-  return require("devtools/shared/system");
+-});
++
+ const promise = require("promise");
+ const Services = require("Services");
+ 
+ this.EXPORTED_SYMBOLS = ["BrowserToolboxProcess"];
+ 
+ var processes = new Set();
+ 
+ /**
+@@ -285,17 +285,17 @@ BrowserToolboxProcess.prototype = {
+ 
+     // During local development, incremental builds can trigger the main process
+     // to clear its startup cache with the "flag file" .purgecaches, but this
+     // file is removed during app startup time, so we aren't able to know if it
+     // was present in order to also clear the child profile's startup cache as
+     // well.
+     //
+     // As an approximation of "isLocalBuild", check for an unofficial build.
+-    if (!system.constants.MOZILLA_OFFICIAL) {
++    if (!AppConstants.MOZILLA_OFFICIAL) {
+       args.push("-purgecaches");
+     }
+ 
+     this._dbgProcessPromise = Subprocess.call({
+       command,
+       arguments: args,
+       environmentAppend: true,
+       stderr: "stdout",
+diff --git a/devtools/client/framework/toolbox-hosts.js b/devtools/client/framework/toolbox-hosts.js
+--- a/devtools/client/framework/toolbox-hosts.js
++++ b/devtools/client/framework/toolbox-hosts.js
+@@ -7,17 +7,17 @@
+ "use strict";
+ 
+ const EventEmitter = require("devtools/shared/event-emitter");
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ const {DOMHelpers} = require("resource://devtools/client/shared/DOMHelpers.jsm");
+ 
+-loader.lazyRequireGetter(this, "system", "devtools/shared/system");
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
+ loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
+ 
+ /* A host should always allow this much space for the page to be displayed.
+  * There is also a min-height on the browser, but we still don't want to set
+  * frame.height to be larger than that, since it can cause problems with
+  * resizing the toolbox and panel layout. */
+ const MIN_PAGE_SIZE = 25;
+ 
+@@ -290,17 +290,17 @@ WindowHost.prototype = {
+     let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
+                                      flags, null);
+ 
+     let frameLoad = () => {
+       win.removeEventListener("load", frameLoad, true);
+       win.focus();
+ 
+       let key;
+-      if (system.constants.platform === "macosx") {
++      if (AppConstants.platform === "macosx") {
+         key = win.document.getElementById("toolbox-key-toggle-osx");
+       } else {
+         key = win.document.getElementById("toolbox-key-toggle");
+       }
+       key.removeAttribute("disabled");
+ 
+       this.frame = win.document.getElementById("toolbox-iframe");
+       this.emit("ready", this.frame);
+diff --git a/devtools/client/framework/toolbox-options.js b/devtools/client/framework/toolbox-options.js
+--- a/devtools/client/framework/toolbox-options.js
++++ b/devtools/client/framework/toolbox-options.js
+@@ -6,17 +6,17 @@
+ 
+ const Services = require("Services");
+ const defer = require("devtools/shared/defer");
+ const {gDevTools} = require("devtools/client/framework/devtools");
+ 
+ const {LocalizationHelper} = require("devtools/shared/l10n");
+ const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
+ 
+-loader.lazyRequireGetter(this, "system", "devtools/shared/system");
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
+ 
+ exports.OptionsPanel = OptionsPanel;
+ 
+ function GetPref(name) {
+   let type = Services.prefs.getPrefType(name);
+   switch (type) {
+     case Services.prefs.PREF_STRING:
+       return Services.prefs.getCharPref(name);
+@@ -305,17 +305,17 @@ OptionsPanel.prototype = {
+ 
+     this.updateCurrentTheme();
+   },
+ 
+   /**
+    * Add common preferences enabled only on Nightly.
+    */
+   setupNightlyOptions: function() {
+-    let isNightly = system.constants.NIGHTLY_BUILD;
++    let isNightly = AppConstants.NIGHTLY_BUILD;
+     if (!isNightly) {
+       return;
+     }
+ 
+     // Labels for these new buttons are nightly only and mostly intended for working on
+     // devtools.
+     let prefDefinitions = [{
+       pref: "devtools.webconsole.new-frontend-enabled",
+diff --git a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js
+--- a/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js
++++ b/devtools/client/inspector/rules/test/browser_rules_edit-property-increments.js
+@@ -5,17 +5,17 @@
+ "use strict";
+ 
+ // Test that increasing/decreasing values in rule view using
+ // arrow keys works correctly.
+ 
+ // Bug 1275446 - This test happen to hit the default timeout on linux32
+ requestLongerTimeout(2);
+ 
+-loader.lazyRequireGetter(this, "system", "devtools/shared/system");
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
+ 
+ const TEST_URI = `
+   <style>
+     #test {
+       margin-top: 0px;
+       padding-top: 0px;
+       color: #000000;
+       background-color: #000000;
+@@ -289,17 +289,17 @@ function* testIncrement(editor, options,
+   key = options.down ? "VK_DOWN" : "VK_UP";
+   if (options.pageDown) {
+     key = "VK_PAGE_DOWN";
+   } else if (options.pageUp) {
+     key = "VK_PAGE_UP";
+   }
+ 
+   let smallIncrementKey = {ctrlKey: options.ctrl};
+-  if (system.constants.platform === "macosx") {
++  if (AppConstants.platform === "macosx") {
+     smallIncrementKey = {altKey: options.alt};
+   }
+ 
+   EventUtils.synthesizeKey(key, {...smallIncrementKey, shiftKey: options.shift},
+     view.styleWindow);
+ 
+   yield onKeyUp;
+ 
+@@ -308,13 +308,13 @@ function* testIncrement(editor, options,
+     view.debounce.flush();
+     yield onRuleViewChanged;
+   }
+ 
+   is(input.value, options.end, "Value changed to " + options.end);
+ }
+ 
+ function getSmallIncrementKey() {
+-  if (system.constants.platform === "macosx") {
++  if (AppConstants.platform === "macosx") {
+     return { alt: true };
+   }
+   return { ctrl: true };
+ }
+diff --git a/devtools/client/shared/inplace-editor.js b/devtools/client/shared/inplace-editor.js
+--- a/devtools/client/shared/inplace-editor.js
++++ b/devtools/client/shared/inplace-editor.js
+@@ -22,17 +22,17 @@
+  */
+ 
+ "use strict";
+ 
+ const Services = require("Services");
+ const focusManager = Services.focus;
+ const {KeyCodes} = require("devtools/client/shared/keycodes");
+ 
+-loader.lazyRequireGetter(this, "system", "devtools/shared/system");
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
+ 
+ const HTML_NS = "http://www.w3.org/1999/xhtml";
+ const CONTENT_TYPES = {
+   PLAIN_TEXT: 0,
+   CSS_VALUE: 1,
+   CSS_MIXED: 2,
+   CSS_PROPERTY: 3,
+ };
+@@ -1204,17 +1204,17 @@ InplaceEditor.prototype = {
+     this.popup.hidePopup();
+   },
+ 
+   /**
+    * Get the increment/decrement step to use for the provided key event.
+    */
+   _getIncrement: function(event) {
+     const getSmallIncrementKey = (evt) => {
+-      if (system.constants.platform === "macosx") {
++      if (AppConstants.platform === "macosx") {
+         return evt.altKey;
+       }
+       return evt.ctrlKey;
+     };
+ 
+     const largeIncrement = 100;
+     const mediumIncrement = 10;
+     const smallIncrement = 0.1;
+diff --git a/devtools/client/shared/webpack/shims/system-stub.js b/devtools/client/shared/webpack/shims/app-constants-stub.js
+rename from devtools/client/shared/webpack/shims/system-stub.js
+rename to devtools/client/shared/webpack/shims/app-constants-stub.js
+--- a/devtools/client/shared/webpack/shims/system-stub.js
++++ b/devtools/client/shared/webpack/shims/app-constants-stub.js
+@@ -6,12 +6,12 @@
+ 
+ var platform = "";
+ 
+ if (/Mac OS X/.test(window.navigator.userAgent)) {
+   platform = "macosx";
+ }
+ 
+ module.exports = {
+-  constants: {
+-    platform: platform
++  AppConstants: {
++    platform
+   }
+ };
+diff --git a/devtools/client/webconsole/new-webconsole.js b/devtools/client/webconsole/new-webconsole.js
+--- a/devtools/client/webconsole/new-webconsole.js
++++ b/devtools/client/webconsole/new-webconsole.js
+@@ -11,17 +11,19 @@ const EventEmitter = require("devtools/s
+ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ const { gDevTools } = require("devtools/client/framework/devtools");
+ const { JSTerm } = require("devtools/client/webconsole/jsterm");
+ const { WebConsoleConnectionProxy } = require("devtools/client/webconsole/webconsole-connection-proxy");
+ const KeyShortcuts = require("devtools/client/shared/key-shortcuts");
+ const { l10n } = require("devtools/client/webconsole/new-console-output/utils/messages");
+-const system = require("devtools/shared/system");
++
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
++
+ const ZoomKeys = require("devtools/client/shared/zoom-keys");
+ 
+ const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
+ const PREF_PERSISTLOG = "devtools.webconsole.persistlog";
+ const PREF_SIDEBAR_ENABLED = "devtools.webconsole.sidebarToggle";
+ 
+ // XXX: This file is incomplete (see bug 1326937).
+ // It's used when loading the webconsole with devtools-launchpad, but will ultimately be
+@@ -235,31 +237,31 @@ NewWebConsoleFrame.prototype = {
+ 
+     shortcuts.on(l10n.getStr("webconsole.find.key"),
+                  event => {
+                    this.filterBox.focus();
+                    event.preventDefault();
+                  });
+ 
+     let clearShortcut;
+-    if (system.constants.platform === "macosx") {
++    if (AppConstants.platform === "macosx") {
+       clearShortcut = l10n.getStr("webconsole.clear.keyOSX");
+     } else {
+       clearShortcut = l10n.getStr("webconsole.clear.key");
+     }
+ 
+     shortcuts.on(clearShortcut, () => this.jsterm.clearOutput(true));
+ 
+     if (this.isBrowserConsole) {
+       shortcuts.on(l10n.getStr("webconsole.close.key"),
+                    this.window.top.close.bind(this.window.top));
+ 
+       ZoomKeys.register(this.window);
+ 
+-      if (!system.constants.MOZILLA_OFFICIAL) {
++      if (!AppConstants.MOZILLA_OFFICIAL) {
+         // In local builds, inject the "quick restart" shortcut.
+         // This script expects to have Services on the global and we haven't yet imported
+         // it into the window, so assign it.
+         this.window.Services = Services;
+         Services.scriptloader.loadSubScript(
+           "chrome://browser/content/browser-development-helpers.js", this.window);
+         shortcuts.on("CmdOrCtrl+Alt+R", this.window.DevelopmentHelpers.quickRestart);
+       }
+diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js
+--- a/devtools/client/webconsole/webconsole.js
++++ b/devtools/client/webconsole/webconsole.js
+@@ -20,22 +20,22 @@ const promise = require("promise");
+ const defer = require("devtools/shared/defer");
+ const Services = require("Services");
+ const Telemetry = require("devtools/client/shared/telemetry");
+ const {PrefObserver} = require("devtools/client/shared/prefs");
+ 
+ loader.lazyServiceGetter(this, "clipboardHelper",
+                          "@mozilla.org/widget/clipboardhelper;1",
+                          "nsIClipboardHelper");
++loader.lazyRequireGetter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm", true);
+ loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
+ loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
+ loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
+ loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/environment-client");
+ loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/object-client");
+-loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+ loader.lazyRequireGetter(this, "JSTerm", "devtools/client/webconsole/jsterm", true);
+ loader.lazyRequireGetter(this, "gSequenceId", "devtools/client/webconsole/jsterm", true);
+ loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
+ loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
+ loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
+ loader.lazyRequireGetter(this, "KeyShortcuts", "devtools/client/shared/key-shortcuts");
+ loader.lazyRequireGetter(this, "ZoomKeys", "devtools/client/shared/zoom-keys");
+ loader.lazyRequireGetter(this, "WebConsoleConnectionProxy", "devtools/client/webconsole/webconsole-connection-proxy", true);
+@@ -637,17 +637,17 @@ WebConsoleFrame.prototype = {
+ 
+     shortcuts.on(l10n.getStr("webconsole.find.key"),
+                  event => {
+                    this.filterBox.focus();
+                    event.preventDefault();
+                  });
+ 
+     let clearShortcut;
+-    if (system.constants.platform === "macosx") {
++    if (AppConstants.platform === "macosx") {
+       clearShortcut = l10n.getStr("webconsole.clear.keyOSX");
+     } else {
+       clearShortcut = l10n.getStr("webconsole.clear.key");
+     }
+     shortcuts.on(clearShortcut,
+                  () => this.jsterm.clearOutput(true));
+ 
+     if (this.isBrowserConsole) {
+diff --git a/devtools/client/webconsole/webpack.config.js b/devtools/client/webconsole/webpack.config.js
+--- a/devtools/client/webconsole/webpack.config.js
++++ b/devtools/client/webconsole/webpack.config.js
+@@ -82,17 +82,17 @@ webpackConfig.resolve = {
+ 
+     "devtools/client/shared/vendor/immutable": "immutable",
+     "devtools/client/shared/vendor/react": "react",
+     "devtools/client/shared/vendor/react-dom": "react-dom",
+     "devtools/client/shared/vendor/react-redux": "react-redux",
+     "devtools/client/shared/vendor/redux": "redux",
+     "devtools/client/shared/vendor/reselect": "reselect",
+ 
+-    "devtools/shared/system": path.join(__dirname, "../../client/shared/webpack/shims/system-stub"),
++    "resource://gre/modules/AppConstants.jsm": path.join(__dirname, "../../client/shared/webpack/shims/app-constants-stub"),
+ 
+     "devtools/client/framework/devtools": path.join(__dirname, "../../client/shared/webpack/shims/framework-devtools-shim"),
+     "devtools/client/framework/menu": "devtools-modules/src/menu",
+     "devtools/client/sourceeditor/editor": "devtools-source-editor/src/source-editor",
+ 
+     "devtools/client/shared/zoom-keys": "devtools-modules/src/zoom-keys",
+ 
+     "devtools/shared/fronts/timeline": path.join(__dirname, "../../client/shared/webpack/shims/fronts-timeline-shim"),
+diff --git a/devtools/shared/system.js b/devtools/shared/system.js
+--- a/devtools/shared/system.js
++++ b/devtools/shared/system.js
+@@ -269,9 +269,8 @@ function getSetting(name) {
+     deferred.reject(new Error("No settings service"));
+   }
+   return deferred.promise;
+ }
+ 
+ exports.getSystemInfo = getSystemInfo;
+ exports.getSetting = getSetting;
+ exports.getScreenDimensions = getScreenDimensions;
+-exports.constants = AppConstants;

+ 32 - 0
frg/work-js/mozilla-release/patches/1447180-61a1.patch

@@ -0,0 +1,32 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1523239081 -32400
+# Node ID a3555e4774717ea62b9bf66f41d24d0d58e4c863
+# Parent  51f4171efe30404f82b53dc8e0f6e3a19e5d6f35
+Bug 1447180 - Wait for the end of the second tab streams before continuing test for first tab in browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js. r=dao
+
+diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
++++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+@@ -310,17 +310,20 @@ var gTests = [
+ 
+     info("removing the second tab");
+     BrowserTestUtils.removeTab(tab);
+ 
+     // When both tabs use the same content process, the frame script for the
+     // first tab receives observer notifications for things happening in the
+     // second tab, so let's clear the observer call counts before we cleanup
+     // in the first tab.
+-    await ignoreObserversCalled();
++    await Promise.all([
++      TestUtils.waitForCondition(() => webrtcUI.getActiveStreams(true, true, true).length == 1),
++      ignoreObserversCalled(),
++    ]);
+ 
+     // Close the first tab's stream and verify that all indicators are removed.
+     await closeStream();
+ 
+     ok(!webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator hidden");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 0, "0 active streams");
+   }
+ },

+ 94 - 0
frg/work-js/mozilla-release/patches/1447528-61a1.patch

@@ -0,0 +1,94 @@
+# HG changeset patch
+# User Gabriel Luong <gabriel.luong@gmail.com>
+# Date 1521661172 14400
+# Node ID 9c72849930369a16fcb0eb5ae407526972a87982
+# Parent  f46b5008e506a753762af0b90fbaf6a78a205386
+Bug 1447528 - Remove the pref observer for the split rule view pref in the inspector. r=pbro
+
+diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js
+--- a/devtools/client/inspector/inspector.js
++++ b/devtools/client/inspector/inspector.js
+@@ -132,20 +132,18 @@ function Inspector(toolbox) {
+   this.onPanelWindowResize = this.onPanelWindowResize.bind(this);
+   this.onShowBoxModelHighlighterForNode =
+     this.onShowBoxModelHighlighterForNode.bind(this);
+   this.onSidebarHidden = this.onSidebarHidden.bind(this);
+   this.onSidebarResized = this.onSidebarResized.bind(this);
+   this.onSidebarSelect = this.onSidebarSelect.bind(this);
+   this.onSidebarShown = this.onSidebarShown.bind(this);
+   this.onSidebarToggle = this.onSidebarToggle.bind(this);
+-  this.onSplitRuleViewPrefChanged = this.onSplitRuleViewPrefChanged.bind(this);
+ 
+   this._target.on("will-navigate", this._onBeforeNavigate);
+-  this.prefsObserver.on(SPLIT_RULE_VIEW_PREF, this.onSplitRuleViewPrefChanged);
+ }
+ 
+ Inspector.prototype = {
+   /**
+    * open is effectively an asynchronous constructor
+    */
+   init: Task.async(function* () {
+     // Localize all the nodes containing a data-localization attribute.
+@@ -576,23 +574,19 @@ Inspector.prototype = {
+   },
+ 
+   onSidebarShown: function() {
+     let { width, height, splitSidebarWidth } = this.getSidebarSize();
+     this.splitBox.setState({ width, height });
+     this.sidebarSplitBox.setState({ width: splitSidebarWidth });
+   },
+ 
+-  onSidebarToggle: function() {
+-    Services.prefs.setBoolPref(SPLIT_RULE_VIEW_PREF, !this.isSplitRuleViewEnabled);
+-  },
+-
+-  async onSplitRuleViewPrefChanged() {
+-    // Update the stored value of the split rule view preference since it changed.
+-    this.isSplitRuleViewEnabled = Services.prefs.getBoolPref(SPLIT_RULE_VIEW_PREF);
++  async onSidebarToggle() {
++    this.isSplitRuleViewEnabled = !this.isSplitRuleViewEnabled;
++    Services.prefs.setBoolPref(SPLIT_RULE_VIEW_PREF, this.isSplitRuleViewEnabled);
+ 
+     await this.setupToolbar();
+     await this.addRuleView();
+   },
+ 
+   /**
+    * Adds the rule view to the main or split sidebar depending on whether or not it is
+    * split view mode. The default tab specifies whether or not the rule view should be
+@@ -1258,17 +1252,16 @@ Inspector.prototype = {
+ 
+     if (this.walker) {
+       this.walker.off("new-root", this.onNewRoot);
+       this.pageStyle = null;
+     }
+ 
+     this.cancelUpdate();
+ 
+-    this.prefsObserver.off(SPLIT_RULE_VIEW_PREF, this.onSplitRuleViewPrefChanged);
+     this.target.off("will-navigate", this._onBeforeNavigate);
+     this.target.off("thread-paused", this.updateDebuggerPausedWarning);
+     this.target.off("thread-resumed", this.updateDebuggerPausedWarning);
+     this._toolbox.off("select", this.updateDebuggerPausedWarning);
+ 
+     for (let [, panel] of this._panels) {
+       panel.destroy();
+     }
+@@ -1306,16 +1299,17 @@ Inspector.prototype = {
+     this.highlighters.destroy();
+     this.prefsObserver.destroy();
+     this.reflowTracker.destroy();
+     this.search.destroy();
+ 
+     this._toolbox = null;
+     this.breadcrumbs = null;
+     this.highlighters = null;
++    this.isSplitRuleViewEnabled = null;
+     this.panelDoc = null;
+     this.panelWin.inspector = null;
+     this.panelWin = null;
+     this.prefsObserver = null;
+     this.resultsLength = null;
+     this.search = null;
+     this.searchBox = null;
+     this.sidebar = null;

+ 143 - 0
frg/work-js/mozilla-release/patches/1448288-61a1.patch

@@ -0,0 +1,143 @@
+# HG changeset patch
+# User Jan Odvarko <odvarko@gmail.com>
+# Date 1521812312 -3600
+# Node ID 7e8b408417b8f559efdd9a84fd5412b3c8f3c9c2
+# Parent  25e69b705a40af883474bf37f201f799c94217c1
+Bug 1448288 - Make devtools.network.getHAR API compatible with Chrome; r=rpl
+
+MozReview-Commit-ID: 7woAf8iVC3B
+
+diff --git a/browser/components/extensions/test/browser/browser_ext_devtools_network.js b/browser/components/extensions/test/browser/browser_ext_devtools_network.js
+--- a/browser/components/extensions/test/browser/browser_ext_devtools_network.js
++++ b/browser/components/extensions/test/browser/browser_ext_devtools_network.js
+@@ -44,17 +44,17 @@ function devtools_page() {
+       return;
+     }
+ 
+     harLogCount++;
+ 
+     const harLog = await browser.devtools.network.getHAR();
+     browser.test.sendMessage("getHAR-result", harLog);
+ 
+-    if (harLogCount === 2) {
++    if (harLogCount === 3) {
+       harLogCount = 0;
+       browser.test.onMessage.removeListener(harListener);
+     }
+   };
+   browser.test.onMessage.addListener(harListener);
+ 
+   let requestFinishedListener = async request => {
+     browser.test.assertTrue(request.request, "Request entry must exist");
+@@ -161,21 +161,28 @@ add_task(async function test_devtools_ne
+   // Open the Toolbox
+   let toolbox = await gDevTools.showToolbox(target, "webconsole");
+   info("Developer toolbox opened.");
+ 
+   // Get HAR, it should be empty since the Net panel wasn't selected.
+   const getHAREmptyPromise = extension.awaitMessage("getHAR-result");
+   extension.sendMessage("getHAR");
+   const getHAREmptyResult = await getHAREmptyPromise;
+-  is(getHAREmptyResult.log.entries.length, 0, "HAR log should be empty");
++  is(getHAREmptyResult.entries.length, 0, "HAR log should be empty");
+ 
+   // Select the Net panel.
+   await toolbox.selectTool("netmonitor");
+ 
++  // Get HAR again, it should be empty because the Panel is selected
++  // but no data collected yet.
++  const getHAREmptyPromiseWithPanel = extension.awaitMessage("getHAR-result");
++  extension.sendMessage("getHAR");
++  const emptyResultWithPanel = await getHAREmptyPromiseWithPanel;
++  is(emptyResultWithPanel.entries.length, 0, "HAR log should be empty");
++
+   // Reload the page to collect some HTTP requests.
+   extension.sendMessage("navigate");
+ 
+   // Wait till the navigation is complete and request
+   // added into the net panel.
+   await Promise.all([
+     extension.awaitMessage("tabUpdated"),
+     extension.awaitMessage("onNavigatedFired"),
+@@ -184,17 +191,17 @@ add_task(async function test_devtools_ne
+     extension.awaitMessage("onRequestFinished-promiseResolved"),
+     waitForRequestAdded(toolbox),
+   ]);
+ 
+   // Get HAR, it should not be empty now.
+   const getHARPromise = extension.awaitMessage("getHAR-result");
+   extension.sendMessage("getHAR");
+   const getHARResult = await getHARPromise;
+-  is(getHARResult.log.entries.length, 1, "HAR log should not be empty");
++  is(getHARResult.entries.length, 1, "HAR log should not be empty");
+ 
+   // Shutdown
+   await gDevTools.closeToolbox(target);
+ 
+   await target.destroy();
+ 
+   await extension.unload();
+ 
+diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js
+--- a/devtools/client/framework/toolbox.js
++++ b/devtools/client/framework/toolbox.js
+@@ -3036,28 +3036,33 @@ Toolbox.prototype = {
+     return viewSource.viewSource(this, sourceURL, sourceLine);
+   },
+ 
+   // Support for WebExtensions API (`devtools.network.*`)
+ 
+   /**
+    * Returns data (HAR) collected by the Network panel.
+    */
+-  getHARFromNetMonitor: function() {
++  getHARFromNetMonitor: async function() {
+     let netPanel = this.getPanel("netmonitor");
+ 
+     // The panel doesn't have to exist (it must be selected
+     // by the user at least once to be created).
+-    // Return default empty HAR file in such case.
++    // Return default empty HAR log in such case.
+     if (!netPanel) {
+-      return Promise.resolve(buildHarLog(Services.appinfo));
++      let har = await buildHarLog(Services.appinfo);
++      return har.log;
+     }
+ 
+     // Use Netmonitor object to get the current HAR log.
+-    return netPanel.panelWin.Netmonitor.getHar();
++    let har = await netPanel.panelWin.Netmonitor.getHar();
++
++    // Return the log directly to be compatible with
++    // Chrome WebExtension API.
++    return har.log;
+   },
+ 
+   /**
+    * Add listener for `onRequestFinished` events.
+    *
+    * @param {Object} listener
+    *        The listener to be called it's expected to be
+    *        a function that takes ({harEntry, requestId})
+diff --git a/devtools/client/netmonitor/initializer.js b/devtools/client/netmonitor/initializer.js
+--- a/devtools/client/netmonitor/initializer.js
++++ b/devtools/client/netmonitor/initializer.js
+@@ -105,16 +105,18 @@ window.Netmonitor = {
+    */
+   getHar() {
+     let { HarExporter } = require("devtools/client/netmonitor/src/har/har-exporter");
+     let state = store.getState();
+ 
+     let options = {
+       connector,
+       items: getSortedRequests(state),
++      // Always generate HAR log even if there are no requests.
++      forceExport: true,
+     };
+ 
+     return HarExporter.getHar(options);
+   },
+ 
+   /**
+    * Support for `devtools.network.onRequestFinished`. A hook for
+    * every finished HTTP request used by WebExtensions API.

+ 45 - 0
frg/work-js/mozilla-release/patches/1450163-61a1.patch

@@ -0,0 +1,45 @@
+# HG changeset patch
+# User Tooru Fujisawa <arai_a@mac.com>
+# Date 1523239081 -32400
+# Node ID bfc452f47794b2d9b38a0dfe2b755858235048ff
+# Parent  e15cceecd70379bbca751c0b0c46598491b2a327
+Bug 1450163 - Wait for the end of the second tab streams before checking the number of streams and indicator in browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js. r=dao
+
+diff --git a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
++++ b/browser/base/content/test/webrtc/browser_devices_get_user_media_multi_process.js
+@@ -81,28 +81,29 @@ var gTests = [
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator shown");
+     ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+     is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+     is(webrtcUI.getActiveStreams(true).length, 1, "1 active video stream");
+     is(webrtcUI.getActiveStreams(true, true, true).length, 2, "2 active streams");
+ 
+     info("removing the second tab");
+-    // FIXME: This should wait for indicator update instead (bug 1444007).
+-    let sessionStorePromise = BrowserTestUtils.waitForSessionStoreUpdate(tab);
++
+     BrowserTestUtils.removeTab(tab);
+-    await sessionStorePromise;
+ 
+     // Check that we still show the sharing indicators for the first tab's stream.
+-    await promiseWaitForCondition(() => !webrtcUI.showCameraIndicator);
++    await Promise.all([
++      TestUtils.waitForCondition(() => !webrtcUI.showCameraIndicator),
++      TestUtils.waitForCondition(() => webrtcUI.getActiveStreams(true, true, true).length == 1),
++    ]);
++
+     ok(webrtcUI.showGlobalIndicator, "webrtcUI wants the global indicator shown");
+     ok(!webrtcUI.showCameraIndicator, "webrtcUI wants the camera indicator hidden");
+     ok(webrtcUI.showMicrophoneIndicator, "webrtcUI wants the mic indicator shown");
+     is(webrtcUI.getActiveStreams(false, true).length, 1, "1 active audio stream");
+-    is(webrtcUI.getActiveStreams(true, true, true).length, 1, "1 active stream");
+ 
+     await checkSharingUI({audio: true});
+ 
+     // When both tabs use the same content process, the frame script for the
+     // first tab receives observer notifications for things happening in the
+     // second tab, so let's clear the observer call counts before we cleanup
+     // in the first tab.
+     await ignoreObserversCalled();

+ 1 - 1
frg/work-js/mozilla-release/patches/1450242-61a1.patch

@@ -1,5 +1,5 @@
 # HG changeset patch
-# User Florian Quèze <florian@queze.net>
+# User Florian Queze <florian@queze.net>
 # Date 1522763045 -7200
 # Node ID fe0e01b08506fe803fa12aa8b84fff9a5c59d1e8
 # Parent  87cb146103e95cedf62f963efe3c75f07e18ba51

+ 2 - 2
frg/work-js/mozilla-release/patches/1463048-64a1.patch

@@ -2,7 +2,7 @@
 # User Gabriele Svelto <gsvelto@mozilla.com>
 # Date 1537217505 0
 # Node ID 588d96e4eec804258700aed86937c5ac1bfcab51
-# Parent  244ba1cd7ed7e2a77c05ab1ce7fdb1af4145f755
+# Parent  b6c4c753fad0b3df4ea5fecaaa62c670fcb80b2c
 Bug 1463048 - Remove asynchronous minidump generation r=ted
 
 This reverts the changes in bug 1360308, bug 1390143 and bug 1469603. Minidump
@@ -95,7 +95,7 @@ diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp
 --- a/dom/ipc/ProcessHangMonitor.cpp
 +++ b/dom/ipc/ProcessHangMonitor.cpp
 @@ -234,19 +234,16 @@ public:
-     mHangMonitor->Dispatch(Move(aRunnable));
+     mHangMonitor->Dispatch(std::move(aRunnable));
    }
    bool IsOnThread() { return mHangMonitor->IsOnThread(); }
  

+ 1 - 1
frg/work-js/mozilla-release/patches/1463596-62a1.patch

@@ -152,7 +152,7 @@ diff --git a/mozglue/misc/interceptor/TargetFunction.h b/mozglue/misc/intercepto
      , mOffset(aOther.mOffset)
      , mStartWriteOffset(aOther.mStartWriteOffset)
 -    , mPrevProt(aOther.mPrevProt)
-     , mLocalBytes(Move(aOther.mLocalBytes))
+     , mLocalBytes(std::move(aOther.mLocalBytes))
      , mAccumulatedStatus(aOther.mAccumulatedStatus)
 +    , mProtect(std::move(aOther.mProtect))
    {

+ 564 - 0
frg/work-js/mozilla-release/patches/1463924-62a1.patch

@@ -0,0 +1,564 @@
+# HG changeset patch
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1527118236 18000
+# Node ID 73d696f9e675f144e6284f0f6517f04f88612ba5
+# Parent  b49efc8e82635a1e20031f51f297d6ed18d1bcd3
+Bug 1463924 - Remove Dev. Edition promo doorhanger. r=nchevobbe
+
+MozReview-Commit-ID: LIg6o35CvD2
+
+diff --git a/devtools/client/framework/dev-edition-promo/dev-edition-logo.png b/devtools/client/framework/dev-edition-promo/dev-edition-logo.png
+deleted file mode 100644
+index 4b90768d2d172703a185da55bf3ab9e619ca6a63..0000000000000000000000000000000000000000
+GIT binary patch
+literal 0
+Hc$@<O00001
+
+diff --git a/devtools/client/framework/dev-edition-promo/dev-edition-promo.css b/devtools/client/framework/dev-edition-promo/dev-edition-promo.css
+deleted file mode 100644
+--- a/devtools/client/framework/dev-edition-promo/dev-edition-promo.css
++++ /dev/null
+@@ -1,94 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this
+- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-window {
+-  -moz-appearance: none;
+-  background-color: transparent;
+-}
+-
+-#doorhanger-container {
+-  width: 450px;
+-}
+-
+-#top-panel {
+-  padding: 20px;
+-  background: #343c45; /* toolbars */
+-  color: #8fa1b2; /* body text */
+-/*
+- * Sloppy preprocessing since UNIX_BUT_NOT_MAC is only defined
+- * in `browser/app/profile/firefox.js`, which this file cannot
+- * depend on. Must style font-size to target linux.
+- */
+-%ifdef XP_UNIX
+-%ifndef XP_MACOSX
+-  font-size: 13px;
+-%else
+-  font-size: 15px;
+-%endif
+-%else
+-  font-size: 15px;
+-%endif
+-  line-height: 19px;
+-  min-height: 100px;
+-}
+-
+-#top-panel h1 {
+-  font-weight: bold;
+-  font-family: Open Sans, sans-serif;
+-  font-size: 1.1em;
+-}
+-
+-#top-panel p {
+-  font-family: Open Sans, sans-serif;
+-  font-size: 0.9em;
+-  width: 300px;
+-  display: block;
+-  margin: 5px 0px 0px 0px;
+-}
+-
+-#icon {
+-  background-image: url("chrome://devtools/content/framework/dev-edition-promo/dev-edition-logo.png");
+-  background-size: 64px 64px;
+-  background-repeat: no-repeat;
+-  width: 64px;
+-  height: 64px;
+-  margin-right: 20px;
+-}
+-
+-#lower-panel {
+-  padding: 20px;
+-  background-color: #252c33; /* tab toolbars */
+-  min-height: 75px;
+-  border-top: 1px solid #292e33; /* text high contrast (light) */
+-}
+-
+-#button-container {
+-  margin: auto 20px;
+-}
+-
+-#button-container button {
+-  font: message-box !important;
+-  font-size: 16px !important;
+-  cursor: pointer;
+-  width: 125px;
+-  opacity: 1;
+-  position: static;
+-  -moz-appearance: none;
+-  border-radius: 5px;
+-  height: 30px;
+-  width: 450px;
+-  /* Override embossed borders on Windows/Linux */
+-  border: none;
+-}
+-
+-#close {
+-  background-color: transparent;
+-  color: #8fa1b2; /* body text */
+-}
+-
+-#go {
+-  margin-left: 100px;
+-  background-color: #70bf53; /* green */
+-  color: #f5f7fa; /* selection text color */
+-}
+diff --git a/devtools/client/framework/dev-edition-promo/dev-edition-promo.xul b/devtools/client/framework/dev-edition-promo/dev-edition-promo.xul
+deleted file mode 100644
+--- a/devtools/client/framework/dev-edition-promo/dev-edition-promo.xul
++++ /dev/null
+@@ -1,36 +0,0 @@
+-<?xml version="1.0" encoding="utf-8"?>
+-<!-- 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/. -->
+-<!DOCTYPE window [
+-<!ENTITY % toolboxDTD SYSTEM "chrome://devtools/locale/toolbox.dtd" >
+- %toolboxDTD;
+-]>
+-<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+-<?xml-stylesheet rel="stylesheet" href="chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.css" type="text/css"?>
+-
+-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" id="dev-edition-promo">
+-  <vbox id="doorhanger-container">
+-    <hbox flex="1" id="top-panel">
+-      <image id="icon" />
+-      <vbox id="info">
+-        <h1>Using Developer Tools in your browser?</h1>
+-        <p>Download Firefox Developer Edition, our first browser made just for you.</p>
+-      </vbox>
+-    </hbox>
+-    <hbox id="lower-panel" flex="1">
+-      <hbox id="button-container" flex="1">
+-        <button id="close"
+-                flex="1"
+-                standalone="true"
+-                label="No thanks">
+-        </button>
+-        <button id="go"
+-                flex="1"
+-                standalone="true"
+-                label="Learn more »">
+-        </button>
+-      </hbox>
+-    </hbox>
+-  </vbox>
+-</window>
+diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js
+--- a/devtools/client/framework/toolbox.js
++++ b/devtools/client/framework/toolbox.js
+@@ -40,18 +40,16 @@ const L10N = new LocalizationHelper("dev
+ loader.lazyRequireGetter(this, "getHighlighterUtils",
+   "devtools/client/framework/toolbox-highlighter-utils", true);
+ loader.lazyRequireGetter(this, "Selection",
+   "devtools/client/framework/selection", true);
+ loader.lazyRequireGetter(this, "InspectorFront",
+   "devtools/shared/fronts/inspector", true);
+ loader.lazyRequireGetter(this, "flags",
+   "devtools/shared/flags");
+-loader.lazyRequireGetter(this, "showDoorhanger",
+-  "devtools/client/shared/doorhanger", true);
+ loader.lazyRequireGetter(this, "createPerformanceFront",
+   "devtools/shared/fronts/performance", true);
+ loader.lazyRequireGetter(this, "system",
+   "devtools/shared/system");
+ loader.lazyRequireGetter(this, "getPreferenceFront",
+   "devtools/shared/fronts/preference", true);
+ loader.lazyRequireGetter(this, "KeyShortcuts",
+   "devtools/client/shared/key-shortcuts");
+@@ -132,17 +130,16 @@ function Toolbox(target, selectedTool, h
+   this._highlighterReady = this._highlighterReady.bind(this);
+   this._highlighterHidden = this._highlighterHidden.bind(this);
+   this._applyCacheSettings = this._applyCacheSettings.bind(this);
+   this._applyServiceWorkersTestingSettings =
+     this._applyServiceWorkersTestingSettings.bind(this);
+   this._saveSplitConsoleHeight = this._saveSplitConsoleHeight.bind(this);
+   this._onFocus = this._onFocus.bind(this);
+   this._onBrowserMessage = this._onBrowserMessage.bind(this);
+-  this._showDevEditionPromo = this._showDevEditionPromo.bind(this);
+   this._updateTextBoxMenuItems = this._updateTextBoxMenuItems.bind(this);
+   this._onBottomHostMinimized = this._onBottomHostMinimized.bind(this);
+   this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
+   this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
+   this._onPerformanceFrontEvent = this._onPerformanceFrontEvent.bind(this);
+   this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
+   this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
+   this._onToolbarFocus = this._onToolbarFocus.bind(this);
+@@ -174,18 +171,16 @@ function Toolbox(target, selectedTool, h
+   this._target.on("navigate", this._refreshHostTitle);
+   this._target.on("frame-update", this._updateFrames);
+   this._target.on("inspect-object", this._onInspectObject);
+ 
+   this.on("host-changed", this._refreshHostTitle);
+   this.on("select", this._refreshHostTitle);
+   this.on("select", this._updatePickerButton);
+ 
+-  this.on("ready", this._showDevEditionPromo);
+-
+   gDevTools.on("tool-registered", this._toolRegistered);
+   gDevTools.on("tool-unregistered", this._toolUnregistered);
+ 
+   this.on("picker-started", this._onPickerStarted);
+   this.on("picker-stopped", this._onPickerStopped);
+ 
+   /**
+    * Get text direction for the current locale direction.
+@@ -2669,17 +2664,16 @@ Toolbox.prototype = {
+ 
+     this._target.off("inspect-object", this._onInspectObject);
+     this._target.off("will-navigate", this._onWillNavigate);
+     this._target.off("navigate", this._refreshHostTitle);
+     this._target.off("frame-update", this._updateFrames);
+     this.off("select", this._refreshHostTitle);
+     this.off("select", this._updatePickerButton);
+     this.off("host-changed", this._refreshHostTitle);
+-    this.off("ready", this._showDevEditionPromo);
+ 
+     gDevTools.off("tool-registered", this._toolRegistered);
+     gDevTools.off("tool-unregistered", this._toolUnregistered);
+ 
+     Services.prefs.removeObserver("devtools.cache.disabled", this._applyCacheSettings);
+     Services.prefs.removeObserver("devtools.serviceWorkers.testing.enabled",
+                                   this._applyServiceWorkersTestingSettings);
+ 
+@@ -2840,28 +2834,16 @@ Toolbox.prototype = {
+     this.emit("highlighter-ready");
+   },
+ 
+   _highlighterHidden: function() {
+     this.emit("highlighter-hide");
+   },
+ 
+   /**
+-   * For displaying the promotional Doorhanger on first opening of
+-   * the developer tools, promoting the Developer Edition.
+-   */
+-  _showDevEditionPromo: function() {
+-    // Do not display in browser toolbox
+-    if (this.target.chrome) {
+-      return;
+-    }
+-    showDoorhanger({ window: this.win, type: "deveditionpromo" });
+-  },
+-
+-  /**
+    * Enable / disable necessary textbox menu items using globalOverlay.js.
+    */
+   _updateTextBoxMenuItems: function() {
+     let window = this.win;
+     ["cmd_undo", "cmd_delete", "cmd_cut",
+      "cmd_copy", "cmd_paste", "cmd_selectAll"].forEach(window.goUpdateCommand);
+   },
+ 
+diff --git a/devtools/client/jar.mn b/devtools/client/jar.mn
+--- a/devtools/client/jar.mn
++++ b/devtools/client/jar.mn
+@@ -82,19 +82,16 @@ devtools.jar:
+     content/commandline/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
+     content/framework/toolbox-window.xul (framework/toolbox-window.xul)
+     content/framework/toolbox-options.xhtml (framework/toolbox-options.xhtml)
+     content/framework/toolbox.xul (framework/toolbox.xul)
+     content/framework/toolbox-init.js (framework/toolbox-init.js)
+     content/framework/options-panel.css (framework/options-panel.css)
+     content/framework/toolbox-process-window.xul (framework/toolbox-process-window.xul)
+     content/framework/toolbox-process-window.js (framework/toolbox-process-window.js)
+-    content/framework/dev-edition-promo/dev-edition-promo.xul (framework/dev-edition-promo/dev-edition-promo.xul)
+-*   content/framework/dev-edition-promo/dev-edition-promo.css (framework/dev-edition-promo/dev-edition-promo.css)
+-    content/framework/dev-edition-promo/dev-edition-logo.png (framework/dev-edition-promo/dev-edition-logo.png)
+     content/inspector/inspector.xhtml (inspector/inspector.xhtml)
+     content/framework/connect/connect.xhtml (framework/connect/connect.xhtml)
+     content/framework/connect/connect.css (framework/connect/connect.css)
+     content/framework/connect/connect.js (framework/connect/connect.js)
+     content/shared/widgets/graphs-frame.xhtml (shared/widgets/graphs-frame.xhtml)
+     content/shared/widgets/cubic-bezier.css (shared/widgets/cubic-bezier.css)
+     content/shared/widgets/filter-widget.css (shared/widgets/filter-widget.css)
+     content/shared/widgets/color-widget.css (shared/widgets/color-widget.css)
+diff --git a/devtools/client/preferences/devtools.js b/devtools/client/preferences/devtools.js
+--- a/devtools/client/preferences/devtools.js
++++ b/devtools/client/preferences/devtools.js
+@@ -1,23 +1,12 @@
+ /* This Source Code Form is subject to the terms of the Mozilla Public
+  * License, v. 2.0. If a copy of the MPL was not distributed with this
+  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+ 
+-// Developer edition promo preferences
+-pref("devtools.devedition.promo.shown", false);
+-pref("devtools.devedition.promo.url", "https://www.mozilla.org/firefox/developer/?utm_source=firefox-dev-tools&utm_medium=firefox-browser&utm_content=betadoorhanger");
+-
+-// Only potentially show in beta release
+-#if MOZ_UPDATE_CHANNEL == beta
+-  pref("devtools.devedition.promo.enabled", true);
+-#else
+-  pref("devtools.devedition.promo.enabled", false);
+-#endif
+-
+ // Developer toolbar preferences
+ pref("devtools.toolbar.enabled", true);
+ 
+ // Toolbox preferences
+ pref("devtools.toolbox.footer.height", 250);
+ pref("devtools.toolbox.sidebar.width", 500);
+ pref("devtools.toolbox.host", "bottom");
+ pref("devtools.toolbox.previousHost", "side");
+diff --git a/devtools/client/shared/doorhanger.js b/devtools/client/shared/doorhanger.js
+deleted file mode 100644
+--- a/devtools/client/shared/doorhanger.js
++++ /dev/null
+@@ -1,155 +0,0 @@
+-/* This Source Code Form is subject to the terms of the Mozilla Public
+- * License, v. 2.0. If a copy of the MPL was not distributed with this
+- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+-
+-"use strict";
+-
+-const Services = require("Services");
+-const { DOMHelpers } = require("resource://devtools/client/shared/DOMHelpers.jsm");
+-
+-const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+-const DEV_EDITION_PROMO_URL = "chrome://devtools/content/framework/dev-edition-promo/dev-edition-promo.xul";
+-const DEV_EDITION_PROMO_ENABLED_PREF = "devtools.devedition.promo.enabled";
+-const DEV_EDITION_PROMO_SHOWN_PREF = "devtools.devedition.promo.shown";
+-const DEV_EDITION_PROMO_URL_PREF = "devtools.devedition.promo.url";
+-
+-/**
+- * Only show Dev Edition promo if it's enabled (beta channel),
+- * if it has not been shown before, and it's a locale build
+- * for `en-US`
+- */
+-function shouldDevEditionPromoShow() {
+-  return Services.prefs.getBoolPref(DEV_EDITION_PROMO_ENABLED_PREF) &&
+-         !Services.prefs.getBoolPref(DEV_EDITION_PROMO_SHOWN_PREF) &&
+-         Services.locale.getAppLocaleAsLangTag() === "en-US";
+-}
+-
+-var TYPES = {
+-  // The Developer Edition promo doorhanger, called by
+-  // opening the toolbox, browser console or responsive design mode
+-  // in Beta releases. Only displayed once per profile.
+-  deveditionpromo: {
+-    predicate: shouldDevEditionPromoShow,
+-    success: () => {
+-      return Services.prefs.setBoolPref(DEV_EDITION_PROMO_SHOWN_PREF, true);
+-    },
+-    action: () => {
+-      let url = Services.prefs.getCharPref(DEV_EDITION_PROMO_URL_PREF);
+-      getGBrowser().selectedTab = getGBrowser().addTab(url);
+-    },
+-    url: DEV_EDITION_PROMO_URL
+-  }
+-};
+-
+-var panelAttrs = {
+-  orient: "vertical",
+-  hidden: "false",
+-  consumeoutsideclicks: "true",
+-  noautofocus: "true",
+-  align: "start",
+-  role: "alert"
+-};
+-
+-/**
+- * Helper to call a doorhanger, defined in `TYPES`, with defined conditions,
+- * success handlers and loads its own XUL in a frame. Takes an object with
+- * several properties:
+- *
+- * @param {XULWindow} window
+- *        The window that should house the doorhanger.
+- * @param {String} type
+- *        The type of doorhanger to be displayed is, using the `TYPES`
+- *        definition.
+- * @param {String} selector
+- *        The selector that the doorhanger should be appended to within
+- *        `window`.  Defaults to a XUL Document's `window` element.
+- */
+-exports.showDoorhanger = async function({ window, type, anchor }) {
+-  let { predicate, success, url, action } = TYPES[type];
+-  // Abort if predicate fails
+-  if (!predicate()) {
+-    return;
+-  }
+-
+-  // Call success function to set preferences/cleanup immediately,
+-  // so if triggered multiple times, only happens once (Windows/Linux)
+-  success();
+-
+-  // Wait 200ms to prevent flickering where the popup is displayed
+-  // before the underlying window (Windows 7, 64bit)
+-  await wait(200);
+-
+-  let document = window.document;
+-
+-  let panel = document.createElementNS(XULNS, "panel");
+-  let frame = document.createElementNS(XULNS, "iframe");
+-  let parentEl = document.querySelector("window");
+-
+-  frame.setAttribute("src", url);
+-  let close = () => parentEl.removeChild(panel);
+-
+-  setDoorhangerStyle(panel, frame);
+-
+-  panel.appendChild(frame);
+-  parentEl.appendChild(panel);
+-
+-  await onFrameLoad(frame);
+-
+-  panel.openPopup(anchor);
+-
+-  let closeBtn = frame.contentDocument.querySelector("#close");
+-  if (closeBtn) {
+-    closeBtn.addEventListener("click", close);
+-  }
+-
+-  let goBtn = frame.contentDocument.querySelector("#go");
+-  if (goBtn) {
+-    goBtn.addEventListener("click", () => {
+-      if (action) {
+-        action();
+-      }
+-      close();
+-    });
+-  }
+-};
+-
+-function setDoorhangerStyle(panel, frame) {
+-  Object.keys(panelAttrs).forEach(prop => {
+-    return panel.setAttribute(prop, panelAttrs[prop]);
+-  });
+-  panel.style.margin = "20px";
+-  panel.style.borderRadius = "5px";
+-  panel.style.border = "none";
+-  panel.style.MozAppearance = "none";
+-  panel.style.backgroundColor = "transparent";
+-
+-  frame.style.borderRadius = "5px";
+-  frame.setAttribute("flex", "1");
+-  frame.setAttribute("width", "450");
+-  frame.setAttribute("height", "179");
+-}
+-
+-function onFrameLoad(frame) {
+-  return new Promise((resolve, reject) => {
+-    if (frame.contentWindow) {
+-      let domHelper = new DOMHelpers(frame.contentWindow);
+-      domHelper.onceDOMReady(resolve);
+-    } else {
+-      let callback = () => {
+-        frame.removeEventListener("DOMContentLoaded", callback);
+-        resolve();
+-      };
+-      frame.addEventListener("DOMContentLoaded", callback);
+-    }
+-  });
+-}
+-
+-function getGBrowser() {
+-  return Services.wm.getMostRecentWindow("navigator:browser").gBrowser;
+-}
+-
+-function wait(n) {
+-  return new Promise((resolve, reject) => {
+-    setTimeout(resolve, n);
+-  });
+-}
+diff --git a/devtools/client/shared/moz.build b/devtools/client/shared/moz.build
+--- a/devtools/client/shared/moz.build
++++ b/devtools/client/shared/moz.build
+@@ -24,17 +24,16 @@ DevToolsModules(
+     'autocomplete-popup.js',
+     'browser-loader.js',
+     'css-angle.js',
+     'curl.js',
+     'demangle.js',
+     'developer-toolbar.js',
+     'devices.js',
+     'DOMHelpers.jsm',
+-    'doorhanger.js',
+     'enum.js',
+     'file-saver.js',
+     'getjson.js',
+     'inplace-editor.js',
+     'key-shortcuts.js',
+     'keycodes.js',
+     'natural-sort.js',
+     'network-throttling-profiles.js',
+diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js
+--- a/devtools/client/webconsole/hudservice.js
++++ b/devtools/client/webconsole/hudservice.js
+@@ -11,17 +11,16 @@ loader.lazyRequireGetter(this, "TargetFa
+ loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
+ loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
+ loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
+ loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/webconsole", true);
+ loader.lazyRequireGetter(this, "NewWebConsoleFrame", "devtools/client/webconsole/new-webconsole", true);
+ loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
+ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
+ loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/debugger-client", true);
+-loader.lazyRequireGetter(this, "showDoorhanger", "devtools/client/shared/doorhanger", true);
+ loader.lazyRequireGetter(this, "viewSource", "devtools/client/shared/view-source");
+ loader.lazyRequireGetter(this, "l10n", "devtools/client/webconsole/webconsole-l10n");
+ const BC_WINDOW_FEATURES = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+ 
+ // The preference prefix for all of the Browser Console filters.
+ const BC_FILTER_PREFS_PREFIX = "devtools.browserconsole.filter.";
+ 
+ var gHudId = 0;
+@@ -604,28 +603,21 @@ BrowserConsole.prototype = extend(WebCon
+ 
+     this.ui._filterPrefsPrefix = BC_FILTER_PREFS_PREFIX;
+ 
+     let window = this.iframeWindow;
+ 
+     // Make sure that the closing of the Browser Console window destroys this
+     // instance.
+     window.addEventListener("unload", () => {
+-      window.removeEventListener("focus", onFocus);
+       this.destroy();
+     }, {once: true});
+ 
+     this._telemetry.toolOpened("browserconsole");
+ 
+-    // Create an onFocus handler just to display the dev edition promo.
+-    // This is to prevent race conditions in some environments.
+-    // Hook to display promotional Developer Edition doorhanger. Only displayed once.
+-    let onFocus = () => showDoorhanger({ window, type: "deveditionpromo" });
+-    window.addEventListener("focus", onFocus);
+-
+     this._bcInit = this.$init();
+     return this._bcInit;
+   },
+ 
+   $destroy: WebConsole.prototype.destroy,
+ 
+   /**
+    * Destroy the object.
+diff --git a/testing/profiles/common/user.js b/testing/profiles/common/user.js
+--- a/testing/profiles/common/user.js
++++ b/testing/profiles/common/user.js
+@@ -25,17 +25,16 @@ user_pref("browser.shell.checkDefaultBro
+ user_pref("shell.checkDefaultClient", false);
+ user_pref("browser.warnOnQuit", false);
+ user_pref("accessibility.typeaheadfind.autostart", false);
+ user_pref("findbar.highlightAll", false);
+ user_pref("findbar.modalHighlight", false);
+ user_pref("javascript.options.showInConsole", true);
+ user_pref("devtools.browsertoolbox.panel", "jsdebugger");
+ user_pref("devtools.debugger.remote-port", 6023);
+-user_pref("devtools.devedition.promo.enabled", false);
+ user_pref("devtools.chrome.enabled", false);
+ user_pref("devtools.debugger.remote-enabled", false);
+ user_pref("devtools.debugger.prompt-connection", true);
+ user_pref("browser.EULA.override", true);
+ user_pref("gfx.color_management.force_srgb", true);
+ user_pref("gfx.logging.level", 1);
+ user_pref("network.manage-offline-status", false);
+ // Disable speculative connections so they aren't reported as leaking when they're hanging around.

+ 0 - 286
frg/work-js/mozilla-release/patches/1465060-1-PARTIAL-js-62a1.patch

@@ -1,286 +0,0 @@
-# HG changeset patch
-# User Miko Mynttinen <mikokm@gmail.com>
-# Date 1527868747 -7200
-# Node ID a0d11b55d5957a488b41420c4f6cc178df7cd2e7
-# Parent  9ef33c5744895107566e4590afe3c3b86ede4528
-Bug 1465060 - Part 1: Fix warnings for std::move() use r=froydnj
-
-MozReview-Commit-ID: HpdFXqQdIOO
-
-diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
---- a/js/src/builtin/TestingFunctions.cpp
-+++ b/js/src/builtin/TestingFunctions.cpp
-@@ -3523,17 +3523,17 @@ struct FindPathHandler {
-         if (!first)
-             return true;
- 
-         // Record how we reached this node. This is the last edge on a
-         // shortest path to this node.
-         EdgeName edgeName = DuplicateString(cx, edge.name.get());
-         if (!edgeName)
-             return false;
--        *backEdge = std::move(BackEdge(origin, std::move(edgeName)));
-+        *backEdge = BackEdge(origin, std::move(edgeName));
- 
-         // Have we reached our final target node?
-         if (edge.referent == target) {
-             // Record the path that got us here, which must be a shortest path.
-             if (!recordPath(traversal))
-                 return false;
-             foundPath = true;
-             traversal.stop();
-diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp
---- a/js/src/ctypes/CTypes.cpp
-+++ b/js/src/ctypes/CTypes.cpp
-@@ -5754,17 +5754,17 @@ ArrayType::BuildFFIType(JSContext* cx, J
-     JS_ReportAllocationOverflow(cx);
-     return nullptr;
-   }
- 
-   for (size_t i = 0; i < length; ++i)
-     ffiType->elements[i] = ffiBaseType;
-   ffiType->elements[length] = nullptr;
- 
--  return std::move(ffiType);
-+  return ffiType;
- }
- 
- bool
- ArrayType::IsArrayType(HandleValue v)
- {
-   if (!v.isObject())
-     return false;
-   JSObject* obj = &v.toObject();
-@@ -6302,17 +6302,17 @@ StructType::BuildFFIType(JSContext* cx, 
-   // Fill in the ffi_type's size and align fields. This makes libffi treat the
-   // type as initialized; it will not recompute the values. (We assume
-   // everything agrees; if it doesn't, we really want to know about it, which
-   // is the purpose of the above debug-only check.)
-   ffiType->size = structSize;
-   ffiType->alignment = structAlign;
- #endif
- 
--  return std::move(ffiType);
-+  return ffiType;
- }
- 
- bool
- StructType::Define(JSContext* cx, unsigned argc, Value* vp)
- {
-   CallArgs args = CallArgsFromVp(argc, vp);
-   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
-   if (!obj)
-diff --git a/js/src/ds/LifoAlloc.cpp b/js/src/ds/LifoAlloc.cpp
---- a/js/src/ds/LifoAlloc.cpp
-+++ b/js/src/ds/LifoAlloc.cpp
-@@ -46,21 +46,21 @@ BumpChunk::canAlloc(size_t n)
- 
- } // namespace detail
- } // namespace js
- 
- void
- LifoAlloc::freeAll()
- {
-     while (!chunks_.empty()) {
--        BumpChunk bc = std::move(chunks_.popFirst());
-+        BumpChunk bc = chunks_.popFirst();
-         decrementCurSize(bc->computedSizeOfIncludingThis());
-     }
-     while (!unused_.empty()) {
--        BumpChunk bc = std::move(unused_.popFirst());
-+        BumpChunk bc = unused_.popFirst();
-         decrementCurSize(bc->computedSizeOfIncludingThis());
-     }
- 
-     // Nb: maintaining curSize_ correctly isn't easy.  Fortunately, this is an
-     // excellent sanity check.
-     MOZ_ASSERT(curSize_ == 0);
- }
- 
-@@ -100,27 +100,27 @@ LifoAlloc::newChunkWithCapacity(size_t n
- bool
- LifoAlloc::getOrCreateChunk(size_t n)
- {
-     // Look for existing unused BumpChunks to satisfy the request, and pick the
-     // first one which is large enough, and move it into the list of used
-     // chunks.
-     if (!unused_.empty()) {
-         if (unused_.begin()->canAlloc(n)) {
--            chunks_.append(std::move(unused_.popFirst()));
-+            chunks_.append(unused_.popFirst());
-             return true;
-         }
- 
-         BumpChunkList::Iterator e(unused_.end());
-         for (BumpChunkList::Iterator i(unused_.begin()); i->next() != e.get(); ++i) {
-             detail::BumpChunk* elem = i->next();
-             MOZ_ASSERT(elem->empty());
-             if (elem->canAlloc(n)) {
--                BumpChunkList temp = std::move(unused_.splitAfter(i.get()));
--                chunks_.append(std::move(temp.popFirst()));
-+                BumpChunkList temp = unused_.splitAfter(i.get());
-+                chunks_.append(temp.popFirst());
-                 unused_.appendAll(std::move(temp));
-                 return true;
-             }
-         }
-     }
- 
-     // Allocate a new BumpChunk with enough space for the next allocation.
-     BumpChunk newChunk = newChunkWithCapacity(n);
-diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
---- a/js/src/ds/LifoAlloc.h
-+++ b/js/src/ds/LifoAlloc.h
-@@ -706,17 +706,17 @@ class LifoAlloc
-     void release(Mark mark) {
-         markCount--;
- 
-         // Move the blocks which are after the mark to the set of unused chunks.
-         BumpChunkList released;
-         if (!mark.markedChunk())
-             released = std::move(chunks_);
-         else
--            released = std::move(chunks_.splitAfter(mark.markedChunk()));
-+            released = chunks_.splitAfter(mark.markedChunk());
- 
-         // Release the content of all the blocks which are after the marks.
-         for (detail::BumpChunk& bc : released)
-             bc.release();
-         unused_.appendAll(std::move(released));
- 
-         // Release everything which follows the mark in the last chunk.
-         if (!chunks_.empty())
-diff --git a/js/src/vm/CodeCoverage.cpp b/js/src/vm/CodeCoverage.cpp
---- a/js/src/vm/CodeCoverage.cpp
-+++ b/js/src/vm/CodeCoverage.cpp
-@@ -517,17 +517,17 @@ LCovRealm::lookupOrAdd(JS::Realm* realm,
- 
-     char* source_name = js_strdup(name);
-     if (!source_name) {
-         outTN_.reportOutOfMemory();
-         return nullptr;
-     }
- 
-     // Allocate a new LCovSource for the current top-level.
--    if (!sources_->append(std::move(LCovSource(&alloc_, source_name)))) {
-+    if (!sources_->append(LCovSource(&alloc_, source_name))) {
-         outTN_.reportOutOfMemory();
-         return nullptr;
-     }
- 
-     return &sources_->back();
- }
- 
- void
-diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp
---- a/js/src/vm/UbiNode.cpp
-+++ b/js/src/vm/UbiNode.cpp
-@@ -262,17 +262,17 @@ class EdgeVectorTracer : public JS::Call
-                 name16[i] = name[i];
-             name16[i] = '\0';
-         }
- 
-         // The simplest code is correct! The temporary Edge takes
-         // ownership of name; if the append succeeds, the vector element
-         // then takes ownership; if the append fails, then the temporary
-         // retains it, and its destructor will free it.
--        if (!vec->append(std::move(Edge(name16, Node(thing))))) {
-+        if (!vec->append(Edge(name16, Node(thing)))) {
-             okay = false;
-             return;
-         }
-     }
- 
-   public:
-     // True if no errors (OOM, say) have yet occurred.
-     bool okay;
-@@ -543,17 +543,17 @@ RootList::addRoot(Node node, const char1
- 
-     UniqueTwoByteChars name;
-     if (edgeName) {
-         name = js::DuplicateString(edgeName);
-         if (!name)
-             return false;
-     }
- 
--    return edges.append(std::move(Edge(name.release(), node)));
-+    return edges.append(Edge(name.release(), node));
- }
- 
- const char16_t Concrete<RootList>::concreteTypeName[] = u"JS::ubi::RootList";
- 
- UniquePtr<EdgeRange>
- Concrete<RootList>::edges(JSContext* cx, bool wantNames) const {
-     MOZ_ASSERT_IF(wantNames, get().wantNames);
-     return UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
-diff --git a/js/src/vm/UbiNodeShortestPaths.cpp b/js/src/vm/UbiNodeShortestPaths.cpp
---- a/js/src/vm/UbiNodeShortestPaths.cpp
-+++ b/js/src/vm/UbiNodeShortestPaths.cpp
-@@ -23,17 +23,17 @@ BackEdge::clone() const
-         return nullptr;
- 
-     clone->predecessor_ = predecessor();
-     if (name()) {
-         clone->name_ = js::DuplicateString(name().get());
-         if (!clone->name_)
-             return nullptr;
-     }
--    return std::move(clone);
-+    return clone;
- }
- 
- #ifdef DEBUG
- 
- static void
- dumpNode(const JS::ubi::Node& node)
- {
-     fprintf(stderr, "    %p ", (void*) node.identifier());
-diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp
---- a/js/src/wasm/WasmValidate.cpp
-+++ b/js/src/wasm/WasmValidate.cpp
-@@ -1596,17 +1596,17 @@ DecodeExportName(Decoder& d, CStringSet*
-     if (p) {
-         d.fail("duplicate export");
-         return nullptr;
-     }
- 
-     if (!dupSet->add(p, exportName.get()))
-         return nullptr;
- 
--    return std::move(exportName);
-+    return exportName;
- }
- 
- static bool
- DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet)
- {
-     UniqueChars fieldName = DecodeExportName(d, dupSet);
-     if (!fieldName)
-         return false;
-diff --git a/js/xpconnect/loader/URLPreloader.cpp b/js/xpconnect/loader/URLPreloader.cpp
---- a/js/xpconnect/loader/URLPreloader.cpp
-+++ b/js/xpconnect/loader/URLPreloader.cpp
-@@ -612,21 +612,21 @@ URLPreloader::ShallowSizeOfIncludingThis
- 
- Result<FileLocation, nsresult>
- URLPreloader::CacheKey::ToFileLocation()
- {
-     if (mType == TypeFile) {
-         nsCOMPtr<nsIFile> file;
-         MOZ_TRY(NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPath), false,
-                                 getter_AddRefs(file)));
--        return std::move(FileLocation(file));
-+        return FileLocation(file);
-     }
- 
-     RefPtr<nsZipArchive> zip = Archive();
--    return std::move(FileLocation(zip, mPath.get()));
-+    return FileLocation(zip, mPath.get());
- }
- 
- Result<const nsCString, nsresult>
- URLPreloader::URLEntry::Read()
- {
-     FileLocation location;
-     MOZ_TRY_VAR(location, ToFileLocation());
- 

+ 2938 - 0
frg/work-js/mozilla-release/patches/1465060-1-std-62a1.patch

@@ -0,0 +1,2938 @@
+# HG changeset patch
+# User Miko Mynttinen <mikokm@gmail.com>
+# Date 1527868747 -7200
+# Node ID a0d11b55d5957a488b41420c4f6cc178df7cd2e7
+# Parent  cb1d6bfc93659e34080db0abdfd9e29d016e3182
+Bug 1465060 - Part 1: Fix warnings for std::move() use r=froydnj
+
+MozReview-Commit-ID: HpdFXqQdIOO
+
+diff --git a/accessible/ipc/other/ProxyAccessible.cpp b/accessible/ipc/other/ProxyAccessible.cpp
+--- a/accessible/ipc/other/ProxyAccessible.cpp
++++ b/accessible/ipc/other/ProxyAccessible.cpp
+@@ -73,17 +73,17 @@ ProxyAccessible::RelationByType(Relation
+                                      &targetIDs);
+ 
+   size_t targetCount = targetIDs.Length();
+   nsTArray<ProxyAccessible*> targets(targetCount);
+   for (size_t i = 0; i < targetCount; i++)
+     if (ProxyAccessible* proxy = mDoc->GetAccessible(targetIDs[i]))
+       targets.AppendElement(proxy);
+ 
+-  return std::move(targets);
++  return targets;
+ }
+ 
+ void
+ ProxyAccessible::Relations(nsTArray<RelationType>* aTypes,
+                            nsTArray<nsTArray<ProxyAccessible*>>* aTargetSets)
+   const
+ {
+   nsTArray<RelationTargets> ipcRelations;
+diff --git a/devtools/shared/heapsnapshot/DeserializedNode.cpp b/devtools/shared/heapsnapshot/DeserializedNode.cpp
+--- a/devtools/shared/heapsnapshot/DeserializedNode.cpp
++++ b/devtools/shared/heapsnapshot/DeserializedNode.cpp
+@@ -82,18 +82,18 @@ class DeserializedEdgeRange : public Edg
+   void settle() {
+     if (i >= node->edges.length()) {
+       front_ = nullptr;
+       return;
+     }
+ 
+     auto& edge = node->edges[i];
+     auto referent = node->getEdgeReferent(edge);
+-    currentEdge = std::move(Edge(edge.name ? NS_strdup(edge.name) : nullptr,
+-                                     referent));
++    currentEdge = Edge(edge.name
++                ? NS_strdup(edge.name) : nullptr, referent);
+     front_ = &currentEdge;
+   }
+ 
+ public:
+   explicit DeserializedEdgeRange(DeserializedNode& node)
+     : node(&node)
+     , i(0)
+   {
+diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp
+--- a/docshell/base/nsDocShell.cpp
++++ b/docshell/base/nsDocShell.cpp
+@@ -14878,20 +14878,20 @@ nsDocShell::NotifyJSRunToCompletionStart
+                                          const uint32_t aLineNumber,
+                                          JS::Handle<JS::Value> aAsyncStack,
+                                          const char* aAsyncCause)
+ {
+   // If first start, mark interval start.
+   if (mJSRunToCompletionDepth == 0) {
+     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+     if (timelines && timelines->HasConsumer(this)) {
+-      timelines->AddMarkerForDocShell(this, std::move(
++      timelines->AddMarkerForDocShell(this,
+         mozilla::MakeUnique<JavascriptTimelineMarker>(
+           aReason, aFunctionName, aFilename, aLineNumber, MarkerTracingType::START,
+-          aAsyncStack, aAsyncCause)));
++          aAsyncStack, aAsyncCause));
+     }
+   }
+ 
+   mJSRunToCompletionDepth++;
+ }
+ 
+ void
+ nsDocShell::NotifyJSRunToCompletionStop()
+diff --git a/docshell/base/timeline/AutoRestyleTimelineMarker.cpp b/docshell/base/timeline/AutoRestyleTimelineMarker.cpp
+--- a/docshell/base/timeline/AutoRestyleTimelineMarker.cpp
++++ b/docshell/base/timeline/AutoRestyleTimelineMarker.cpp
+@@ -27,34 +27,34 @@ AutoRestyleTimelineMarker::AutoRestyleTi
+   }
+ 
+   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+   if (!timelines || !timelines->HasConsumer(aDocShell)) {
+     return;
+   }
+ 
+   mDocShell = aDocShell;
+-  timelines->AddMarkerForDocShell(mDocShell, std::move(
++  timelines->AddMarkerForDocShell(mDocShell,
+     MakeUnique<RestyleTimelineMarker>(
+       mIsAnimationOnly,
+-      MarkerTracingType::START)));
++      MarkerTracingType::START));
+ }
+ 
+ AutoRestyleTimelineMarker::~AutoRestyleTimelineMarker()
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   if (!mDocShell) {
+     return;
+   }
+ 
+   RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+   if (!timelines || !timelines->HasConsumer(mDocShell)) {
+     return;
+   }
+ 
+-  timelines->AddMarkerForDocShell(mDocShell, std::move(
++  timelines->AddMarkerForDocShell(mDocShell,
+     MakeUnique<RestyleTimelineMarker>(
+       mIsAnimationOnly,
+-      MarkerTracingType::END)));
++      MarkerTracingType::END));
+ }
+ 
+ } // namespace mozilla
+diff --git a/docshell/base/timeline/TimelineConsumers.cpp b/docshell/base/timeline/TimelineConsumers.cpp
+--- a/docshell/base/timeline/TimelineConsumers.cpp
++++ b/docshell/base/timeline/TimelineConsumers.cpp
+@@ -180,30 +180,30 @@ TimelineConsumers::IsEmpty()
+ void
+ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+                                         const char* aName,
+                                         MarkerTracingType aTracingType,
+                                         MarkerStackRequest aStackRequest)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   if (HasConsumer(aDocShell)) {
+-    aDocShell->mObserved->AddMarker(std::move(MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest)));
++    aDocShell->mObserved->AddMarker(MakeUnique<TimelineMarker>(aName, aTracingType, aStackRequest));
+   }
+ }
+ 
+ void
+ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+                                         const char* aName,
+                                         const TimeStamp& aTime,
+                                         MarkerTracingType aTracingType,
+                                         MarkerStackRequest aStackRequest)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+   if (HasConsumer(aDocShell)) {
+-    aDocShell->mObserved->AddMarker(std::move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest)));
++    aDocShell->mObserved->AddMarker(MakeUnique<TimelineMarker>(aName, aTime, aTracingType, aStackRequest));
+   }
+ }
+ 
+ void
+ TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+                                         UniquePtr<AbstractTimelineMarker>&& aMarker)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+diff --git a/dom/animation/EffectSet.h b/dom/animation/EffectSet.h
+--- a/dom/animation/EffectSet.h
++++ b/dom/animation/EffectSet.h
+@@ -88,17 +88,17 @@ public:
+   // This allows us to avoid exposing mEffects directly and saves the
+   // caller from having to dereference hashtable iterators using
+   // the rather complicated: iter.Get()->GetKey().
+   class Iterator
+   {
+   public:
+     explicit Iterator(EffectSet& aEffectSet)
+       : mEffectSet(aEffectSet)
+-      , mHashIterator(std::move(aEffectSet.mEffects.Iter()))
++      , mHashIterator(aEffectSet.mEffects.Iter())
+       , mIsEndIterator(false)
+     {
+ #ifdef DEBUG
+       mEffectSet.mActiveIterators++;
+ #endif
+     }
+ 
+     Iterator(Iterator&& aOther)
+diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp
+--- a/dom/base/CustomElementRegistry.cpp
++++ b/dom/base/CustomElementRegistry.cpp
+@@ -402,17 +402,17 @@ CustomElementRegistry::CreateCustomEleme
+ 
+   if (aArgs) {
+     callback->SetArgs(*aArgs);
+   }
+ 
+   if (aAdoptedCallbackArgs) {
+     callback->SetAdoptedCallbackArgs(*aAdoptedCallbackArgs);
+   }
+-  return std::move(callback);
++  return callback;
+ }
+ 
+ /* static */ void
+ CustomElementRegistry::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
+                                                 Element* aCustomElement,
+                                                 LifecycleCallbackArgs* aArgs,
+                                                 LifecycleAdoptedCallbackArgs* aAdoptedCallbackArgs,
+                                                 CustomElementDefinition* aDefinition)
+diff --git a/dom/base/nsContentPermissionHelper.cpp b/dom/base/nsContentPermissionHelper.cpp
+--- a/dom/base/nsContentPermissionHelper.cpp
++++ b/dom/base/nsContentPermissionHelper.cpp
+@@ -406,17 +406,17 @@ nsContentPermissionUtils::GetContentPerm
+ {
+   nsTArray<PContentPermissionRequestParent*> parentArray;
+   for (auto& it : ContentPermissionRequestParentMap()) {
+     if (it.second == aTabId) {
+       parentArray.AppendElement(it.first);
+     }
+   }
+ 
+-  return std::move(parentArray);
++  return parentArray;
+ }
+ 
+ /* static */ void
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(
+   PContentPermissionRequestParent* aParent)
+ {
+   auto it = ContentPermissionRequestParentMap().find(aParent);
+   MOZ_ASSERT(it != ContentPermissionRequestParentMap().end());
+@@ -429,17 +429,17 @@ nsContentPermissionUtils::GetContentPerm
+ {
+   nsTArray<PContentPermissionRequestChild*> childArray;
+   for (auto& it : ContentPermissionRequestChildMap()) {
+     if (it.second == aTabId) {
+       childArray.AppendElement(it.first);
+     }
+   }
+ 
+-  return std::move(childArray);
++  return childArray;
+ }
+ 
+ /* static */ void
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(
+   PContentPermissionRequestChild* aChild)
+ {
+   auto it = ContentPermissionRequestChildMap().find(aChild);
+   MOZ_ASSERT(it != ContentPermissionRequestChildMap().end());
+diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp
+--- a/dom/base/nsGlobalWindow.cpp
++++ b/dom/base/nsGlobalWindow.cpp
+@@ -11616,17 +11616,17 @@ nsGlobalWindow::ShowSlowScriptDialog(con
+   auto getString = [&] (const char* name,
+                         nsContentUtils::PropertiesFile propFile = nsContentUtils::eDOM_PROPERTIES) {
+     nsAutoString result;
+     nsresult rv = nsContentUtils::GetLocalizedString(
+       propFile, name, result);
+ 
+     // GetStringFromName can return NS_OK and still give nullptr string
+     failed = failed || NS_FAILED(rv) || result.IsEmpty();
+-    return std::move(result);
++    return result;
+   };
+ 
+   bool isAddonScript = !aAddonId.IsEmpty();
+   bool showDebugButton = debugCallback && !isAddonScript;
+ 
+   // Get localizable strings
+ 
+   nsAutoString title, checkboxMsg, debugButton, msg;
+diff --git a/dom/canvas/ImageBitmap.cpp b/dom/canvas/ImageBitmap.cpp
+--- a/dom/canvas/ImageBitmap.cpp
++++ b/dom/canvas/ImageBitmap.cpp
+@@ -791,17 +791,17 @@ ImageBitmap::ToCloneData() const
+   UniquePtr<ImageBitmapCloneData> result(new ImageBitmapCloneData());
+   result->mPictureRect = mPictureRect;
+   result->mAlphaType = mAlphaType;
+   result->mIsCroppingAreaOutSideOfSourceImage = mIsCroppingAreaOutSideOfSourceImage;
+   RefPtr<SourceSurface> surface = mData->GetAsSourceSurface();
+   result->mSurface = surface->GetDataSurface();
+   MOZ_ASSERT(result->mSurface);
+ 
+-  return std::move(result);
++  return result;
+ }
+ 
+ /* static */ already_AddRefed<ImageBitmap>
+ ImageBitmap::CreateFromCloneData(nsIGlobalObject* aGlobal,
+                                  ImageBitmapCloneData* aData)
+ {
+   RefPtr<layers::Image> data = CreateImageFromSurface(aData->mSurface);
+ 
+diff --git a/dom/canvas/WebGLFormats.cpp b/dom/canvas/WebGLFormats.cpp
+--- a/dom/canvas/WebGLFormats.cpp
++++ b/dom/canvas/WebGLFormats.cpp
+@@ -822,17 +822,17 @@ FormatUsageAuthority::CreateForWebGL1(gl
+     ptr->AllowRBFormat(LOCAL_GL_DEPTH_STENCIL,
+                        ptr->GetUsage(EffectiveFormat::DEPTH24_STENCIL8));
+ 
+     ////////////////////////////////////////////////////////////////////////////
+ 
+     if (!AddUnsizedFormats(ptr, gl))
+         return nullptr;
+ 
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ UniquePtr<FormatUsageAuthority>
+ FormatUsageAuthority::CreateForWebGL2(gl::GLContext* gl)
+ {
+     UniquePtr<FormatUsageAuthority> ret(new FormatUsageAuthority);
+     const auto ptr = ret.get();
+ 
+@@ -1057,17 +1057,17 @@ FormatUsageAuthority::CreateForWebGL2(gl
+         AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_FLOAT, EffectiveFormat::RGB32F );
+ 
+         AddSimpleUnsized(ptr, LOCAL_GL_RGBA, LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGBA16F);
+         AddSimpleUnsized(ptr, LOCAL_GL_RGB , LOCAL_GL_HALF_FLOAT_OES, EffectiveFormat::RGB16F );
+     }
+ 
+     ////////////////////////////////////
+ 
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ //////////////////////////////////////////////////////////////////////////////////////////
+ 
+ void
+ FormatUsageAuthority::AddTexUnpack(FormatUsageInfo* usage, const PackingInfo& pi,
+                                    const DriverUnpackInfo& dui)
+ {
+diff --git a/dom/canvas/WebGLTextureUpload.cpp b/dom/canvas/WebGLTextureUpload.cpp
+--- a/dom/canvas/WebGLTextureUpload.cpp
++++ b/dom/canvas/WebGLTextureUpload.cpp
+@@ -222,17 +222,17 @@ FromPboOffset(WebGLContext* webgl, const
+                                              isClientData, ptr, availBufferBytes);
+ }
+ 
+ static UniquePtr<webgl::TexUnpackBlob>
+ FromImageBitmap(WebGLContext* webgl, const char* funcName, TexImageTarget target,
+               uint32_t width, uint32_t height, uint32_t depth,
+               const dom::ImageBitmap& imageBitmap)
+ {
+-    UniquePtr<dom::ImageBitmapCloneData> cloneData = std::move(imageBitmap.ToCloneData());
++    UniquePtr<dom::ImageBitmapCloneData> cloneData = imageBitmap.ToCloneData();
+     const RefPtr<gfx::DataSourceSurface> surf = cloneData->mSurface;
+ 
+     if (!width) {
+         width = surf->GetSize().width;
+     }
+ 
+     if (!height) {
+         height = surf->GetSize().height;
+@@ -451,17 +451,17 @@ ValidateTexOrSubImage(WebGLContext* webg
+     if (!ValidateViewType(webgl, funcName, pi.type, src))
+         return nullptr;
+ 
+     auto blob = webgl->From(funcName, target, rawWidth, rawHeight, rawDepth, border, src,
+                             scopedArr);
+     if (!blob || !blob->Validate(webgl, funcName, pi))
+         return nullptr;
+ 
+-    return std::move(blob);
++    return blob;
+ }
+ 
+ void
+ WebGLTexture::TexImage(const char* funcName, TexImageTarget target, GLint level,
+                        GLenum internalFormat, GLsizei width, GLsizei height,
+                        GLsizei depth, GLint border, const webgl::PackingInfo& pi,
+                        const TexImageSource& src)
+ {
+diff --git a/dom/clients/manager/ClientManager.cpp b/dom/clients/manager/ClientManager.cpp
+--- a/dom/clients/manager/ClientManager.cpp
++++ b/dom/clients/manager/ClientManager.cpp
+@@ -112,17 +112,17 @@ ClientManager::CreateSourceInternal(Clie
+   if (NS_WARN_IF(NS_FAILED(rv))) {
+     return nullptr;
+   }
+ 
+   ClientSourceConstructorArgs args(id, aType, aPrincipal, TimeStamp::Now());
+   UniquePtr<ClientSource> source(new ClientSource(this, aEventTarget, args));
+   source->Activate(GetActor());
+ 
+-  return std::move(source);
++  return source;
+ }
+ 
+ already_AddRefed<ClientHandle>
+ ClientManager::CreateHandleInternal(const ClientInfo& aClientInfo,
+                                     nsISerialEventTarget* aSerialEventTarget)
+ {
+   NS_ASSERT_OWNINGTHREAD(ClientManager);
+   MOZ_DIAGNOSTIC_ASSERT(aSerialEventTarget);
+diff --git a/dom/console/Console.cpp b/dom/console/Console.cpp
+--- a/dom/console/Console.cpp
++++ b/dom/console/Console.cpp
+@@ -2459,36 +2459,36 @@ Console::MonotonicTimer(JSContext* aCx, 
+         return false;
+       }
+ 
+       nsAutoJSString key;
+       if (!key.init(aCx, jsString)) {
+         return false;
+       }
+ 
+-      timelines->AddMarkerForDocShell(docShell, std::move(
+-        MakeUnique<TimestampTimelineMarker>(key)));
++      timelines->AddMarkerForDocShell(docShell,
++        MakeUnique<TimestampTimelineMarker>(key));
+     }
+     // For `console.time(foo)` and `console.timeEnd(foo)`.
+     else if (isTimelineRecording && aData.Length() == 1) {
+       JS::Rooted<JS::Value> value(aCx, aData[0]);
+       JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+       if (!jsString) {
+         return false;
+       }
+ 
+       nsAutoJSString key;
+       if (!key.init(aCx, jsString)) {
+         return false;
+       }
+ 
+-      timelines->AddMarkerForDocShell(docShell, std::move(
++      timelines->AddMarkerForDocShell(docShell,
+         MakeUnique<ConsoleTimelineMarker>(
+           key, aMethodName == MethodTime ? MarkerTracingType::START
+-                                         : MarkerTracingType::END)));
++                                         : MarkerTracingType::END));
+     }
+ 
+     return true;
+   }
+ 
+   if (NS_IsMainThread()) {
+     double duration = (TimeStamp::Now() - mCreationTimeStamp).ToMilliseconds();
+ 
+diff --git a/dom/events/EventListenerManager.cpp b/dom/events/EventListenerManager.cpp
+--- a/dom/events/EventListenerManager.cpp
++++ b/dom/events/EventListenerManager.cpp
+@@ -1228,19 +1228,19 @@ EventListenerManager::HandleEventInterna
+               docShell = nsContentUtils::GetDocShellForEventTarget(mTarget);
+               if (docShell) {
+                 if (timelines && timelines->HasConsumer(docShell)) {
+                   needsEndEventMarker = true;
+                   nsAutoString typeStr;
+                   (*aDOMEvent)->GetType(typeStr);
+                   uint16_t phase;
+                   (*aDOMEvent)->GetEventPhase(&phase);
+-                  timelines->AddMarkerForDocShell(docShell, std::move(
++                  timelines->AddMarkerForDocShell(docShell,
+                     MakeUnique<EventTimelineMarker>(
+-                      typeStr, phase, MarkerTracingType::START)));
++                      typeStr, phase, MarkerTracingType::START));
+                 }
+               }
+             }
+ 
+             aEvent->mFlags.mInPassiveListener = listener->mFlags.mPassive;
+             Maybe<Listener> listenerHolder;
+             if (listener->mFlags.mOnce) {
+               // Move the listener to the stack before handling the event.
+diff --git a/dom/file/nsHostObjectProtocolHandler.cpp b/dom/file/nsHostObjectProtocolHandler.cpp
+--- a/dom/file/nsHostObjectProtocolHandler.cpp
++++ b/dom/file/nsHostObjectProtocolHandler.cpp
+@@ -569,17 +569,17 @@ private:
+   {
+     nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
+     NS_ENSURE_TRUE(!!svc, nullptr);
+ 
+     nsCOMPtr<nsIAsyncShutdownClient> phase;
+     nsresult rv = svc->GetXpcomWillShutdown(getter_AddRefs(phase));
+     NS_ENSURE_SUCCESS(rv, nullptr);
+ 
+-    return std::move(phase);
++    return phase;
+   }
+ 
+   nsCString mURI;
+   bool mBroadcastToOtherProcesses;
+ 
+   nsCOMPtr<nsITimer> mTimer;
+ };
+ 
+diff --git a/dom/geolocation/nsGeolocation.cpp b/dom/geolocation/nsGeolocation.cpp
+--- a/dom/geolocation/nsGeolocation.cpp
++++ b/dom/geolocation/nsGeolocation.cpp
+@@ -1221,17 +1221,17 @@ void
+ Geolocation::GetCurrentPosition(PositionCallback& aCallback,
+                                 PositionErrorCallback* aErrorCallback,
+                                 const PositionOptions& aOptions,
+                                 CallerType aCallerType,
+                                 ErrorResult& aRv)
+ {
+   nsresult rv = GetCurrentPosition(GeoPositionCallback(&aCallback),
+                                    GeoPositionErrorCallback(aErrorCallback),
+-                                   std::move(CreatePositionOptionsCopy(aOptions)),
++                                   CreatePositionOptionsCopy(aOptions),
+                                    aCallerType);
+ 
+   if (NS_FAILED(rv)) {
+     aRv.Throw(rv);
+   }
+ }
+ 
+ nsresult
+@@ -1289,17 +1289,17 @@ Geolocation::WatchPosition(PositionCallb
+                            PositionErrorCallback* aErrorCallback,
+                            const PositionOptions& aOptions,
+                            CallerType aCallerType,
+                            ErrorResult& aRv)
+ {
+   int32_t ret = 0;
+   nsresult rv = WatchPosition(GeoPositionCallback(&aCallback),
+                               GeoPositionErrorCallback(aErrorCallback),
+-                              std::move(CreatePositionOptionsCopy(aOptions)),
++                              CreatePositionOptionsCopy(aOptions),
+                               aCallerType,
+                               &ret);
+ 
+   if (NS_FAILED(rv)) {
+     aRv.Throw(rv);
+   }
+ 
+   return ret;
+diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
+--- a/dom/ipc/ContentParent.cpp
++++ b/dom/ipc/ContentParent.cpp
+@@ -4251,18 +4251,18 @@ ContentParent::RecvNotifyTabDestroying(c
+ {
+   NotifyTabDestroying(aTabId, aCpId);
+   return IPC_OK();
+ }
+ 
+ nsTArray<TabContext>
+ ContentParent::GetManagedTabContext()
+ {
+-  return std::move(ContentProcessManager::GetSingleton()->
+-          GetTabContextByContentProcess(this->ChildID()));
++  return ContentProcessManager::GetSingleton()->
++    GetTabContextByContentProcess(this->ChildID());
+ }
+ 
+ mozilla::docshell::POfflineCacheUpdateParent*
+ ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
+                                               const URIParams& aDocumentURI,
+                                               const PrincipalInfo& aLoadingPrincipalInfo,
+                                               const bool& aStickDocument)
+ {
+diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp
+--- a/dom/ipc/ContentProcessManager.cpp
++++ b/dom/ipc/ContentProcessManager.cpp
+@@ -120,26 +120,26 @@ nsTArray<ContentParentId>
+ ContentProcessManager::GetAllChildProcessById(const ContentParentId& aParentCpId)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   nsTArray<ContentParentId> cpIdArray;
+   auto iter = mContentParentMap.find(aParentCpId);
+   if (NS_WARN_IF(iter == mContentParentMap.end())) {
+     ASSERT_UNLESS_FUZZING();
+-    return std::move(cpIdArray);
++    return cpIdArray;
+   }
+ 
+   for (auto cpIter = iter->second.mChildrenCpId.begin();
+        cpIter != iter->second.mChildrenCpId.end();
+        ++cpIter) {
+     cpIdArray.AppendElement(*cpIter);
+   }
+ 
+-  return std::move(cpIdArray);
++  return cpIdArray;
+ }
+ 
+ bool
+ ContentProcessManager::RegisterRemoteFrame(const TabId& aTabId,
+                                            const ContentParentId& aOpenerCpId,
+                                            const TabId& aOpenerTabId,
+                                            const IPCTabContext& aContext,
+                                            const ContentParentId& aChildCpId)
+@@ -231,26 +231,26 @@ nsTArray<TabContext>
+ ContentProcessManager::GetTabContextByContentProcess(const ContentParentId& aChildCpId)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   nsTArray<TabContext> tabContextArray;
+   auto iter = mContentParentMap.find(aChildCpId);
+   if (NS_WARN_IF(iter == mContentParentMap.end())) {
+     ASSERT_UNLESS_FUZZING();
+-    return std::move(tabContextArray);
++    return tabContextArray;
+   }
+ 
+   for (auto remoteFrameIter = iter->second.mRemoteFrames.begin();
+        remoteFrameIter != iter->second.mRemoteFrames.end();
+        ++remoteFrameIter) {
+     tabContextArray.AppendElement(remoteFrameIter->second.mContext);
+   }
+ 
+-  return std::move(tabContextArray);
++  return tabContextArray;
+ }
+ 
+ bool
+ ContentProcessManager::GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
+                                                  const TabId& aChildTabId,
+                                                  /*out*/ContentParentId* aOpenerCpId,
+                                                  /*out*/TabId* aOpenerTabId)
+ {
+@@ -332,26 +332,26 @@ nsTArray<TabId>
+ ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   nsTArray<TabId> tabIdList;
+   auto iter = mContentParentMap.find(aChildCpId);
+   if (NS_WARN_IF(iter == mContentParentMap.end())) {
+     ASSERT_UNLESS_FUZZING();
+-    return std::move(tabIdList);
++    return tabIdList;
+   }
+ 
+   for (auto remoteFrameIter = iter->second.mRemoteFrames.begin();
+       remoteFrameIter != iter->second.mRemoteFrames.end();
+       ++remoteFrameIter) {
+     tabIdList.AppendElement(remoteFrameIter->first);
+   }
+ 
+-  return std::move(tabIdList);
++  return tabIdList;
+ }
+ 
+ uint32_t
+ ContentProcessManager::GetTabParentCountByProcessId(const ContentParentId& aChildCpId)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+ 
+   auto iter = mContentParentMap.find(aChildCpId);
+diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp
+--- a/dom/media/MediaStreamGraph.cpp
++++ b/dom/media/MediaStreamGraph.cpp
+@@ -2594,17 +2594,17 @@ MediaStream::SetTrackEnabledImpl(TrackID
+     }
+   } else {
+     for (const DisabledTrack& t : mDisabledTracks) {
+       if (aTrackID == t.mTrackID) {
+         NS_ERROR("Changing disabled track mode for a track is not allowed");
+         return;
+       }
+     }
+-    mDisabledTracks.AppendElement(std::move(DisabledTrack(aTrackID, aMode)));
++    mDisabledTracks.AppendElement(DisabledTrack(aTrackID, aMode));
+   }
+ }
+ 
+ DisabledTrackMode
+ MediaStream::GetDisabledTrackMode(TrackID aTrackID)
+ {
+   for (const DisabledTrack& t : mDisabledTracks) {
+     if (t.mTrackID == aTrackID) {
+diff --git a/dom/media/eme/MediaKeySystemAccess.cpp b/dom/media/eme/MediaKeySystemAccess.cpp
+--- a/dom/media/eme/MediaKeySystemAccess.cpp
++++ b/dom/media/eme/MediaKeySystemAccess.cpp
+@@ -757,17 +757,17 @@ GetSupportedCapabilities(
+     if (!supportedCapabilities.AppendElement(capabilities, mozilla::fallible)) {
+       NS_WARNING("GetSupportedCapabilities: Malloc failure");
+       return Sequence<MediaKeySystemMediaCapability>();
+     }
+ 
+     // Note: omitting steps 3.13.2, our robustness is not sophisticated enough
+     // to require considering all requirements together.
+   }
+-  return std::move(supportedCapabilities);
++  return supportedCapabilities;
+ }
+ 
+ // "Get Supported Configuration and Consent" algorithm, steps 4-7 for
+ // distinctive identifier, and steps 8-11 for persistent state. The steps
+ // are the same for both requirements/features, so we factor them out into
+ // a single function.
+ static bool
+ CheckRequirement(const MediaKeysRequirement aRequirement,
+diff --git a/dom/media/fake-cdm/cdm-test-decryptor.cpp b/dom/media/fake-cdm/cdm-test-decryptor.cpp
+--- a/dom/media/fake-cdm/cdm-test-decryptor.cpp
++++ b/dom/media/fake-cdm/cdm-test-decryptor.cpp
+@@ -106,17 +106,17 @@ Tokenize(const std::string& aString)
+ static const string TruncateRecordId = "truncate-record-id";
+ static const string TruncateRecordData = "I will soon be truncated";
+ 
+ template<class Continuation>
+ class WriteRecordSuccessTask {
+ public:
+   WriteRecordSuccessTask(string aId, Continuation aThen)
+     : mId(aId)
+-    , mThen(move(aThen))
++    , mThen(std::move(aThen))
+   {}
+ 
+   void operator()()
+   {
+     ReadRecord(FakeDecryptor::sInstance->mHost, mId, mThen);
+   }
+ 
+   string mId;
+diff --git a/dom/media/fake-cdm/cdm-test-storage.cpp b/dom/media/fake-cdm/cdm-test-storage.cpp
+--- a/dom/media/fake-cdm/cdm-test-storage.cpp
++++ b/dom/media/fake-cdm/cdm-test-storage.cpp
+@@ -14,18 +14,18 @@ using namespace std;
+ 
+ class WriteRecordClient : public FileIOClient
+ {
+ public:
+   WriteRecordClient(function<void()>&& aOnSuccess,
+                     function<void()>&& aOnFailure,
+                     const uint8_t* aData,
+                     uint32_t aDataSize)
+-    : mOnSuccess(move(aOnSuccess))
+-    , mOnFailure(move(aOnFailure))
++    : mOnSuccess(std::move(aOnSuccess))
++    , mOnFailure(std::move(aOnFailure))
+   {
+     mData.insert(mData.end(), aData, aData + aDataSize);
+   }
+ 
+   void OnOpenComplete(Status aStatus) override
+   {
+     // If we hit an error, fail.
+     if (aStatus != Status::kSuccess) {
+@@ -85,43 +85,43 @@ void
+ WriteRecord(Host_9* aHost,
+             const std::string& aRecordName,
+             const uint8_t* aData,
+             uint32_t aNumBytes,
+             function<void()>&& aOnSuccess,
+             function<void()>&& aOnFailure)
+ {
+   // client will be delete in WriteRecordClient::Done
+-  WriteRecordClient* client = new WriteRecordClient(move(aOnSuccess),
+-                                                    move(aOnFailure),
++  WriteRecordClient* client = new WriteRecordClient(std::move(aOnSuccess),
++                                                    std::move(aOnFailure),
+                                                     aData,
+                                                     aNumBytes);
+   client->Do(aRecordName, aHost);
+ }
+ 
+ void
+ WriteRecord(Host_9* aHost,
+             const std::string& aRecordName,
+             const std::string& aData,
+             function<void()> &&aOnSuccess,
+             function<void()>&& aOnFailure)
+ {
+   return WriteRecord(aHost,
+                      aRecordName,
+                      (const uint8_t*)aData.c_str(),
+                      aData.size(),
+-                     move(aOnSuccess),
+-                     move(aOnFailure));
++                     std::move(aOnSuccess),
++                     std::move(aOnFailure));
+ }
+ 
+ class ReadRecordClient : public FileIOClient
+ {
+ public:
+   explicit ReadRecordClient(function<void(bool, const uint8_t*, uint32_t)>&& aOnReadComplete)
+-    : mOnReadComplete(move(aOnReadComplete))
++    : mOnReadComplete(std::move(aOnReadComplete))
+   {
+   }
+ 
+   void OnOpenComplete(Status aStatus) override
+   {
+     auto err = aStatus;
+     if (aStatus != Status::kSuccess) {
+       Done(err, reinterpret_cast<const uint8_t*>(""), 0);
+@@ -176,25 +176,25 @@ private:
+ };
+ 
+ void
+ ReadRecord(Host_9* aHost,
+            const std::string& aRecordName,
+            function<void(bool, const uint8_t*, uint32_t)>&& aOnReadComplete)
+ {
+   // client will be delete in ReadRecordClient::Done
+-  ReadRecordClient* client = new ReadRecordClient(move(aOnReadComplete));
++  ReadRecordClient* client = new ReadRecordClient(std::move(aOnReadComplete));
+   client->Do(aRecordName, aHost);
+ }
+ 
+ class OpenRecordClient : public FileIOClient
+ {
+ public:
+   explicit OpenRecordClient(function<void(bool)>&& aOpenComplete)
+-    : mOpenComplete(move(aOpenComplete))
++    : mOpenComplete(std::move(aOpenComplete))
+   {
+   }
+ 
+   void OnOpenComplete(Status aStatus) override
+   {
+     Done(aStatus);
+   }
+ 
+@@ -242,11 +242,11 @@ private:
+ };
+ 
+ void
+ OpenRecord(Host_9* aHost,
+            const std::string& aRecordName,
+            function<void(bool)>&& aOpenComplete)
+ {
+   // client will be delete in OpenRecordClient::Done
+-  OpenRecordClient* client = new OpenRecordClient(move(aOpenComplete));
++  OpenRecordClient* client = new OpenRecordClient(std::move(aOpenComplete));
+   client->Do(aRecordName, aHost);
+ }
+diff --git a/dom/media/gmp/ChromiumCDMParent.cpp b/dom/media/gmp/ChromiumCDMParent.cpp
+--- a/dom/media/gmp/ChromiumCDMParent.cpp
++++ b/dom/media/gmp/ChromiumCDMParent.cpp
+@@ -1073,17 +1073,17 @@ ChromiumCDMParent::RecvDrainComplete()
+ {
+   if (mIsShutdown) {
+     MOZ_ASSERT(mDecodePromise.IsEmpty());
+     return IPC_OK();
+   }
+ 
+   MediaDataDecoder::DecodedData samples;
+   while (!mReorderQueue.IsEmpty()) {
+-    samples.AppendElement(std::move(mReorderQueue.Pop()));
++    samples.AppendElement(mReorderQueue.Pop());
+   }
+ 
+   mDecodePromise.ResolveIfExists(std::move(samples), __func__);
+   return IPC_OK();
+ }
+ RefPtr<ShutdownPromise>
+ ChromiumCDMParent::ShutdownVideoDecoder()
+ {
+diff --git a/dom/media/gmp/GMPParent.cpp b/dom/media/gmp/GMPParent.cpp
+--- a/dom/media/gmp/GMPParent.cpp
++++ b/dom/media/gmp/GMPParent.cpp
+@@ -946,17 +946,17 @@ GMPParent::GetGMPContentParent(UniquePtr
+     }
+   }
+ }
+ 
+ already_AddRefed<GMPContentParent>
+ GMPParent::ForgetGMPContentParent()
+ {
+   MOZ_ASSERT(mGetContentParentPromises.IsEmpty());
+-  return std::move(mGMPContentParent.forget());
++  return mGMPContentParent.forget();
+ }
+ 
+ bool
+ GMPParent::EnsureProcessLoaded(base::ProcessId* aID)
+ {
+   if (!EnsureProcessLoaded()) {
+     return false;
+   }
+diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp
+--- a/dom/media/gmp/GMPServiceParent.cpp
++++ b/dom/media/gmp/GMPServiceParent.cpp
+@@ -1879,17 +1879,17 @@ GMPServiceParent::ActorDestroy(ActorDest
+   // Make sure the IPC channel is closed before destroying mToDelete.
+   MonitorAutoLock lock(monitor);
+   RefPtr<Runnable> task = NewNonOwningRunnableMethod<Monitor*, bool*>(
+     "gmp::GMPServiceParent::CloseTransport",
+     this,
+     &GMPServiceParent::CloseTransport,
+     &monitor,
+     &completed);
+-  XRE_GetIOMessageLoop()->PostTask(std::move(task.forget()));
++  XRE_GetIOMessageLoop()->PostTask(task.forget());
+ 
+   while (!completed) {
+     lock.Wait();
+   }
+ 
+   // Dispatch a task to the current thread to ensure we don't delete the
+   // GMPServiceParent until the current calling context is finished with
+   // the object.
+diff --git a/dom/media/gmp/GMPStorageChild.cpp b/dom/media/gmp/GMPStorageChild.cpp
+--- a/dom/media/gmp/GMPStorageChild.cpp
++++ b/dom/media/gmp/GMPStorageChild.cpp
+@@ -21,17 +21,17 @@
+     } \
+   } while(false)
+ 
+ static nsTArray<uint8_t>
+ ToArray(const uint8_t* aData, uint32_t aDataSize)
+ {
+   nsTArray<uint8_t> data;
+   data.AppendElements(aData, aDataSize);
+-  return std::move(data);
++  return data;
+ }
+ 
+ namespace mozilla {
+ namespace gmp {
+ 
+ GMPRecordImpl::GMPRecordImpl(GMPStorageChild* aOwner,
+                              const nsCString& aName,
+                              GMPRecordClient* aClient)
+diff --git a/dom/media/ipc/VideoDecoderManagerParent.cpp b/dom/media/ipc/VideoDecoderManagerParent.cpp
+--- a/dom/media/ipc/VideoDecoderManagerParent.cpp
++++ b/dom/media/ipc/VideoDecoderManagerParent.cpp
+@@ -33,17 +33,17 @@ using namespace gfx;
+ SurfaceDescriptorGPUVideo
+ VideoDecoderManagerParent::StoreImage(Image* aImage, TextureClient* aTexture)
+ {
+   SurfaceDescriptorGPUVideo ret;
+   aTexture->GPUVideoDesc(&ret);
+ 
+   mImageMap[ret.handle()] = aImage;
+   mTextureMap[ret.handle()] = aTexture;
+-  return std::move(ret);
++  return ret;
+ }
+ 
+ StaticRefPtr<nsIThread> sVideoDecoderManagerThread;
+ StaticRefPtr<TaskQueue> sManagerTaskQueue;
+ 
+ class VideoDecoderManagerThreadHolder
+ {
+   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VideoDecoderManagerThreadHolder)
+diff --git a/dom/media/platforms/apple/AppleVTDecoder.cpp b/dom/media/platforms/apple/AppleVTDecoder.cpp
+--- a/dom/media/platforms/apple/AppleVTDecoder.cpp
++++ b/dom/media/platforms/apple/AppleVTDecoder.cpp
+@@ -248,17 +248,17 @@ AppleVTDecoder::ProcessDrain()
+   AssertOnTaskQueueThread();
+   nsresult rv = WaitForAsynchronousFrames();
+   if (NS_FAILED(rv)) {
+     LOG("AppleVTDecoder::Drain failed waiting for platform decoder");
+   }
+   MonitorAutoLock mon(mMonitor);
+   DecodedData samples;
+   while (!mReorderQueue.IsEmpty()) {
+-    samples.AppendElement(std::move(mReorderQueue.Pop()));
++    samples.AppendElement(mReorderQueue.Pop());
+   }
+   return DecodePromise::CreateAndResolve(std::move(samples), __func__);
+ }
+ 
+ AppleVTDecoder::AppleFrameRef*
+ AppleVTDecoder::CreateAppleFrameRef(const MediaRawData* aSample)
+ {
+   MOZ_ASSERT(aSample);
+diff --git a/dom/media/platforms/omx/OmxPlatformLayer.cpp b/dom/media/platforms/omx/OmxPlatformLayer.cpp
+--- a/dom/media/platforms/omx/OmxPlatformLayer.cpp
++++ b/dom/media/platforms/omx/OmxPlatformLayer.cpp
+@@ -172,17 +172,17 @@ ConfigForMime(const nsACString& aMimeTyp
+                 aMimeType.EqualsLiteral("audio/mpeg")) {
+       conf.reset(new OmxMp3Config());
+     } else if (aMimeType.EqualsLiteral("audio/3gpp")) {
+       conf.reset(new OmxAmrConfig<OmxAmrSampleRate::kNarrowBand>());
+     } else if (aMimeType.EqualsLiteral("audio/amr-wb")) {
+       conf.reset(new OmxAmrConfig<OmxAmrSampleRate::kWideBand>());
+     }
+   }
+-  return std::move(conf);
++  return conf;
+ }
+ 
+ // There should be a better way to calculate it.
+ #define MIN_VIDEO_INPUT_BUFFER_SIZE 64 * 1024
+ 
+ class OmxCommonVideoConfig : public OmxVideoConfig
+ {
+ public:
+@@ -230,17 +230,17 @@ template<>
+ UniquePtr<OmxVideoConfig>
+ ConfigForMime(const nsACString& aMimeType)
+ {
+   UniquePtr<OmxVideoConfig> conf;
+ 
+   if (OmxPlatformLayer::SupportsMimeType(aMimeType)) {
+     conf.reset(new OmxCommonVideoConfig());
+   }
+-  return std::move(conf);
++  return conf;
+ }
+ 
+ OMX_ERRORTYPE
+ OmxPlatformLayer::Config()
+ {
+   MOZ_ASSERT(mInfo);
+ 
+   OMX_PORT_PARAM_TYPE portParam;
+diff --git a/dom/u2f/U2F.cpp b/dom/u2f/U2F.cpp
+--- a/dom/u2f/U2F.cpp
++++ b/dom/u2f/U2F.cpp
+@@ -345,17 +345,17 @@ U2F::Register(const nsAString& aAppId,
+   WebAuthnMakeCredentialInfo info(rpIdHash,
+                                   clientDataHash,
+                                   adjustedTimeoutMillis,
+                                   excludeList,
+                                   extensions,
+                                   authSelection);
+ 
+   MOZ_ASSERT(mTransaction.isNothing());
+-  mTransaction = Some(U2FTransaction(clientData, std::move(AsVariant(callback))));
++  mTransaction = Some(U2FTransaction(clientData, AsVariant(callback)));
+   mChild->SendRequestRegister(mTransaction.ref().mId, info);
+ }
+ 
+ void
+ U2F::FinishMakeCredential(const uint64_t& aTransactionId,
+                           const WebAuthnMakeCredentialResult& aResult)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+@@ -486,17 +486,17 @@ U2F::Sign(const nsAString& aAppId,
+   WebAuthnGetAssertionInfo info(rpIdHash,
+                                 clientDataHash,
+                                 adjustedTimeoutMillis,
+                                 permittedList,
+                                 false, /* requireUserVerification */
+                                 extensions);
+ 
+   MOZ_ASSERT(mTransaction.isNothing());
+-  mTransaction = Some(U2FTransaction(clientData, std::move(AsVariant(callback))));
++  mTransaction = Some(U2FTransaction(clientData, AsVariant(callback)));
+   mChild->SendRequestSign(mTransaction.ref().mId, info);
+ }
+ 
+ void
+ U2F::FinishGetAssertion(const uint64_t& aTransactionId,
+                         const WebAuthnGetAssertionResult& aResult)
+ {
+   MOZ_ASSERT(NS_IsMainThread());
+diff --git a/dom/workers/ServiceWorkerRegistrar.cpp b/dom/workers/ServiceWorkerRegistrar.cpp
+--- a/dom/workers/ServiceWorkerRegistrar.cpp
++++ b/dom/workers/ServiceWorkerRegistrar.cpp
+@@ -1174,17 +1174,17 @@ ServiceWorkerRegistrar::GetShutdownPhase
+   // memory), and there's no point in continuing startup. Include as much
+   // information as possible in the crash report.
+   RELEASE_ASSERT_SUCCEEDED(rv, "async shutdown service");
+ 
+ 
+   nsCOMPtr<nsIAsyncShutdownClient> client;
+   rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
+   RELEASE_ASSERT_SUCCEEDED(rv, "profileBeforeChange shutdown blocker");
+-  return std::move(client);
++  return client;
+ }
+ 
+ #undef RELEASE_ASSERT_SUCCEEDED
+ 
+ void
+ ServiceWorkerRegistrar::Shutdown()
+ {
+   AssertIsOnBackgroundThread();
+diff --git a/editor/libeditor/HTMLAnonymousNodeEditor.cpp b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+--- a/editor/libeditor/HTMLAnonymousNodeEditor.cpp
++++ b/editor/libeditor/HTMLAnonymousNodeEditor.cpp
+@@ -250,17 +250,17 @@ HTMLEditor::CreateAnonymousElement(nsIAt
+   // sort of ok.
+   newContent->SetProperty(nsGkAtoms::restylableAnonymousNode,
+ 			  reinterpret_cast<void*>(true));
+ #endif // DEBUG
+ 
+   // display the element
+   ps->PostRecreateFramesFor(newContent);
+ 
+-  return std::move(newContent);
++  return newContent;
+ }
+ 
+ // Removes event listener and calls DeleteRefToAnonymousNode.
+ void
+ HTMLEditor::RemoveListenerAndDeleteRef(const nsAString& aEvent,
+                                        nsIDOMEventListener* aListener,
+                                        bool aUseCapture,
+                                        ManualNACPtr aElement,
+diff --git a/editor/libeditor/HTMLEditRules.cpp b/editor/libeditor/HTMLEditRules.cpp
+--- a/editor/libeditor/HTMLEditRules.cpp
++++ b/editor/libeditor/HTMLEditRules.cpp
+@@ -4904,33 +4904,32 @@ HTMLEditRules::CreateStyleForInsertText(
+   NS_ENSURE_STATE(aSelection.GetRangeAt(0));
+   nsCOMPtr<nsINode> node = aSelection.GetRangeAt(0)->GetStartContainer();
+   int32_t offset = aSelection.GetRangeAt(0)->StartOffset();
+ 
+   nsCOMPtr<Element> rootElement = aDoc.GetRootElement();
+   NS_ENSURE_STATE(rootElement);
+ 
+   // process clearing any styles first
+-  UniquePtr<PropItem> item =
+-    std::move(mHTMLEditor->mTypeInState->TakeClearProperty());
++  UniquePtr<PropItem> item = mHTMLEditor->mTypeInState->TakeClearProperty();
+   while (item && node != rootElement) {
+     NS_ENSURE_STATE(mHTMLEditor);
+     // XXX If we redesign ClearStyle(), we can use EditorDOMPoint in this
+     //     method.
+     nsresult rv =
+       mHTMLEditor->ClearStyle(address_of(node), &offset,
+                               item->tag, item->attr);
+     NS_ENSURE_SUCCESS(rv, rv);
+-    item = std::move(mHTMLEditor->mTypeInState->TakeClearProperty());
++    item = mHTMLEditor->mTypeInState->TakeClearProperty();
+     weDidSomething = true;
+   }
+ 
+   // then process setting any styles
+   int32_t relFontSize = mHTMLEditor->mTypeInState->TakeRelativeFontSize();
+-  item = std::move(mHTMLEditor->mTypeInState->TakeSetProperty());
++  item = mHTMLEditor->mTypeInState->TakeSetProperty();
+ 
+   if (item || relFontSize) {
+     // we have at least one style to add; make a new text node to insert style
+     // nodes above.
+     if (RefPtr<Text> text = node->GetAsText()) {
+       if (NS_WARN_IF(!mHTMLEditor)) {
+         return NS_ERROR_FAILURE;
+       }
+diff --git a/editor/libeditor/HTMLEditorObjectResizer.cpp b/editor/libeditor/HTMLEditorObjectResizer.cpp
+--- a/editor/libeditor/HTMLEditorObjectResizer.cpp
++++ b/editor/libeditor/HTMLEditorObjectResizer.cpp
+@@ -146,17 +146,17 @@ HTMLEditor::CreateResizer(int16_t aLocat
+     case nsIHTMLObjectResizer::eBottomRight:
+       locationStr = kBottomRight;
+       break;
+   }
+ 
+   nsresult rv =
+     ret->SetAttr(kNameSpaceID_None, nsGkAtoms::anonlocation, locationStr, true);
+   NS_ENSURE_SUCCESS(rv, nullptr);
+-  return std::move(ret);
++  return ret;
+ }
+ 
+ ManualNACPtr
+ HTMLEditor::CreateShadow(nsIContent& aParentContent,
+                          Element& aOriginalObject)
+ {
+   // let's create an image through the element factory
+   nsCOMPtr<nsIAtom> name;
+diff --git a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp
+--- a/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp
++++ b/extensions/spellcheck/hunspell/glue/RemoteSpellCheckEngineChild.cpp
+@@ -34,17 +34,17 @@ RemoteSpellcheckEngineChild::SetCurrentD
+   if (!SendSetDictionaryFromList(
+          aList,
+          reinterpret_cast<intptr_t>(promiseHolder.get()))) {
+     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+   }
+   RefPtr<GenericPromise> result = promiseHolder->Ensure(__func__);
+   // promiseHolder will removed by receive message
+   mResponsePromises.AppendElement(std::move(promiseHolder));
+-  return std::move(result);
++  return result;
+ }
+ 
+ mozilla::ipc::IPCResult
+ RemoteSpellcheckEngineChild::RecvNotifyOfCurrentDictionary(
+                                const nsString& aDictionary,
+                                const intptr_t& aId)
+ {
+   MozPromiseHolder<GenericPromise>* promiseHolder =
+diff --git a/gfx/2d/SFNTData.cpp b/gfx/2d/SFNTData.cpp
+--- a/gfx/2d/SFNTData.cpp
++++ b/gfx/2d/SFNTData.cpp
+@@ -134,25 +134,25 @@ SFNTData::Create(const uint8_t *aFontDat
+     const BigEndianUint32* endOfOffsets = offset + numFonts;
+     while (offset != endOfOffsets) {
+       if (!sfntData->AddFont(aFontData, aDataLength, *offset)) {
+         return nullptr;
+       }
+       ++offset;
+     }
+ 
+-    return std::move(sfntData);
++    return sfntData;
+   }
+ 
+   UniquePtr<SFNTData> sfntData(new SFNTData);
+   if (!sfntData->AddFont(aFontData, aDataLength, 0)) {
+     return nullptr;
+   }
+ 
+-  return std::move(sfntData);
++  return sfntData;
+ }
+ 
+ /* static */
+ uint64_t
+ SFNTData::GetUniqueKey(const uint8_t *aFontData, uint32_t aDataLength,
+                        uint32_t aVarDataSize, const void* aVarData)
+ {
+   uint64_t hash;
+diff --git a/gfx/gl/GLScreenBuffer.cpp b/gfx/gl/GLScreenBuffer.cpp
+--- a/gfx/gl/GLScreenBuffer.cpp
++++ b/gfx/gl/GLScreenBuffer.cpp
+@@ -43,28 +43,28 @@ UniquePtr<GLScreenBuffer>
+ GLScreenBuffer::Create(GLContext* gl,
+                        const gfx::IntSize& size,
+                        const SurfaceCaps& caps)
+ {
+     UniquePtr<GLScreenBuffer> ret;
+     if (caps.antialias &&
+         !gl->IsSupported(GLFeature::framebuffer_multisample))
+     {
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     layers::TextureFlags flags = layers::TextureFlags::ORIGIN_BOTTOM_LEFT;
+     if (!caps.premultAlpha) {
+         flags |= layers::TextureFlags::NON_PREMULTIPLIED;
+     }
+ 
+     UniquePtr<SurfaceFactory> factory = MakeUnique<SurfaceFactory_Basic>(gl, caps, flags);
+ 
+     ret.reset( new GLScreenBuffer(gl, caps, std::move(factory)) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ /* static */ UniquePtr<SurfaceFactory>
+ GLScreenBuffer::CreateFactory(GLContext* gl,
+                               const SurfaceCaps& caps,
+                               KnowsCompositor* compositorConnection,
+                               const layers::TextureFlags& flags)
+ {
+@@ -948,17 +948,17 @@ ReadBuffer::Create(GLContext* gl,
+     const bool isComplete = gl->IsFramebufferComplete(fb);
+     if (needsAcquire) {
+         surf->ProducerReadRelease();
+     }
+ 
+     if (!isComplete)
+         return nullptr;
+ 
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ ReadBuffer::~ReadBuffer()
+ {
+     if (!mGL->MakeCurrent())
+         return;
+ 
+     GLuint fb = mFB;
+diff --git a/gfx/gl/MozFramebuffer.cpp b/gfx/gl/MozFramebuffer.cpp
+--- a/gfx/gl/MozFramebuffer.cpp
++++ b/gfx/gl/MozFramebuffer.cpp
+@@ -129,17 +129,17 @@ MozFramebuffer::CreateWith(GLContext* co
+     }
+ 
+     const auto status = gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER);
+     if (status != LOCAL_GL_FRAMEBUFFER_COMPLETE) {
+         MOZ_ASSERT(false);
+         return nullptr;
+     }
+ 
+-    return std::move(mozFB);
++    return mozFB;
+ }
+ 
+ ////////////////////
+ 
+ MozFramebuffer::MozFramebuffer(GLContext* const gl, const gfx::IntSize& size,
+                                const uint32_t samples, const bool depthStencil,
+                                const GLenum colorTarget, const GLuint colorName)
+     : mWeakGL(gl)
+diff --git a/gfx/gl/SharedSurface.cpp b/gfx/gl/SharedSurface.cpp
+--- a/gfx/gl/SharedSurface.cpp
++++ b/gfx/gl/SharedSurface.cpp
+@@ -327,17 +327,17 @@ SurfaceFactory::NewTexClient(const gfx::
+         if (cur->Surf()->mSize == size) {
+             cur->Surf()->WaitForBufferOwnership();
+             return cur.forget();
+         }
+ 
+         StopRecycling(cur);
+     }
+ 
+-    UniquePtr<SharedSurface> surf = std::move(CreateShared(size));
++    UniquePtr<SharedSurface> surf = CreateShared(size);
+     if (!surf)
+         return nullptr;
+ 
+     RefPtr<layers::SharedSurfaceTextureClient> ret;
+     ret = layers::SharedSurfaceTextureClient::Create(std::move(surf), this, mAllocator, mFlags);
+ 
+     StartRecycling(ret);
+ 
+diff --git a/gfx/gl/SharedSurfaceEGL.cpp b/gfx/gl/SharedSurfaceEGL.cpp
+--- a/gfx/gl/SharedSurfaceEGL.cpp
++++ b/gfx/gl/SharedSurfaceEGL.cpp
+@@ -25,37 +25,37 @@ SharedSurface_EGLImage::Create(GLContext
+ {
+     GLLibraryEGL* egl = &sEGLLibrary;
+     MOZ_ASSERT(egl);
+     MOZ_ASSERT(context);
+ 
+     UniquePtr<SharedSurface_EGLImage> ret;
+ 
+     if (!HasExtensions(egl, prodGL)) {
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     MOZ_ALWAYS_TRUE(prodGL->MakeCurrent());
+     GLuint prodTex = CreateTextureForOffscreen(prodGL, formats, size);
+     if (!prodTex) {
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     EGLClientBuffer buffer = reinterpret_cast<EGLClientBuffer>(uintptr_t(prodTex));
+     EGLImage image = egl->fCreateImage(egl->Display(), context,
+                                        LOCAL_EGL_GL_TEXTURE_2D, buffer,
+                                        nullptr);
+     if (!image) {
+         prodGL->fDeleteTextures(1, &prodTex);
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     ret.reset( new SharedSurface_EGLImage(prodGL, egl, size, hasAlpha,
+                                           formats, prodTex, image) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ bool
+ SharedSurface_EGLImage::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
+ {
+     return egl->HasKHRImageBase() &&
+            egl->IsExtensionSupported(GLLibraryEGL::KHR_gl_texture_2D_image) &&
+            (gl->IsExtensionSupported(GLContext::OES_EGL_image_external) ||
+@@ -172,17 +172,17 @@ SurfaceFactory_EGLImage::Create(GLContex
+     typedef SurfaceFactory_EGLImage ptrT;
+     UniquePtr<ptrT> ret;
+ 
+     GLLibraryEGL* egl = &sEGLLibrary;
+     if (SharedSurface_EGLImage::HasExtensions(egl, prodGL)) {
+         ret.reset( new ptrT(prodGL, caps, allocator, flags, context) );
+     }
+ 
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ ////////////////////////////////////////////////////////////////////////
+ 
+ #ifdef MOZ_WIDGET_ANDROID
+ 
+ /*static*/ UniquePtr<SharedSurface_SurfaceTexture>
+ SharedSurface_SurfaceTexture::Create(GLContext* prodGL,
+@@ -195,22 +195,22 @@ SharedSurface_SurfaceTexture::Create(GLC
+ 
+     UniquePtr<SharedSurface_SurfaceTexture> ret;
+ 
+     AndroidNativeWindow window(surface);
+     GLContextEGL* egl = GLContextEGL::Cast(prodGL);
+     MOZ_ASSERT(egl);
+     EGLSurface eglSurface = egl->CreateCompatibleSurface(window.NativeWindow());
+     if (!eglSurface) {
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     ret.reset(new SharedSurface_SurfaceTexture(prodGL, size, hasAlpha,
+                                                formats, surface, eglSurface));
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ SharedSurface_SurfaceTexture::SharedSurface_SurfaceTexture(GLContext* gl,
+                                                            const gfx::IntSize& size,
+                                                            bool hasAlpha,
+                                                            const GLFormats& formats,
+                                                            java::GeckoSurface::Param surface,
+                                                            EGLSurface eglSurface)
+@@ -287,17 +287,17 @@ SharedSurface_SurfaceTexture::ToSurfaceD
+ 
+ /*static*/ UniquePtr<SurfaceFactory_SurfaceTexture>
+ SurfaceFactory_SurfaceTexture::Create(GLContext* prodGL, const SurfaceCaps& caps,
+                                       const RefPtr<layers::LayersIPCChannel>& allocator,
+                                       const layers::TextureFlags& flags)
+ {
+     UniquePtr<SurfaceFactory_SurfaceTexture> ret(
+         new SurfaceFactory_SurfaceTexture(prodGL, caps, allocator, flags));
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ UniquePtr<SharedSurface>
+ SurfaceFactory_SurfaceTexture::CreateShared(const gfx::IntSize& size)
+ {
+     bool hasAlpha = mReadCaps.alpha;
+ 
+     jni::Object::LocalRef surface = java::SurfaceAllocator::AcquireSurface(size.width, size.height, true);
+diff --git a/gfx/gl/SharedSurfaceGL.cpp b/gfx/gl/SharedSurfaceGL.cpp
+--- a/gfx/gl/SharedSurfaceGL.cpp
++++ b/gfx/gl/SharedSurfaceGL.cpp
+@@ -28,35 +28,35 @@ SharedSurface_Basic::Create(GLContext* g
+ 
+     GLContext::LocalErrorScope localError(*gl);
+     GLuint tex = CreateTextureForOffscreen(gl, formats, size);
+ 
+     GLenum err = localError.GetError();
+     MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
+     if (err) {
+         gl->fDeleteTextures(1, &tex);
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     bool ownsTex = true;
+     ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, tex, ownsTex) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ 
+ /*static*/ UniquePtr<SharedSurface_Basic>
+ SharedSurface_Basic::Wrap(GLContext* gl,
+                           const IntSize& size,
+                           bool hasAlpha,
+                           GLuint tex)
+ {
+     bool ownsTex = false;
+     UniquePtr<SharedSurface_Basic> ret( new SharedSurface_Basic(gl, size, hasAlpha, tex,
+                                                                 ownsTex) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ SharedSurface_Basic::SharedSurface_Basic(GLContext* gl,
+                                          const IntSize& size,
+                                          bool hasAlpha,
+                                          GLuint tex,
+                                          bool ownsTex)
+     : SharedSurface(SharedSurfaceType::Basic,
+@@ -121,22 +121,22 @@ SharedSurface_GLTexture::Create(GLContex
+     GLContext::LocalErrorScope localError(*prodGL);
+ 
+     GLuint tex = CreateTextureForOffscreen(prodGL, formats, size);
+ 
+     GLenum err = localError.GetError();
+     MOZ_ASSERT_IF(err, err == LOCAL_GL_OUT_OF_MEMORY);
+     if (err) {
+         prodGL->fDeleteTextures(1, &tex);
+-        return std::move(ret);
++        return ret;
+     }
+ 
+     ret.reset(new SharedSurface_GLTexture(prodGL, size,
+                                           hasAlpha, tex));
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ SharedSurface_GLTexture::~SharedSurface_GLTexture()
+ {
+     if (!mGL->MakeCurrent())
+         return;
+ 
+     if (mTex) {
+diff --git a/gfx/gl/SharedSurfaceGLX.cpp b/gfx/gl/SharedSurfaceGLX.cpp
+--- a/gfx/gl/SharedSurfaceGLX.cpp
++++ b/gfx/gl/SharedSurfaceGLX.cpp
+@@ -32,17 +32,17 @@ SharedSurface_GLXDrawable::Create(GLCont
+     Screen* screen = XDefaultScreenOfDisplay(display);
+     Visual* visual = gfxXlibSurface::FindVisual(screen, gfx::SurfaceFormat::A8R8G8B8_UINT32);
+ 
+     RefPtr<gfxXlibSurface> surf = gfxXlibSurface::Create(screen, visual, size);
+     if (!deallocateClient)
+         surf->ReleasePixmap();
+ 
+     ret.reset(new SharedSurface_GLXDrawable(prodGL, size, inSameProcess, surf));
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ 
+ SharedSurface_GLXDrawable::SharedSurface_GLXDrawable(GLContext* gl,
+                                                      const gfx::IntSize& size,
+                                                      bool inSameProcess,
+                                                      const RefPtr<gfxXlibSurface>& xlibSurface)
+     : SharedSurface(SharedSurfaceType::GLXDrawable,
+@@ -124,17 +124,17 @@ SurfaceFactory_GLXDrawable::Create(GLCon
+                                    const RefPtr<layers::LayersIPCChannel>& allocator,
+                                    const layers::TextureFlags& flags)
+ {
+     MOZ_ASSERT(caps.alpha, "GLX surfaces require an alpha channel!");
+ 
+     typedef SurfaceFactory_GLXDrawable ptrT;
+     UniquePtr<ptrT> ret(new ptrT(prodGL, caps, allocator,
+                                  flags & ~layers::TextureFlags::ORIGIN_BOTTOM_LEFT));
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ UniquePtr<SharedSurface>
+ SurfaceFactory_GLXDrawable::CreateShared(const gfx::IntSize& size)
+ {
+     bool deallocateClient = !!(mFlags & layers::TextureFlags::DEALLOCATE_CLIENT);
+     return SharedSurface_GLXDrawable::Create(mGL, mCaps, size, deallocateClient,
+                                              mAllocator->IsSameProcess());
+diff --git a/gfx/gl/SharedSurfaceIO.cpp b/gfx/gl/SharedSurfaceIO.cpp
+--- a/gfx/gl/SharedSurfaceIO.cpp
++++ b/gfx/gl/SharedSurfaceIO.cpp
+@@ -21,17 +21,17 @@ SharedSurface_IOSurface::Create(const Re
+ {
+     MOZ_ASSERT(ioSurf);
+     MOZ_ASSERT(gl);
+ 
+     auto size = gfx::IntSize::Truncate(ioSurf->GetWidth(), ioSurf->GetHeight());
+ 
+     typedef SharedSurface_IOSurface ptrT;
+     UniquePtr<ptrT> ret( new ptrT(ioSurf, gl, size, hasAlpha) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ void
+ SharedSurface_IOSurface::ProducerReleaseImpl()
+ {
+     mGL->MakeCurrent();
+     mGL->fFlush();
+ }
+@@ -214,17 +214,17 @@ SurfaceFactory_IOSurface::Create(GLConte
+                                  const RefPtr<layers::LayersIPCChannel>& allocator,
+                                  const layers::TextureFlags& flags)
+ {
+     auto maxDims = gfx::IntSize::Truncate(MacIOSurface::GetMaxWidth(),
+                                           MacIOSurface::GetMaxHeight());
+ 
+     typedef SurfaceFactory_IOSurface ptrT;
+     UniquePtr<ptrT> ret( new ptrT(gl, caps, allocator, flags, maxDims) );
+-    return std::move(ret);
++    return ret;
+ }
+ 
+ UniquePtr<SharedSurface>
+ SurfaceFactory_IOSurface::CreateShared(const gfx::IntSize& size)
+ {
+     if (size.width > mMaxDims.width ||
+         size.height > mMaxDims.height)
+     {
+diff --git a/gfx/layers/AnimationHelper.cpp b/gfx/layers/AnimationHelper.cpp
+--- a/gfx/layers/AnimationHelper.cpp
++++ b/gfx/layers/AnimationHelper.cpp
+@@ -231,18 +231,18 @@ AnimationHelper::SampleAnimationForEachN
+     TimingParams timing {
+       animation.duration(),
+       animation.delay(),
+       animation.endDelay(),
+       animation.iterations(),
+       animation.iterationStart(),
+       static_cast<dom::PlaybackDirection>(animation.direction()),
+       static_cast<dom::FillMode>(animation.fillMode()),
+-      std::move(AnimationUtils::TimingFunctionToComputedTimingFunction(
+-           animation.easingFunction()))
++      AnimationUtils::TimingFunctionToComputedTimingFunction(
++           animation.easingFunction())
+     };
+ 
+     ComputedTiming computedTiming =
+       dom::AnimationEffectReadOnly::GetComputedTimingAt(
+         Nullable<TimeDuration>(elapsedDuration), timing,
+         animation.playbackRate());
+ 
+     if (computedTiming.mProgress.IsNull()) {
+diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp
+--- a/gfx/layers/LayerTreeInvalidation.cpp
++++ b/gfx/layers/LayerTreeInvalidation.cpp
+@@ -341,17 +341,17 @@ struct ContainerLayerProperties : public
+ {
+   explicit ContainerLayerProperties(ContainerLayer* aLayer)
+     : LayerPropertiesBase(aLayer)
+     , mPreXScale(aLayer->GetPreXScale())
+     , mPreYScale(aLayer->GetPreYScale())
+   {
+     for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
+       child->CheckCanary();
+-      mChildren.AppendElement(std::move(CloneLayerTreePropertiesInternal(child)));
++      mChildren.AppendElement(CloneLayerTreePropertiesInternal(child));
+     }
+   }
+ 
+ protected:
+   ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
+   ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
+ 
+ public:
+diff --git a/gfx/layers/ipc/CompositorVsyncScheduler.cpp b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+--- a/gfx/layers/ipc/CompositorVsyncScheduler.cpp
++++ b/gfx/layers/ipc/CompositorVsyncScheduler.cpp
+@@ -139,17 +139,17 @@ CompositorVsyncScheduler::PostVRTask(Tim
+   MonitorAutoLock lockVR(mCurrentVRListenerTaskMonitor);
+   if (mCurrentVRListenerTask == nullptr && VRListenerThreadHolder::Loop()) {
+     RefPtr<Runnable> task = NewRunnableMethod<TimeStamp>(
+       "layers::CompositorVsyncScheduler::DispatchVREvents",
+       this,
+       &CompositorVsyncScheduler::DispatchVREvents,
+       aTimestamp);
+     mCurrentVRListenerTask = task;
+-    VRListenerThreadHolder::Loop()->PostDelayedTask(std::move(task.forget()), 0);
++    VRListenerThreadHolder::Loop()->PostDelayedTask(task.forget(), 0);
+   }
+ }
+ 
+ void
+ CompositorVsyncScheduler::ScheduleComposition()
+ {
+   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+   if (!mVsyncObserver) {
+diff --git a/gfx/layers/ipc/UiCompositorControllerParent.cpp b/gfx/layers/ipc/UiCompositorControllerParent.cpp
+--- a/gfx/layers/ipc/UiCompositorControllerParent.cpp
++++ b/gfx/layers/ipc/UiCompositorControllerParent.cpp
+@@ -24,17 +24,17 @@ typedef CompositorBridgeParent::LayerTre
+ /* static */ RefPtr<UiCompositorControllerParent>
+ UiCompositorControllerParent::GetFromRootLayerTreeId(const LayersId& aRootLayerTreeId)
+ {
+   RefPtr<UiCompositorControllerParent> controller;
+   CompositorBridgeParent::CallWithIndirectShadowTree(aRootLayerTreeId,
+     [&](LayerTreeState& aState) -> void {
+       controller = aState.mUiControllerParent;
+     });
+-  return std::move(controller);
++  return controller;
+ }
+ 
+ /* static */ RefPtr<UiCompositorControllerParent>
+ UiCompositorControllerParent::Start(const LayersId& aRootLayerTreeId, Endpoint<PUiCompositorControllerParent>&& aEndpoint)
+ {
+   RefPtr<UiCompositorControllerParent> parent = new UiCompositorControllerParent(aRootLayerTreeId);
+ 
+   RefPtr<Runnable> task =
+diff --git a/gfx/layers/wr/WebRenderCommandBuilder.cpp b/gfx/layers/wr/WebRenderCommandBuilder.cpp
+--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
++++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
+@@ -350,17 +350,17 @@ PaintByLayer(nsDisplayItem* aItem,
+              nsDisplayListBuilder* aDisplayListBuilder,
+              const RefPtr<BasicLayerManager>& aManager,
+              gfxContext* aContext,
+              const gfx::Size& aScale,
+              const std::function<void()>& aPaintFunc)
+ {
+   UniquePtr<LayerProperties> props;
+   if (aManager->GetRoot()) {
+-    props = std::move(LayerProperties::CloneFrom(aManager->GetRoot()));
++    props = LayerProperties::CloneFrom(aManager->GetRoot());
+   }
+   FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
+   layerBuilder->Init(aDisplayListBuilder, aManager, nullptr, true);
+   layerBuilder->DidBeginRetainedLayerTransaction(aManager);
+ 
+   aManager->SetDefaultTarget(aContext);
+   aManager->BeginTransactionWithTarget(aContext);
+   bool isInvalidated = false;
+diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp
+--- a/gfx/thebes/gfxFont.cpp
++++ b/gfx/thebes/gfxFont.cpp
+@@ -2514,17 +2514,17 @@ gfxFont::Measure(const gfxTextRun *aText
+ {
+     // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
+     // and the underlying cairo font may be antialiased,
+     // we need to create a copy in order to avoid getting cached extents.
+     // This is only used by MathML layout at present.
+     if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
+         mAntialiasOption != kAntialiasNone) {
+         if (!mNonAAFont) {
+-            mNonAAFont = std::move(CopyWithAntialiasOption(kAntialiasNone));
++            mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
+         }
+         // if font subclass doesn't implement CopyWithAntialiasOption(),
+         // it will return null and we'll proceed to use the existing font
+         if (mNonAAFont) {
+             return mNonAAFont->Measure(aTextRun, aStart, aEnd,
+                                        TIGHT_HINTED_OUTLINE_EXTENTS,
+                                        aRefDrawTarget, aSpacing, aOrientation);
+         }
+diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp
+--- a/gfx/thebes/gfxPlatform.cpp
++++ b/gfx/thebes/gfxPlatform.cpp
+@@ -1818,17 +1818,17 @@ gfxPlatform::GetBackendPrefs()
+   data.mContentBitmask = BackendTypeBit(BackendType::CAIRO);
+ #ifdef USE_SKIA
+   data.mCanvasBitmask |= BackendTypeBit(BackendType::SKIA);
+   data.mContentBitmask |= BackendTypeBit(BackendType::SKIA);
+ #endif
+   data.mCanvasDefault = BackendType::CAIRO;
+   data.mContentDefault = BackendType::CAIRO;
+ 
+-  return std::move(data);
++  return data;
+ }
+ 
+ void
+ gfxPlatform::InitBackendPrefs(BackendPrefsData&& aPrefsData)
+ {
+     mPreferredCanvasBackend = GetCanvasBackendPref(aPrefsData.mCanvasBitmask);
+     if (mPreferredCanvasBackend == BackendType::NONE) {
+         mPreferredCanvasBackend = aPrefsData.mCanvasDefault;
+diff --git a/gfx/thebes/gfxPlatformMac.cpp b/gfx/thebes/gfxPlatformMac.cpp
+--- a/gfx/thebes/gfxPlatformMac.cpp
++++ b/gfx/thebes/gfxPlatformMac.cpp
+@@ -94,17 +94,17 @@ gfxPlatformMac::GetBackendPrefs()
+ {
+   BackendPrefsData data;
+ 
+   data.mCanvasBitmask = BackendTypeBit(BackendType::SKIA);
+   data.mContentBitmask = BackendTypeBit(BackendType::SKIA);
+   data.mCanvasDefault = BackendType::SKIA;
+   data.mContentDefault = BackendType::SKIA;
+ 
+-  return std::move(data);
++  return data;
+ }
+ 
+ bool
+ gfxPlatformMac::UsesTiling() const
+ {
+     // The non-tiling ContentClient requires CrossProcessSemaphore which
+     // isn't implemented for OSX.
+     return true;
+diff --git a/image/decoders/nsICODecoder.cpp b/image/decoders/nsICODecoder.cpp
+--- a/image/decoders/nsICODecoder.cpp
++++ b/image/decoders/nsICODecoder.cpp
+@@ -198,17 +198,17 @@ LexerTransition<ICOState>
+ nsICODecoder::IterateUnsizedDirEntry()
+ {
+   MOZ_ASSERT(!mUnsizedDirEntries.IsEmpty());
+ 
+   if (!mDirEntry) {
+     // The first time we are here, there is no entry selected. We must prepare a
+     // new iterator for the contained decoder to advance as it wills. Cloning at
+     // this point ensures it will begin at the end of the dir entries.
+-    mReturnIterator = std::move(mLexer.Clone(*mIterator, SIZE_MAX));
++    mReturnIterator = mLexer.Clone(*mIterator, SIZE_MAX);
+     if (mReturnIterator.isNothing()) {
+       // If we cannot read further than this point, then there is no resource
+       // data to read.
+       return Transition::TerminateFailure();
+     }
+   } else {
+     // We have already selected an entry which means a metadata decoder has
+     // finished. Verify the size is valid and if so, add to the discovered
+@@ -218,17 +218,17 @@ nsICODecoder::IterateUnsizedDirEntry()
+     }
+ 
+     // Remove the entry from the unsized list either way.
+     mDirEntry = nullptr;
+     mUnsizedDirEntries.RemoveElementAt(0);
+ 
+     // Our iterator is at an unknown point, so reset it to the point that we
+     // saved.
+-    mIterator = std::move(mLexer.Clone(*mReturnIterator, SIZE_MAX));
++    mIterator = mLexer.Clone(*mReturnIterator, SIZE_MAX);
+     if (mIterator.isNothing()) {
+       MOZ_ASSERT_UNREACHABLE("Cannot re-clone return iterator");
+       return Transition::TerminateFailure();
+     }
+   }
+ 
+   // There are no more unsized entries, so we can finally decide which entry to
+   // select for decoding.
+diff --git a/image/test/gtest/TestADAM7InterpolatingFilter.cpp b/image/test/gtest/TestADAM7InterpolatingFilter.cpp
+--- a/image/test/gtest/TestADAM7InterpolatingFilter.cpp
++++ b/image/test/gtest/TestADAM7InterpolatingFilter.cpp
+@@ -236,18 +236,18 @@ WriteUninterpolatedPixels(SurfaceFilter*
+                           uint8_t aPass,
+                           const vector<BGRAColor>& aColors)
+ {
+   WriteState result = WriteState::NEED_MORE_DATA;
+ 
+   for (int32_t row = 0; row < aSize.height; ++row) {
+     // Compute uninterpolated pixels for this row.
+     vector<BGRAColor> pixels =
+-      std::move(ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width,
+-                                            ShouldInterpolate::eNo, aColors));
++      ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width,
++                                       ShouldInterpolate::eNo, aColors);
+ 
+     // Write them to the surface.
+     auto pixelIterator = pixels.cbegin();
+     result = aFilter->WritePixelsToRow<uint32_t>([&]{
+       return AsVariant((*pixelIterator++).AsPixel());
+     });
+ 
+     if (result != WriteState::NEED_MORE_DATA) {
+@@ -270,18 +270,18 @@ CheckHorizontallyInterpolatedImage(Decod
+   for (int32_t row = 0; row < aSize.height; ++row) {
+     if (!IsImportantRow(row, aPass)) {
+       continue;  // Don't check rows which aren't important on this pass.
+     }
+ 
+     // Compute the expected pixels, *with* interpolation to match what the
+     // filter should have done.
+     vector<BGRAColor> expectedPixels =
+-      std::move(ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width,
+-                                            ShouldInterpolate::eYes, aColors));
++      ADAM7HorizontallyInterpolatedRow(aPass, row, aSize.width,
++                                       ShouldInterpolate::eYes, aColors);
+ 
+     if (!RowHasPixels(surface, row, expectedPixels)) {
+       return false;
+     }
+   }
+ 
+   return true;
+ }
+diff --git a/image/test/gtest/TestAnimationFrameBuffer.cpp b/image/test/gtest/TestAnimationFrameBuffer.cpp
+--- a/image/test/gtest/TestAnimationFrameBuffer.cpp
++++ b/image/test/gtest/TestAnimationFrameBuffer.cpp
+@@ -23,17 +23,17 @@ CreateEmptyFrame()
+ }
+ 
+ static bool
+ Fill(AnimationFrameBuffer& buffer, size_t aLength)
+ {
+   bool keepDecoding = false;
+   for (size_t i = 0; i < aLength; ++i) {
+     RawAccessFrameRef frame = CreateEmptyFrame();
+-    keepDecoding = buffer.Insert(std::move(frame->RawAccessRef()));
++    keepDecoding = buffer.Insert(frame->RawAccessRef());
+   }
+   return keepDecoding;
+ }
+ 
+ static void
+ CheckFrames(const AnimationFrameBuffer& buffer, size_t aStart, size_t aEnd, bool aExpected)
+ {
+   for (size_t i = aStart; i < aEnd; ++i) {
+@@ -128,17 +128,17 @@ TEST_F(ImageAnimationFrameBuffer, Finish
+   buffer.Initialize(kThreshold, kBatch, 0);
+   const auto& frames = buffer.Frames();
+ 
+   EXPECT_EQ(kBatch * 2, buffer.PendingDecode());
+ 
+   RawAccessFrameRef firstFrame;
+   for (size_t i = 0; i < 5; ++i) {
+     RawAccessFrameRef frame = CreateEmptyFrame();
+-    bool keepDecoding = buffer.Insert(std::move(frame->RawAccessRef()));
++    bool keepDecoding = buffer.Insert(frame->RawAccessRef());
+     EXPECT_TRUE(keepDecoding);
+     EXPECT_FALSE(buffer.SizeKnown());
+ 
+     if (i == 4) {
+       EXPECT_EQ(size_t(15), buffer.PendingDecode());
+       keepDecoding = buffer.MarkComplete();
+       EXPECT_FALSE(keepDecoding);
+       EXPECT_TRUE(buffer.SizeKnown());
+diff --git a/image/test/gtest/TestSourceBuffer.cpp b/image/test/gtest/TestSourceBuffer.cpp
+--- a/image/test/gtest/TestSourceBuffer.cpp
++++ b/image/test/gtest/TestSourceBuffer.cpp
+@@ -589,30 +589,32 @@ TEST_F(ImageSourceBuffer, SourceBufferIt
+   auto GetIterator = [&]{
+     SourceBufferIterator lambdaIterator = mSourceBuffer->Iterator();
+     CheckedAdvanceIterator(lambdaIterator, chunkLength);
+     return lambdaIterator;
+   };
+ 
+   // Move-construct |movedIterator| from the iterator returned from
+   // GetIterator() and check that its state is as we expect.
+-  SourceBufferIterator movedIterator = std::move(GetIterator());
++  SourceBufferIterator tmpIterator = GetIterator();
++  SourceBufferIterator movedIterator(std::move(tmpIterator));
+   EXPECT_TRUE(movedIterator.Data());
+   EXPECT_EQ(chunkLength, movedIterator.Length());
+   ExpectChunkAndByteCount(movedIterator, 1, chunkLength);
+ 
+   // Make sure that we can advance the iterator.
+   CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength);
+ 
+   // Make sure that the iterator handles completion properly.
+   CheckIteratorIsComplete(movedIterator, 2, totalLength);
+ 
+   // Move-assign |movedIterator| from the iterator returned from
+   // GetIterator() and check that its state is as we expect.
+-  movedIterator = std::move(GetIterator());
++  tmpIterator = GetIterator();
++  movedIterator = std::move(tmpIterator);
+   EXPECT_TRUE(movedIterator.Data());
+   EXPECT_EQ(chunkLength, movedIterator.Length());
+   ExpectChunkAndByteCount(movedIterator, 1, chunkLength);
+ 
+   // Make sure that we can advance the iterator.
+   CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength);
+ 
+   // Make sure that the iterator handles completion properly.
+diff --git a/image/test/gtest/TestSurfacePipeIntegration.cpp b/image/test/gtest/TestSurfacePipeIntegration.cpp
+--- a/image/test/gtest/TestSurfacePipeIntegration.cpp
++++ b/image/test/gtest/TestSurfacePipeIntegration.cpp
+@@ -21,17 +21,17 @@ namespace mozilla {
+ namespace image {
+ 
+ class TestSurfacePipeFactory
+ {
+ public:
+   static SurfacePipe SimpleSurfacePipe()
+   {
+     SurfacePipe pipe;
+-    return std::move(pipe);
++    return pipe;
+   }
+ 
+   template <typename T>
+   static SurfacePipe SurfacePipeFromPipeline(T&& aPipeline)
+   {
+     return SurfacePipe { std::move(aPipeline) };
+   }
+ 
+diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp
+--- a/js/src/builtin/TestingFunctions.cpp
++++ b/js/src/builtin/TestingFunctions.cpp
+@@ -3523,17 +3523,17 @@ struct FindPathHandler {
+         if (!first)
+             return true;
+ 
+         // Record how we reached this node. This is the last edge on a
+         // shortest path to this node.
+         EdgeName edgeName = DuplicateString(cx, edge.name.get());
+         if (!edgeName)
+             return false;
+-        *backEdge = std::move(BackEdge(origin, std::move(edgeName)));
++        *backEdge = BackEdge(origin, std::move(edgeName));
+ 
+         // Have we reached our final target node?
+         if (edge.referent == target) {
+             // Record the path that got us here, which must be a shortest path.
+             if (!recordPath(traversal))
+                 return false;
+             foundPath = true;
+             traversal.stop();
+diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp
+--- a/js/src/ctypes/CTypes.cpp
++++ b/js/src/ctypes/CTypes.cpp
+@@ -5754,17 +5754,17 @@ ArrayType::BuildFFIType(JSContext* cx, J
+     JS_ReportAllocationOverflow(cx);
+     return nullptr;
+   }
+ 
+   for (size_t i = 0; i < length; ++i)
+     ffiType->elements[i] = ffiBaseType;
+   ffiType->elements[length] = nullptr;
+ 
+-  return std::move(ffiType);
++  return ffiType;
+ }
+ 
+ bool
+ ArrayType::IsArrayType(HandleValue v)
+ {
+   if (!v.isObject())
+     return false;
+   JSObject* obj = &v.toObject();
+@@ -6302,17 +6302,17 @@ StructType::BuildFFIType(JSContext* cx, 
+   // Fill in the ffi_type's size and align fields. This makes libffi treat the
+   // type as initialized; it will not recompute the values. (We assume
+   // everything agrees; if it doesn't, we really want to know about it, which
+   // is the purpose of the above debug-only check.)
+   ffiType->size = structSize;
+   ffiType->alignment = structAlign;
+ #endif
+ 
+-  return std::move(ffiType);
++  return ffiType;
+ }
+ 
+ bool
+ StructType::Define(JSContext* cx, unsigned argc, Value* vp)
+ {
+   CallArgs args = CallArgsFromVp(argc, vp);
+   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
+   if (!obj)
+diff --git a/js/src/ds/LifoAlloc.cpp b/js/src/ds/LifoAlloc.cpp
+--- a/js/src/ds/LifoAlloc.cpp
++++ b/js/src/ds/LifoAlloc.cpp
+@@ -46,21 +46,21 @@ BumpChunk::canAlloc(size_t n)
+ 
+ } // namespace detail
+ } // namespace js
+ 
+ void
+ LifoAlloc::freeAll()
+ {
+     while (!chunks_.empty()) {
+-        BumpChunk bc = std::move(chunks_.popFirst());
++        BumpChunk bc = chunks_.popFirst();
+         decrementCurSize(bc->computedSizeOfIncludingThis());
+     }
+     while (!unused_.empty()) {
+-        BumpChunk bc = std::move(unused_.popFirst());
++        BumpChunk bc = unused_.popFirst();
+         decrementCurSize(bc->computedSizeOfIncludingThis());
+     }
+ 
+     // Nb: maintaining curSize_ correctly isn't easy.  Fortunately, this is an
+     // excellent sanity check.
+     MOZ_ASSERT(curSize_ == 0);
+ }
+ 
+@@ -100,27 +100,27 @@ LifoAlloc::newChunkWithCapacity(size_t n
+ bool
+ LifoAlloc::getOrCreateChunk(size_t n)
+ {
+     // Look for existing unused BumpChunks to satisfy the request, and pick the
+     // first one which is large enough, and move it into the list of used
+     // chunks.
+     if (!unused_.empty()) {
+         if (unused_.begin()->canAlloc(n)) {
+-            chunks_.append(std::move(unused_.popFirst()));
++            chunks_.append(unused_.popFirst());
+             return true;
+         }
+ 
+         BumpChunkList::Iterator e(unused_.end());
+         for (BumpChunkList::Iterator i(unused_.begin()); i->next() != e.get(); ++i) {
+             detail::BumpChunk* elem = i->next();
+             MOZ_ASSERT(elem->empty());
+             if (elem->canAlloc(n)) {
+-                BumpChunkList temp = std::move(unused_.splitAfter(i.get()));
+-                chunks_.append(std::move(temp.popFirst()));
++                BumpChunkList temp = unused_.splitAfter(i.get());
++                chunks_.append(temp.popFirst());
+                 unused_.appendAll(std::move(temp));
+                 return true;
+             }
+         }
+     }
+ 
+     // Allocate a new BumpChunk with enough space for the next allocation.
+     BumpChunk newChunk = newChunkWithCapacity(n);
+diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h
+--- a/js/src/ds/LifoAlloc.h
++++ b/js/src/ds/LifoAlloc.h
+@@ -706,17 +706,17 @@ class LifoAlloc
+     void release(Mark mark) {
+         markCount--;
+ 
+         // Move the blocks which are after the mark to the set of unused chunks.
+         BumpChunkList released;
+         if (!mark.markedChunk())
+             released = std::move(chunks_);
+         else
+-            released = std::move(chunks_.splitAfter(mark.markedChunk()));
++            released = chunks_.splitAfter(mark.markedChunk());
+ 
+         // Release the content of all the blocks which are after the marks.
+         for (detail::BumpChunk& bc : released)
+             bc.release();
+         unused_.appendAll(std::move(released));
+ 
+         // Release everything which follows the mark in the last chunk.
+         if (!chunks_.empty())
+diff --git a/js/src/vm/CodeCoverage.cpp b/js/src/vm/CodeCoverage.cpp
+--- a/js/src/vm/CodeCoverage.cpp
++++ b/js/src/vm/CodeCoverage.cpp
+@@ -517,17 +517,17 @@ LCovRealm::lookupOrAdd(JS::Realm* realm,
+ 
+     char* source_name = js_strdup(name);
+     if (!source_name) {
+         outTN_.reportOutOfMemory();
+         return nullptr;
+     }
+ 
+     // Allocate a new LCovSource for the current top-level.
+-    if (!sources_->append(std::move(LCovSource(&alloc_, source_name)))) {
++    if (!sources_->append(LCovSource(&alloc_, source_name))) {
+         outTN_.reportOutOfMemory();
+         return nullptr;
+     }
+ 
+     return &sources_->back();
+ }
+ 
+ void
+diff --git a/js/src/vm/UbiNode.cpp b/js/src/vm/UbiNode.cpp
+--- a/js/src/vm/UbiNode.cpp
++++ b/js/src/vm/UbiNode.cpp
+@@ -262,17 +262,17 @@ class EdgeVectorTracer : public JS::Call
+                 name16[i] = name[i];
+             name16[i] = '\0';
+         }
+ 
+         // The simplest code is correct! The temporary Edge takes
+         // ownership of name; if the append succeeds, the vector element
+         // then takes ownership; if the append fails, then the temporary
+         // retains it, and its destructor will free it.
+-        if (!vec->append(std::move(Edge(name16, Node(thing))))) {
++        if (!vec->append(Edge(name16, Node(thing)))) {
+             okay = false;
+             return;
+         }
+     }
+ 
+   public:
+     // True if no errors (OOM, say) have yet occurred.
+     bool okay;
+@@ -543,17 +543,17 @@ RootList::addRoot(Node node, const char1
+ 
+     UniqueTwoByteChars name;
+     if (edgeName) {
+         name = js::DuplicateString(edgeName);
+         if (!name)
+             return false;
+     }
+ 
+-    return edges.append(std::move(Edge(name.release(), node)));
++    return edges.append(Edge(name.release(), node));
+ }
+ 
+ const char16_t Concrete<RootList>::concreteTypeName[] = u"JS::ubi::RootList";
+ 
+ UniquePtr<EdgeRange>
+ Concrete<RootList>::edges(JSContext* cx, bool wantNames) const {
+     MOZ_ASSERT_IF(wantNames, get().wantNames);
+     return UniquePtr<EdgeRange>(js_new<PreComputedEdgeRange>(get().edges));
+diff --git a/js/src/vm/UbiNodeShortestPaths.cpp b/js/src/vm/UbiNodeShortestPaths.cpp
+--- a/js/src/vm/UbiNodeShortestPaths.cpp
++++ b/js/src/vm/UbiNodeShortestPaths.cpp
+@@ -23,17 +23,17 @@ BackEdge::clone() const
+         return nullptr;
+ 
+     clone->predecessor_ = predecessor();
+     if (name()) {
+         clone->name_ = js::DuplicateString(name().get());
+         if (!clone->name_)
+             return nullptr;
+     }
+-    return std::move(clone);
++    return clone;
+ }
+ 
+ #ifdef DEBUG
+ 
+ static void
+ dumpNode(const JS::ubi::Node& node)
+ {
+     fprintf(stderr, "    %p ", (void*) node.identifier());
+diff --git a/js/src/wasm/WasmValidate.cpp b/js/src/wasm/WasmValidate.cpp
+--- a/js/src/wasm/WasmValidate.cpp
++++ b/js/src/wasm/WasmValidate.cpp
+@@ -1596,17 +1596,17 @@ DecodeExportName(Decoder& d, CStringSet*
+     if (p) {
+         d.fail("duplicate export");
+         return nullptr;
+     }
+ 
+     if (!dupSet->add(p, exportName.get()))
+         return nullptr;
+ 
+-    return std::move(exportName);
++    return exportName;
+ }
+ 
+ static bool
+ DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet)
+ {
+     UniqueChars fieldName = DecodeExportName(d, dupSet);
+     if (!fieldName)
+         return false;
+diff --git a/js/xpconnect/loader/URLPreloader.cpp b/js/xpconnect/loader/URLPreloader.cpp
+--- a/js/xpconnect/loader/URLPreloader.cpp
++++ b/js/xpconnect/loader/URLPreloader.cpp
+@@ -612,21 +612,21 @@ URLPreloader::ShallowSizeOfIncludingThis
+ 
+ Result<FileLocation, nsresult>
+ URLPreloader::CacheKey::ToFileLocation()
+ {
+     if (mType == TypeFile) {
+         nsCOMPtr<nsIFile> file;
+         MOZ_TRY(NS_NewLocalFile(NS_ConvertUTF8toUTF16(mPath), false,
+                                 getter_AddRefs(file)));
+-        return std::move(FileLocation(file));
++        return FileLocation(file);
+     }
+ 
+     RefPtr<nsZipArchive> zip = Archive();
+-    return std::move(FileLocation(zip, mPath.get()));
++    return FileLocation(zip, mPath.get());
+ }
+ 
+ Result<const nsCString, nsresult>
+ URLPreloader::URLEntry::Read()
+ {
+     FileLocation location;
+     MOZ_TRY_VAR(location, ToFileLocation());
+ 
+diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp
+--- a/layout/base/PresShell.cpp
++++ b/layout/base/PresShell.cpp
+@@ -6227,17 +6227,17 @@ PresShell::Paint(nsView*         aViewTo
+       bool computeInvalidRect = computeInvalidFunc ||
+                                 (layerManager->GetBackendType() == LayersBackend::LAYERS_BASIC);
+ 
+       UniquePtr<LayerProperties> props;
+       // For WR, the layermanager has no root layer. We want to avoid
+       // calling ComputeDifferences in that case because it assumes non-null
+       // and crashes.
+       if (computeInvalidRect && layerManager->GetRoot()) {
+-        props = std::move(LayerProperties::CloneFrom(layerManager->GetRoot()));
++        props = LayerProperties::CloneFrom(layerManager->GetRoot());
+       }
+ 
+       MaybeSetupTransactionIdAllocator(layerManager, presContext);
+ 
+       if (layerManager->EndEmptyTransaction((aFlags & PAINT_COMPOSITE) ?
+             LayerManager::END_DEFAULT : LayerManager::END_NO_COMPOSITE)) {
+         nsIntRegion invalid;
+         bool areaOverflowed = false;
+diff --git a/layout/painting/FrameLayerBuilder.cpp b/layout/painting/FrameLayerBuilder.cpp
+--- a/layout/painting/FrameLayerBuilder.cpp
++++ b/layout/painting/FrameLayerBuilder.cpp
+@@ -6192,18 +6192,18 @@ FrameLayerBuilder::DrawPaintedLayer(Pain
+     FlashPaint(aContext);
+   }
+ 
+   if (presContext->GetDocShell() && isActiveLayerManager) {
+     nsDocShell* docShell = static_cast<nsDocShell*>(presContext->GetDocShell());
+     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+ 
+     if (timelines && timelines->HasConsumer(docShell)) {
+-      timelines->AddMarkerForDocShell(docShell, std::move(
+-        MakeUnique<LayerTimelineMarker>(aRegionToDraw)));
++      timelines->AddMarkerForDocShell(docShell,
++        MakeUnique<LayerTimelineMarker>(aRegionToDraw));
+     }
+   }
+ 
+   if (!aRegionToInvalidate.IsEmpty()) {
+     aLayer->AddInvalidRect(aRegionToInvalidate.GetBounds());
+   }
+ }
+ 
+diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
+--- a/layout/painting/nsDisplayList.cpp
++++ b/layout/painting/nsDisplayList.cpp
+@@ -2663,17 +2663,17 @@ already_AddRefed<LayerManager> nsDisplay
+ 
+   UniquePtr<LayerProperties> props;
+ 
+   bool computeInvalidRect = (computeInvalidFunc ||
+                              (!layerManager->IsCompositingCheap() && layerManager->NeedsWidgetInvalidation())) &&
+                             widgetTransaction;
+ 
+   if (computeInvalidRect) {
+-    props = std::move(LayerProperties::CloneFrom(layerManager->GetRoot()));
++    props = LayerProperties::CloneFrom(layerManager->GetRoot());
+   }
+ 
+   if (doBeginTransaction) {
+     if (aCtx) {
+       if (!layerManager->BeginTransactionWithTarget(aCtx)) {
+         return nullptr;
+       }
+     } else {
+diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp
+--- a/layout/style/nsAnimationManager.cpp
++++ b/layout/style/nsAnimationManager.cpp
+@@ -915,17 +915,17 @@ GeckoCSSAnimationBuilder::GetKeyframePro
+     DebugOnly<bool> uncomputeResult =
+       StyleAnimationValue::UncomputeValue(prop, std::move(computedValue),
+                                           propertyValue);
+     MOZ_ASSERT(uncomputeResult,
+                "Unable to get specified value from computed value");
+     MOZ_ASSERT(propertyValue.GetUnit() != eCSSUnit_Null,
+                "Not expecting to read invalid properties");
+ 
+-    result.AppendElement(std::move(PropertyValuePair(prop, std::move(propertyValue))));
++    result.AppendElement(PropertyValuePair(prop, std::move(propertyValue)));
+     aAnimatedProperties.AddProperty(prop);
+   }
+ 
+   return result;
+ }
+ 
+ void
+ GeckoCSSAnimationBuilder::FillInMissingKeyframeValues(
+@@ -985,22 +985,22 @@ GeckoCSSAnimationBuilder::FillInMissingK
+        prop < eCSSProperty_COUNT_no_shorthands;
+        prop = nsCSSPropertyID(prop + 1)) {
+     if (!aAnimatedProperties.HasProperty(prop)) {
+       continue;
+     }
+ 
+     if (startKeyframe && !aPropertiesSetAtStart.HasProperty(prop)) {
+       // An uninitialized nsCSSValue represents the underlying value.
+-      PropertyValuePair propertyValue(prop, std::move(nsCSSValue()));
++      PropertyValuePair propertyValue(prop, nsCSSValue());
+       startKeyframe->mPropertyValues.AppendElement(std::move(propertyValue));
+     }
+     if (endKeyframe && !aPropertiesSetAtEnd.HasProperty(prop)) {
+       // An uninitialized nsCSSValue represents the underlying value.
+-      PropertyValuePair propertyValue(prop, std::move(nsCSSValue()));
++      PropertyValuePair propertyValue(prop, nsCSSValue());
+       endKeyframe->mPropertyValues.AppendElement(std::move(propertyValue));
+     }
+   }
+ }
+ #endif
+ 
+ template<class BuilderType>
+ static nsAnimationManager::OwningCSSAnimationPtrArray
+diff --git a/layout/style/nsTransitionManager.cpp b/layout/style/nsTransitionManager.cpp
+--- a/layout/style/nsTransitionManager.cpp
++++ b/layout/style/nsTransitionManager.cpp
+@@ -794,27 +794,27 @@ AppendKeyframe(double aOffset,
+ {
+   Keyframe& frame = *aKeyframes.AppendElement();
+   frame.mOffset.emplace(aOffset);
+ 
+   if (aValue.mServo) {
+     RefPtr<RawServoDeclarationBlock> decl =
+       Servo_AnimationValue_Uncompute(aValue.mServo).Consume();
+     frame.mPropertyValues.AppendElement(
+-      std::move(PropertyValuePair(aProperty, std::move(decl))));
++      PropertyValuePair(aProperty, std::move(decl)));
+   } else {
+ #ifdef MOZ_OLD_STYLE
+     nsCSSValue propertyValue;
+     DebugOnly<bool> uncomputeResult =
+       StyleAnimationValue::UncomputeValue(aProperty, std::move(aValue.mGecko),
+                                           propertyValue);
+     MOZ_ASSERT(uncomputeResult,
+                "Unable to get specified value from computed value");
+     frame.mPropertyValues.AppendElement(
+-      std::move(PropertyValuePair(aProperty, std::move(propertyValue))));
++      PropertyValuePair(aProperty, std::move(propertyValue)));
+ #else
+     MOZ_CRASH("old style system disabled");
+ #endif
+   }
+   return frame;
+ }
+ 
+ static nsTArray<Keyframe>
+diff --git a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+--- a/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
++++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
+@@ -59,17 +59,17 @@ ClearKeyPersistence::ReadAllRecordsFromI
+     [self, aOnComplete] ()
+   {
+     CK_LOGD("ClearKeyPersistence: Failed to load index file (it might not exist");
+     self->mPersistentKeyState = PersistentKeyState::LOADED;
+     aOnComplete();
+   };
+ 
+   string filename = "index";
+-  ReadData(mHost, filename, move(onIndexSuccess), move(onIndexFailed));
++  ReadData(mHost, filename, std::move(onIndexSuccess), std::move(onIndexFailed));
+ }
+ 
+ void
+ ClearKeyPersistence::WriteIndex() {
+   function <void()> onIndexSuccess =
+     [] ()
+   {
+     CK_LOGD("ClearKeyPersistence: Wrote index file");
+@@ -91,34 +91,34 @@ ClearKeyPersistence::WriteIndex() {
+   string dataString = ss.str();
+   uint8_t* dataArray = (uint8_t*)dataString.data();
+   vector<uint8_t> data(dataArray, dataArray + dataString.size());
+ 
+   string filename = "index";
+   WriteData(mHost,
+             filename,
+             data,
+-            move(onIndexSuccess),
+-            move(onIndexFail));
++            std::move(onIndexSuccess),
++            std::move(onIndexFail));
+ }
+ 
+ 
+ ClearKeyPersistence::ClearKeyPersistence(Host_9* aHost)
+ {
+   this->mHost = aHost;
+ }
+ 
+ void
+ ClearKeyPersistence::EnsureInitialized(bool aPersistentStateAllowed,
+                                        function<void()>&& aOnInitialized)
+ {
+   if (aPersistentStateAllowed &&
+       mPersistentKeyState == PersistentKeyState::UNINITIALIZED) {
+     mPersistentKeyState = LOADING;
+-    ReadAllRecordsFromIndex(move(aOnInitialized));
++    ReadAllRecordsFromIndex(std::move(aOnInitialized));
+   } else {
+     mPersistentKeyState = PersistentKeyState::LOADED;
+     aOnInitialized();
+   }
+ }
+ 
+ bool ClearKeyPersistence::IsLoaded() const
+ {
+diff --git a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
++++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+@@ -62,17 +62,17 @@ ClearKeySessionManager::Init(bool aDisti
+       function<void()> func = self->mDeferredInitialize.front();
+       self->mDeferredInitialize.pop();
+ 
+       func();
+     }
+   };
+ 
+   mPersistence->EnsureInitialized(aPersistentStateAllowed,
+-                                  move(onPersistentStateLoaded));
++                                  std::move(onPersistentStateLoaded));
+ }
+ 
+ void
+ ClearKeySessionManager::CreateSession(uint32_t aPromiseId,
+                                       InitDataType aInitDataType,
+                                       const uint8_t* aInitData,
+                                       uint32_t aInitDataSize,
+                                       SessionType aSessionType)
+@@ -89,17 +89,17 @@ ClearKeySessionManager::CreateSession(ui
+     self->CreateSession(aPromiseId,
+                         aInitDataType,
+                         initData.data(),
+                         initData.size(),
+                         aSessionType);
+   };
+ 
+   // If we haven't loaded, don't do this yet
+-  if (MaybeDeferTillInitialized(move(deferrer))) {
++  if (MaybeDeferTillInitialized(std::move(deferrer))) {
+     CK_LOGD("Deferring CreateSession");
+     return;
+   }
+ 
+   CK_LOGARRAY("ClearKeySessionManager::CreateSession initdata: ",
+               aInitData,
+               aInitDataSize);
+ 
+@@ -195,17 +195,17 @@ ClearKeySessionManager::LoadSession(uint
+   // we try to use it.
+   RefPtr<ClearKeySessionManager> self(this);
+   function<void()> deferrer =
+     [self, aPromiseId, sessionId] ()
+   {
+     self->LoadSession(aPromiseId, sessionId.data(), sessionId.size());
+   };
+ 
+-  if (MaybeDeferTillInitialized(move(deferrer))) {
++  if (MaybeDeferTillInitialized(std::move(deferrer))) {
+     CK_LOGD("Deferring LoadSession");
+     return;
+   }
+ 
+   // If the SessionManager has been shutdown mHost will be null and we won't
+   // be able to resolve the promise.
+   if (!mHost) {
+     return;
+@@ -233,17 +233,17 @@ ClearKeySessionManager::LoadSession(uint
+   function<void()> failure = [self, aPromiseId] {
+     if (!self->mHost) {
+       return;
+     }
+     // As per the API described in ContentDecryptionModule_8
+     self->mHost->OnResolveNewSessionPromise(aPromiseId, nullptr, 0);
+   };
+ 
+-  ReadData(mHost, sessionId, move(success), move(failure));
++  ReadData(mHost, sessionId, std::move(success), std::move(failure));
+ }
+ 
+ void
+ ClearKeySessionManager::PersistentSessionDataLoaded(uint32_t aPromiseId,
+                                                     const string& aSessionId,
+                                                     const uint8_t* aKeyData,
+                                                     uint32_t aKeyDataSize)
+ {
+@@ -331,17 +331,17 @@ ClearKeySessionManager::UpdateSession(ui
+     self->UpdateSession(aPromiseId,
+                         sessionId.data(),
+                         sessionId.size(),
+                         response.data(),
+                         response.size());
+   };
+ 
+   // If we haven't fully loaded, defer calling this method
+-  if (MaybeDeferTillInitialized(move(deferrer))) {
++  if (MaybeDeferTillInitialized(std::move(deferrer))) {
+     CK_LOGD("Deferring LoadSession");
+     return;
+   }
+ 
+   // Make sure the SessionManager has not been shutdown before we try and
+   // resolve any promises.
+   if (!mHost) {
+     return;
+@@ -441,17 +441,17 @@ ClearKeySessionManager::UpdateSession(ui
+     static const char* message = "Couldn't store cenc key init data";
+     self->mHost->OnRejectPromise(aPromiseId,
+                                  Exception::kExceptionInvalidStateError,
+                                  0,
+                                  message,
+                                  strlen(message));
+   };
+ 
+-  WriteData(mHost, sessionId, keydata, move(resolve), move(reject));
++  WriteData(mHost, sessionId, keydata, std::move(resolve), std::move(reject));
+ }
+ 
+ void
+ ClearKeySessionManager::Serialize(const ClearKeySession* aSession,
+                                   std::vector<uint8_t>& aOutKeyData)
+ {
+   const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
+   for (size_t i = 0; i < keyIds.size(); i++) {
+@@ -481,17 +481,17 @@ ClearKeySessionManager::CloseSession(uin
+   RefPtr<ClearKeySessionManager> self(this);
+   function<void()> deferrer =
+     [self, aPromiseId, sessionId] ()
+   {
+     self->CloseSession(aPromiseId, sessionId.data(), sessionId.size());
+   };
+ 
+   // If we haven't loaded, call this method later.
+-  if (MaybeDeferTillInitialized(move(deferrer))) {
++  if (MaybeDeferTillInitialized(std::move(deferrer))) {
+     CK_LOGD("Deferring CloseSession");
+     return;
+   }
+ 
+   // If DecryptingComplete has been called mHost will be null and we won't
+   // be able to resolve our promise.
+   if (!mHost) {
+     return;
+@@ -540,17 +540,17 @@ ClearKeySessionManager::RemoveSession(ui
+   RefPtr<ClearKeySessionManager> self(this);
+   function<void()> deferrer =
+     [self, aPromiseId, sessionId] ()
+   {
+     self->RemoveSession(aPromiseId, sessionId.data(), sessionId.size());
+   };
+ 
+   // If we haven't fully loaded, defer calling this method.
+-  if (MaybeDeferTillInitialized(move(deferrer))) {
++  if (MaybeDeferTillInitialized(std::move(deferrer))) {
+     CK_LOGD("Deferring RemoveSession");
+     return;
+   }
+ 
+   // Check that the SessionManager has not been shutdown before we try and
+   // resolve any promises.
+   if (!mHost) {
+     return;
+@@ -600,17 +600,17 @@ ClearKeySessionManager::RemoveSession(ui
+     static const char* message = "Could not remove session";
+     self->mHost->OnRejectPromise(aPromiseId,
+                                  Exception::kExceptionTypeError,
+                                  0,
+                                  message,
+                                  strlen(message));
+   };
+ 
+-  WriteData(mHost, sessionId, emptyKeydata, move(resolve), move(reject));
++  WriteData(mHost, sessionId, emptyKeydata, std::move(resolve), std::move(reject));
+ }
+ 
+ void
+ ClearKeySessionManager::SetServerCertificate(uint32_t aPromiseId,
+                                              const uint8_t* aServerCert,
+                                              uint32_t aServerCertSize)
+ {
+   // ClearKey CDM doesn't support this method by spec.
+@@ -669,11 +669,11 @@ ClearKeySessionManager::DecryptingComple
+ }
+ 
+ bool ClearKeySessionManager::MaybeDeferTillInitialized(function<void()>&& aMaybeDefer)
+ {
+   if (mPersistence->IsLoaded()) {
+     return false;
+   }
+ 
+-  mDeferredInitialize.emplace(move(aMaybeDefer));
++  mDeferredInitialize.emplace(std::move(aMaybeDefer));
+   return true;
+ }
+diff --git a/media/gmp-clearkey/0.1/ClearKeyStorage.cpp b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
+--- a/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
++++ b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
+@@ -39,18 +39,18 @@ public:
+    */
+   static void Write(Host_9* aHost,
+                     string& aRecordName,
+                     const vector<uint8_t>& aData,
+                     function<void()>&& aOnSuccess,
+                     function<void()>&& aOnFailure)
+ {
+     WriteRecordClient* client = new WriteRecordClient(aData,
+-                                                      move(aOnSuccess),
+-                                                      move(aOnFailure));
++                                                      std::move(aOnSuccess),
++                                                      std::move(aOnFailure));
+     client->Do(aRecordName, aHost);
+   }
+ 
+   void OnOpenComplete(Status aStatus) override
+   {
+     // If we hit an error, fail.
+     if (aStatus != Status::kSuccess) {
+       Done(aStatus);
+@@ -73,18 +73,18 @@ public:
+     Done(aStatus);
+   }
+ 
+ private:
+   explicit WriteRecordClient(const vector<uint8_t>& aData,
+                              function<void()>&& aOnSuccess,
+                              function<void()>&& aOnFailure)
+     : mFileIO(nullptr)
+-    , mOnSuccess(move(aOnSuccess))
+-    , mOnFailure(move(aOnFailure))
++    , mOnSuccess(std::move(aOnSuccess))
++    , mOnFailure(std::move(aOnFailure))
+     , mData(aData) {}
+ 
+   void Do(const string& aName, Host_9* aHost)
+   {
+     // Initialize the FileIO.
+     mFileIO = aHost->CreateFileIO(this);
+     mFileIO->Open(aName.c_str(), aName.size());
+   }
+@@ -122,34 +122,34 @@ WriteData(Host_9* aHost,
+           string& aRecordName,
+           const vector<uint8_t>& aData,
+           function<void()>&& aOnSuccess,
+           function<void()>&& aOnFailure)
+ {
+   WriteRecordClient::Write(aHost,
+                            aRecordName,
+                            aData,
+-                           move(aOnSuccess),
+-                           move(aOnFailure));
++                           std::move(aOnSuccess),
++                           std::move(aOnFailure));
+ }
+ 
+ class ReadRecordClient : public FileIOClient
+ {
+ public:
+   /*
+    * This function will take the memory ownership of the parameters and
+    * delete them when done.
+    */
+   static void Read(Host_9* aHost,
+                    string& aRecordName,
+                    function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+                    function<void()>&& aOnFailure)
+   {
+ 
+-    (new ReadRecordClient(move(aOnSuccess), move(aOnFailure)))->
++    (new ReadRecordClient(std::move(aOnSuccess), std::move(aOnFailure)))->
+       Do(aRecordName, aHost);
+   }
+ 
+   void OnOpenComplete(Status aStatus) override
+   {
+     auto err = aStatus;
+     if (aStatus != Status::kSuccess) {
+       Done(err, nullptr, 0);
+@@ -170,18 +170,18 @@ public:
+     // We should never reach here, this client only ever reads data.
+     assert(false);
+   }
+ 
+ private:
+   explicit ReadRecordClient(function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+                             function<void()>&& aOnFailure)
+     : mFileIO(nullptr)
+-    , mOnSuccess(move(aOnSuccess))
+-    , mOnFailure(move(aOnFailure))
++    , mOnSuccess(std::move(aOnSuccess))
++    , mOnFailure(std::move(aOnFailure))
+   {}
+ 
+   void Do(const string& aName, Host_9* aHost)
+   {
+     mFileIO = aHost->CreateFileIO(this);
+     mFileIO->Open(aName.c_str(), aName.size());
+   }
+ 
+@@ -216,11 +216,11 @@ private:
+ void
+ ReadData(Host_9* mHost,
+          string& aRecordName,
+          function<void(const uint8_t*, uint32_t)>&& aOnSuccess,
+          function<void()>&& aOnFailure)
+ {
+   ReadRecordClient::Read(mHost,
+                          aRecordName,
+-                         move(aOnSuccess),
+-                         move(aOnFailure));
++                         std::move(aOnSuccess),
++                         std::move(aOnFailure));
+ }
+diff --git a/media/webrtc/signaling/gtest/sdp_unittests.cpp.1465060.later b/media/webrtc/signaling/gtest/sdp_unittests.cpp.1465060.later
+new file mode 100644
+--- /dev/null
++++ b/media/webrtc/signaling/gtest/sdp_unittests.cpp.1465060.later
+@@ -0,0 +1,46 @@
++--- sdp_unittests.cpp
+++++ sdp_unittests.cpp
++@@ -1506,39 +1506,39 @@ class NewSdpTest : public ::testing::Tes
++                    public ::testing::WithParamInterface<
++                      ::testing::tuple<bool, bool> > {
++   public:
++     NewSdpTest() {}
++ 
++     void ParseSdp(const std::string &sdp, bool expectSuccess = true) {
++       if (::testing::get<1>(GetParam())) {
++         mSdpErrorHolder = &mSipccParser;
++-        mSdp = std::move(mSipccParser.Parse(sdp));
+++        mSdp = mSipccParser.Parse(sdp);
++       } else {
++         mSdpErrorHolder = &mRustParser;
++-        mSdp = std::move(mRustParser.Parse(sdp));
+++        mSdp = mRustParser.Parse(sdp);
++       }
++ 
++       // Are we configured to do a parse and serialize before actually
++       // running the test?
++       if (::testing::get<0>(GetParam())) {
++         std::stringstream os;
++ 
++         if (expectSuccess) {
++           ASSERT_TRUE(!!mSdp) << "Parse failed on first pass: "
++                               << GetParseErrors();
++         }
++ 
++         if (mSdp) {
++           // Serialize and re-parse
++           mSdp->Serialize(os);
++           if (::testing::get<1>(GetParam())) {
++-            mSdp = std::move(mSipccParser.Parse(os.str()));
+++            mSdp = mSipccParser.Parse(os.str());
++           } else {
++-            mSdp = std::move(mRustParser.Parse(os.str()));
+++            mSdp = mRustParser.Parse(os.str());
++           }
++ 
++           // Whether we expected the parse to work or not, it should
++           // succeed the second time if it succeeded the first.
++           ASSERT_TRUE(!!mSdp) << "Parse failed on second pass, SDP was: "
++             << std::endl << os.str() <<  std::endl
++             << "Errors were: " << GetParseErrors();
++ 
+diff --git a/mfbt/tests/TestUniquePtr.cpp b/mfbt/tests/TestUniquePtr.cpp
+--- a/mfbt/tests/TestUniquePtr.cpp
++++ b/mfbt/tests/TestUniquePtr.cpp
+@@ -70,17 +70,17 @@ ReturnUniqueA()
+ {
+   return UniqueA(new B);
+ }
+ 
+ static UniqueA
+ ReturnLocalA()
+ {
+   UniqueA a(new A);
+-  return std::move(a);
++  return a;
+ }
+ 
+ static void
+ TestDeleterType()
+ {
+   // Make sure UniquePtr will use its deleter's pointer type if it defines one.
+   typedef int* Ptr;
+   struct Deleter {
+@@ -363,17 +363,17 @@ SetMallocedInt(UniquePtr<int, FreeSignat
+ }
+ 
+ static UniquePtr<int, FreeSignature>
+ MallocedInt(int aI)
+ {
+   UniquePtr<int, FreeSignature>
+     ptr(static_cast<int*>(malloc(sizeof(int))), free);
+   *ptr = aI;
+-  return std::move(ptr);
++  return ptr;
+ }
+ static bool
+ TestFunctionReferenceDeleter()
+ {
+   // Look for allocator mismatches and leaks to verify these bits
+   UniquePtr<int, FreeSignature> i1(MallocedInt(17));
+   CHECK(*i1 == 17);
+ 
+diff --git a/security/sandbox/linux/broker/SandboxBroker.cpp b/security/sandbox/linux/broker/SandboxBroker.cpp
+--- a/security/sandbox/linux/broker/SandboxBroker.cpp
++++ b/security/sandbox/linux/broker/SandboxBroker.cpp
+@@ -72,17 +72,17 @@ SandboxBroker::Create(UniquePtr<const Po
+   // Can't use MakeUnique here because the constructor is private.
+   UniquePtr<SandboxBroker> rv(new SandboxBroker(std::move(aPolicy), aChildPid,
+                                                 clientFd));
+   if (clientFd < 0) {
+     rv = nullptr;
+   } else {
+     aClientFdOut = ipc::FileDescriptor(clientFd);
+   }
+-  return std::move(rv);
++  return rv;
+ }
+ 
+ SandboxBroker::~SandboxBroker() {
+   // If the constructor failed, there's nothing to be done here.
+   if (mFileDesc < 0) {
+     return;
+   }
+ 
+diff --git a/security/sandbox/linux/reporter/SandboxReporter.cpp b/security/sandbox/linux/reporter/SandboxReporter.cpp
+--- a/security/sandbox/linux/reporter/SandboxReporter.cpp
++++ b/security/sandbox/linux/reporter/SandboxReporter.cpp
+@@ -284,16 +284,12 @@ SandboxReporter::GetSnapshot()
+   snapshot.mOffset = start;
+   snapshot.mReports.Clear();
+   snapshot.mReports.SetCapacity(mCount - start);
+   for (size_t i = start; i < mCount; ++i) {
+     const SandboxReport* rep = &mBuffer[i % kSandboxReporterBufferSize];
+     MOZ_ASSERT(rep->IsValid());
+     snapshot.mReports.AppendElement(*rep);
+   }
+-  // Named Return Value Optimization would apply here, but C++11
+-  // doesn't require it; so, instead of possibly copying the entire
+-  // array contents, invoke the move constructor and copy at most a
+-  // few words.
+-  return std::move(snapshot);
++  return snapshot;
+ }
+ 
+ } // namespace mozilla
+diff --git a/toolkit/components/extensions/webrequest/WebRequestService.cpp b/toolkit/components/extensions/webrequest/WebRequestService.cpp
+--- a/toolkit/components/extensions/webrequest/WebRequestService.cpp
++++ b/toolkit/components/extensions/webrequest/WebRequestService.cpp
+@@ -42,17 +42,17 @@ UniquePtr<WebRequestChannelEntry>
+ WebRequestService::RegisterChannel(ChannelWrapper* aChannel)
+ {
+   UniquePtr<ChannelEntry> entry(new ChannelEntry(aChannel));
+ 
+   auto key = mChannelEntries.LookupForAdd(entry->mChannelId);
+   MOZ_DIAGNOSTIC_ASSERT(!key);
+   key.OrInsert([&entry]() { return entry.get(); });
+ 
+-  return std::move(entry);
++  return entry;
+ 
+ }
+ 
+ already_AddRefed<nsITraceableChannel>
+ WebRequestService::GetTraceableChannel(uint64_t aChannelId,
+                                        nsIAtom* aAddonId,
+                                        nsIContentParent* aContentParent)
+ {
+diff --git a/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp b/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp
+--- a/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp
++++ b/toolkit/components/url-classifier/tests/gtest/TestClassifier.cpp
+@@ -17,17 +17,17 @@ GetClassifier()
+ {
+   nsCOMPtr<nsIFile> file;
+   NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(file));
+ 
+   UniquePtr<Classifier> classifier = MakeUnique<Classifier>();
+   nsresult rv = classifier->Open(*file);
+   EXPECT_TRUE(rv == NS_OK);
+ 
+-  return std::move(classifier);
++  return classifier;
+ }
+ 
+ static nsresult
+ SetupLookupCacheV4(Classifier* classifier,
+                    const _PrefixArray& aPrefixArray,
+                    const nsACString& aTable)
+ {
+   LookupCacheV4* lookupCache =
+diff --git a/xpcom/tests/gtest/TestPLDHash.cpp b/xpcom/tests/gtest/TestPLDHash.cpp
+--- a/xpcom/tests/gtest/TestPLDHash.cpp
++++ b/xpcom/tests/gtest/TestPLDHash.cpp
+@@ -192,17 +192,24 @@ static const PLDHashTableOps trivialOps 
+ 
+ TEST(PLDHashTableTest, MoveSemantics)
+ {
+   PLDHashTable t1(&trivialOps, sizeof(PLDHashEntryStub));
+   t1.Add((const void*)88);
+   PLDHashTable t2(&trivialOps, sizeof(PLDHashEntryStub));
+   t2.Add((const void*)99);
+ 
++#if defined(__clang__)
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wself-move"
++#endif
+   t1 = std::move(t1);   // self-move
++#if defined(__clang__)
++#pragma clang diagnostic pop
++#endif
+ 
+   t1 = std::move(t2);   // empty overwritten with empty
+ 
+   PLDHashTable t3(&trivialOps, sizeof(PLDHashEntryStub));
+   PLDHashTable t4(&trivialOps, sizeof(PLDHashEntryStub));
+   t3.Add((const void*)88);
+ 
+   t3 = std::move(t4);   // non-empty overwritten with empty
+diff --git a/xpcom/tests/gtest/TestTArray.cpp b/xpcom/tests/gtest/TestTArray.cpp
+--- a/xpcom/tests/gtest/TestTArray.cpp
++++ b/xpcom/tests/gtest/TestTArray.cpp
+@@ -133,18 +133,26 @@ TEST(TArray, Assign)
+ 
+ TEST(TArray, AssignmentOperatorSelfAssignment)
+ {
+   nsTArray<int> array;
+   array = DummyArray();
+ 
+   array = array;
+   ASSERT_EQ(DummyArray(), array);
+-  array = std::move(array);
++
++#if defined(__clang__)
++#pragma clang diagnostic push
++#pragma clang diagnostic ignored "-Wself-move"
++#endif
++  array = std::move(array); // self-move
+   ASSERT_EQ(DummyArray(), array);
++#if defined(__clang__)
++#pragma clang diagnostic pop
++#endif
+ }
+ 
+ TEST(TArray, CopyOverlappingForwards)
+ {
+   const size_t rangeLength = 8;
+   const size_t initialLength = 2 * rangeLength;
+   uint32_t destructionCounters[initialLength];
+   nsTArray<Movable> array;

Some files were not shown because too many files changed in this diff