Merge mozilla-central to mozilla-inbound. on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Fri, 19 Apr 2019 00:50:26 +0300
changeset 528871 e0a826fcd85be5a510e8b093aa56e53bc27e75ac
parent 528870 854896d598c17d7254157ad1d4e15a8eb1b46514 (current diff)
parent 528866 b44914767f72367a7e4b01c9fd0ba9258c41570c (diff)
child 528872 ce50468eb8b2fa4e48ed70075c144a9b3bf16566
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound. on a CLOSED TREE
devtools/client/debugger/src/components/SecondaryPanes/Frames/WhyPaused.css
devtools/client/debugger/src/components/SecondaryPanes/Frames/WhyPaused.js
js/src/jit-test/tests/binast/lazy/debug/Debugger-debuggees-20.binjs
js/src/jit-test/tests/binast/lazy/debug/Debugger-findAllGlobals-02.binjs
js/src/jit-test/tests/binast/lazy/debug/Debugger-onNewGlobalObject-13.binjs
js/src/jit-test/tests/binast/lazy/debug/Object-executeInGlobal-05.binjs
js/src/jit-test/tests/binast/lazy/debug/Object-executeInGlobal-06.binjs
js/src/jit-test/tests/binast/lazy/debug/Object-getOwnPropertyDescriptor-02.binjs
js/src/jit-test/tests/binast/lazy/debug/Object-unwrap-02.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Debugger-debuggees-20.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Debugger-findAllGlobals-02.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Debugger-onNewGlobalObject-13.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Object-executeInGlobal-05.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Object-executeInGlobal-06.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Object-getOwnPropertyDescriptor-02.binjs
js/src/jit-test/tests/binast/nonlazy/debug/Object-unwrap-02.binjs
modules/libpref/init/all.js
services/common/tests/unit/test_async_iterator.js
testing/mochitest/tests/SimpleTest/AddTask.js
testing/web-platform/meta/css/css-transitions/detached-container-001.html.ini
testing/web-platform/tests/css/css-transitions/detached-container-001.html
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1272,16 +1272,28 @@ void Accessible::ApplyARIAState(uint64_t
     while ((ancestor = ancestor->Parent()) && !ancestor->IsDoc()) {
       dom::Element* el = ancestor->Elm();
       if (el && el->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
                                 nsGkAtoms::_true, eCaseMatters)) {
         *aState |= states::UNAVAILABLE;
         break;
       }
     }
+  } else {
+    // Sometimes, we use aria-activedescendant targeting something which isn't
+    // actually a descendant. This is technically a spec violation, but it's a
+    // useful hack which makes certain things much easier. For example, we use
+    // this for "fake focus" for multi select browser tabs and Quantumbar
+    // autocomplete suggestions.
+    // In these cases, the aria-activedescendant code above won't make the
+    // active item focusable. It doesn't make sense for something to have
+    // focus when it isn't focusable, so fix that here.
+    if (FocusMgr()->IsActiveItem(this)) {
+      *aState |= states::FOCUSABLE;
+    }
   }
 
   // special case: A native button element whose role got transformed by ARIA to
   // a toggle button Also applies to togglable button menus, like in the Dev
   // Tools Web Console.
   if (IsButton() || IsMenuButton())
     aria::MapToState(aria::eARIAPressed, element, aState);
 
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1057,19 +1057,16 @@ void DocAccessible::ContentStateChanged(
 
   if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
     RefPtr<AccEvent> event =
         new AccStateChangeEvent(accessible, states::TRAVERSED, true);
     FireDelayedEvent(event);
   }
 }
 
-void DocAccessible::DocumentStatesChanged(dom::Document* aDocument,
-                                          EventStates aStateMask) {}
-
 void DocAccessible::CharacterDataWillChange(nsIContent* aContent,
                                             const CharacterDataChangeInfo&) {}
 
 void DocAccessible::CharacterDataChanged(nsIContent* aContent,
                                          const CharacterDataChangeInfo&) {}
 
 void DocAccessible::ContentInserted(nsIContent* aChild) {}
 
@@ -2088,18 +2085,21 @@ void DocAccessible::PutChildrenBack(nsTA
 
   aChildren->RemoveElementsAt(aStartIdx, aChildren->Length() - aStartIdx);
 }
 
 bool DocAccessible::MoveChild(Accessible* aChild, Accessible* aNewParent,
                               int32_t aIdxInParent) {
   MOZ_ASSERT(aChild, "No child");
   MOZ_ASSERT(aChild->Parent(), "No parent");
-  MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
-             "Wrong insertion point for a moving child");
+  // We can't guarantee MoveChild works correctly for accessibilities storing
+  // children outside mChildren.
+  MOZ_ASSERT(
+      aIdxInParent <= static_cast<int32_t>(aNewParent->mChildren.Length()),
+      "Wrong insertion point for a moving child");
 
   Accessible* curParent = aChild->Parent();
 
   if (!aNewParent->IsAcceptableChild(aChild->GetContent())) {
     return false;
   }
 
 #ifdef A11Y_LOG
@@ -2127,24 +2127,21 @@ bool DocAccessible::MoveChild(Accessible
 
 #ifdef A11Y_LOG
     logging::TreeInfo("move child: parent tree after", logging::eVerbose,
                       curParent);
 #endif
     return true;
   }
 
-  MOZ_ASSERT(aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()),
-             "Wrong insertion point for a moving child");
-
   // If the child cannot be re-inserted into the tree, then make sure to remove
   // it from its present parent and then shutdown it.
   bool hasInsertionPoint =
-      (aIdxInParent != -1) ||
-      (aIdxInParent <= static_cast<int32_t>(aNewParent->ChildCount()));
+      (aIdxInParent >= 0) &&
+      (aIdxInParent <= static_cast<int32_t>(aNewParent->mChildren.Length()));
 
   TreeMutation rmut(curParent);
   rmut.BeforeRemoval(aChild, hasInsertionPoint && TreeMutation::kNoShutdown);
   curParent->RemoveChild(aChild);
   rmut.Done();
 
   // No insertion point for the child.
   if (!hasInsertionPoint) {
@@ -2175,17 +2172,17 @@ void DocAccessible::CacheChildrenInSubtr
     *aFocusedAcc = aRoot;
 
   Accessible* root = aRoot->IsHTMLCombobox() ? aRoot->FirstChild() : aRoot;
   if (root->KidsFromDOM()) {
     TreeMutation mt(root, TreeMutation::kNoEvents);
     TreeWalker walker(root);
     while (Accessible* child = walker.Next()) {
       if (child->IsBoundToParent()) {
-        MoveChild(child, root, root->ChildCount());
+        MoveChild(child, root, root->mChildren.Length());
         continue;
       }
 
       root->AppendChild(child);
       mt.AfterInsertion(child);
 
       CacheChildrenInSubtree(child, aFocusedAcc);
     }
--- a/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
+++ b/accessible/tests/mochitest/events/test_focus_aria_activedescendant.html
@@ -140,16 +140,19 @@ https://bugzilla.mozilla.org/show_bug.cg
       gQueue.push(new changeARIAActiveDescendant("listbox", "item1"));
       gQueue.push(new changeARIAActiveDescendantInvalid("listbox", "invalid"));
 
       gQueue.push(new changeARIAActiveDescendant("listbox", "roaming"));
       gQueue.push(new moveARIAActiveDescendantID("roaming2", "roaming"));
       gQueue.push(new changeARIAActiveDescendantInvalid("listbox", "roaming3"));
       gQueue.push(new moveARIAActiveDescendantID("roaming", "roaming3"));
 
+      gQueue.push(new synthFocus("activedesc_nondesc_input",
+        new focusChecker("activedesc_nondesc_option")));
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
@@ -180,10 +183,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   <div role="combobox" id="combobox">
     <input id="combobox_entry">
     <ul>
       <li role="option" id="combobox_option1">option1</li>
       <li role="option" id="combobox_option2">option2</li>
     </ul>
   </div>
+
+  <!-- aria-activedescendant targeting a non-descendant -->
+  <input id="activedesc_nondesc_input" aria-activedescendant="activedesc_nondesc_option">
+  <div role="listbox">
+    <div role="option" id="activedesc_nondesc_option">option</div>
+  </div>
 </body>
 </html>
--- a/browser/base/content/test/static/browser_all_files_referenced.js
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -168,19 +168,16 @@ var whitelist = [
   // find the references)
   {file: "chrome://devtools/skin/images/aboutdebugging-firefox-aurora.svg",
    isFromDevTools: true},
   {file: "chrome://devtools/skin/images/aboutdebugging-firefox-beta.svg",
    isFromDevTools: true},
   {file: "chrome://devtools/skin/images/aboutdebugging-firefox-release.svg",
    isFromDevTools: true},
   {file: "chrome://devtools/skin/images/next.svg", isFromDevTools: true},
-  // kvstore.jsm wraps the API in nsIKeyValue.idl in a more ergonomic API
-  // It landed in bug 1490496, and we expect to start using it shortly.
-  {file: "resource://gre/modules/kvstore.jsm"},
   // Bug 1526672
   {file: "resource://app/localization/en-US/browser/touchbar/touchbar.ftl",
    platforms: ["linux", "win"]},
   // Referenced by the webcompat system addon for localization
   {file: "resource://gre/localization/en-US/toolkit/about/aboutCompat.ftl"},
 ];
 
 whitelist = new Set(whitelist.filter(item =>
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/content/fence.svg
@@ -0,0 +1,7 @@
+<!-- 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="32" height="32" viewBox="0 0 32 32">
+  <path fill="context-fill" d="M28,4l-2,2v4h-4V6l-2-2l-2,2v4h-4V6l-2-2l-2,2v4H6V6L4,4L2,6v22h4v-4h4v4h4v-4h4v4h4v-4h4v4h4V6L28,4z M6,22V12 h4v10H6z M14,22V12h4v10H14z M22,22V12h4v10H22z"/>
+</svg>
--- a/browser/components/contextualidentity/content/usercontext.css
+++ b/browser/components/contextualidentity/content/usercontext.css
@@ -33,16 +33,25 @@
   --identity-icon-color: #ff4bda;
 }
 
 .identity-color-purple {
   --identity-tab-color: #af51f5;
   --identity-icon-color: #af51f5;
 }
 
+.identity-color-toolbar {
+  --identity-tab-color: var(--lwt-toolbar-field-color, -moz-FieldText);
+  --identity-icon-color: var(--lwt-toolbar-field-color, -moz-FieldText);
+}
+
+.identity-icon-fence {
+  --identity-icon: url("resource://usercontext-content/fence.svg");
+}
+
 .identity-icon-fingerprint {
   --identity-icon: url("resource://usercontext-content/fingerprint.svg");
 }
 
 .identity-icon-briefcase {
   --identity-icon: url("resource://usercontext-content/briefcase.svg");
 }
 
--- a/browser/components/contextualidentity/jar.mn
+++ b/browser/components/contextualidentity/jar.mn
@@ -5,16 +5,17 @@
 browser.jar:
     content/browser/usercontext/usercontext.css (content/usercontext.css)
 
 % resource usercontext-content %content/ contentaccessible=yes
     content/briefcase.svg     (content/briefcase.svg)
     content/cart.svg          (content/cart.svg)
     content/circle.svg        (content/circle.svg)
     content/dollar.svg        (content/dollar.svg)
+    content/fence.svg         (content/fence.svg)
     content/fingerprint.svg   (content/fingerprint.svg)
     content/gift.svg          (content/gift.svg)
     content/vacation.svg      (content/vacation.svg)
     content/food.svg          (content/food.svg)
     content/fruit.svg         (content/fruit.svg)
     content/pet.svg           (content/pet.svg)
     content/tree.svg          (content/tree.svg)
     content/chill.svg         (content/chill.svg)
--- a/browser/components/extensions/test/mochitest/test_ext_all_apis.html
+++ b/browser/components/extensions/test/mochitest/test_ext_all_apis.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>WebExtension test</title>
   <meta charset="utf-8">
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <script>
 "use strict";
 /* exported expectedContentApisTargetSpecific, expectedBackgroundApisTargetSpecific */
 let expectedContentApisTargetSpecific = [
--- a/browser/components/payments/test/mochitest/formautofill/test_editCreditCard.html
+++ b/browser/components/payments/test/mochitest/formautofill/test_editCreditCard.html
@@ -3,17 +3,16 @@
 <!--
 Test that editCreditCard.xhtml is accessible for tests in the parent directory.
 -->
 <head>
   <meta charset="utf-8">
   <title>Test that editCreditCard.xhtml is accessible</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <iframe id="editCreditCard" src="editCreditCard.xhtml"></iframe>
   </p>
 <div id="content" style="display: none">
 
--- a/browser/components/payments/test/mochitest/test_ObservedPropertiesMixin.html
+++ b/browser/components/payments/test/mochitest/test_ObservedPropertiesMixin.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the ObservedPropertiesMixin
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the ObservedPropertiesMixin</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <test-element id="el1" one="foo" two-word="bar"></test-element>
   </p>
--- a/browser/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html
+++ b/browser/components/payments/test/mochitest/test_PaymentStateSubscriberMixin.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the PaymentStateSubscriberMixin
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the PaymentStateSubscriberMixin</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <test-element id="el1"></test-element>
--- a/browser/components/payments/test/mochitest/test_PaymentsStore.html
+++ b/browser/components/payments/test/mochitest/test_PaymentsStore.html
@@ -3,17 +3,16 @@
 <!--
 Test the PaymentsStore
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the PaymentsStore</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
 
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
--- a/browser/components/payments/test/mochitest/test_accepted_cards.html
+++ b/browser/components/payments/test/mochitest/test_accepted_cards.html
@@ -3,17 +3,16 @@
 <!--
 Test the accepted-cards element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the accepted-cards element</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/accepted-cards.css"/>
 </head>
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -3,17 +3,16 @@
 <!--
 Test the address-form element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the address-form element</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="editDialog-shared.css"/>
--- a/browser/components/payments/test/mochitest/test_address_option.html
+++ b/browser/components/payments/test/mochitest/test_address_option.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the address-option component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the address-option component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
--- a/browser/components/payments/test/mochitest/test_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_address_picker.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the address-picker component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the address-picker component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -3,17 +3,16 @@
 <!--
 Test the basic-card-form element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the basic-card-form element</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/accepted-cards.css"/>
--- a/browser/components/payments/test/mochitest/test_basic_card_option.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_option.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the basic-card-option component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the basic-card-option component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/basic-card-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
--- a/browser/components/payments/test/mochitest/test_billing_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_billing_address_picker.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the address-picker component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the billing-address-picker component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
--- a/browser/components/payments/test/mochitest/test_completion_error_page.html
+++ b/browser/components/payments/test/mochitest/test_completion_error_page.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the completion-error-page component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the completion-error-page component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <completion-error-page id="completion-timeout-error" class="illustrated"
--- a/browser/components/payments/test/mochitest/test_currency_amount.html
+++ b/browser/components/payments/test/mochitest/test_currency_amount.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the currency-amount component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the currency-amount component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <currency-amount id="amount1"></currency-amount>
   </p>
--- a/browser/components/payments/test/mochitest/test_labelled_checkbox.html
+++ b/browser/components/payments/test/mochitest/test_labelled_checkbox.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the labelled-checkbox component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the labelled-checkbox component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <labelled-checkbox id="box0"></labelled-checkbox>
     <labelled-checkbox id="box1" label="the label" value="the value"></labelled-checkbox>
--- a/browser/components/payments/test/mochitest/test_order_details.html
+++ b/browser/components/payments/test/mochitest/test_order_details.html
@@ -2,17 +2,16 @@
 <html>
 <!--
   Test the order-details component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the order-details component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/order-details.css"/>
 
   <template id="order-details-template">
     <ul class="main-list"></ul>
--- a/browser/components/payments/test/mochitest/test_payer_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_payer_address_picker.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the paymentOptions address-picker
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the paymentOptions address-picker</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
 
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
--- a/browser/components/payments/test/mochitest/test_payment_details_item.html
+++ b/browser/components/payments/test/mochitest/test_payment_details_item.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the payment-details-item component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the payment-details-item component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
     <payment-details-item id="item1"></payment-details-item>
     <payment-details-item id="item2" label="Some item" amount-value="2" amount-currency="USD"></payment-details-item>
--- a/browser/components/payments/test/mochitest/test_payment_dialog.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the payment-dialog custom element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the payment-dialog element</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="sinon-7.2.7.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
--- a/browser/components/payments/test/mochitest/test_payment_dialog_required_top_level_items.html
+++ b/browser/components/payments/test/mochitest/test_payment_dialog_required_top_level_items.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the payment-dialog custom element
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the payment-dialog element</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
--- a/browser/components/payments/test/mochitest/test_payment_method_picker.html
+++ b/browser/components/payments/test/mochitest/test_payment_method_picker.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the payment-method-picker component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the payment-method-picker component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/basic-card-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
--- a/browser/components/payments/test/mochitest/test_rich_select.html
+++ b/browser/components/payments/test/mochitest/test_rich_select.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the rich-select component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the rich-select component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
   <script src="../../res/unprivileged-fallbacks.js"></script>
   <script src="autofillEditForms.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/basic-card-option.css"/>
--- a/browser/components/payments/test/mochitest/test_shipping_option_picker.html
+++ b/browser/components/payments/test/mochitest/test_shipping_option_picker.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Test the shipping-option-picker component
 -->
 <head>
   <meta charset="utf-8">
   <title>Test the shipping-option-picker component</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="payments_common.js"></script>
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/shipping-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
--- a/browser/components/preferences/containers.js
+++ b/browser/components/preferences/containers.js
@@ -30,27 +30,29 @@ let gContainersManager = {
     "circle",
     "gift",
     "vacation",
     "food",
     "fruit",
     "pet",
     "tree",
     "chill",
+    "fence",
   ],
 
   colors: [
     "blue",
     "turquoise",
     "green",
     "yellow",
     "orange",
     "red",
     "pink",
     "purple",
+    "toolbar",
   ],
 
   onLoad() {
     let params = window.arguments[0] || {};
     this.init(params);
   },
 
   init(aParams) {
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -145,25 +145,25 @@
                     <label data-l10n-id="content-blocking-private-trackers"/>
                   </hbox>
                   <hbox class="extra-information-label trackers-option" hidden="true">
                     <image class="content-blocking-trackers-image"/>
                     <label data-l10n-id="content-blocking-all-windows-trackers"/>
                   </hbox>
                   <hbox class="extra-information-label all-third-party-cookies-option" hidden="true">
                     <image class="content-blocking-cookies-image"/>
-                    <label data-l10n-id="content-blocking-third-party-cookies"/>
+                    <label data-l10n-id="content-blocking-all-third-party-cookies"/>
                   </hbox>
                   <hbox class="extra-information-label all-cookies-option" hidden="true">
                     <image class="content-blocking-cookies-image"/>
-                    <label data-l10n-id="content-blocking-third-party-cookies"/>
+                    <label data-l10n-id="content-blocking-all-cookies"/>
                   </hbox>
                   <hbox class="extra-information-label unvisited-cookies-option" hidden="true">
                     <image class="content-blocking-cookies-image"/>
-                    <label data-l10n-id="content-blocking-third-party-cookies"/>
+                    <label data-l10n-id="content-blocking-unvisited-cookies"/>
                   </hbox>
                   <hbox class="extra-information-label third-party-tracking-cookies-option" hidden="true">
                     <image class="content-blocking-cookies-image"/>
                     <label data-l10n-id="content-blocking-third-party-cookies"/>
                   </hbox>
                   <hbox class="extra-information-label cryptominers-option" hidden="true">
                     <image class="content-blocking-cryptominers-image"/>
                     <label data-l10n-id="content-blocking-cryptominers"/>
--- a/browser/components/resistfingerprinting/test/mochitest/test_bug863246_resource_uri.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_bug863246_resource_uri.html
@@ -1,12 +1,11 @@
 <!DOCTYPE html>
 <meta charset="utf8">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
-<script src="/tests/SimpleTest/AddTask.js"></script>
 <script>
 /* global SimpleTest SpecialPowers add_task */
 
 function testResourceUri(aTest, aUri, aContentAccessible) {
   return new Promise((aResolve) => {
     let link = document.createElement("link");
     link.rel = "stylesheet";
     link.onload = () => {
--- a/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
+++ b/browser/components/resistfingerprinting/test/mochitest/test_reduce_time_precision.html
@@ -3,17 +3,16 @@
 <!--
 Tor bug
 https://trac.torproject.org/projects/tor/ticket/1517
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Tor Bug 1517 and Mozilla Bug 1424341</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://trac.torproject.org/projects/tor/ticket/1517">Tor Bug 1517</a>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1424341">Mozilla Bug 1424341</a>
 
 <!-- The main testing script -->
 <script type="application/javascript">
--- a/browser/components/translation/TranslationDocument.jsm
+++ b/browser/components/translation/TranslationDocument.jsm
@@ -201,21 +201,17 @@ this.TranslationDocument.prototype = {
    * @param target   A string that is either "translation"
    *                 or "original".
    */
   _swapDocumentContent(target) {
     (async () => {
       // Let the event loop breath on every 100 nodes
       // that are replaced.
       const YIELD_INTERVAL = 100;
-      let maybeYield = Async.jankYielder(YIELD_INTERVAL);
-      for (let root of this.roots) {
-        root.swapText(target);
-        await maybeYield();
-      }
+      await Async.yieldingForEach(this.roots, root => root.swapText(target), YIELD_INTERVAL);
     })();
   },
 };
 
 /**
  * This class represents an item for translation. It's basically our
  * wrapper class around a node returned by getTranslationNode, with
  * more data and structural information on it.
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -87,16 +87,17 @@ class UrlbarInput {
     this.controller.setInput(this);
     this.view = new UrlbarView(this);
     this.valueIsTyped = false;
     this.userInitiatedFocus = false;
     this.isPrivate = PrivateBrowsingUtils.isWindowPrivate(this.window);
     this.lastQueryContextPromise = Promise.resolve();
     this._actionOverrideKeyCount = 0;
     this._autofillPlaceholder = "";
+    this._deletedEndOfAutofillPlaceholder = false;
     this._lastSearchString = "";
     this._resultForCurrentValue = null;
     this._suppressStartQuery = false;
     this._untrimmedValue = "";
 
     // This exists only for tests.
     this._enableAutofillPlaceholder = true;
 
@@ -177,16 +178,18 @@ class UrlbarInput {
    * Uninitializes this input object, detaching it from the inputField.
    */
   uninit() {
     for (let name of this._inputFieldEvents) {
       this.inputField.removeEventListener(name, this);
     }
     this.removeEventListener("mousedown", this);
 
+    this.editor.removeEditActionListener(this);
+
     this.view.panel.remove();
 
     this.inputField.controllers.removeControllerAt(0);
 
     if (Object.getOwnPropertyDescriptor(this, "valueFormatter").get) {
       this.valueFormatter.uninit();
     }
 
@@ -556,18 +559,16 @@ class UrlbarInput {
     this._autofillValue(value, selectionStart, selectionEnd);
   }
 
   /**
    * Starts a query based on the current input value.
    *
    * @param {boolean} [options.allowAutofill]
    *   Whether or not to allow providers to include autofill results.
-   * @param {number} [options.lastKey]
-   *   The last key the user entered (as a key code).
    * @param {string} [options.searchString]
    *   The search string.  If not given, the current input value is used.
    *   Otherwise, the current input value must start with this value.
    * @param {boolean} [options.resetSearchState]
    *   If this is the first search of a user interaction with the input, set
    *   this to true (the default) so that search-related state from the previous
    *   interaction doesn't interfere with the new interaction.  Otherwise set it
    *   to false so that state is maintained during a single interaction.  The
@@ -575,17 +576,16 @@ class UrlbarInput {
    *   this method is called due to input events.
    * @param {boolean} [options.allowEmptyInput]
    *   If true and the search string is empty, then the input will become empty
    *   when no result is selected.  If false, the input will continue showing
    *   the last non-empty search string when no result is selected.
    */
   startQuery({
     allowAutofill = true,
-    lastKey = null,
     searchString = null,
     resetSearchState = true,
     allowEmptyInput = true,
   } = {}) {
     if (this._suppressStartQuery) {
       return;
     }
 
@@ -605,17 +605,16 @@ class UrlbarInput {
     }
 
     // TODO (Bug 1522902): This promise is necessary for tests, because some
     // tests are not listening for completion when starting a query through
     // other methods than startQuery (input events for example).
     this.lastQueryContextPromise = this.controller.startQuery(new UrlbarQueryContext({
       allowAutofill,
       isPrivate: this.isPrivate,
-      lastKey,
       maxResults: UrlbarPrefs.get("maxRichResults"),
       muxer: "UnifiedComplete",
       providers: ["UnifiedComplete"],
       searchString,
       userContextId: this.window.gBrowser.selectedBrowser.getAttribute("usercontextid"),
     }));
   }
 
@@ -663,16 +662,41 @@ class UrlbarInput {
   /**
    * Remove the hidden focus styles.
    * This is used by Activity Stream and about:privatebrowsing for search hand-off.
    */
   removeHiddenFocus() {
     this.textbox.classList.remove("hidden-focus");
   }
 
+  /**
+   * nsIEditActionListener method implementation.  We use this to detect when
+   * the user deletes autofilled substrings.
+   *
+   * There is also a DidDeleteSelection method, but it's called before the input
+   * event is fired.  So the order is: WillDeleteSelection, DidDeleteSelection,
+   * input event.  Further, in DidDeleteSelection, the passed-in selection
+   * object is the same as the object passed to WillDeleteSelection, but by that
+   * point its properties have been adjusted to account for the deletion.  For
+   * example, the endOffset property of its range will be smaller than it was in
+   * WillDeleteSelection.  Therefore we compute whether the user deleted the
+   * autofilled substring here in WillDeleteSelection instead of deferring it to
+   * when we handle the input event.
+   *
+   * @param {Selection} selection
+   *   The Selection object.
+   */
+  WillDeleteSelection(selection) {
+    this._deletedEndOfAutofillPlaceholder =
+      selection &&
+      selection.getRangeAt(0).endOffset ==
+        this._autofillPlaceholder.length &&
+      this._autofillPlaceholder.endsWith(String(selection));
+  }
+
   // Getters and Setters below.
 
   get focused() {
     return this.textbox.getAttribute("focused") == "true";
   }
 
   get goButton() {
     return this.document.getAnonymousElementByAttribute(this.textbox, "anonid",
@@ -1268,16 +1292,19 @@ class UrlbarInput {
   }
 
   _on_input() {
     let value = this.textValue;
     this.valueIsTyped = true;
     this._untrimmedValue = value;
     this.window.gBrowser.userTypedValue = value;
 
+    let deletedEndOfAutofillPlaceholder = this._deletedEndOfAutofillPlaceholder;
+    this._deletedEndOfAutofillPlaceholder = false;
+
     let compositionState = this._compositionState;
     let compositionClosedPopup = this._compositionClosedPopup;
 
     // Clear composition values if we're no more composing.
     if (this._compositionState != UrlbarUtils.COMPOSITION.COMPOSING) {
       this._compositionState = UrlbarUtils.COMPOSITION.NONE;
       this._compositionClosedPopup = false;
     }
@@ -1306,52 +1333,52 @@ class UrlbarInput {
     // and we didn't close the popup on composition start.
     if (compositionState == UrlbarUtils.COMPOSITION.COMPOSING ||
         (compositionState == UrlbarUtils.COMPOSITION.CANCELED &&
          !compositionClosedPopup)) {
       return;
     }
 
     let sameSearchStrings = value == this._lastSearchString;
-
-    // TODO (bug 1524550): Properly detect autofill removal, rather than
-    // guessing based on string prefixes.
     let deletedAutofilledSubstring =
-      sameSearchStrings &&
-      value.length < this._autofillPlaceholder.length &&
-      this._autofillPlaceholder.startsWith(value);
+      deletedEndOfAutofillPlaceholder && sameSearchStrings;
 
     // Don't search again when the new search would produce the same results.
     // If we're handling a composition input, we must continue the search
     // because we canceled the previous search on composition start.
     if (sameSearchStrings &&
         !deletedAutofilledSubstring &&
         compositionState == UrlbarUtils.COMPOSITION.NONE &&
         value.length > 0) {
       return;
     }
 
     let allowAutofill =
       this._maybeAutofillOnInput(value, deletedAutofilledSubstring);
 
-    // TODO Bug 1524550: Fill in lastKey, and add anything else we need.
     this.startQuery({
       searchString: value,
       allowAutofill,
-      lastKey: null,
       resetSearchState: false,
     });
   }
 
   _on_select(event) {
-    if (!Services.clipboard.supportsSelectionClipboard()) {
+    if (!this.window.windowUtils.isHandlingUserInput) {
+      // Register the editor listener we use to detect when the user deletes
+      // autofilled substrings.  The editor is destroyed and removes all its
+      // listeners at various surprising times, and autofill causes a non-user
+      // select, which is why we do this here instead of, for example, in the
+      // constructor.  addEditActionListener is idempotent, so it's OK to call
+      // it even when we're already registered.
+      this.editor.addEditActionListener(this);
       return;
     }
 
-    if (!this.window.windowUtils.isHandlingUserInput) {
+    if (!Services.clipboard.supportsSelectionClipboard()) {
       return;
     }
 
     let val = this._getSelectedValueForClipboard();
     if (!val) {
       return;
     }
 
--- a/browser/components/urlbar/UrlbarUtils.jsm
+++ b/browser/components/urlbar/UrlbarUtils.jsm
@@ -389,33 +389,29 @@ class UrlbarQueryContext {
   /**
    * Constructs the UrlbarQueryContext instance.
    *
    * @param {object} options
    *   The initial options for UrlbarQueryContext.
    * @param {string} options.searchString
    *   The string the user entered in autocomplete. Could be the empty string
    *   in the case of the user opening the popup via the mouse.
-   * @param {number} options.lastKey
-   *   The last key the user entered (as a key code). Could be null if the search
-   *   was started via the mouse.
    * @param {boolean} options.isPrivate
    *   Set to true if this query was started from a private browsing window.
    * @param {number} options.maxResults
    *   The maximum number of results that will be displayed for this query.
    * @param {boolean} options.allowAutofill
    *   Whether or not to allow providers to include autofill results.
    * @param {number} options.userContextId
    *   The container id where this context was generated, if any.
    */
   constructor(options = {}) {
     this._checkRequiredOptions(options, [
       "allowAutofill",
       "isPrivate",
-      "lastKey",
       "maxResults",
       "searchString",
     ]);
 
     if (isNaN(parseInt(options.maxResults))) {
       throw new Error(`Invalid maxResults property provided to UrlbarQueryContext`);
     }
 
--- a/browser/components/urlbar/UrlbarView.jsm
+++ b/browser/components/urlbar/UrlbarView.jsm
@@ -348,17 +348,16 @@ class UrlbarView {
     let width = documentRect.right - documentRect.left;
     this.panel.setAttribute("width", width);
     this._mainContainer.style.maxWidth = px(width);
 
     // Keep the popup items' site icons aligned with the input's identity
     // icon if it's not too far from the edge of the window.  We define
     // "too far" as "more than 30% of the window's width AND more than
     // 250px".
-    let contentWidth = width;
     let boundToCheck = this.window.RTL_UI ? "right" : "left";
     let inputRect = this._getBoundsWithoutFlushing(this.input.textbox);
     let startOffset = Math.abs(inputRect[boundToCheck] - documentRect[boundToCheck]);
     let alignSiteIcons = startOffset / width <= 0.3 || startOffset <= 250;
     if (alignSiteIcons) {
       // Calculate the end margin if we have a start margin.
       let boundToCheckEnd = this.window.RTL_UI ? "left" : "right";
       let endOffset = Math.abs(inputRect[boundToCheckEnd] -
@@ -372,22 +371,20 @@ class UrlbarView {
       let identityIcon = this.document.getElementById("identity-icon");
       let identityRect = this._getBoundsWithoutFlushing(identityIcon);
       let start = this.window.RTL_UI ?
                     documentRect.right - identityRect.right :
                     identityRect.left;
 
       this.panel.style.setProperty("--item-padding-start", px(start));
       this.panel.style.setProperty("--item-padding-end", px(endOffset));
-      contentWidth -= start + endOffset;
     } else {
       this.panel.style.removeProperty("--item-padding-start");
       this.panel.style.removeProperty("--item-padding-end");
     }
-    this.panel.style.setProperty("--item-content-width", px(contentWidth));
 
     // Align the panel with the input's parent toolbar.
     let toolbarRect =
       this._getBoundsWithoutFlushing(this.input.textbox.closest("toolbar"));
     this.panel.style.marginInlineStart = px(this.window.RTL_UI ?
       inputRect.right - documentRect.right :
       documentRect.left - inputRect.left);
     this.panel.style.marginTop = px(inputRect.top - toolbarRect.top);
@@ -678,23 +675,25 @@ class UrlbarView {
     let row = event.target;
     while (!row.classList.contains("urlbarView-row")) {
       row = row.parentNode;
     }
     this.input.pickResult(event, parseInt(row.getAttribute("resultIndex")));
   }
 
   _on_overflow(event) {
-    if (event.target.classList.contains("urlbarView-row-inner")) {
+    if (event.target.classList.contains("urlbarView-url") ||
+        event.target.classList.contains("urlbarView-title")) {
       event.target.toggleAttribute("overflow", true);
     }
   }
 
   _on_underflow(event) {
-    if (event.target.classList.contains("urlbarView-row-inner")) {
+    if (event.target.classList.contains("urlbarView-url") ||
+        event.target.classList.contains("urlbarView-title")) {
       event.target.toggleAttribute("overflow", false);
     }
   }
 
   _on_popupshowing() {
     this.window.addEventListener("resize", this);
   }
 
--- a/browser/components/urlbar/tests/unit/head.js
+++ b/browser/components/urlbar/tests/unit/head.js
@@ -32,17 +32,16 @@ const {sinon} = ChromeUtils.import("reso
  * @param {object} properties Overrides for the default values.
  * @returns {UrlbarQueryContext} Creates a dummy query context with pre-filled
  *          required options.
  */
 function createContext(searchString = "foo", properties = {}) {
   let context = new UrlbarQueryContext({
     allowAutofill: UrlbarPrefs.get("autoFill"),
     isPrivate: true,
-    lastKey: searchString ? searchString[searchString.length - 1] : "",
     maxResults: UrlbarPrefs.get("maxRichResults"),
     searchString,
   });
   return Object.assign(context, properties);
 }
 
 /**
  * Waits for the given notification from the supplied controller.
--- a/browser/components/urlbar/tests/unit/test_UrlbarQueryContext.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarQueryContext.js
@@ -6,56 +6,42 @@
 add_task(function test_constructor() {
   Assert.throws(() => new UrlbarQueryContext(),
     /Missing or empty allowAutofill provided to UrlbarQueryContext/,
     "Should throw with no arguments");
 
   Assert.throws(() => new UrlbarQueryContext({
     allowAutofill: true,
     isPrivate: false,
-    maxResults: 1,
-    searchString: "foo",
-  }), /Missing or empty lastKey provided to UrlbarQueryContext/,
-    "Should throw with a missing lastKey parameter");
-
-  Assert.throws(() => new UrlbarQueryContext({
-    allowAutofill: true,
-    isPrivate: false,
-    lastKey: "b",
     searchString: "foo",
   }), /Missing or empty maxResults provided to UrlbarQueryContext/,
     "Should throw with a missing maxResults parameter");
 
   Assert.throws(() => new UrlbarQueryContext({
     allowAutofill: true,
-    lastKey: "b",
     maxResults: 1,
     searchString: "foo",
   }), /Missing or empty isPrivate provided to UrlbarQueryContext/,
     "Should throw with a missing isPrivate parameter");
 
   Assert.throws(() => new UrlbarQueryContext({
     isPrivate: false,
-    lastKey: "b",
     maxResults: 1,
     searchString: "foo",
   }), /Missing or empty allowAutofill provided to UrlbarQueryContext/,
     "Should throw with a missing allowAutofill parameter");
 
   let qc = new UrlbarQueryContext({
     allowAutofill: false,
     isPrivate: true,
-    lastKey: "b",
     maxResults: 1,
     searchString: "foo",
   });
 
   Assert.strictEqual(qc.allowAutofill, false,
     "Should have saved the correct value for allowAutofill");
   Assert.strictEqual(qc.isPrivate, true,
     "Should have saved the correct value for isPrivate");
-  Assert.equal(qc.lastKey, "b",
-    "Should have saved the correct value for lastKey");
   Assert.equal(qc.maxResults, 1,
     "Should have saved the correct value for maxResults");
   Assert.equal(qc.searchString, "foo",
     "Should have saved the correct value for searchString");
 });
--- a/browser/components/urlbar/tests/unit/test_UrlbarUtils_getShortcutOrURIAndPostData.js
+++ b/browser/components/urlbar/tests/unit/test_UrlbarUtils_getShortcutOrURIAndPostData.js
@@ -98,17 +98,17 @@ var testData = [
 
   // Test using a non-bmKeywordData object, to test the behavior of
   // getShortcutOrURIAndPostData for non-keywords (setupKeywords only adds keywords for
   // bmKeywordData objects)
   [{keyword: "http://gavinsharp.com"},
    new keywordResult(null, null, true)],
 ];
 
-AddonTestUtils.init(this);
+AddonTestUtils.init(this, false);
 AddonTestUtils.overrideCertDB();
 AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 add_task(async function setup() {
   await AddonTestUtils.promiseStartupManager();
 });
 
 add_task(async function test_getshortcutoruri() {
--- a/browser/components/urlbar/tests/unit/test_providerUnifiedComplete.js
+++ b/browser/components/urlbar/tests/unit/test_providerUnifiedComplete.js
@@ -7,17 +7,17 @@
 // intended to check all the edge cases, because that component is already
 // covered by a good amount of tests.
 
 const {AddonTestUtils} = ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 
 const SUGGEST_PREF = "browser.urlbar.suggest.searches";
 const SUGGEST_ENABLED_PREF = "browser.search.suggest.enabled";
 
-AddonTestUtils.init(this);
+AddonTestUtils.init(this, false);
 AddonTestUtils.overrideCertDB();
 AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
 
 add_task(async function setup() {
   await AddonTestUtils.promiseStartupManager();
 });
 
 add_task(async function test_unifiedComplete() {
--- a/browser/config/mozconfigs/linux64/asan-fuzzing-ccov
+++ b/browser/config/mozconfigs/linux64/asan-fuzzing-ccov
@@ -20,16 +20,19 @@ ac_add_options --disable-sandbox
 ac_add_options --disable-profiling
 ac_add_options --disable-warnings-as-errors
 ac_add_options --enable-coverage
 
 export CFLAGS="--coverage"
 export CXXFLAGS="--coverage"
 export LDFLAGS="--coverage -Wl,--compress-debug-sections=zlib"
 
+# gold is required for libFuzzer to work properly
+ac_add_options --enable-gold
+
 ac_add_options --enable-fuzzing
 unset MOZ_STDCXX_COMPAT
 
 # Package js shell.
 export MOZ_PACKAGE_JSSHELL=1
 
 # Need this to prevent name conflicts with the normal nightly build packages
 export MOZ_PKG_SPECIAL=asan-ccov
--- a/browser/config/mozconfigs/linux64/nightly-fuzzing-asan
+++ b/browser/config/mozconfigs/linux64/nightly-fuzzing-asan
@@ -10,16 +10,19 @@ ac_add_options "MOZ_ALLOW_LEGACY_EXTENSI
 . $topsrcdir/build/mozconfig.stylo
 
 # ASan specific options on Linux
 ac_add_options --enable-valgrind
 
 . $topsrcdir/build/unix/mozconfig.asan
 ac_add_options --disable-elf-hack
 
+# gold is required for libFuzzer to work properly
+ac_add_options --enable-gold
+
 ac_add_options --enable-fuzzing
 unset MOZ_STDCXX_COMPAT
 
 # Piggybacking UBSan for now since only a small subset of checks are enabled.
 # A new build can be created when appropriate.
 ac_add_options --enable-undefined-sanitizer
 
 # Package js shell.
--- a/browser/config/whats_new_page.yml
+++ b/browser/config/whats_new_page.yml
@@ -51,8 +51,61 @@
           - sk
           - sl
           - sq
           - sv-SE
           - tr
           - vi
           - zh-CN
           - zh-TW
+- type: show-url
+  # yamllint disable-line rule:line-length
+  url: "https://www.mozilla.org/%LOCALE%/{product}/{version.major_number}.0beta/whatsnew/?oldversion=%OLD_VERSION%"
+  conditions:
+      blob-types: [wnp]
+      release-types: [beta, release-rc]
+      products: [firefox]
+      update-channel: beta
+      # e.g.: ["<61.0"]. {version.major_number} reflects the current version.
+      # This is done by taskgraph.
+      versions: ["<{version.major_number}.0"]
+      locales:
+          - be
+          - cak
+          - cs
+          - cy
+          - da
+          - de
+          - dsb
+          - en-CA
+          - en-GB
+          - en-US
+          - es-AR
+          - es-CL
+          - es-ES
+          - es-MX
+          - fr
+          - fy-NL
+          - gn
+          - hsb
+          - hu
+          - ia
+          - id
+          - it
+          - ka
+          - lij
+          - nl
+          - nn-NO
+          - pl
+          - pt-BR
+          - pt-PT
+          - rm
+          - ro
+          - ru
+          - sk
+          - sl
+          - sq
+          - sv-SE
+          - tr
+          - uk
+          - vi
+          - zh-CN
+          - zh-TW
--- a/browser/docs/AddressBar.rst
+++ b/browser/docs/AddressBar.rst
@@ -55,19 +55,16 @@ It is augmented as it progresses through
 .. code::
 
   UrlbarQueryContext {
     allowAutofill; // {boolean} If true, providers are allowed to return
                    // autofill results.  Even if true, it's up to providers
                    // whether to include autofill results, but when false, no
                    // provider should include them.
     isPrivate; // {boolean} Whether the search started in a private context.
-    lastKey; // {string} The last key pressed by the user. This can affect the
-             // behavior, for example by not autofilling again when the user
-             // hit backspace.
     maxResults; // {integer} The maximum number of results requested. It is
                 // possible to request more results than the shown ones, and
                 // do additional filtering at the View level.
     searchString; // {string} The user typed string.
     userContextId; // {integer} The user context ID (containers feature).
 
     // Optional properties.
     muxer; // {string} Name of a registered muxer. Muxers can be registered
--- a/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_basic_creditcard_autocomplete_form.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="../formautofill_common.js"></script>
   <script type="text/javascript" src="../../../../../..//toolkit/components/satchel/test/satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: simple form credit card autofill
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_clear_form.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test form autofill - clear form button</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="../formautofill_common.js"></script>
   <script type="text/javascript" src="../../../../../../toolkit/components/satchel/test/satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: clear form button
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
+++ b/browser/extensions/formautofill/test/mochitest/creditCard/test_creditcard_autocomplete_off.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="../formautofill_common.js"></script>
   <script type="text/javascript" src="../../../../../../toolkit/components/satchel/test/satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: simple form credit card autofill
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/formautofill_common.js
+++ b/browser/extensions/formautofill/test/mochitest/formautofill_common.js
@@ -1,11 +1,10 @@
 /* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/SimpleTest.js */
 /* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/EventUtils.js */
-/* import-globals-from ../../../../../testing/mochitest/tests/SimpleTest/AddTask.js */
 /* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
 /* eslint-disable no-unused-vars */
 
 "use strict";
 
 let formFillChromeScript;
 let defaultTextColor;
 let expectingPopup = null;
--- a/browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html
+++ b/browser/extensions/formautofill/test/mochitest/test_address_level_1_submission.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test autofill submission for a country without address-level1</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: Test autofill submission for a country without address-level1
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html
+++ b/browser/extensions/formautofill/test/mochitest/test_autofill_and_ordinal_forms.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test autofill submit</title>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <script>
 /* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
--- a/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_autofocus_form.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: autocomplete on an autofocus form
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_basic_autocomplete_form.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: simple form address autofill
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_form_changes.html
+++ b/browser/extensions/formautofill/test/mochitest/test_form_changes.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: autocomplete on an autofocus form
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html
+++ b/browser/extensions/formautofill/test/mochitest/test_formautofill_preview_highlight.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test form autofill - preview and highlight</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: preview and highlight
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
+++ b/browser/extensions/formautofill/test/mochitest/test_multi_locale_CA_address_form.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test basic autofill</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: simple form address autofill
 
 <script>
--- a/browser/extensions/formautofill/test/mochitest/test_multiple_forms.html
+++ b/browser/extensions/formautofill/test/mochitest/test_multiple_forms.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test autofill submit</title>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 
 <script>
 /* import-globals-from ../../../../../toolkit/components/satchel/test/satchel_common.js */
--- a/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
+++ b/browser/extensions/formautofill/test/mochitest/test_on_address_submission.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test autofill submit</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="formautofill_common.js"></script>
   <script type="text/javascript" src="satchel_common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 Form autofill test: check if address is saved/updated correctly
 
 <script>
--- a/browser/extensions/webcompat/aboutCompat.css
+++ b/browser/extensions/webcompat/aboutCompat.css
@@ -1,9 +1,9 @@
-@media screen and (min-device-width:481px) {
+@media (any-pointer: fine) {
   :root {
     font-family: sans-serif;
     margin: 40px auto;
     min-width: 30em;
     max-width: 60em;
   }
 
   table {
@@ -73,17 +73,17 @@
     text-align: left;
   }
 
   html[dir=rtl] th {
     text-align: right;
   }
 }
 
-@media screen and (min-device-width:320px) and (max-device-width:480px) {
+@media (any-pointer: coarse), (any-pointer: none) {
   * {
     margin: 0;
     padding: 0;
   }
 
   html {
     font-family: sans-serif;
     font-size: 14px;
--- a/browser/extensions/webcompat/aboutCompat.js
+++ b/browser/extensions/webcompat/aboutCompat.js
@@ -126,17 +126,17 @@ function redrawTable(table, data) {
     td.innerText = row.domain;
     tr.appendChild(td);
 
     td = document.createElement("td");
     const a = document.createElement("a");
     const bug = row.bug;
     a.href = `https://bugzilla.mozilla.org/show_bug.cgi?id=${bug}`;
     document.l10n.setAttributes(a, "label-more-information", {bug});
-    a.target = "aboutCompatBug";
+    a.target = "_blank";
     td.appendChild(a);
     tr.appendChild(td);
 
     td = document.createElement("td");
     tr.appendChild(td);
     const button = document.createElement("button");
     document.l10n.setAttributes(button,
                         row.active ? "label-disable" : "label-enable");
--- a/browser/extensions/webcompat/ua_overrides.js
+++ b/browser/extensions/webcompat/ua_overrides.js
@@ -126,17 +126,17 @@ for (const override of [
   }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    id: "bug1177298",
+    id: "bug1177298-1",
     platform: "android",
     domain: "weather.yahoo.co.jp",
     bug: "1177298",
     config: {
       matches: ["*://weather.yahoo.co.jp/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
@@ -144,17 +144,17 @@ for (const override of [
   }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    id: "bug1177298",
+    id: "bug1177298-2",
     platform: "android",
     domain: "lohaco.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.lohaco.jp/*"],
       uaTransformer: (_) => {
         return "Mozilla/5.0 (Linux; Android 5.0.2; Galaxy Nexus Build/IMM76B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36";
       },
@@ -162,17 +162,17 @@ for (const override of [
   }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    id: "bug1177298",
+    id: "bug1177298-3",
     platform: "android",
     domain: "nhk.or.jp",
     bug: "1177298",
     config: {
       matches: ["*://*.nhk.or.jp/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " AppleWebKit";
       },
@@ -180,17 +180,17 @@ for (const override of [
   }, {
     /*
      * Bug 1177298 - Write UA overrides for top Japanese Sites
      * (Imported from ua-update.json.in)
      *
      * To receive the proper mobile version instead of the desktop version or
      * a lower grade mobile experience, the UA is spoofed.
      */
-    id: "bug1177298",
+    id: "bug1177298-4",
     platform: "android",
     domain: "uniqlo.com",
     bug: "1177298",
     config: {
       matches: ["*://*.uniqlo.com/*"],
       uaTransformer: (originalUA) => {
         return originalUA + " Mobile Safari";
       },
--- a/browser/locales/en-US/browser/preferences/containers.ftl
+++ b/browser/locales/en-US/browser/preferences/containers.ftl
@@ -55,17 +55,21 @@ containers-color-yellow =
 containers-color-orange =
     .label = Orange
 containers-color-red =
     .label = Red
 containers-color-pink =
     .label = Pink
 containers-color-purple =
     .label = Purple
+containers-color-toolbar =
+    .label = Match toolbar
 
+containers-icon-fence =
+    .label = Fence
 containers-icon-fingerprint =
     .label = Fingerprint
 containers-icon-briefcase =
     .label = Briefcase
 # String represents a money sign but currently uses a dollar sign
 # so don't change to local currency. See Bug 1291672.
 containers-icon-dollar =
     .label = Dollar sign
--- a/browser/themes/shared/urlbar-autocomplete.inc.css
+++ b/browser/themes/shared/urlbar-autocomplete.inc.css
@@ -44,37 +44,40 @@
   padding: @urlbarViewPadding@;
   white-space: nowrap;
 }
 
 .urlbarView-row {
   border-radius: 2px;
   fill: currentColor;
   fill-opacity: .6;
-  padding-top: 5px;
-  padding-bottom: 5px;
+  padding-top: 6px;
+  padding-bottom: 6px;
   padding-inline-start: calc(var(--item-padding-start, calc(5px + @urlbarViewFaviconOffset@)) - @urlbarViewFaviconOffset@);
   padding-inline-end: var(--item-padding-end, 5px);
 }
 
 :root[uidensity=touch] .urlbarView-row {
-  padding-top: 10px;
-  padding-bottom: 10px;
+  padding-top: 11px;
+  padding-bottom: 11px;
 }
 
 .urlbarView-row-inner {
-  overflow: hidden;
-  display: block;
+  display: flex;
+  flex-wrap: nowrap;
+  align-items: end;
+  justify-content: start;
 }
 
-.urlbarView-row-inner[overflow] {
+.urlbarView-title[overflow],
+.urlbarView-url[overflow] {
   mask-image: linear-gradient(to left, transparent, black 2em);
 }
 
-.urlbarView-row-inner[overflow]:-moz-locale-dir(rtl) {
+.urlbarView-title[overflow]:-moz-locale-dir(rtl) {
   mask-image: linear-gradient(to right, transparent, black 2em);
 }
 
 .urlbarView-row:hover {
   background: var(--arrowpanel-dimmed);
 }
 
 .urlbarView-row[selected] {
@@ -82,18 +85,17 @@
   color: var(--autocomplete-popup-highlight-color);
   fill-opacity: 1;
 }
 
 .urlbarView-type-icon,
 .urlbarView-favicon {
   display: inline-block;
   vertical-align: middle;
-  transform: translateY(-2px);
-  width: 16px;
+  min-width: 16px;
   height: 16px;
   margin-inline-end: @urlbarViewIconMarginEnd@;
   background-repeat: no-repeat;
   background-size: contain;
   -moz-context-properties: fill, fill-opacity;
 }
 
 @media (min-resolution: 2dppx) {
@@ -112,18 +114,20 @@
   background-image: url(chrome://browser/skin/tab.svg);
 }
 
 .urlbarView-title {
   display: inline-block;
   vertical-align: text-bottom;
   overflow: hidden;
   white-space: nowrap;
-  text-overflow: ellipsis;
-  max-width: calc(var(--item-content-width) * .7 - 2 * (16px + @urlbarViewIconMarginEnd@));
+  /* We prioritize the title over the url, so it can grow freely, but it should
+     never crop the url completely */
+  flex-shrink: 0;
+  max-width: calc(70% - 2 * (16px + @urlbarViewIconMarginEnd@));
 }
 
 .urlbarView-title-separator::before {
   content: "\2014";
   color: var(--panel-disabled-color);
   margin: 0 .4em;
 }
 
@@ -143,16 +147,17 @@
 }
 
 .urlbarView-secondary {
   display: inline-block;
   color: var(--urlbar-popup-action-color);
 }
 
 .urlbarView-url {
+  overflow: hidden;
   color: var(--urlbar-popup-url-color);
 }
 
 .urlbarView-url:-moz-locale-dir(rtl) {
   direction: ltr !important;
 }
 
 .urlbarView-row[selected] > .urlbarView-row-inner > .urlbarView-title-separator::before,
@@ -167,16 +172,17 @@
 .urlbarView-row[type=search]:not([selected]):not(:hover) > .urlbarView-row-inner > .urlbarView-title-separator,
 #urlbar-results[actionoverride] .urlbarView-row[type=switchtab] > .urlbarView-row-inner > .urlbarView-action,
 #urlbar-results:not([actionoverride]) .urlbarView-row[type=switchtab] > .urlbarView-row-inner > .urlbarView-url {
   display: none;
 }
 
 .urlbarView-tags {
   display: inline-block;
+  overflow: hidden;
 }
 
 .urlbarView-tag {
   background-color: var(--arrowpanel-dimmed);
   border-radius: 2px;
   border: 1px solid var(--panel-separator-color);
   padding: 0 1px;
   margin-inline-start: .4em;
--- a/build/build-clang/clang-8-android.json
+++ b/build/build-clang/clang-8-android.json
@@ -37,16 +37,25 @@
       "aarch64-linux-android": {
         "ndk_toolchain": "/builds/worker/workspace/build/src/android-ndk/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64",
         "ndk_sysroot": "/builds/worker/workspace/build/src/android-ndk/platforms/android-21/arch-arm64",
         "ndk_includes": [
           "/builds/worker/workspace/build/src/android-ndk/sysroot/usr/include/aarch64-linux-android",
           "/builds/worker/workspace/build/src/android-ndk/sysroot/usr/include"
         ],
         "api_level": 21
+      },
+      "x86_64-linux-android": {
+        "ndk_toolchain": "/builds/worker/workspace/build/src/android-ndk/toolchains/x86_64-4.9/prebuilt/linux-x86_64",
+        "ndk_sysroot": "/builds/worker/workspace/build/src/android-ndk/platforms/android-21/arch-x86_64",
+        "ndk_includes": [
+          "/builds/worker/workspace/build/src/android-ndk/sysroot/usr/include/x86_64-linux-android",
+          "/builds/worker/workspace/build/src/android-ndk/sysroot/usr/include"
+        ],
+        "api_level": 21
       }
     },
     "patches": [
       "static-llvm-symbolizer.patch",
       "find_symbolizer_linux.patch",
       "rename_gcov_flush.patch"
     ]
 }
--- a/build/moz.configure/bindgen.configure
+++ b/build/moz.configure/bindgen.configure
@@ -1,40 +1,70 @@
 # -*- 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/.
 
-# cbindgen is needed by the style system build.
-cbindgen = check_prog('CBINDGEN', ['cbindgen'], paths=toolchain_search_path,
-                      when=depends(build_project)
-                      (lambda build_project: build_project != 'js'))
+# cbindgen is needed by the style system build and webrender.
+cbindgen_is_needed = depends(build_project)(lambda build_project: build_project != 'js')
+
+option(env='CBINDGEN', nargs=1, when=cbindgen_is_needed,
+       help='Path to cbindgen')
 
 
-@depends_if(cbindgen)
-@checking('cbindgen version')
 @imports(_from='textwrap', _import='dedent')
-def cbindgen_version(cbindgen):
+def check_cbindgen_version(cbindgen, fatal=False):
+    log.debug("trying cbindgen: %s" % cbindgen)
+
     cbindgen_min_version = Version('0.8.3')
 
     # cbindgen x.y.z
     version = Version(check_cmd_output(cbindgen, '--version').strip().split(" ")[1])
+    log.debug("%s has version %s" % (cbindgen, version))
+    if version >= cbindgen_min_version:
+        return True
+    if not fatal:
+        return False
 
-    if version < cbindgen_min_version:
-        die(dedent('''\
-        cbindgen version {} is too old. At least version {} is required.
+    die(dedent('''\
+    cbindgen version {} is too old. At least version {} is required.
+
+    Please update using 'cargo install cbindgen --force' or running
+    './mach bootstrap', after removing the existing executable located at
+    {}.
+    '''.format(version, cbindgen_min_version, cbindgen)))
+
 
-        Please update using 'cargo install cbindgen --force' or running
-        './mach bootstrap', after removing the existing executable located at
-        {}.
-        '''.format(version, cbindgen_min_version, cbindgen)))
+@depends_if('CBINDGEN', toolchain_search_path, when=cbindgen_is_needed)
+@checking('for cbindgen')
+@imports(_from='textwrap', _import='dedent')
+def cbindgen(cbindgen_override, toolchain_search_path):
+    if cbindgen_override:
+        check_cbindgen_version(cbindgen_override[0], fatal=True)
+        return cbindgen_override[0]
 
-    return version
+    candidates = []
+    for path in toolchain_search_path:
+        candidate = find_program('cbindgen', [path])
+        if not candidate:
+            continue
+        if check_cbindgen_version(candidate):
+            return candidate
+        candidates.append(candidate)
 
+    if not candidates:
+        raise FatalCheckError(dedent('''\
+        Cannot find cbindgen. Please run `mach bootstrap`,
+        `cargo install cbindgen`, ensure that `cbindgen` is on your PATH,
+        or point at an executable with `CBINDGEN`.
+        '''))
+    check_cbindgen_version(candidates[0], fatal=True)
+
+set_config('CBINDGEN', cbindgen)
 
 # Bindgen can use rustfmt to format Rust file, but it's not required.
 js_option(env='RUSTFMT', nargs=1, help='Path to the rustfmt program')
 
 rustfmt = check_prog('RUSTFMT', ['rustfmt'], paths=toolchain_search_path,
                      input='RUSTFMT', allow_missing=True)
 
 
index fe1d307533a9a85b905ff3d49acee61e65122d7d..449e713dd95de0a52c62ecdd45c0788cc60931e1
GIT binary patch
literal 229376
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
z2H`H@GGT=<TNo$oA#@aK2o(xHVO0VE00000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000002M{~suj
zw26xeu@2Io_R^mnq(AMnN%llEi^=Ar+;o(|r14Q(X_k#H$(hLJL<tg8Q98dXi{FQq
zj#^7It<*^7L;;)674Y~d!%{kxtVOaTCJK^0DDFOp4;|(5Eu=Z-`26V{dJ2<|^7v-b
zsirz4M<OqUMu|t+4D7*Y@^ek3dB%8&jARZE<z}H=Bk5E_J(4Stk;|r~GwI<>Hk-*#
z?nYx6NOSe|NyvA(JvbbOo-|w6faKP)(lj;$&B<VLQHG8*UmIV28Vl<RHr5YZ4u`KL
z&CxU>QHjj-bS4{HNmgz=gUOTDq9L6_U7bY6>P};Gvf1%;nwoU7D&C1aK8^qN>Z(XH
zl~qW_MCm#xNvA2|4aB;{&6E07rzl8s<?*HPP*xI;naoBRa?;7NN+f-}ujn+Abc&27
z$xaF7(P<edjnAji(<sW=&frj3KoH#&?Gr5#m5S0ty+s{GTEdsYGs4Zn>B4+roUpUd
zQb;PiU3fqgDSRuOQ@C1mPjpzcQZz=CA@UIE3oi<{3x^65g+W3)p<>~K!pVhoSd{<(
z00000000000000000000000000000000000000000000000000000000000000000
z00000000000000000000000000002+(+o)-#EGGEP8h1GE}NWoeu?^;DYyJ1<W4!@
z83p;<1BOkpeYSmx$fbMA^%Tz)Yn|-z3~KV+9eeJPnqC(f5uc1Hv%OrTme4_#L?GI=
zRi7=MYtvSx)_AT}Te+5aF1f8-3q04ntz0ua*R-u%6Fk?rtz08K*RZYj8sNG5ZI#u-
zb9LLw)xmSM+p1X$&(&<JtOlN|-d3&}o~znc%_?}Va$9AU@La{Vaux7g`L=T9@Lbup
Qa!Giui~-3*skPky2O!>c(f|Me
index c40fb77c5bfb3176f51dac2cbcd68045f4d1da49..33da46fa1c9d223d0dcc95d05555632eb48408fe
GIT binary patch
literal 294912
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
zXnIw2@7TWmevdCR87D4$q3KZ)DVIyoIbtFnZP`iU`7oE{#|$aOwn(ZstB%9bgXbFs
z<kYTs1ov~-Y&MHV7nzI!M<<O!`*X$CU*a!kuQUojsP6s|H@n(e7P%H}VNF!AX#e8r
zBNaE~&WTd#DY*veSPbQX8~6N&eMk!Qj;Lg<5UXq*zW=9pcIux;&ZsG(sMV<X%^Ls!
z00000000000000000000000000000000000000000000000000000000000000000
z000000000000000000000000000000;Qz-cNy&(*BOPc+w`Kx`AChb%C8MHtRjH!h
zTuK_XNEG!wYGv~V00000000000000000000000000000000000000000000000000
z000000000000000000000000000000000000000000000008(Of{c`inDpm_gp`zs
zihA=A(#UY4sQxIY%^Ls!000000000000000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000{|8`Q$WH1LBeBDh
zG)O86QY$hFMiMKFzwS^nl4xp3G9h>Uyt9pw&d4XoI}nBO=C;P5gC%*9JU<_&W@Iz+
z3B`DNqPWGlwXL~b&`v>a+=1wTK<>?rIgso>SE6CuWr21M^2Y>*aBF+JVZ703e~h=A
zq#RP_=VDSuvCS<lH@De*kXvx`S^R?n0)u`01KmS}Hiwb$MtPxC(8t7n4lCx2k`zS>
zQ&0)V>Bg<l5mON}aX7o9{5?^DC~x(^UxSFFkE6VNJ<;OMK3=4LWLOtsq=%#+QsCFK
zaap3he0}^;{vq6&X#YTrE5;cWi00M^3Uv4RnNgAj$@J@c+y2-0_OEb~d`QHvZ;0L0
z-^Uf>iRRV`!nmMaxJ`TmF<zKZR3OI3n?!`vjtVL8*UF@{>>8edXn*gYPh$D=Tiaa!
zzn+YGb3c$qJtK;mk9rn0`G5K+0ssI200000000000000000000000000000000000
z00000000000000000000000000000000000000000000000000000000002^{|W=C
zATjzF#yb%0?}c{3paRjPRHRJAzHUCwD3Tqd0#qo^%|+gukNf-h1X6EqK^j#}6g3^y
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
R2?z^}8Kf6XCY#Qv{x?Y-*|q=x
index c6c8637200d95393f6fcb51b67fbbfc9cf49259e..c7a408b7a6cbc0d3e2a77fbdbf4abaffb578aa8d
GIT binary patch
literal 2448
zc$`&~c{~%28^>qn*krD`GGgv@Z0=Hy95LjIh=j@2#Me!QCPy;n%vFw%)D(?~BytRE
zj?#o84LL?;jtSp-y?(#n_xJtdc|EV^{XEZ~?++3OP6KjqB5`1QZt&qG^Q29F4jztD
z9M~9y0~`FtdPp26^`8}YDUP%KH);iP0DiZI|9e8hxgq}<4sdWIp&;Jcnn;H18r}v7
z<ORgzI8(a9Uav;F;oZB)7bZNm;&j@`od9LWyHR4qQ7hJgA4^-!TArw33wh{OjlgN2
z8HPo%y-~~Bo2#t%<j!A(p~t^xag`Eh3n>=5E?Q6(3#+Ed#*r6Z#j4=+$etkFiwGyW
zpu09WRfD?YS@G11wxwY=+4O$IL!~7;*03Af+0N2Kc8ROJND<e(aplJ}IuE9j6hRQ;
zR6>&c9S{?9mk2DxxaE3|pstIEV!%n(@^g<H-V}gk*q5NgtuM}m?u2w`)tO?-p{}!*
zZsqg&eThMaC8~L@^flWm7q48wn-4Tyfn*~~Z*7Ce{9}$mLpCynGii`&8$DU)UV5?M
z{YL32W&<AyUT<_kqeMVr6c~4gSb9?TxvhyLPIUWtf)Q}kkeT&zS+i5O6b?0I&DpDD
zAbO;jF|_k2Hnr-*_Xf53Mz6`lZ^^rxQw-_9nr^{!Lz`ex9bj|!Cv5y!U6d`SXsZhz
z8@J&>$_j=3urFvrB5b}aMus&}$sWQ*WHChoEp5ITy?yBfsu8gBNEpjj800^XmUp_8
zVP+{@F@n!@ubauS>yPx1r@UIy^vkj_dz-quNJmrdxY^Wv7yTIgbwy9=j6f!Mjqz@*
z#-v4=5Wn=MPlA@YH!^-Z-!5l)=Jq&GRcuG{?orfjs<nNEPGQdYE^Ie(J+0qF!&)Ts
znsmH>KhAQN$^2xY_3EqSP<64NXdbbo=G_rqMgP=iA`HLR6QPy?pjD3m4ExhG<-u8l
z&F0M_=>E~S0zHum;iXVXV67|0^N?O6cfaJ2f#*dYRv;{-r~AwO*$TcV^#gRZ($IU)
ziuozwH=DJ;ZFURf=RtDyl7}Pd^xU@IY#Jq6`iS+8$d~f8IO6F`D*-9p?7|h>u;?p7
zD}K-#_L7<Gb?+!k_4`|%e&&~?dBV?{O$b|Bj_`&QcY*N<9N}2=2x0@x=xv!D6MWs{
z_)QcT$o4!2xO{>@mOC)3IZq7anXL+wvoA5l#^{|xP_>P%>q0W3A7I;!B2(jYL6lbN
z4fKcEV0V#0LLwU@(b8~GDx3nF&?D{3li1FOr@b=jK}BZig+D_qiMkUTs&SQ4ni8qy
zn==Ylq&e7j;VfI$dtOQC_9HG6SEV{clrLVu(NxMnyFhfHY~VAoOlJKe0?kuvxO2@q
z+oyU2nTT-M4SKjLK2z0vn8(Sk1Bv7E{+I1sjvySD-EVCDyDYf*{;M&B16Ya!F_1XW
z!oTe1{A0IQa+W^lc4EsPyFoZm?;GIiM}|>*&vH6gb%D1V{_|w#K4uS=W+-NHm>PP7
zEGs`g%&#F?*HfbCaVUQMTtw}DTkzrzU@?%B_9VEE!=t<7VY0mXlpNzk2PC<LxrG$-
z!;<PxQR>7IGDYRi5(_G;AgyX)eYD`6v5&m&R5|8PAg|4lUyN?I6-UGK$FkwDG4xi;
zOV96J3WbPq8&|(a+6`;Orwb&NPj%6sjVb9{>VT4Ol`iS32&ciAuL2>FS}&sw70T5W
z_ba?=R4$BiD9I`Q6(C*buori<-oe==`6NMcu^ofcX!3i<XicczYCJC0Q+7NPT06}r
zA^qI`ag-Gy-&9uLaUuO1Giy%FPzb*8*7guuz0vBClX=)som#z$`po)xUte|Y@LJ^A
zQ74~$=Z97rD>LqU3TgE!%2q)n`W+#SK-S}pXQVoySGWgdmu_~NtyJM$X7VJgmk@la
z_>M0~st12X&mpO;o@u)?G8|AObCmFumd32~jp8QANMcw~*6n~5VS@iwb!rqx?Czq=
z``(n|=Xq1Kw{?MQgv3v8^T2IWsP{KtLnQ~Su#qXeeHi0UCn~GliMbO8#6vKrlQcQj
z%O4}pFSl{5A4*S5@-`I9u2$yv+U4jP)t-nkum)~^6VabdfbOx>LY5|<J?5|7*JgRX
zxOVPVSVUicg}jkb{`a0(N$fA=l=WhW_LhCln_j%>{2eh=O=jhOE?X`y^_~`2^{SEL
zRpAYzf@R;TVKKFM0l~`HpG61m7ccem>5k~$?v|(@EV$*Qn%^A(gt}(RC9GMx(dE$G
z5RJKs>^uQi6}{nOgvXH6IVZQ~f64e+UA_h}V^ei0Cco~>m#1Bj<3?XjW7S#+xv%@`
zi$+@OvF@$nqkY>~DY^zaH@&^qnN34F6G`cJ2k=Wj#OECH*ScPA)7S4MHYci8*riO|
zg?Pe3_IXd)>6bjE$!3mY7F(T=y2B|>nU21xs@bq*NFP+$?!lvH1E(?!>X&*Drp|gb
zS63$oK19O{X`5;74S<#ChE$L^$pkEJFy${G)EwTV9HY~re9)B;`t=Rf<;m9EU!JXy
z2Tl5^SLzOb%w|4sWT^r%LU-HteUeN*`ol5#pUvqgV=v_GFQX1a5AEY1qDsLFI-Rpt
zuGh6e1jWg$OB7<~G(Glduf=`oq0{gfXpA}kuopgoI_$7{Hd^r3u4Y`-9@$-Sc%}fS
zWb_ib?I}N!2QyAJY|9C7TT(T@{^}c}^ug48y3L>}8c*0DkCT$JbW+d?jAOSf+D3S#
z6yEo8D%$majV%FfWRzoWxQz0(<_XSF&-myJAu2Drt%tpHP<=X0?3-jLWt5(Ub34U9
zVlW3yYDw_DSvzH#w4vrHz6drK*}%kfA~x_Qt#4=9Y3?&~kjD}JvVG>occ<o$8?Uil
zGxo=Vikw?6bI*R@HT5Z5LAooLZ9o6;Ec;cTF*e9IDYu$@t9@+0tG})Hop+qF%<+nL
zEJ)lFzM`Hi8>O}~<3ye0-7C4rW2=xZ3V$sLxp>GYu|?aS=hdBbX9VQolL8r!Y$mLV
zT)Z^~cc@twn7%Qt41}B8X5Hv&aHh(=`@knYmp7t>ch@|<qS`hZnQ-JswMZDG3uTU!
z`(HYOVJMi4Fdz^R1i%3N0RDh5z!g9!APg!0&xt5X3<Mk1lR$S?26K^zL|~+m3n=h}
zw>%PsltY5KxzwPXz=K=>5Uiolz{YuVgnbHou6<O-K1!7N>H>&&2sl>|US<&fCrJMW
DZU=~x
--- a/build/pgo/server-locations.txt
+++ b/build/pgo/server-locations.txt
@@ -173,16 +173,21 @@ http://tracking.example.com:80
 http://cryptomining.example.com:80
 http://fingerprinting.example.com:80
 http://not-tracking.example.com:80
 http://tracking.example.org:80
 http://another-tracking.example.net:80
 http://itisatracker.org:80
 https://itisatracker.org:443
 http://trackertest.org:80
+#
+# Used while testing TLS session ticket resumption for third-party trackers (bug 1500533)
+# (DO NOT USE THIS HOST IN OTHER TESTS!)
+#
+https://tlsresumptiontest.example.org:443
 
 https://malware.example.com:443
 https://unwanted.example.com:443
 https://tracking.example.com:443
 https://cryptomining.example.com:443
 https://fingerprinting.example.com:443
 https://not-tracking.example.com:443
 https://tracking.example.org:443
--- a/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
+++ b/devtools/client/aboutdebugging-new/src/actions/debug-targets.js
@@ -41,46 +41,56 @@ const {
   TEMPORARY_EXTENSION_RELOAD_FAILURE,
   TEMPORARY_EXTENSION_RELOAD_START,
   TEMPORARY_EXTENSION_RELOAD_SUCCESS,
   RUNTIMES,
 } = require("../constants");
 
 const Actions = require("./index");
 
+function isCachedActorNeeded(runtime, type, id) {
+  // Unique ids for workers were introduced in Firefox 68 (Bug 1539328). When debugging
+  // older browsers, the id falls back to the actor ID. Check if the target id is a worker
+  // actorID (which means getActor() should return an actor with id).
+  // Can be removed when Firefox 68 is in Release channel.
+  return type === DEBUG_TARGETS.WORKER &&
+         runtime.runtimeDetails.clientWrapper.client.getActor(id);
+}
+
 function getTabForUrl(url) {
   for (const navigator of Services.wm.getEnumerator("navigator:browser")) {
     for (const browser of navigator.gBrowser.browsers) {
       if (browser.contentWindow && browser.contentWindow.location.href === url) {
         return navigator.gBrowser.getTabForBrowser(browser);
       }
     }
   }
 
   return null;
 }
 
 function inspectDebugTarget(type, id) {
   return async (dispatch, getState) => {
     const runtime = getCurrentRuntime(getState().runtimes);
-    const remoteId = remoteClientManager.getRemoteId(runtime.id, runtime.type);
+    id = encodeURIComponent(id);
 
     let url;
-    if (runtime.id === RUNTIMES.THIS_FIREFOX && type !== DEBUG_TARGETS.WORKER) {
+    if (runtime.id === RUNTIMES.THIS_FIREFOX && !isCachedActorNeeded(runtime, type, id)) {
       // Even when debugging on This Firefox we need to re-use the client since the worker
       // actor is cached in the client instance. Instead we should pass an id that does
       // not depend on the client (such as the worker url). This will be fixed in
       // Bug 1539328.
       // Once the target is destroyed after closing the toolbox, the front will be gone
       // and can no longer be used. When debugging This Firefox, workers are regularly
       // updated so this is not an issue. On remote runtimes however, trying to inspect a
       // worker a second time after closing the corresponding about:devtools-toolbox tab
       // will fail. See Bug 1534201.
       url = `about:devtools-toolbox?type=${type}&id=${id}`;
     } else {
+      const remoteId = remoteClientManager.getRemoteId(runtime.id, runtime.type);
       url = `about:devtools-toolbox?type=${type}&id=${id}&remoteId=${remoteId}`;
     }
 
     const existingTab = getTabForUrl(url);
     if (existingTab) {
       const navigator = existingTab.ownerGlobal;
       navigator.gBrowser.selectedTab = existingTab;
       navigator.focus();
--- a/devtools/client/aboutdebugging-new/src/actions/runtimes.js
+++ b/devtools/client/aboutdebugging-new/src/actions/runtimes.js
@@ -1,14 +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 Services = require("Services");
+
 const Actions = require("./index");
 
 const {
   getAllRuntimes,
   getCurrentRuntime,
   findRuntimeById,
 } = require("../modules/runtimes-state-helper");
 
@@ -18,16 +20,17 @@ const {
   isExtensionDebugSettingNeeded,
   isSupportedDebugTargetPane,
 } = require("../modules/debug-target-support");
 
 const { remoteClientManager } =
   require("devtools/client/shared/remote-debugging/remote-client-manager");
 
 const {
+  CONNECT_RUNTIME_CANCEL,
   CONNECT_RUNTIME_FAILURE,
   CONNECT_RUNTIME_NOT_RESPONDING,
   CONNECT_RUNTIME_START,
   CONNECT_RUNTIME_SUCCESS,
   DEBUG_TARGET_PANE,
   DISCONNECT_RUNTIME_FAILURE,
   DISCONNECT_RUNTIME_START,
   DISCONNECT_RUNTIME_SUCCESS,
@@ -49,16 +52,17 @@ const {
   UPDATE_RUNTIME_MULTIE10S_START,
   UPDATE_RUNTIME_MULTIE10S_SUCCESS,
   WATCH_RUNTIME_FAILURE,
   WATCH_RUNTIME_START,
   WATCH_RUNTIME_SUCCESS,
 } = require("../constants");
 
 const CONNECTION_TIMING_OUT_DELAY = 3000;
+const CONNECTION_CANCEL_DELAY = 13000;
 
 async function getRuntimeIcon(channel) {
   return (channel === "release" || channel === "beta" || channel === "aurora")
     ? `chrome://devtools/skin/images/aboutdebugging-firefox-${ channel }.svg`
     : "chrome://devtools/skin/images/aboutdebugging-firefox-nightly.svg";
 }
 
 function onRemoteDebuggerClientClosed() {
@@ -68,22 +72,39 @@ function onRemoteDebuggerClientClosed() 
 
 function onMultiE10sUpdated() {
   window.AboutDebugging.store.dispatch(updateMultiE10s());
 }
 
 function connectRuntime(id) {
   return async (dispatch, getState) => {
     dispatch({ type: CONNECT_RUNTIME_START, id });
+
+    // The preferences test-connection-timing-out-delay and test-connection-cancel-delay
+    // don't have a default value but will be overridden during our tests.
+    const connectionTimingOutDelay = Services.prefs.getIntPref(
+      "devtools.aboutdebugging.test-connection-timing-out-delay",
+      CONNECTION_TIMING_OUT_DELAY);
+    const connectionCancelDelay = Services.prefs.getIntPref(
+      "devtools.aboutdebugging.test-connection-cancel-delay", CONNECTION_CANCEL_DELAY);
+
     const connectionNotRespondingTimer = setTimeout(() => {
       // If connecting to the runtime takes time over CONNECTION_TIMING_OUT_DELAY,
       // we assume the connection prompt is showing on the runtime, show a dialog
       // to let user know that.
       dispatch({ type: CONNECT_RUNTIME_NOT_RESPONDING, id });
-    }, CONNECTION_TIMING_OUT_DELAY);
+    }, connectionTimingOutDelay);
+    const connectionCancelTimer = setTimeout(() => {
+      // Connect button of the runtime will be disabled during connection, but the status
+      // continues till the connection was either succeed or failed. This may have a
+      // possibility that the disabling continues unless page reloading, user will not be
+      // able to click again. To avoid this, revert the connect button status after
+      // CONNECTION_CANCEL_DELAY ms.
+      dispatch({ type: CONNECT_RUNTIME_CANCEL, id });
+    }, connectionCancelDelay);
 
     try {
       const runtime = findRuntimeById(id, getState().runtimes);
       const clientWrapper = await createClientForRuntime(runtime);
 
       const deviceDescription = await clientWrapper.getDeviceDescription();
       const compatibilityReport = await clientWrapper.checkVersionCompatibility();
       const icon = await getRuntimeIcon(deviceDescription.channel);
@@ -143,27 +164,29 @@ function connectRuntime(id) {
           runtimeDetails,
           type: runtime.type,
         },
       });
     } catch (e) {
       dispatch({ type: CONNECT_RUNTIME_FAILURE, id, error: e });
     } finally {
       clearTimeout(connectionNotRespondingTimer);
+      clearTimeout(connectionCancelTimer);
     }
   };
 }
 
 function createThisFirefoxRuntime() {
   return (dispatch, getState) => {
     const thisFirefoxRuntime = {
       id: RUNTIMES.THIS_FIREFOX,
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
+      isConnectionTimeout: false,
       isUnavailable: false,
       isUnplugged: false,
       name: l10n.getString("about-debugging-this-firefox-runtime-name"),
       type: RUNTIMES.THIS_FIREFOX,
     };
     dispatch({ type: THIS_FIREFOX_RUNTIME_CREATED, runtime: thisFirefoxRuntime });
   };
 }
@@ -316,18 +339,20 @@ function updateNetworkRuntimes(locations
     return {
       id: location,
       extra: {
         connectionParameters: { host, port: parseInt(port, 10) },
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
+      isConnectionTimeout: false,
       isUnavailable: false,
       isUnplugged: false,
+      isUnknown: false,
       name: location,
       type: RUNTIMES.NETWORK,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.NETWORK);
 }
 
 function updateUSBRuntimes(adbRuntimes) {
@@ -340,16 +365,17 @@ function updateUSBRuntimes(adbRuntimes) 
       id: adbRuntime.id,
       extra: {
         connectionParameters,
         deviceName: adbRuntime.deviceName,
       },
       isConnecting: false,
       isConnectionFailed: false,
       isConnectionNotResponding: false,
+      isConnectionTimeout: false,
       isUnavailable: adbRuntime.isUnavailable,
       isUnplugged: adbRuntime.isUnplugged,
       name: adbRuntime.shortName,
       type: RUNTIMES.USB,
     };
   });
   return updateRemoteRuntimes(runtimes, RUNTIMES.USB);
 }
@@ -358,28 +384,28 @@ function updateUSBRuntimes(adbRuntimes) 
  * Check that a given runtime can still be found in the provided array of runtimes, and
  * that the connection of the associated DebuggerClient is still valid.
  * Note that this check is only valid for runtimes which match the type of the runtimes
  * in the array.
  */
 function _isRuntimeValid(runtime, runtimes) {
   const isRuntimeAvailable = runtimes.some(r => r.id === runtime.id);
   const isConnectionValid = runtime.runtimeDetails &&
-    !runtime.runtimeDetails.clientWrapper.isClosed();
+                            !runtime.runtimeDetails.clientWrapper.isClosed();
   return isRuntimeAvailable && isConnectionValid;
 }
 
 function updateRemoteRuntimes(runtimes, type) {
   return async (dispatch, getState) => {
     const currentRuntime = getCurrentRuntime(getState().runtimes);
 
     // Check if the updated remote runtimes should trigger a navigation out of the current
     // runtime page.
     if (currentRuntime && currentRuntime.type === type &&
-      !_isRuntimeValid(currentRuntime, runtimes)) {
+        !_isRuntimeValid(currentRuntime, runtimes)) {
       // Since current remote runtime is invalid, move to this firefox page.
       // This case is considered as followings and so on:
       // * Remove ADB addon
       // * (Physically) Disconnect USB runtime
       //
       // The reason we call selectPage before REMOTE_RUNTIMES_UPDATED is fired is below.
       // Current runtime can not be retrieved after REMOTE_RUNTIMES_UPDATED action, since
       // that updates runtime state. So, before that we fire selectPage action to execute
@@ -389,26 +415,29 @@ function updateRemoteRuntimes(runtimes, 
 
     // For existing runtimes, transfer all properties that are not available in the
     // runtime objects passed to this method:
     // - runtimeDetails (set by about:debugging after a successful connection)
     // - isConnecting (set by about:debugging during the connection)
     // - isConnectionFailed (set by about:debugging if connection was failed)
     // - isConnectionNotResponding
     //     (set by about:debugging if connection is taking too much time)
+    // - isConnectionTimeout (set by about:debugging if connection was timeout)
     runtimes.forEach(runtime => {
       const existingRuntime = findRuntimeById(runtime.id, getState().runtimes);
       const isConnectionValid = existingRuntime && existingRuntime.runtimeDetails &&
-        !existingRuntime.runtimeDetails.clientWrapper.isClosed();
+                                !existingRuntime.runtimeDetails.clientWrapper.isClosed();
       runtime.runtimeDetails = isConnectionValid ? existingRuntime.runtimeDetails : null;
       runtime.isConnecting = existingRuntime ? existingRuntime.isConnecting : false;
       runtime.isConnectionFailed =
         existingRuntime ? existingRuntime.isConnectionFailed : false;
       runtime.isConnectionNotResponding =
         existingRuntime ? existingRuntime.isConnectionNotResponding : false;
+      runtime.isConnectionTimeout =
+        existingRuntime ? existingRuntime.isConnectionTimeout : false;
     });
 
     const existingRuntimes = getAllRuntimes(getState().runtimes);
     for (const runtime of existingRuntimes) {
       // Runtime was connected before.
       const isConnected = runtime.runtimeDetails;
       // Runtime is of the same type as the updated runtimes array, so we should check it.
       const isSameType = runtime.type === type;
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/Sidebar.js
@@ -111,16 +111,17 @@ class Sidebar extends PureComponent {
         deviceName: runtime.extra.deviceName,
         dispatch,
         icon,
         key: keyId,
         isConnected: runtimeHasDetails,
         isConnecting: runtime.isConnecting,
         isConnectionFailed: runtime.isConnectionFailed,
         isConnectionNotResponding: runtime.isConnectionNotResponding,
+        isConnectionTimeout: runtime.isConnectionTimeout,
         isSelected,
         isUnavailable: runtime.isUnavailable,
         isUnplugged: runtime.isUnplugged,
         name,
         runtimeId: runtime.id,
       });
     });
   }
--- a/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarRuntimeItem.js
+++ b/devtools/client/aboutdebugging-new/src/components/sidebar/SidebarRuntimeItem.js
@@ -26,16 +26,17 @@ class SidebarRuntimeItem extends PureCom
       dispatch: PropTypes.func.isRequired,
       // Provided by wrapping the component with FluentReact.withLocalization.
       getString: PropTypes.func.isRequired,
       icon: PropTypes.string.isRequired,
       isConnected: PropTypes.bool.isRequired,
       isConnecting: PropTypes.bool.isRequired,
       isConnectionFailed: PropTypes.bool.isRequired,
       isConnectionNotResponding: PropTypes.bool.isRequired,
+      isConnectionTimeout: PropTypes.bool.isRequired,
       isSelected: PropTypes.bool.isRequired,
       isUnavailable: PropTypes.bool.isRequired,
       isUnplugged: PropTypes.bool.isRequired,
       name: PropTypes.string.isRequired,
       runtimeId: PropTypes.string.isRequired,
     };
   }
 
@@ -57,56 +58,26 @@ class SidebarRuntimeItem extends PureCom
             dispatch(Actions.connectRuntime(runtimeId));
           },
         },
         localizationId
       )
     );
   }
 
-  renderConnectionError() {
-    const { isConnectionFailed } = this.props;
-
-    if (!isConnectionFailed) {
+  renderMessage(flag, level, localizationId, className) {
+    if (!flag) {
       return null;
     }
 
-    const localizationId =
-      "about-debugging-sidebar-item-connect-button-connection-failed";
-
     return Message(
       {
-        level: MESSAGE_LEVEL.ERROR,
-        key: "connection-error",
-        className: "qa-connection-error",
-      },
-      Localized(
-        {
-          id: localizationId,
-        },
-        dom.p({ className: "word-wrap-anywhere" }, localizationId)
-      )
-    );
-  }
-
-  renderConnectionNotResponding() {
-    const { isConnectionNotResponding } = this.props;
-
-    if (!isConnectionNotResponding) {
-      return null;
-    }
-
-    const localizationId =
-      "about-debugging-sidebar-item-connect-button-connection-not-responding";
-
-    return Message(
-      {
-        level: MESSAGE_LEVEL.WARNING,
-        key: "connection-not-responding",
-        className: "qa-connection-not-responding",
+        level,
+        key: className,
+        className,
       },
       Localized(
         {
           id: localizationId,
         },
         dom.p({ className: "word-wrap-anywhere" }, localizationId)
       )
     );
@@ -171,16 +142,19 @@ class SidebarRuntimeItem extends PureCom
     );
   }
 
   render() {
     const {
       getString,
       icon,
       isConnected,
+      isConnectionFailed,
+      isConnectionTimeout,
+      isConnectionNotResponding,
       isSelected,
       isUnavailable,
       runtimeId,
     } = this.props;
 
     const connectionStatus = isConnected ?
       getString("aboutdebugging-sidebar-runtime-connection-status-connected") :
       getString("aboutdebugging-sidebar-runtime-connection-status-disconnected");
@@ -204,15 +178,31 @@ class SidebarRuntimeItem extends PureCom
               alt: connectionStatus,
               title: connectionStatus,
             }
           ),
           this.renderName(),
           !isUnavailable && !isConnected ? this.renderConnectButton() : null
         ),
       ),
-      this.renderConnectionError(),
-      this.renderConnectionNotResponding(),
+      this.renderMessage(
+        isConnectionFailed,
+        MESSAGE_LEVEL.ERROR,
+        "about-debugging-sidebar-item-connect-button-connection-failed",
+        "qa-connection-error"
+      ),
+      this.renderMessage(
+        isConnectionTimeout,
+        MESSAGE_LEVEL.ERROR,
+        "about-debugging-sidebar-item-connect-button-connection-timeout",
+        "qa-connection-timeout"
+      ),
+      this.renderMessage(
+        isConnectionNotResponding,
+        MESSAGE_LEVEL.WARNING,
+        "about-debugging-sidebar-item-connect-button-connection-not-responding",
+        "qa-connection-not-responding"
+      ),
     ];
   }
 }
 
 module.exports = FluentReact.withLocalization(SidebarRuntimeItem);
--- a/devtools/client/aboutdebugging-new/src/constants.js
+++ b/devtools/client/aboutdebugging-new/src/constants.js
@@ -10,16 +10,17 @@ const { CONNECTION_TYPES, DEBUG_TARGET_T
 const actionTypes = {
   ADB_ADDON_INSTALL_START: "ADB_ADDON_INSTALL_START",
   ADB_ADDON_INSTALL_SUCCESS: "ADB_ADDON_INSTALL_SUCCESS",
   ADB_ADDON_INSTALL_FAILURE: "ADB_ADDON_INSTALL_FAILURE",
   ADB_ADDON_UNINSTALL_START: "ADB_ADDON_UNINSTALL_START",
   ADB_ADDON_UNINSTALL_SUCCESS: "ADB_ADDON_UNINSTALL_SUCCESS",
   ADB_ADDON_UNINSTALL_FAILURE: "ADB_ADDON_UNINSTALL_FAILURE",
   ADB_ADDON_STATUS_UPDATED: "ADB_ADDON_STATUS_UPDATED",
+  CONNECT_RUNTIME_CANCEL: "CONNECT_RUNTIME_CANCEL",
   CONNECT_RUNTIME_FAILURE: "CONNECT_RUNTIME_FAILURE",
   CONNECT_RUNTIME_NOT_RESPONDING: "CONNECT_RUNTIME_NOT_RESPONDING",
   CONNECT_RUNTIME_START: "CONNECT_RUNTIME_START",
   CONNECT_RUNTIME_SUCCESS: "CONNECT_RUNTIME_SUCCESS",
   DEBUG_TARGET_COLLAPSIBILITY_UPDATED: "DEBUG_TARGET_COLLAPSIBILITY_UPDATED",
   DISCONNECT_RUNTIME_FAILURE: "DISCONNECT_RUNTIME_FAILURE",
   DISCONNECT_RUNTIME_START: "DISCONNECT_RUNTIME_START",
   DISCONNECT_RUNTIME_SUCCESS: "DISCONNECT_RUNTIME_SUCCESS",
--- a/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js
+++ b/devtools/client/aboutdebugging-new/src/middleware/worker-component-data.js
@@ -45,28 +45,23 @@ function getServiceWorkerStatus(worker) 
 
 function toComponentData(workers, isServiceWorker) {
   return workers.map(worker => {
     // Here `worker` is the worker object created by RootFront.listAllWorkers
     const type = DEBUG_TARGETS.WORKER;
     const icon = "chrome://devtools/skin/images/debugging-workers.svg";
     let { fetch } = worker;
     const {
+      id,
       name,
       registrationFront,
       scope,
       subscription,
-      workerTargetFront,
     } = worker;
 
-    // For registering service workers, workerTargetFront will not be available.
-    // The only valid identifier we can use at that point is the actorID for the
-    // service worker registration.
-    const id = workerTargetFront ? workerTargetFront.actorID : registrationFront.actorID;
-
     let pushServiceEndpoint = null;
     let status = null;
 
     if (isServiceWorker) {
       fetch = fetch ? SERVICE_WORKER_FETCH_STATES.LISTENING
                     : SERVICE_WORKER_FETCH_STATES.NOT_LISTENING;
       status = getServiceWorkerStatus(worker);
       pushServiceEndpoint = subscription ? subscription.endpoint : null;
--- a/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
+++ b/devtools/client/aboutdebugging-new/src/modules/client-wrapper.js
@@ -140,19 +140,17 @@ class ClientWrapper {
     return this.client.mainRoot.getAddon({ id });
   }
 
   async getMainProcess() {
     return this.client.mainRoot.getMainProcess();
   }
 
   async getServiceWorkerFront({ id }) {
-    const { serviceWorkers } = await this.listWorkers();
-    const workerFronts = serviceWorkers.map(sw => sw.workerTargetFront);
-    return workerFronts.find(front => front && front.actorID === id);
+    return this.client.mainRoot.getWorker(id);
   }
 
   async listWorkers() {
     const { other, service, shared } = await this.client.mainRoot.listAllWorkers();
 
     return {
       otherWorkers: other,
       serviceWorkers: service,
--- a/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
+++ b/devtools/client/aboutdebugging-new/src/reducers/runtimes-state.js
@@ -1,15 +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 {
+  CONNECT_RUNTIME_CANCEL,
   CONNECT_RUNTIME_FAILURE,
   CONNECT_RUNTIME_NOT_RESPONDING,
   CONNECT_RUNTIME_START,
   CONNECT_RUNTIME_SUCCESS,
   DISCONNECT_RUNTIME_SUCCESS,
   RUNTIMES,
   UPDATE_CONNECTION_PROMPT_SETTING_SUCCESS,
   UPDATE_EXTENSION_DEBUG_SETTING_SUCCESS,
@@ -76,43 +77,57 @@ function _updateRuntimeById(runtimeId, u
 function runtimesReducer(state = RuntimesState(), action) {
   switch (action.type) {
     case CONNECT_RUNTIME_START: {
       const { id } = action;
       const updatedState = {
         isConnecting: true,
         isConnectionFailed: false,
         isConnectionNotResponding: false,
+        isConnectionTimeout: false,
       };
       return _updateRuntimeById(id, updatedState, state);
     }
 
     case CONNECT_RUNTIME_NOT_RESPONDING: {
       const { id } = action;
       return _updateRuntimeById(id, { isConnectionNotResponding: true }, state);
     }
 
+    case CONNECT_RUNTIME_CANCEL: {
+      const { id } = action;
+      const updatedState = {
+        isConnecting: false,
+        isConnectionFailed: false,
+        isConnectionNotResponding: false,
+        isConnectionTimeout: true,
+      };
+      return _updateRuntimeById(id, updatedState, state);
+    }
+
     case CONNECT_RUNTIME_SUCCESS: {
       const { id, runtimeDetails, type } = action.runtime;
       remoteClientManager.setClient(id, type, runtimeDetails.clientWrapper.client);
       const updatedState = {
         isConnecting: false,
         isConnectionFailed: false,
         isConnectionNotResponding: false,
+        isConnectionTimeout: false,
         runtimeDetails,
       };
       return _updateRuntimeById(id, updatedState, state);
     }
 
     case CONNECT_RUNTIME_FAILURE: {
       const { id } = action;
       const updatedState = {
         isConnecting: false,
         isConnectionFailed: true,
         isConnectionNotResponding: false,
+        isConnectionTimeout: false,
       };
       return _updateRuntimeById(id, updatedState, state);
     }
 
     case DISCONNECT_RUNTIME_SUCCESS: {
       const { id, type } = action.runtime;
       remoteClientManager.removeClient(id, type);
       return _updateRuntimeById(id, { runtimeDetails: null }, state);
--- a/devtools/client/aboutdebugging-new/src/types/runtime.js
+++ b/devtools/client/aboutdebugging-new/src/types/runtime.js
@@ -121,16 +121,19 @@ const runtime = {
 
   // this flag will be true when the connection failed.
   isConnectionFailed: PropTypes.bool.isRequired,
 
   // will be true if connecting to runtime is taking time, will be false after connecting
   // or failing.
   isConnectionNotResponding: PropTypes.bool.isRequired,
 
+  // this flag will be true when the connection was timeout.
+  isConnectionTimeout: PropTypes.bool.isRequired,
+
   // unavailable runtimes are placeholders for devices where the runtime has not been
   // started yet. For instance an ADB device connected without a compatible runtime
   // running.
   isUnavailable: PropTypes.bool.isRequired,
 
   // unplugged runtimes are placeholders for devices that are no longer available. For
   // instance a USB device that was unplugged from the computer.
   isUnplugged: PropTypes.bool.isRequired,
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_sidebar_connection_state.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_sidebar_connection_state.js
@@ -3,21 +3,26 @@
 
 "use strict";
 
 const RUNTIME_ID = "test-runtime-id";
 const RUNTIME_NAME = "test runtime name";
 const RUNTIME_DEVICE_NAME = "test device name";
 const RUNTIME_SHORT_NAME = "test short name";
 
+const CONNECTION_TIMING_OUT_DELAY = 1000;
+const CONNECTION_CANCEL_DELAY = 2000;
+
 // Test following connection state tests.
 // * Connect button label and state will change during connecting.
 // * Show error message if connection failed.
 // * Show warninng if connection has been taken time.
 add_task(async function() {
+  await setupPreferences();
+
   const mocks = new Mocks();
 
   const { document, tab } = await openAboutDebugging();
 
   mocks.createUSBRuntime(RUNTIME_ID, {
     name: RUNTIME_NAME,
     deviceName: RUNTIME_DEVICE_NAME,
     shortName: RUNTIME_SHORT_NAME,
@@ -46,21 +51,21 @@ add_task(async function() {
   });
   mocks.runtimeClientFactoryMock.createClientForRuntime = async (runtime) => {
     await resumeConnectionPromise;
     return mocks._clients[runtime.type][runtime.id];
   };
 
   info("Click on the connect button and wait until it disappears");
   connectButton.click();
-  info("Check whether a warning of connection not responding displays after 3sec");
+  info("Check whether a warning of connection not responding displays");
   await waitUntil(() => document.querySelector(".qa-connection-not-responding"));
   ok(document.querySelector(".qa-connection-not-responding"),
      "A warning of connection not responding displays");
-  ok(connectButton.disabled, "State of the connect button displays");
+  ok(connectButton.disabled, "Connect button is disabled");
   ok(connectButton.textContent.startsWith("Connecting"),
      "Label of the connect button changes");
   ok(!document.querySelector(".qa-connection-error"), "Error message disappears");
 
   info("Unblock the connection and check the message and connect button disappear");
   resumeConnection();
   await waitUntil(() => !usbRuntimeSidebarItem.querySelector(".js-connect-button"));
   ok(!document.querySelector(".qa-connection-error"), "Error disappears");
@@ -68,8 +73,70 @@ add_task(async function() {
 
   info("Remove a USB runtime");
   mocks.removeUSBRuntime(RUNTIME_ID);
   mocks.emitUSBUpdate();
   await waitUntilUsbDeviceIsUnplugged(RUNTIME_DEVICE_NAME, document);
 
   await removeTab(tab);
 });
+
+// Test whether the status of all will be reverted after a certain period of time during
+// waiting connection.
+add_task(async function() {
+  await setupPreferences();
+
+  const mocks = new Mocks();
+
+  const { document, tab } = await openAboutDebugging();
+
+  mocks.createUSBRuntime(RUNTIME_ID, {
+    name: RUNTIME_NAME,
+    deviceName: RUNTIME_DEVICE_NAME,
+    shortName: RUNTIME_SHORT_NAME,
+  });
+  mocks.emitUSBUpdate();
+
+  info("Wait until the USB sidebar item appears");
+  await waitUntil(() => findSidebarItemByText(RUNTIME_DEVICE_NAME, document));
+  const usbRuntimeSidebarItem = findSidebarItemByText(RUNTIME_DEVICE_NAME, document);
+  const connectButton = usbRuntimeSidebarItem.querySelector(".js-connect-button");
+
+  let resumeConnection;
+  const resumeConnectionPromise = new Promise(r => {
+    resumeConnection = r;
+  });
+  mocks.runtimeClientFactoryMock.createClientForRuntime = async (runtime) => {
+    await resumeConnectionPromise;
+    return mocks._clients[runtime.type][runtime.id];
+  };
+
+  info("Click on the connect button and wait until it disappears");
+  connectButton.click();
+  await waitUntil(() => document.querySelector(".qa-connection-not-responding"));
+  info("Check whether the all status will be reverted");
+  await waitUntil(() => !document.querySelector(".qa-connection-not-responding"));
+  ok(document.querySelector(".qa-connection-timeout"),
+     "Connection timeout message displays");
+  ok(!connectButton.disabled, "Connect button is enabled");
+  is(connectButton.textContent, "Connect", "Label of the connect button reverted");
+  ok(!document.querySelector(".qa-connection-error"), "Error message disappears");
+
+  info("Check whether the timeout message disappears");
+  resumeConnection();
+  await waitUntil(() => !document.querySelector(".qa-connection-timeout"));
+
+  info("Remove a USB runtime");
+  mocks.removeUSBRuntime(RUNTIME_ID);
+  mocks.emitUSBUpdate();
+
+  info("Wait until the USB sidebar item disappears");
+  await waitUntilUsbDeviceIsUnplugged(RUNTIME_DEVICE_NAME, document);
+
+  await removeTab(tab);
+});
+
+async function setupPreferences() {
+  await pushPref("devtools.aboutdebugging.test-connection-timing-out-delay",
+                 CONNECTION_TIMING_OUT_DELAY);
+  await pushPref("devtools.aboutdebugging.test-connection-cancel-delay",
+                 CONNECTION_CANCEL_DELAY);
+}
--- a/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox_worker_inspection.js
+++ b/devtools/client/aboutdebugging-new/test/browser/browser_aboutdebugging_thisfirefox_worker_inspection.js
@@ -3,32 +3,33 @@
 
 "use strict";
 
 const { gDevToolsBrowser } = require("devtools/client/framework/devtools-browser");
 
 add_task(async function() {
   const thisFirefoxClient = createThisFirefoxClientMock();
   // Prepare a worker mock.
-  const testWorkerTargetFront = {
-    actorID: "test-worker-id",
-  };
   const testWorker = {
+    id: "test-worker-id",
     name: "Test Worker",
-    workerTargetFront: testWorkerTargetFront,
   };
   // Add a worker mock as other worker.
   thisFirefoxClient.listWorkers = () => ({
     otherWorkers: [testWorker],
     serviceWorkers: [],
     sharedWorkers: [],
   });
-  // Override getActor function of client which is used in inspect action for worker.
-  thisFirefoxClient.client.getActor = id => {
-    return id === testWorkerTargetFront.actorID ? testWorkerTargetFront : null;
+  // Override getActor of client and getWorker function of root of client
+  // which is used in inspect action for worker.
+  thisFirefoxClient.client.getActor = id => null;
+  thisFirefoxClient.client.mainRoot = {
+    getWorker: id => {
+      return id === testWorker.id ? testWorker : null;
+    },
   };
 
   const runtimeClientFactoryMock = createRuntimeClientFactoryMock();
   runtimeClientFactoryMock.createClientForRuntime = runtime => {
     const { RUNTIMES } = require("devtools/client/aboutdebugging-new/src/constants");
     if (runtime.id === RUNTIMES.THIS_FIREFOX) {
       return thisFirefoxClient;
     }
@@ -47,16 +48,16 @@ add_task(async function() {
   await selectThisFirefoxPage(document, window.AboutDebugging.store);
 
   info("Open a toolbox to debug the worker");
   const { devtoolsTab, devtoolsWindow } =
     await openAboutDevtoolsToolbox(document, tab, window, testWorker.name, false);
 
   info("Check whether the correct actor front will be opened in worker toolbox");
   const url = new window.URL(devtoolsWindow.location.href);
-  const workderID = url.searchParams.get("id");
-  is(workderID, testWorkerTargetFront.actorID,
+  const workerID = url.searchParams.get("id");
+  is(workerID, testWorker.id,
      "Correct actor front will be opened in worker toolbox");
 
   await removeTab(devtoolsTab);
   await waitUntil(() => !findDebugTargetByText("about:devtools-toolbox?", document));
   await removeTab(tab);
 });
--- a/devtools/client/aboutdebugging-new/test/browser/head.js
+++ b/devtools/client/aboutdebugging-new/test/browser/head.js
@@ -83,16 +83,22 @@ async function openAboutDevtoolsToolbox(
 
   info("Wait for about:devtools-toolbox tab will be selected");
   const devtoolsTab = tab.nextElementSibling;
   await waitUntil(() => gBrowser.selectedTab === devtoolsTab);
   const devtoolsBrowser = gBrowser.selectedBrowser;
   await waitUntil(() =>
     devtoolsBrowser.contentWindow.location.href.startsWith("about:devtools-toolbox?"));
 
+  if (!shouldWaitToolboxReady) {
+    // Wait for show error page.
+    await waitUntil(() =>
+      devtoolsBrowser.contentDocument.querySelector(".js-error-page"));
+  }
+
   return {
     devtoolsBrowser,
     devtoolsDocument: devtoolsBrowser.contentDocument,
     devtoolsTab,
     devtoolsWindow: devtoolsBrowser.contentWindow,
   };
 }
 
--- a/devtools/client/accessibility/reducers/audit.js
+++ b/devtools/client/accessibility/reducers/audit.js
@@ -4,16 +4,17 @@
 "use strict";
 
 const {
   AUDIT,
   AUDITING,
   FILTER_TOGGLE,
   FILTERS,
   RESET,
+  SELECT,
 } = require("../constants");
 
 /**
  * Initial state definition
  */
 function getInitialState() {
   return {
     filters: {
@@ -45,16 +46,17 @@ function audit(state = getInitialState()
         ...state,
         auditing,
       };
     case AUDIT:
       return {
         ...state,
         auditing: null,
       };
+    case SELECT:
     case RESET:
       return getInitialState();
     default:
       return state;
   }
 }
 
 exports.audit = audit;
--- a/devtools/client/accessibility/test/browser/browser.ini
+++ b/devtools/client/accessibility/test/browser/browser.ini
@@ -19,13 +19,14 @@ skip-if = (os == 'win' && processor == '
 skip-if = (os == 'win' && processor == 'aarch64') # bug 1533534
 [browser_accessibility_panel_highlighter.js]
 [browser_accessibility_panel_highlighter_multi_tab.js]
 skip-if = (os == 'linux' && debug && bits == 64) # Bug 1511247
 [browser_accessibility_relation_navigation.js]
 [browser_accessibility_reload.js]
 [browser_accessibility_sidebar_checks.js]
 [browser_accessibility_sidebar.js]
+[browser_accessibility_tree_audit_reset.js]
 [browser_accessibility_tree_audit_toolbar.js]
 [browser_accessibility_tree_audit.js]
 [browser_accessibility_tree_contrast.js]
 [browser_accessibility_tree_nagivation.js]
 [browser_accessibility_tree.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/accessibility/test/browser/browser_accessibility_tree_audit_reset.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/* global toggleFilter, selectAccessibleForNode */
+
+const TEST_URI = `<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Accessibility Panel Test</title>
+  </head>
+  <body>
+    <h1 style="color:rgba(255,0,0,0.1); background-color:rgba(255,255,255,1);">
+      Top level header
+    </h1>
+    <h2 style="color:rgba(0,255,0,0.1); background-color:rgba(255,255,255,1);">
+      Second level header
+    </h2>
+  </body>
+</html>`;
+
+/**
+* Test data has the format of:
+* {
+*   desc     {String}    description for better logging
+*   setup    {Function}  An optional setup that needs to be performed before
+*                        the state of the tree and the sidebar can be checked.
+*   expected {JSON}      An expected states for the tree and the sidebar.
+* }
+*/
+const tests = [{
+  desc: "Check initial state.",
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`,
+    }],
+  },
+}, {
+  desc: "Run an audit from a11y panel toolbar by activating a filter.",
+  setup: async ({ doc }) => {
+    await toggleFilter(doc, 0);
+  },
+  expected: {
+    tree: [{
+      role: "text leaf",
+      name: `"Top level header "contrast`,
+      badges: [ "contrast" ],
+    }, {
+      role: "text leaf",
+      name: `"Second level header "contrast`,
+      badges: [ "contrast" ],
+    }],
+  },
+}, {
+  desc: "Select an accessible object.",
+  setup: async (env) => {
+    await selectAccessibleForNode(env, "body");
+  },
+  expected: {
+    tree: [{
+      role: "document",
+      name: `"Accessibility Panel Test"`,
+    }, {
+      role: "heading",
+      name: `"Top level header"`,
+    }, {
+      role: "text leaf",
+      name: `"Top level header "contrast`,
+      badges: [ "contrast" ],
+    }, {
+      role: "heading",
+      name: `"Second level header"`,
+    }, {
+      role: "text leaf",
+      name: `"Second level header "contrast`,
+      badges: [ "contrast" ],
+    }],
+  },
+}];
+
+/**
+* Simple test that checks content of the Accessibility panel tree when the
+* audit is activated via the panel's toolbar.
+*/
+addA11yPanelTestsTask(tests, TEST_URI,
+  "Test Accessibility panel tree with contrast filter audit activation.");
--- a/devtools/client/accessibility/test/browser/head.js
+++ b/devtools/client/accessibility/test/browser/head.js
@@ -404,16 +404,33 @@ async function toggleFilter(doc, filterI
     ".devtools-toolbar .badge.toggle-button")[filterIndex];
   const expected = !filter.classList.contains("checked");
 
   EventUtils.synthesizeMouseAtCenter(filter, {}, win);
   await BrowserTestUtils.waitForCondition(() =>
     expected === filter.classList.contains("checked"), "Filter updated.");
 }
 
+async function findAccessibleFor({
+  toolbox: { walker: domWalker },
+  panel: { walker: a11yWalker },
+}, selector) {
+  const node = await domWalker.querySelector(domWalker.rootNode, selector);
+  return a11yWalker.getAccessibleFor(node);
+}
+
+async function selectAccessibleForNode(env, selector) {
+  const { panel, win } = env;
+  const front = await findAccessibleFor(env, selector);
+  const { EVENTS } = win;
+  const onSelected = win.once(EVENTS.NEW_ACCESSIBLE_FRONT_SELECTED);
+  panel.selectAccessible(front);
+  await onSelected;
+}
+
 /**
  * Iterate over setups/tests structure and test the state of the
  * accessibility panel.
  * @param  {JSON}   tests test data that has the format of:
  *                    {
  *                      desc     {String}    description for better logging
  *                      setup    {Function}  An optional setup that needs to be
  *                                           performed before the state of the
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/images/info.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 fill="context-fill" d="M8 1a7 7 0 1 0 7 7 7.008 7.008 0 0 0-7-7zm0 13a6 6 0 1 1 6-6 6.007 6.007 0 0 1-6 6zm0-7a1 1 0 0 0-1 1v3a1 1 0 1 0 2 0V8a1 1 0 0 0-1-1zm0-3.188A1.188 1.188 0 1 0 9.188 5 1.188 1.188 0 0 0 8 3.812z"></path>
+</svg>
--- a/devtools/client/debugger/images/moz.build
+++ b/devtools/client/debugger/images/moz.build
@@ -22,16 +22,17 @@ DevToolsModules(
     'command-chevron.svg',
     'disable-pausing.svg',
     'file-small.svg',
     'folder.svg',
     'globe-small.svg',
     'globe.svg',
     'help.svg',
     'home.svg',
+    'info.svg',
     'loader.svg',
     'next-circle.svg',
     'next.svg',
     'pane-collapse.svg',
     'pane-expand.svg',
     'pause.svg',
     'plus.svg',
     'prettyPrint.svg',
--- a/devtools/client/debugger/src/components/App.js
+++ b/devtools/client/debugger/src/components/App.js
@@ -53,16 +53,17 @@ import SplitBox from "devtools-splitter"
 import ProjectSearch from "./ProjectSearch";
 import PrimaryPanes from "./PrimaryPanes";
 import Editor from "./Editor";
 import SecondaryPanes from "./SecondaryPanes";
 import WelcomeBox from "./WelcomeBox";
 import EditorTabs from "./Editor/Tabs";
 import EditorFooter from "./Editor/Footer";
 import QuickOpenModal from "./QuickOpenModal";
+import WhyPaused from "./SecondaryPanes/WhyPaused";
 
 type Props = {
   selectedSource: Source,
   orientation: OrientationType,
   startPanelCollapsed: boolean,
   endPanelCollapsed: boolean,
   activeSearch: ActiveSearchType,
   quickOpenEnabled: boolean,
@@ -229,16 +230,19 @@ class App extends Component<Props, State
             startPanelSize={startPanelSize}
             endPanelSize={endPanelSize}
           />
           <Editor
             horizontal={horizontal}
             startPanelSize={startPanelSize}
             endPanelSize={endPanelSize}
           />
+          {this.props.endPanelCollapsed ? (
+            <WhyPaused horizontal={horizontal} />
+          ) : null}
           {!this.props.selectedSource ? (
             <WelcomeBox
               horizontal={horizontal}
               toggleShortcutsModal={() => this.toggleShortcutsModal()}
             />
           ) : null}
           <EditorFooter horizontal={horizontal} />
           <ProjectSearch />
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/Breakpoint.js
@@ -16,25 +16,25 @@ import { CloseButton } from "../../share
 
 import {
   getLocationWithoutColumn,
   getSelectedText,
   makeBreakpointId
 } from "../../../utils/breakpoint";
 import { getSelectedLocation } from "../../../utils/source-maps";
 import { features } from "../../../utils/prefs";
-import { getEditor } from "../../../utils/editor";
 
 import type {
   Breakpoint as BreakpointType,
   Frame,
   Source,
   SourceLocation,
   Context
 } from "../../../types";
+import type SourceEditor from "../../../utils/editor/source-editor";
 
 type FormattedFrame = Frame & {
   selectedLocation: SourceLocation
 };
 
 import {
   getBreakpointsList,
   getSelectedFrame,
@@ -45,16 +45,17 @@ import {
 
 type Props = {
   cx: Context,
   breakpoint: BreakpointType,
   breakpoints: BreakpointType[],
   selectedSource: Source,
   source: Source,
   frame: FormattedFrame,
+  editor: SourceEditor,
   enableBreakpoint: typeof actions.enableBreakpoint,
   removeBreakpoint: typeof actions.removeBreakpoint,
   removeBreakpoints: typeof actions.removeBreakpoints,
   removeAllBreakpoints: typeof actions.removeAllBreakpoints,
   disableBreakpoint: typeof actions.disableBreakpoint,
   setBreakpointOptions: typeof actions.setBreakpointOptions,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
   toggleBreakpoints: typeof actions.toggleBreakpoints,
@@ -132,32 +133,26 @@ class Breakpoint extends PureComponent<P
   getBreakpointText() {
     const { breakpoint, selectedSource } = this.props;
     const { condition, logValue } = breakpoint.options;
     return logValue || condition || getSelectedText(breakpoint, selectedSource);
   }
 
   highlightText = memoize(
     (text = "", editor) => {
-      if (!editor.CodeMirror) {
-        return { __html: text };
-      }
-
       const node = document.createElement("div");
       editor.CodeMirror.runMode(text, "application/javascript", node);
       return { __html: node.innerHTML };
     },
-    (text, editor) => `${text} - ${editor.CodeMirror ? "editor" : ""}`
+    text => text
   );
 
-  /* eslint-disable react/no-danger */
   render() {
-    const { breakpoint } = this.props;
+    const { breakpoint, editor } = this.props;
     const text = this.getBreakpointText();
-    const editor = getEditor();
     const labelId = `${breakpoint.id}-label`;
 
     return (
       <div
         className={classnames({
           breakpoint,
           paused: this.isCurrentlyPausedAtBreakpoint(),
           disabled: breakpoint.disabled,
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/index.js
@@ -11,43 +11,66 @@ import { connect } from "../../../utils/
 import ExceptionOption from "./ExceptionOption";
 
 import Breakpoint from "./Breakpoint";
 import BreakpointHeading from "./BreakpointHeading";
 
 import actions from "../../../actions";
 import { getDisplayPath } from "../../../utils/source";
 import { getSelectedLocation } from "../../../utils/source-maps";
+import { createHeadlessEditor } from "../../../utils/editor/create-editor";
 
 import {
   makeBreakpointId,
   sortSelectedBreakpoints
 } from "../../../utils/breakpoint";
 
 import {
   getSelectedSource,
   getBreakpointSources,
   getSkipPausing
 } from "../../../selectors";
 
 import type { Source } from "../../../types";
 import type { BreakpointSources } from "../../../selectors/breakpointSources";
+import type SourceEditor from "../../../utils/editor/source-editor";
 
 import "./Breakpoints.css";
 
 type Props = {
   breakpointSources: BreakpointSources,
   selectedSource: Source,
   skipPausing: boolean,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
   pauseOnExceptions: Function
 };
 
 class Breakpoints extends Component<Props> {
+  headlessEditor: ?SourceEditor;
+
+  componentWillUnmount() {
+    this.removeEditor();
+  }
+
+  getEditor(): SourceEditor {
+    if (!this.headlessEditor) {
+      this.headlessEditor = createHeadlessEditor();
+    }
+    return this.headlessEditor;
+  }
+
+  removeEditor() {
+    if (!this.headlessEditor) {
+      return;
+    }
+    this.headlessEditor.destroy();
+    this.headlessEditor = (null: any);
+  }
+
   renderExceptionsOptions() {
     const {
       breakpointSources,
       shouldPauseOnExceptions,
       shouldPauseOnCaughtExceptions,
       pauseOnExceptions
     } = this.props;
 
@@ -106,16 +129,17 @@ class Breakpoints extends Component<Prop
               path={path}
               key={source.url}
             />,
             ...sortedBreakpoints.map(breakpoint => (
               <Breakpoint
                 breakpoint={breakpoint}
                 source={source}
                 selectedSource={selectedSource}
+                editor={this.getEditor()}
                 key={makeBreakpointId(
                   getSelectedLocation(breakpoint, selectedSource)
                 )}
               />
             ))
           ];
         })}
       </div>
--- a/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/tests/Breakpoint.spec.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Breakpoints/tests/Breakpoint.spec.js
@@ -82,11 +82,18 @@ function generateDefaults(overrides = {}
   const source = createSourceObject("foo");
   const breakpoint = makeBreakpoint(breakpointOverrides);
   const selectedSource = createSourceObject("foo");
   return {
     source,
     breakpoint,
     selectedSource,
     frame: (null: any),
+    editor: {
+      CodeMirror: {
+        runMode: function() {
+          return "";
+        }
+      }
+    },
     ...overrides
   };
 }
deleted file mode 100644
--- a/devtools/client/debugger/src/components/SecondaryPanes/Frames/WhyPaused.css
+++ /dev/null
@@ -1,37 +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/>. */
-
-.why-paused {
-  display: flex;
-  background-color: var(--theme-body-background);
-  color: var(--theme-body-color);
-  opacity: 0.6;
-  font-size: 12px;
-  text-align: center;
-  font-style: italic;
-  font-weight: 300;
-  cursor: default;
-  min-height: 24px;
-  white-space: normal;
-}
-
-.why-paused > div {
-  margin: auto;
-}
-
-.theme-dark .secondary-panes .why-paused {
-  color: white;
-}
-
-.why-paused .message {
-  padding: 4px;
-  font-size: 10px;
-}
-
-.why-paused .message.warning {
-  font-size: 10px;
-  color: var(--theme-graphs-red);
-  font-weight: bold;
-  font-style: normal;
-}
deleted file mode 100644
--- a/devtools/client/debugger/src/components/SecondaryPanes/Frames/WhyPaused.js
+++ /dev/null
@@ -1,58 +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/>. */
-
-// @flow
-import React from "react";
-
-import { getPauseReason } from "../../../utils/pause";
-import type { Grip, ExceptionReason } from "../../../types";
-
-import "./WhyPaused.css";
-
-function renderExceptionSummary(exception: string | Grip) {
-  if (typeof exception === "string") {
-    return exception;
-  }
-
-  const preview = exception.preview;
-  if (!preview || !preview.name || !preview.message) {
-    return;
-  }
-
-  return `${preview.name}: ${preview.message}`;
-}
-
-function renderMessage(why: ExceptionReason) {
-  if (why.type == "exception" && why.exception) {
-    return (
-      <div className={"message warning"}>
-        {renderExceptionSummary(why.exception)}
-      </div>
-    );
-  }
-
-  if (typeof why.message == "string") {
-    return <div className={"message"}>{why.message}</div>;
-  }
-
-  return null;
-}
-
-export default function renderWhyPaused(why: Object) {
-  const reason = getPauseReason(why);
-
-  if (!reason) {
-    return null;
-  }
-
-  return (
-    <div className={"pane why-paused"}>
-      <div>
-        <div>{L10N.getStr(reason)}</div>
-        {renderMessage(why)}
-      </div>
-    </div>
-  );
-}
-renderWhyPaused.displayName = "whyPaused";
--- a/devtools/client/debugger/src/components/SecondaryPanes/Frames/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Frames/index.js
@@ -3,46 +3,42 @@
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
 import React, { Component } from "react";
 import { connect } from "../../../utils/connect";
 import PropTypes from "prop-types";
 
-import type { Frame, Why, ThreadContext } from "../../../types";
+import type { Frame, ThreadContext } from "../../../types";
 
 import FrameComponent from "./Frame";
 import Group from "./Group";
 
-import renderWhyPaused from "./WhyPaused";
-
 import actions from "../../../actions";
 import { collapseFrames, formatCopyName } from "../../../utils/pause/frames";
 import { copyToTheClipboard } from "../../../utils/clipboard";
 
 import {
   getFrameworkGroupingState,
   getSelectedFrame,
   getCallStackFrames,
-  getPauseReason,
   getCurrentThread,
   getThreadContext
 } from "../../../selectors";
 
 import "./Frames.css";
 
 const NUM_FRAMES_SHOWN = 7;
 
 type Props = {
   cx: ThreadContext,
   frames: Array<Frame>,
   frameworkGroupingOn: boolean,
   selectedFrame: Object,
-  why: Why,
   selectFrame: typeof actions.selectFrame,
   toggleBlackBox: Function,
   toggleFrameworkGrouping: Function,
   disableFrameTruncate: boolean,
   disableContextMenu: boolean,
   displayFullUrl: boolean,
   getFrameTitle?: string => string,
   selectable?: boolean
@@ -191,44 +187,42 @@ class Frames extends Component<Props, St
         <button className="show-more" onClick={this.toggleFramesDisplay}>
           {buttonMessage}
         </button>
       </div>
     );
   }
 
   render() {
-    const { frames, disableFrameTruncate, why } = this.props;
+    const { frames, disableFrameTruncate } = this.props;
 
     if (!frames) {
       return (
         <div className="pane frames">
           <div className="pane-info empty">
             {L10N.getStr("callStack.notPaused")}
           </div>
         </div>
       );
     }
 
     return (
       <div className="pane frames">
         {this.renderFrames(frames)}
-        {renderWhyPaused(why)}
         {disableFrameTruncate ? null : this.renderToggleButton(frames)}
       </div>
     );
   }
 }
 
 Frames.contextTypes = { l10n: PropTypes.object };
 
 const mapStateToProps = state => ({
   cx: getThreadContext(state),
   frames: getCallStackFrames(state),
-  why: getPauseReason(state, getCurrentThread(state)),
   frameworkGroupingOn: getFrameworkGroupingState(state),
   selectedFrame: getSelectedFrame(state, getCurrentThread(state))
 });
 
 export default connect(
   mapStateToProps,
   {
     selectFrame: actions.selectFrame,
--- a/devtools/client/debugger/src/components/SecondaryPanes/Frames/moz.build
+++ b/devtools/client/debugger/src/components/SecondaryPanes/Frames/moz.build
@@ -8,16 +8,14 @@ DIRS += [
 ]
 
 CompiledModules(
     'Frame.js',
     'FrameIndent.js',
     'FrameMenu.js',
     'Group.js',
     'index.js',
-    'WhyPaused.js',
 )
 
 DevToolsModules(
     'Frames.css',
     'Group.css',
-    'WhyPaused.css',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/src/components/SecondaryPanes/WhyPaused.css
@@ -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/>. */
+
+.why-paused {
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  border-bottom: 1px solid var(--theme-splitter-color);
+  background-color: hsl(54, 100%, 92%);
+  color: var(--theme-body-color);
+  font-size: 12px;
+  cursor: default;
+  min-height: 44px;
+  padding: 4px;
+  white-space: normal;
+  font-weight: bold;
+}
+
+.why-paused > div {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+}
+
+.why-paused .info.icon {
+  align-self: center;
+  padding: 0px 4px;
+}
+
+.why-paused .pause.reason {
+  display: flex;
+  flex-direction: column;
+  padding-left: 16px;
+  padding-right: 4px;
+}
+
+.theme-dark .secondary-panes .why-paused {
+  background-color: hsl(42, 37%, 19%);
+  color: hsl(43, 94%, 81%);
+}
+
+.why-paused .message {
+  font-style: italic;
+  font-weight: 100;
+}
+
+.why-paused .message.warning {
+  color: var(--theme-graphs-red);
+  font-weight: bold;
+  font-style: italic;
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/src/components/SecondaryPanes/WhyPaused.js
@@ -0,0 +1,110 @@
+/* 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/>. */
+
+// @flow
+import React, { PureComponent } from "react";
+import { connect } from "../../utils/connect";
+import AccessibleImage from "../shared/AccessibleImage";
+
+import { getPauseReason } from "../../utils/pause";
+import {
+  getCurrentThread,
+  getPaneCollapse,
+  getPauseReason as getWhy
+} from "../../selectors";
+import type { Grip, ExceptionReason } from "../../types";
+
+import "./WhyPaused.css";
+
+type Props = {
+  endPanelCollapsed: boolean,
+  delay: ?number,
+  why: ExceptionReason
+};
+
+type State = {
+  hideWhyPaused: string
+};
+
+class WhyPaused extends PureComponent<Props, State> {
+  constructor(props) {
+    super(props);
+    this.state = { hideWhyPaused: "" };
+  }
+
+  componentDidUpdate() {
+    const { delay } = this.props;
+
+    if (delay) {
+      setTimeout(() => {
+        this.setState({ hideWhyPaused: "" });
+      }, delay);
+    } else {
+      this.setState({ hideWhyPaused: "pane why-paused" });
+    }
+  }
+
+  renderExceptionSummary(exception: string | Grip) {
+    if (typeof exception === "string") {
+      return exception;
+    }
+
+    const preview = exception.preview;
+    if (!preview || !preview.name || !preview.message) {
+      return;
+    }
+
+    return `${preview.name}: ${preview.message}`;
+  }
+
+  renderMessage(why: ExceptionReason) {
+    if (why.type == "exception" && why.exception) {
+      return (
+        <div className={"message warning"}>
+          {this.renderExceptionSummary(why.exception)}
+        </div>
+      );
+    }
+
+    if (typeof why.message == "string") {
+      return <div className={"message"}>{why.message}</div>;
+    }
+
+    return null;
+  }
+
+  render() {
+    const { endPanelCollapsed, why } = this.props;
+    const reason = getPauseReason(why);
+
+    if (reason) {
+      if (!endPanelCollapsed) {
+        return (
+          <div className={"pane why-paused"}>
+            <div>
+              <div className="pause reason">
+                {L10N.getStr(reason)}
+                {this.renderMessage(why)}
+              </div>
+              <div className="info icon">
+                <AccessibleImage className="info" />
+              </div>
+            </div>
+          </div>
+        );
+      }
+    }
+    return <div className={this.state.hideWhyPaused} />;
+  }
+}
+
+const mapStateToProps = state => {
+  const thread = getCurrentThread(state);
+  return {
+    endPanelCollapsed: getPaneCollapse(state, "end"),
+    why: getWhy(state, thread)
+  };
+};
+
+export default connect(mapStateToProps)(WhyPaused);
--- a/devtools/client/debugger/src/components/SecondaryPanes/index.js
+++ b/devtools/client/debugger/src/components/SecondaryPanes/index.js
@@ -11,16 +11,17 @@ import { List } from "immutable";
 
 import actions from "../../actions";
 import {
   getTopFrame,
   getBreakpointsList,
   getBreakpointsDisabled,
   getExpressions,
   getIsWaitingOnBreak,
+  getPauseCommand,
   isMapScopesEnabled,
   getSelectedFrame,
   getShouldPauseOnExceptions,
   getShouldPauseOnCaughtExceptions,
   getWorkers,
   getCurrentThread,
   getThreadContext
 } from "../../selectors";
@@ -33,16 +34,17 @@ import Expressions from "./Expressions";
 import SplitBox from "devtools-splitter";
 import Frames from "./Frames";
 import Workers from "./Workers";
 import Accordion from "../shared/Accordion";
 import CommandBar from "./CommandBar";
 import UtilsBar from "./UtilsBar";
 import XHRBreakpoints from "./XHRBreakpoints";
 import EventListeners from "./EventListeners";
+import WhyPaused from "./WhyPaused";
 
 import Scopes from "./Scopes";
 
 import "./SecondaryPanes.css";
 
 import type { Expression, Frame, WorkerList, ThreadContext } from "../../types";
 
 type AccordionPaneItem = {
@@ -76,16 +78,17 @@ type Props = {
   cx: ThreadContext,
   expressions: List<Expression>,
   hasFrames: boolean,
   horizontal: boolean,
   breakpoints: Object,
   selectedFrame: ?Frame,
   breakpointsDisabled: boolean,
   isWaitingOnBreak: boolean,
+  renderWhyPauseDelay: number,
   mapScopesEnabled: boolean,
   shouldPauseOnExceptions: boolean,
   shouldPauseOnCaughtExceptions: boolean,
   workers: WorkerList,
   toggleShortcutsModal: () => void,
   toggleAllBreakpoints: typeof actions.toggleAllBreakpoints,
   toggleMapScopes: typeof actions.toggleMapScopes,
   evaluateExpressions: typeof actions.evaluateExpressions,
@@ -355,30 +358,31 @@ class SecondaryPanes extends Component<P
       onToggle: opened => {
         prefs.eventListenersVisible = opened;
       }
     };
   }
 
   getStartItems(): AccordionPaneItem[] {
     const items: AccordionPaneItem[] = [];
+    const { horizontal, hasFrames } = this.props;
 
-    if (this.props.horizontal) {
+    if (horizontal) {
       if (features.workers && this.props.workers.length > 0) {
         items.push(this.getWorkersItem());
       }
 
       items.push(this.getWatchItem());
     }
 
     items.push(this.getBreakpointsItem());
 
-    if (this.props.hasFrames) {
+    if (hasFrames) {
       items.push(this.getCallStackItem());
-      if (this.props.horizontal) {
+      if (horizontal) {
         items.push(this.getScopeItem());
       }
     }
 
     if (features.xhrBreakpoints) {
       items.push(this.getXHRItem());
     }
 
@@ -408,29 +412,43 @@ class SecondaryPanes extends Component<P
     return items;
   }
 
   getItems(): AccordionPaneItem[] {
     return [...this.getStartItems(), ...this.getEndItems()];
   }
 
   renderHorizontalLayout() {
-    return <Accordion items={this.getItems()} />;
+    const { renderWhyPauseDelay } = this.props;
+
+    return (
+      <div>
+        <WhyPaused delay={renderWhyPauseDelay} />
+        <Accordion items={this.getItems()} />
+      </div>
+    );
   }
 
   renderVerticalLayout() {
     return (
-      <SplitBox
-        initialSize="300px"
-        minSize={10}
-        maxSize="50%"
-        splitterSize={1}
-        startPanel={<Accordion items={this.getStartItems()} />}
-        endPanel={<Accordion items={this.getEndItems()} />}
-      />
+      <div>
+        <SplitBox
+          initialSize="300px"
+          minSize={10}
+          maxSize="50%"
+          splitterSize={1}
+          startPanel={
+            <div style={{ width: "inherit" }}>
+              <WhyPaused delay={this.props.renderWhyPauseDelay} />
+              <Accordion items={this.getStartItems()} />
+            </div>
+          }
+          endPanel={<Accordion items={this.getEndItems()} />}
+        />
+      </div>
     );
   }
 
   renderUtilsBar() {
     if (!features.shortcuts) {
       return;
     }
 
@@ -452,26 +470,39 @@ class SecondaryPanes extends Component<P
             : this.renderVerticalLayout()}
         </div>
         {this.renderUtilsBar()}
       </div>
     );
   }
 }
 
+// Checks if user is in debugging mode and adds a delay preventing
+// excessive vertical 'jumpiness'
+function getRenderWhyPauseDelay(state, thread) {
+  const inPauseCommand = !!getPauseCommand(state, thread);
+
+  if (!inPauseCommand) {
+    return 100;
+  }
+
+  return 0;
+}
+
 const mapStateToProps = state => {
   const thread = getCurrentThread(state);
 
   return {
     cx: getThreadContext(state),
     expressions: getExpressions(state),
     hasFrames: !!getTopFrame(state, thread),
     breakpoints: getBreakpointsList(state),
     breakpointsDisabled: getBreakpointsDisabled(state),
     isWaitingOnBreak: getIsWaitingOnBreak(state, thread),
+    renderWhyPauseDelay: getRenderWhyPauseDelay(state, thread),
     selectedFrame: getSelectedFrame(state, thread),
     mapScopesEnabled: isMapScopesEnabled(state),
     shouldPauseOnExceptions: getShouldPauseOnExceptions(state),
     shouldPauseOnCaughtExceptions: getShouldPauseOnCaughtExceptions(state),
     workers: getWorkers(state)
   };
 };
 
--- a/devtools/client/debugger/src/components/SecondaryPanes/moz.build
+++ b/devtools/client/debugger/src/components/SecondaryPanes/moz.build
@@ -10,22 +10,24 @@ DIRS += [
 
 CompiledModules(
     'CommandBar.js',
     'EventListeners.js',
     'Expressions.js',
     'index.js',
     'Scopes.js',
     'UtilsBar.js',
+    'WhyPaused.js',
     'Worker.js',
     'Workers.js',
     'XHRBreakpoints.js',
 )
 
 DevToolsModules(
     'CommandBar.css',
     'EventListeners.css',
     'Expressions.css',
     'Scopes.css',
     'SecondaryPanes.css',
+    'WhyPaused.css',
     'Workers.css',
     'XHRBreakpoints.css',
 )
--- a/devtools/client/debugger/src/components/shared/AccessibleImage.css
+++ b/devtools/client/debugger/src/components/shared/AccessibleImage.css
@@ -88,16 +88,20 @@ html[dir="rtl"] .img.arrow {
 .img.folder {
   mask-image: url(resource://devtools/client/debugger/images/folder.svg);
 }
 
 .img.home {
   mask-image: url(resource://devtools/client/debugger/images/home.svg);
 }
 
+.img.info {
+  mask-image: url(resource://devtools/client/debugger/images/info.svg);
+}
+
 .img.loader {
   mask-image: url(resource://devtools/client/debugger/images/loader.svg);
 }
 
 .img.more-tabs {
   mask-image: url(resource://devtools/client/debugger/images/command-chevron.svg);
 }
 
--- a/devtools/client/debugger/src/components/test/WhyPaused.spec.js
+++ b/devtools/client/debugger/src/components/test/WhyPaused.spec.js
@@ -1,45 +1,65 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
 
 // @flow
 
+import React from "react";
 import { shallow } from "enzyme";
-import renderWhyPaused from "../SecondaryPanes/Frames/WhyPaused.js";
+import WhyPaused from "../SecondaryPanes/WhyPaused.js";
+
+function render(why: Object, delay: ?number) {
+  const props = {
+    why: why,
+    delay: delay
+  };
+
+  // $FlowIgnore
+  const component = shallow(<WhyPaused.WrappedComponent {...props} />);
+
+  return { component, props };
+}
 
 describe("WhyPaused", () => {
   it("should pause reason with message", () => {
     const why = {
       type: "breakpoint",
       message: "bla is hit"
     };
-    const component = shallow(renderWhyPaused(why));
+    const { component } = render(why);
     expect(component).toMatchSnapshot();
   });
 
   it("should show pause reason with exception details", () => {
     const why = {
       type: "exception",
       exception: {
         class: "Error",
         preview: {
           name: "ReferenceError",
           message: "o is not defined"
         }
       }
     };
 
-    const component = shallow(renderWhyPaused(why));
+    const { component } = render(why);
     expect(component).toMatchSnapshot();
   });
 
   it("should show pause reason with exception string", () => {
     const why = {
       type: "exception",
       exception: "Not Available"
     };
 
-    const component = shallow(renderWhyPaused(why));
+    const { component } = render(why);
+    expect(component).toMatchSnapshot();
+  });
+
+  it("should show an empty div when there is no pause reason", () => {
+    const why = undefined;
+
+    const { component } = render(why);
     expect(component).toMatchSnapshot();
   });
 });
--- a/devtools/client/debugger/src/components/test/__snapshots__/WhyPaused.spec.js.snap
+++ b/devtools/client/debugger/src/components/test/__snapshots__/WhyPaused.spec.js.snap
@@ -1,52 +1,85 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`WhyPaused should pause reason with message 1`] = `
 <div
   className="pane why-paused"
 >
   <div>
-    <div>
+    <div
+      className="pause reason"
+    >
       Paused on breakpoint
+      <div
+        className="message"
+      >
+        bla is hit
+      </div>
     </div>
     <div
-      className="message"
+      className="info icon"
     >
-      bla is hit
+      <AccessibleImage
+        className="info"
+      />
     </div>
   </div>
 </div>
 `;
 
+exports[`WhyPaused should show an empty div when there is no pause reason 1`] = `
+<div
+  className=""
+/>
+`;
+
 exports[`WhyPaused should show pause reason with exception details 1`] = `
 <div
   className="pane why-paused"
 >
   <div>
-    <div>
+    <div
+      className="pause reason"
+    >
       Paused on exception
+      <div
+        className="message warning"
+      >
+        ReferenceError: o is not defined
+      </div>
     </div>
     <div
-      className="message warning"
+      className="info icon"
     >
-      ReferenceError: o is not defined
+      <AccessibleImage
+        className="info"
+      />
     </div>
   </div>
 </div>
 `;
 
 exports[`WhyPaused should show pause reason with exception string 1`] = `
 <div
   className="pane why-paused"
 >
   <div>
-    <div>
+    <div
+      className="pause reason"
+    >
       Paused on exception
+      <div
+        className="message warning"
+      >
+        Not Available
+      </div>
     </div>
     <div
-      className="message warning"
+      className="info icon"
     >
-      Not Available
+      <AccessibleImage
+        className="info"
+      />
     </div>
   </div>
 </div>
 `;
--- a/devtools/client/debugger/src/debugger.css
+++ b/devtools/client/debugger/src/debugger.css
@@ -36,16 +36,16 @@
 @import url("./components/ProjectSearch.css");
 @import url("./components/QuickOpenModal.css");
 @import url("./components/SecondaryPanes/Breakpoints/Breakpoints.css");
 @import url("./components/SecondaryPanes/CommandBar.css");
 @import url("./components/SecondaryPanes/EventListeners.css");
 @import url("./components/SecondaryPanes/Expressions.css");
 @import url("./components/SecondaryPanes/Frames/Frames.css");
 @import url("./components/SecondaryPanes/Frames/Group.css");
-@import url("./components/SecondaryPanes/Frames/WhyPaused.css");
 @import url("./components/SecondaryPanes/Scopes.css");
 @import url("./components/SecondaryPanes/SecondaryPanes.css");
+@import url("./components/SecondaryPanes/WhyPaused.css");
 @import url("./components/SecondaryPanes/Workers.css");
 @import url("./components/SecondaryPanes/XHRBreakpoints.css");
 @import url("./components/ShortcutsModal.css");
 @import url("./components/WelcomeBox.css");
 @import url("./utils/editor/source-editor.css");
--- a/devtools/client/debugger/src/utils/editor/create-editor.js
+++ b/devtools/client/debugger/src/utils/editor/create-editor.js
@@ -32,8 +32,14 @@ export function createEditor() {
       Esc: false,
       "Cmd-F": false,
       "Ctrl-F": false,
       "Cmd-G": false,
       "Ctrl-G": false
     }
   });
 }
+
+export function createHeadlessEditor() {
+  const editor = createEditor();
+  editor.appendToLocalElement(document.createElement("div"));
+  return editor;
+}
--- a/devtools/client/framework/sidebar.js
+++ b/devtools/client/framework/sidebar.js
@@ -260,18 +260,18 @@ ToolSidebar.prototype = {
     } else {
       this._tabbox.tabs.appendChild(tab);
     }
 
     // Add the tab to the allTabs menu if exists
     const allTabsItem = this._addItemToAllTabsMenu(id, tab, options);
 
     const onIFrameLoaded = (event) => {
-      const doc = event.target;
-      const win = doc.defaultView;
+      const win = iframe.contentWindow;
+      const doc = win.document;
       tab.setAttribute("label", doc.title);
 
       if (allTabsItem) {
         allTabsItem.setAttribute("label", doc.title);
       }
 
       iframe.removeEventListener("load", onIFrameLoaded, true);
       if ("setPanel" in win) {
--- a/devtools/client/framework/target-from-url.js
+++ b/devtools/client/framework/target-from-url.js
@@ -38,26 +38,42 @@ const { remoteClientManager } =
  *
  * @return A target object
  */
 exports.targetFromURL = async function targetFromURL(url) {
   const client = await clientFromURL(url);
   const params = url.searchParams;
 
   // Clients retrieved from the remote-client-manager are already connected.
-  if (!params.get("remoteId")) {
+  const isCachedClient = params.get("remoteId");
+  if (!isCachedClient) {
     // Connect any other client.
     await client.connect();
   }
 
+  const id = params.get("id");
   const type = params.get("type");
+  const chrome = params.has("chrome");
+
+  try {
+    return await _targetFromURL(client, id, type, chrome);
+  } catch (e) {
+    if (!isCachedClient) {
+      // If the client was not cached, then the client was created here. If the target
+      // creation failed, we should close the client.
+      await client.close();
+    }
+    throw e;
+  }
+};
+
+async function _targetFromURL(client, id, type, chrome) {
   if (!type) {
     throw new Error("targetFromURL, missing type parameter");
   }
-  let id = params.get("id");
 
   let front;
   if (type === "tab") {
     // Fetch target for a remote tab
     id = parseInt(id, 10);
     if (isNaN(id)) {
       throw new Error(`targetFromURL, wrong tab id '${id}', should be a number`);
     }
@@ -73,17 +89,17 @@ exports.targetFromURL = async function t
     const addonFront = await client.mainRoot.getAddon({ id });
 
     if (!addonFront) {
       throw new Error(`targetFromURL, extension with id '${id}' doesn't exist`);
     }
 
     front = await addonFront.connect();
   } else if (type === "worker") {
-    front = client.getActor(id);
+    front = await client.mainRoot.getWorker(id);
 
     if (!front) {
       throw new Error(`targetFromURL, worker with actor id '${id}' doesn't exist`);
     }
   } else if (type == "process") {
     // Fetch target for a remote chrome actor
     DebuggerServer.allowChromeProcess = true;
     try {
@@ -116,23 +132,22 @@ exports.targetFromURL = async function t
       throw ex;
     }
   } else {
     throw new Error(`targetFromURL, unsupported type '${type}' parameter`);
   }
 
   // Allows to spawn a chrome enabled target for any context
   // (handy to debug chrome stuff in a content process)
-  const chrome = params.has("chrome");
   if (chrome) {
     front.forceChrome();
   }
 
   return front;
-};
+}
 
 /**
  * Create a DebuggerClient for a given URL object having various query parameters:
  *
  * host:
  *    {String} The hostname or IP address to connect to.
  * port:
  *    {Number} The TCP port to connect to, to use with `host` argument.
--- a/devtools/client/framework/test/browser_toolbox_toggle.js
+++ b/devtools/client/framework/test/browser_toolbox_toggle.js
@@ -78,17 +78,17 @@ async function testToggleDetachedToolbox
 
   const onMainWindowFocus = once(window, "focus");
   window.focus();
   await onMainWindowFocus;
   ok(true, "Main window focused");
 
   info("Verify windowed toolbox is focused instead of closed when using " +
     "toggle key from the main window");
-  const toolboxWindow = toolbox.win.top;
+  const toolboxWindow = toolbox.topWindow;
   const 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");
 
--- a/devtools/client/framework/test/head.js
+++ b/devtools/client/framework/test/head.js
@@ -362,17 +362,17 @@ function getElementByToolId(toolbox, id)
 }
 
 function getElementByToolIdOrExtensionIdOrSelector(toolbox, idOrSelector) {
   const tabEl = getElementByToolId(toolbox, idOrSelector);
   return tabEl ? tabEl : toolbox.doc.querySelector(idOrSelector);
 }
 
 function getWindow(toolbox) {
-  return toolbox.win.parent;
+  return toolbox.topWindow;
 }
 
 async function resizeWindow(toolbox, width, height) {
   const hostWindow = toolbox.win.parent;
   const originalWidth = hostWindow.outerWidth;
   const originalHeight = hostWindow.outerHeight;
   const toWidth = width || originalWidth;
   const toHeight = height || originalHeight;
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -61,16 +61,18 @@ loader.lazyRequireGetter(this, "NetMonit
 loader.lazyRequireGetter(this, "sortPanelDefinitions",
   "devtools/client/framework/toolbox-tabs-order-manager", true);
 loader.lazyRequireGetter(this, "createEditContextMenu",
   "devtools/client/framework/toolbox-context-menu", true);
 loader.lazyRequireGetter(this, "remoteClientManager",
   "devtools/client/shared/remote-debugging/remote-client-manager.js", true);
 loader.lazyRequireGetter(this, "ResponsiveUIManager",
   "devtools/client/responsive.html/manager", true);
+loader.lazyRequireGetter(this, "DevToolsUtils",
+  "devtools/shared/DevToolsUtils");
 
 loader.lazyGetter(this, "domNodeConstants", () => {
   return require("devtools/shared/dom-node-constants");
 });
 
 loader.lazyGetter(this, "DEBUG_TARGET_TYPES", () => {
   return require("devtools/client/shared/remote-debugging/constants").DEBUG_TARGET_TYPES;
 });
@@ -382,17 +384,17 @@ Toolbox.prototype = {
   },
 
   /**
    * When the toolbox is loaded in a frame with type="content", win.parent will not return
    * the parent Chrome window. This getter should return the parent Chrome window
    * regardless of the frame type. See Bug 1539979.
    */
   get topWindow() {
-    return this.win.windowRoot.ownerGlobal;
+    return DevToolsUtils.getTopWindow(this.win);
   },
 
   /**
    * Shortcut to the document containing the toolbox UI
    */
   get doc() {
     return this.win.document;
   },
--- a/devtools/client/inspector/animation/components/CurrentTimeScrubber.js
+++ b/devtools/client/inspector/animation/components/CurrentTimeScrubber.js
@@ -1,14 +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 DevToolsUtils = require("devtools/shared/DevToolsUtils");
 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 ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
 const IndicationBar = createFactory(require("./IndicationBar"));
 
 class CurrentTimeScrubber extends PureComponent {
@@ -55,17 +56,17 @@ class CurrentTimeScrubber extends PureCo
     const position = currentTime / timeScale.getDuration();
     this.setState({ position });
   }
 
   onMouseDown(event) {
     event.stopPropagation();
     const thisEl = ReactDOM.findDOMNode(this);
     this.controllerArea = thisEl.getBoundingClientRect();
-    this.listenerTarget = thisEl.ownerGlobal.top;
+    this.listenerTarget = DevToolsUtils.getTopWindow(thisEl.ownerGlobal);
     this.listenerTarget.addEventListener("mousemove", this.onMouseMove);
     this.listenerTarget.addEventListener("mouseup", this.onMouseUp);
     this.decorationTarget = thisEl.closest(".animation-list-container");
     this.decorationTarget.classList.add("active-scrubber");
 
     this.updateAnimationsCurrentTime(event.pageX, true);
   }
 
--- a/devtools/client/inspector/flexbox/components/FlexContainer.js
+++ b/devtools/client/inspector/flexbox/components/FlexContainer.js
@@ -53,17 +53,17 @@ class FlexContainer extends PureComponen
         this.props.onSetFlexboxOverlayColor(previousColor);
       },
       onShow: () => {
         previousColor = this.props.color;
       },
     });
   }
 
-  componentWillUnMount() {
+  componentWillUnmount() {
     const tooltip = this.props.getSwatchColorPickerTooltip();
     tooltip.removeSwatch(this.swatchEl.current);
   }
 
   setFlexboxColor() {
     const color = this.colorValueEl.current.textContent;
     this.props.onSetFlexboxOverlayColor(color);
   }
--- a/devtools/client/locales/en-US/aboutdebugging.ftl
+++ b/devtools/client/locales/en-US/aboutdebugging.ftl
@@ -51,16 +51,19 @@ about-debugging-sidebar-item-connect-button-connecting = Connecting…
 
 # Text displayed in buttons found in sidebar items when the connection failed.
 about-debugging-sidebar-item-connect-button-connection-failed = Connection failed
 
 # Text displayed in connection warning on sidebar item of the runtime when connecting to
 # the runtime is taking too much time.
 about-debugging-sidebar-item-connect-button-connection-not-responding = Connection still pending, check for messages on the target browser
 
+# Text displayed as connection error in sidebar item when the connection has timed out.
+about-debugging-sidebar-item-connect-button-connection-timeout = Connection timed out
+
 # Temporary text displayed in sidebar items representing remote runtimes after
 # successfully connecting to them. Temporary UI, do not localize.
 about-debugging-sidebar-item-connected-label = Connected
 
 # Text displayed in sidebar items for remote devices where a compatible browser (eg
 # Firefox) has not been detected yet. Typically, Android phones connected via USB with
 # USB debugging enabled, but where Firefox is not started.
 about-debugging-sidebar-runtime-item-waiting-for-browser = Waiting for browser…
--- a/devtools/client/shared/components/test/mochitest/test_searchbox-with-autocomplete.html
+++ b/devtools/client/shared/components/test/mochitest/test_searchbox-with-autocomplete.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the searchbox and autocomplete-popup components
 -->
 <head>
   <meta charset="utf-8">
   <title>SearchBox component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 window.onload = async function () {
--- a/devtools/client/shared/components/test/mochitest/test_searchbox.html
+++ b/devtools/client/shared/components/test/mochitest/test_searchbox.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the searchbox component
 -->
 <head>
   <meta charset="utf-8">
   <title>SearchBox component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 window.onload = function () {
--- a/devtools/client/shared/components/test/mochitest/test_smart-trace-grouping.html
+++ b/devtools/client/shared/components/test/mochitest/test_smart-trace-grouping.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the rendering of a stack trace
 -->
 <head>
   <meta charset="utf-8">
   <title>StackTrace component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 window.onload = function() {
--- a/devtools/client/shared/components/test/mochitest/test_smart-trace-source-maps.html
+++ b/devtools/client/shared/components/test/mochitest/test_smart-trace-source-maps.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the rendering of a stack trace
 -->
 <head>
   <meta charset="utf-8">
   <title>StackTrace component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 window.onload = function() {
--- a/devtools/client/shared/components/test/mochitest/test_smart-trace.html
+++ b/devtools/client/shared/components/test/mochitest/test_smart-trace.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the rendering of a stack trace
 -->
 <head>
   <meta charset="utf-8">
   <title>StackTrace component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 window.onload = function() {
--- a/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html
+++ b/devtools/client/shared/components/test/mochitest/test_stack-trace-source-maps.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the rendering of a stack trace with source maps
 -->
 <head>
   <meta charset="utf-8">
   <title>StackTrace component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 window.onload = function () {
--- a/devtools/client/shared/components/test/mochitest/test_stack-trace.html
+++ b/devtools/client/shared/components/test/mochitest/test_stack-trace.html
@@ -5,17 +5,16 @@
 <html>
 <!--
 Test the rendering of a stack trace
 -->
 <head>
   <meta charset="utf-8">
   <title>StackTrace component test</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script src="head.js"></script>
 <script>
 "use strict";
 
 window.onload = function() {
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_location_scratchpad_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_location_scratchpad_link.js
@@ -45,11 +45,11 @@ add_task(async function() {
   });
 
   EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
   await onScratchpadSelected;
 
   is(toolbox.getCurrentPanel(), scratchpadPanel,
     "Clicking link switches to Scratchpad panel");
 
-  is(Services.ww.activeWindow, toolbox.win.parent,
+  is(Services.ww.activeWindow, toolbox.topWindow,
      "Scratchpad's toolbox is focused");
 });
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_stacktrace_location_scratchpad_link.js
@@ -50,10 +50,10 @@ add_task(async function() {
   const onScratchpadSelected = toolbox.once("scratchpad-selected");
 
   EventUtils.sendMouseEvent({ type: "mousedown" }, anchor);
   await onScratchpadSelected;
 
   is(toolbox.getCurrentPanel(), scratchpadPanel,
     "Clicking link in stacktrace switches to Scratchpad panel");
 
-  is(Services.ww.activeWindow, toolbox.win.parent, "Scratchpad's toolbox is focused");
+  is(Services.ww.activeWindow, toolbox.topWindow, "Scratchpad's toolbox is focused");
 });
--- a/devtools/server/actors/targets/worker.js
+++ b/devtools/server/actors/targets/worker.js
@@ -33,16 +33,17 @@ const WorkerTargetActor = protocol.Actor
     this._threadActor = null;
     this._transport = null;
   },
 
   form() {
     const form = {
       actor: this.actorID,
       consoleActor: this._consoleActor,
+      id: this._dbg.id,
       url: this._dbg.url,
       type: this._dbg.type,
     };
     if (this._dbg.type === Ci.nsIWorkerDebugger.TYPE_SERVICE) {
       const registration = this._getServiceWorkerRegistrationInfo();
       form.scope = registration.scope;
       const newestWorker = (registration.activeWorker ||
                           registration.waitingWorker ||
--- a/devtools/server/socket/tests/test_websocket-server.html
+++ b/devtools/server/socket/tests/test_websocket-server.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Mozilla Bug</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script>
 "use strict";
 
 window.onload = function() {
   const CC = Components.Constructor;
--- a/devtools/server/tests/mochitest/test_webextension-addon-debugging-connect.html
+++ b/devtools/server/tests/mochitest/test_webextension-addon-debugging-connect.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Bug 1302702 - Test connect to a webextension addon
 -->
 <head>
   <meta charset="utf-8">
   <title>Mozilla Bug</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
   <script src="webextension-helpers.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
 "use strict";
--- a/devtools/server/tests/mochitest/test_webextension-addon-debugging-reload.html
+++ b/devtools/server/tests/mochitest/test_webextension-addon-debugging-reload.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 Bug 1302702 - Test connect to a webextension addon
 -->
 <head>
   <meta charset="utf-8">
   <title>Mozilla Bug</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script src="webextension-helpers.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <pre id="test">
 <script type="application/javascript">
 "use strict";
 
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -807,8 +807,18 @@ function* makeDebuggeeIterator(object) {
     if (exports.getProperty(nextValue, "done")) {
       break;
     }
     yield exports.getProperty(nextValue, "value");
   }
 }
 
 exports.makeDebuggeeIterator = makeDebuggeeIterator;
+
+/**
+ * Shared helper to retrieve the topmost window. This can be used to retrieve the chrome
+ * window embedding the DevTools frame.
+ */
+function getTopWindow(win) {
+  return win.windowRoot ? win.windowRoot.ownerGlobal : win.top;
+}
+
+exports.getTopWindow = getTopWindow;
--- a/devtools/shared/fronts/root.js
+++ b/devtools/shared/fronts/root.js
@@ -90,26 +90,28 @@ class RootFront extends FrontClassWithSp
     registrations.forEach(front => {
       // All the information is simply mirrored from the registration front.
       // However since registering workers will fetch similar information from the worker
       // target front and will not have a service worker registration front, consumers
       // should not read meta data directly on the registration front instance.
       result.service.push({
         active: front.active,
         fetch: front.fetch,
+        id: front.id,
         lastUpdateTime: front.lastUpdateTime,
         name: front.url,
         registrationFront: front,
         scope: front.scope,
         url: front.url,
       });
     });
 
     workers.forEach(front => {
       const worker = {
+        id: front.id,
         name: front.url,
         url: front.url,
         workerTargetFront: front,
       };
       switch (front.type) {
         case Ci.nsIWorkerDebugger.TYPE_SERVICE:
           const registration = result.service.find(r => r.scope === front.scope);
           if (registration) {
@@ -245,16 +247,31 @@ class RootFront extends FrontClassWithSp
    */
   async getAddon({ id }) {
     const addons = await this.listAddons();
     const addonTargetFront = addons.find(addon => addon.id === id);
     return addonTargetFront;
   }
 
   /**
+   * Fetch the target front for a given worker.
+   * This is just an helper on top of `listAllWorkers` request.
+   *
+   * @param id
+   */
+  async getWorker(id) {
+    const { service, shared, other } = await this.listAllWorkers();
+    const worker = [...service, ...shared, ...other].find(w => w.id === id);
+    if (!worker) {
+      return null;
+    }
+    return worker.workerTargetFront || worker.registrationFront;
+  }
+
+  /**
    * Test request that returns the object passed as first argument.
    *
    * `echo` is special as all the property of the given object have to be passed
    * on the packet object. That's not something that can be achieve by requester helper.
    */
 
   echo(packet) {
     packet.type = "echo";
--- a/devtools/shared/fronts/targets/worker.js
+++ b/devtools/shared/fronts/targets/worker.js
@@ -19,16 +19,20 @@ class WorkerTargetFront extends
     // This event is similar to tabDetached and means that the worker is destroyed.
     // So that we should destroy the target in order to significate that the target
     // is no longer debuggable.
     this.once("worker-close", this.destroy.bind(this));
   }
 
   form(json) {
     this.actorID = json.actor;
+    // `id` was added in Firefox 68 to the worker target actor. Fallback to the actorID
+    // when debugging older clients.
+    // Fallback can be removed when Firefox 68 will be in the Release channel.
+    this.id = json.id || this.actorID;
 
     // Save the full form for Target class usage.
     // Do not use `form` name to avoid colliding with protocol.js's `form` method
     this.targetForm = json;
     this._url = json.url;
     this.type = json.type;
     this.scope = json.scope;
     this.fetch = json.fetch;
--- a/devtools/shared/fronts/worker/service-worker-registration.js
+++ b/devtools/shared/fronts/worker/service-worker-registration.js
@@ -12,16 +12,20 @@ class ServiceWorkerRegistrationFront ext
   get active() {
     return this._form.active;
   }
 
   get fetch() {
     return this._form.fetch;
   }
 
+  get id() {
+    return this.url;
+  }
+
   get lastUpdateTime() {
     return this._form.lastUpdateTime;
   }
 
   get scope() {
     return this._form.scope;
   }
 
--- a/devtools/shared/security/tests/chrome/test_websocket-transport.html
+++ b/devtools/shared/security/tests/chrome/test_websocket-transport.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test the WebSocket debugger transport</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script>
 "use strict";
 
 window.onload = function() {
   const {require} = ChromeUtils.import("resource://devtools/shared/Loader.jsm");
--- a/docshell/test/mochitest/test_bug580069.html
+++ b/docshell/test/mochitest/test_bug580069.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=580069
 -->
 <head>
   <title>Test for Bug 580069</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.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=580069">Mozilla Bug 580069</a>
 
 <script type="application/javascript">
 
 add_task(async function() {
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -462,19 +462,51 @@ Promise* Animation::GetFinished(ErrorRes
     return nullptr;
   }
   if (mFinishedIsResolved) {
     MaybeResolveFinishedPromise();
   }
   return mFinished;
 }
 
-void Animation::Cancel() {
-  CancelNoUpdate();
-  PostUpdate();
+// https://drafts.csswg.org/web-animations/#cancel-an-animation
+void Animation::Cancel(PostRestyleMode aPostRestyle) {
+  bool newlyIdle = false;
+
+  if (PlayState() != AnimationPlayState::Idle) {
+    newlyIdle = true;
+
+    ResetPendingTasks();
+
+    if (mFinished) {
+      mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    }
+    ResetFinishedPromise();
+
+    QueuePlaybackEvent(NS_LITERAL_STRING("cancel"),
+                       GetTimelineCurrentTimeAsTimeStamp());
+  }
+
+  StickyTimeDuration activeTime =
+      mEffect ? mEffect->GetComputedTiming().mActiveTime : StickyTimeDuration();
+
+  mHoldTime.SetNull();
+  mStartTime.SetNull();
+
+  // Allow our effect to remove itself from the its target element's EffectSet.
+  UpdateEffect(aPostRestyle);
+
+  if (mTimeline) {
+    mTimeline->RemoveAnimation(this);
+  }
+  MaybeQueueCancelEvent(activeTime);
+
+  if (newlyIdle && aPostRestyle == PostRestyleMode::IfNeeded) {
+    PostUpdate();
+  }
 }
 
 // https://drafts.csswg.org/web-animations/#finish-an-animation
 void Animation::Finish(ErrorResult& aRv) {
   double effectivePlaybackRate = CurrentOrPendingPlaybackRate();
 
   if (effectivePlaybackRate == 0 ||
       (effectivePlaybackRate > 0 && EffectEnd() == TimeDuration::Forever())) {
@@ -763,44 +795,16 @@ void Animation::SilentlySetCurrentTime(c
     mStartTime =
         StartTimeFromTimelineTime(mTimeline->GetCurrentTimeAsDuration().Value(),
                                   aSeekTime, mPlaybackRate);
   }
 
   mPreviousCurrentTime.SetNull();
 }
 
-// https://drafts.csswg.org/web-animations/#cancel-an-animation
-void Animation::CancelNoUpdate() {
-  if (PlayState() != AnimationPlayState::Idle) {
-    ResetPendingTasks();
-
-    if (mFinished) {
-      mFinished->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
-    }
-    ResetFinishedPromise();
-
-    QueuePlaybackEvent(NS_LITERAL_STRING("cancel"),
-                       GetTimelineCurrentTimeAsTimeStamp());
-  }
-
-  StickyTimeDuration activeTime =
-      mEffect ? mEffect->GetComputedTiming().mActiveTime : StickyTimeDuration();
-
-  mHoldTime.SetNull();
-  mStartTime.SetNull();
-
-  UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
-
-  if (mTimeline) {
-    mTimeline->RemoveAnimation(this);
-  }
-  MaybeQueueCancelEvent(activeTime);
-}
-
 bool Animation::ShouldBeSynchronizedWithMainThread(
     const nsCSSPropertyIDSet& aPropertySet, const nsIFrame* aFrame,
     AnimationPerformanceWarning::Type& aPerformanceWarning) const {
   // Only synchronize playing animations
   if (!IsPlaying()) {
     return false;
   }
 
@@ -1207,17 +1211,17 @@ void Animation::PauseAt(const TimeDurati
   }
 }
 
 void Animation::UpdateTiming(SeekFlag aSeekFlag,
                              SyncNotifyFlag aSyncNotifyFlag) {
   // We call UpdateFinishedState before UpdateEffect because the former
   // can change the current time, which is used by the latter.
   UpdateFinishedState(aSeekFlag, aSyncNotifyFlag);
-  UpdateEffect();
+  UpdateEffect(PostRestyleMode::IfNeeded);
 
   if (mTimeline) {
     mTimeline->NotifyAnimationUpdated(*this);
   }
 }
 
 // https://drafts.csswg.org/web-animations/#update-an-animations-finished-state
 void Animation::UpdateFinishedState(SeekFlag aSeekFlag,
@@ -1262,23 +1266,23 @@ void Animation::UpdateFinishedState(Seek
   } else if (!currentFinishedState && mFinishedIsResolved) {
     ResetFinishedPromise();
   }
   // We must recalculate the current time to take account of any mHoldTime
   // changes the code above made.
   mPreviousCurrentTime = GetCurrentTimeAsDuration();
 }
 
-void Animation::UpdateEffect() {
+void Animation::UpdateEffect(PostRestyleMode aPostRestyle) {
   if (mEffect) {
     UpdateRelevance();
 
     KeyframeEffect* keyframeEffect = mEffect->AsKeyframeEffect();
     if (keyframeEffect) {
-      keyframeEffect->NotifyAnimationTimingUpdated();
+      keyframeEffect->NotifyAnimationTimingUpdated(aPostRestyle);
     }
   }
 }
 
 void Animation::FlushUnanimatedStyle() const {
   if (Document* doc = GetRenderedDocument()) {
     doc->FlushPendingNotifications(
         ChangesToFlush(FlushType::Style, false /* flush animations */));
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -10,16 +10,17 @@
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EffectCompositor.h"  // For EffectCompositor::CascadeLevel
 #include "mozilla/LinkedList.h"
+#include "mozilla/PostRestyleMode.h"
 #include "mozilla/TimeStamp.h"             // for TimeStamp, TimeDuration
 #include "mozilla/dom/AnimationBinding.h"  // for AnimationPlayState
 #include "mozilla/dom/AnimationEffect.h"
 #include "mozilla/dom/AnimationTimeline.h"
 #include "mozilla/dom/Promise.h"
 #include "nsCSSPropertyID.h"
 #include "nsIGlobalObject.h"
 
@@ -121,18 +122,17 @@ class Animation : public DOMEventTargetH
   virtual bool PendingFromJS() const { return Pending(); }
 
   virtual Promise* GetReady(ErrorResult& aRv);
   Promise* GetFinished(ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(finish);
   IMPL_EVENT_HANDLER(cancel);
 
-  void Cancel();
-  virtual void CancelFromStyle() { CancelNoUpdate(); }
+  void Cancel(PostRestyleMode aPostRestyle = PostRestyleMode::IfNeeded);
 
   void Finish(ErrorResult& aRv);
 
   virtual void Play(ErrorResult& aRv, LimitBehavior aLimitBehavior);
   virtual void PlayFromJS(ErrorResult& aRv) {
     Play(aRv, LimitBehavior::AutoRewind);
   }
 
@@ -423,17 +423,17 @@ class Animation : public DOMEventTargetH
    * to a seek or regular playback.
    */
   enum class SeekFlag { NoSeek, DidSeek };
 
   enum class SyncNotifyFlag { Sync, Async };
 
   virtual void UpdateTiming(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag);
   void UpdateFinishedState(SeekFlag aSeekFlag, SyncNotifyFlag aSyncNotifyFlag);
-  void UpdateEffect();
+  void UpdateEffect(PostRestyleMode aPostRestyle);
   /**
    * Flush all pending styles other than throttled animation styles (e.g.
    * animations running on the compositor).
    */
   void FlushUnanimatedStyle() const;
   void PostUpdate();
   void ResetFinishedPromise();
   void MaybeResolveFinishedPromise();
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -149,32 +149,34 @@ void KeyframeEffect::NotifySpecifiedTimi
     if (mAnimation->IsRelevant()) {
       nsNodeUtils::AnimationChanged(mAnimation);
     }
 
     RequestRestyle(EffectCompositor::RestyleType::Layer);
   }
 }
 
-void KeyframeEffect::NotifyAnimationTimingUpdated() {
+void KeyframeEffect::NotifyAnimationTimingUpdated(
+    PostRestyleMode aPostRestyle) {
   UpdateTargetRegistration();
 
   // If the effect is not relevant it will be removed from the target
   // element's effect set. However, effects not in the effect set
   // will not be included in the set of candidate effects for running on
   // the compositor and hence they won't have their compositor status
   // updated. As a result, we need to make sure we clear their compositor
   // status here.
   bool isRelevant = mAnimation && mAnimation->IsRelevant();
   if (!isRelevant) {
     ResetIsRunningOnCompositor();
   }
 
   // Request restyle if necessary.
-  if (mAnimation && !mProperties.IsEmpty() && HasComputedTimingChanged()) {
+  if (aPostRestyle == PostRestyleMode::IfNeeded && mAnimation &&
+      !mProperties.IsEmpty() && HasComputedTimingChanged()) {
     EffectCompositor::RestyleType restyleType =
         CanThrottle() ? EffectCompositor::RestyleType::Throttled
                       : EffectCompositor::RestyleType::Standard;
     RequestRestyle(restyleType);
   }
 
   // Detect changes to "in effect" status since we need to recalculate the
   // animation cascade for this element whenever that changes.
@@ -1657,17 +1659,17 @@ void KeyframeEffect::SetAnimation(Animat
 
   // The order of these function calls is important:
   // NotifyAnimationTimingUpdated() need the updated mIsRelevant flag to check
   // if it should create the effectSet or not, and MarkCascadeNeedsUpdate()
   // needs a valid effectSet, so we should call them in this order.
   if (mAnimation) {
     mAnimation->UpdateRelevance();
   }
-  NotifyAnimationTimingUpdated();
+  NotifyAnimationTimingUpdated(PostRestyleMode::IfNeeded);
   if (mAnimation) {
     MarkCascadeNeedsUpdate();
   }
 }
 
 bool KeyframeEffect::CanIgnoreIfNotVisible() const {
   if (!AnimationUtils::IsOffscreenThrottlingEnabled()) {
     return false;
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -18,16 +18,17 @@
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/AnimationPropertySegment.h"
 #include "mozilla/AnimationTarget.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTimingFunction.h"
 #include "mozilla/EffectCompositor.h"
 #include "mozilla/Keyframe.h"
 #include "mozilla/KeyframeEffectParams.h"
+#include "mozilla/PostRestyleMode.h"
 // RawServoDeclarationBlock and associated RefPtrTraits
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/AnimationEffect.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Element.h"
 
 struct JSContext;
@@ -169,17 +170,17 @@ class KeyframeEffect : public AnimationE
   IterationCompositeOperation IterationComposite() const;
   void SetIterationComposite(
       const IterationCompositeOperation& aIterationComposite);
 
   CompositeOperation Composite() const;
   void SetComposite(const CompositeOperation& aComposite);
 
   void NotifySpecifiedTimingUpdated();
-  void NotifyAnimationTimingUpdated();
+  void NotifyAnimationTimingUpdated(PostRestyleMode aPostRestyle);
   void RequestRestyle(EffectCompositor::RestyleType aRestyleType);
   void SetAnimation(Animation* aAnimation) override;
   void SetKeyframes(JSContext* aContext, JS::Handle<JSObject*> aKeyframes,
                     ErrorResult& aRv);
   void SetKeyframes(nsTArray<Keyframe>&& aKeyframes,
                     const ComputedStyle* aStyle);
 
   // Returns true if the effect includes a property in |aPropertySet| regardless
new file mode 100644
--- /dev/null
+++ b/dom/animation/PostRestyleMode.h
@@ -0,0 +1,16 @@
+/* -*- 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/. */
+
+#ifndef mozilla_PostRestyleMode_h
+#define mozilla_PostRestyleMode_h
+
+namespace mozilla {
+
+enum class PostRestyleMode { IfNeeded, Never };
+
+}  // namespace mozilla
+
+#endif  // mozilla_PostRestyleMode_h
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -29,16 +29,17 @@ EXPORTS.mozilla += [
     'ComputedTiming.h',
     'ComputedTimingFunction.h',
     'EffectCompositor.h',
     'EffectSet.h',
     'Keyframe.h',
     'KeyframeEffectParams.h',
     'KeyframeUtils.h',
     'PendingAnimationTracker.h',
+    'PostRestyleMode.h',
     'PseudoElementHashEntry.h',
     'TimingParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffect.cpp',
     'AnimationEventDispatcher.cpp',
--- a/dom/animation/test/mozilla/file_restyles.html
+++ b/dom/animation/test/mozilla/file_restyles.html
@@ -5,23 +5,23 @@
 <script>
 const ok = opener.ok.bind(opener);
 const is = opener.is.bind(opener);
 const todo = opener.todo.bind(opener);
 const todo_is = opener.todo_is.bind(opener);
 const info = opener.info.bind(opener);
 const original_finish = opener.SimpleTest.finish;
 const SimpleTest = opener.SimpleTest;
+const add_task = opener.add_task;
 SimpleTest.finish = function finish() {
   self.close();
   original_finish();
 }
 </script>
 <script src="/tests/SimpleTest/EventUtils.js"></script>
-<script src="/tests/SimpleTest/AddTask.js"></script>
 <script src="/tests/SimpleTest/paint_listener.js"></script>
 <script src="../testcommon.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 <style>
 @keyframes opacity {
   from { opacity: 1; }
   to { opacity: 0; }
 }
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -4423,17 +4423,17 @@ void Document::SetContainer(nsDocShell* 
 
   mInChromeDocShell =
       aContainer && aContainer->ItemType() == nsIDocShellTreeItem::typeChrome;
 
   EnumerateActivityObservers(NotifyActivityChanged, nullptr);
 
   // IsTopLevelWindowInactive depends on the docshell, so
   // update the cached value now that it's available.
-  UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+  UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, false);
   if (!aContainer) {
     return;
   }
 
   BrowsingContext* context = aContainer->GetBrowsingContext();
   if (context && context->IsContent()) {
     if (!context->GetParent()) {
       SetIsTopLevelContentDocument(true);
@@ -5052,21 +5052,16 @@ void Document::UnblockDOMContentLoaded()
 void Document::ContentStateChanged(nsIContent* aContent,
                                    EventStates aStateMask) {
   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(),
              "Someone forgot a scriptblocker");
   NS_DOCUMENT_NOTIFY_OBSERVERS(ContentStateChanged,
                                (this, aContent, aStateMask));
 }
 
-void Document::DocumentStatesChanged(EventStates aStateMask) {
-  UpdateDocumentStates(aStateMask);
-  NS_DOCUMENT_NOTIFY_OBSERVERS(DocumentStatesChanged, (this, aStateMask));
-}
-
 void Document::StyleRuleChanged(StyleSheet* aSheet, css::Rule* aStyleRule) {
   ApplicableStylesChanged();
 
   if (!StyleSheetChangeEventsEnabled()) {
     return;
   }
 
   DO_STYLESHEET_NOTIFICATION(StyleRuleChangeEvent, "StyleRuleChanged", mRule,
@@ -8506,32 +8501,41 @@ void Document::ForgetImagePreload(nsIURI
     mPreloadingImages.Remove(aURI, getter_AddRefs(req));
     if (req) {
       // Make sure to cancel the request so imagelib knows it's gone.
       req->CancelAndForgetObserver(NS_BINDING_ABORTED);
     }
   }
 }
 
-void Document::UpdateDocumentStates(EventStates aChangedStates) {
-  if (aChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
+void Document::UpdateDocumentStates(EventStates aMaybeChangedStates,
+                                    bool aNotify) {
+  EventStates oldStates = mDocumentState;
+  if (aMaybeChangedStates.HasState(NS_DOCUMENT_STATE_RTL_LOCALE)) {
     if (IsDocumentRightToLeft()) {
       mDocumentState |= NS_DOCUMENT_STATE_RTL_LOCALE;
     } else {
       mDocumentState &= ~NS_DOCUMENT_STATE_RTL_LOCALE;
     }
   }
 
-  if (aChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
+  if (aMaybeChangedStates.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
     if (IsTopLevelWindowInactive()) {
       mDocumentState |= NS_DOCUMENT_STATE_WINDOW_INACTIVE;
     } else {
       mDocumentState &= ~NS_DOCUMENT_STATE_WINDOW_INACTIVE;
     }
   }
+
+  EventStates changedStates = oldStates ^ mDocumentState;
+  if (aNotify && !changedStates.IsEmpty()) {
+    if (PresShell* ps = GetObservingPresShell()) {
+      ps->DocumentStatesChanged(changedStates);
+    }
+  }
 }
 
 namespace {
 
 /**
  * Stub for LoadSheet(), since all we want is to get the sheet into
  * the CSSLoader's style cache
  */
@@ -8566,17 +8570,17 @@ nsresult Document::LoadChromeSheetSync(n
       isAgentSheet ? css::eAgentSheetFeatures : css::eAuthorSheetFeatures;
   return CSSLoader()->LoadSheetSync(uri, mode, isAgentSheet, aSheet);
 }
 
 void Document::ResetDocumentDirection() {
   if (!(nsContentUtils::IsChromeDoc(this) || IsXULDocument())) {
     return;
   }
-  DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
+  UpdateDocumentStates(NS_DOCUMENT_STATE_RTL_LOCALE, true);
 }
 
 bool Document::IsDocumentRightToLeft() {
   if (!(nsContentUtils::IsChromeDoc(this) || IsXULDocument())) {
     return false;
   }
   // setting the localedir attribute on the root element forces a
   // specific direction for the document.
--- a/dom/base/Document.h
+++ b/dom/base/Document.h
@@ -2016,20 +2016,22 @@ class Document : public nsINode,
   void NotifyLoading(const bool& aCurrentParentIsLoading,
                      bool aNewParentIsLoading, const ReadyState& aCurrentState,
                      ReadyState aNewState);
 
   // notify that a content node changed state.  This must happen under
   // a scriptblocker but NOT within a begin/end update.
   void ContentStateChanged(nsIContent* aContent, EventStates aStateMask);
 
-  // Notify that a document state has changed.
+  // Update a set of document states that may have changed.
   // This should only be called by callers whose state is also reflected in the
   // implementation of Document::GetDocumentState.
-  void DocumentStatesChanged(EventStates aStateMask);
+  //
+  // aNotify controls whether we notify our DocumentStatesChanged observers.
+  void UpdateDocumentStates(EventStates aStateMask, bool aNotify);
 
   void ResetDocumentDirection();
 
   // Observation hooks for style data to propagate notifications
   // to document observers
   void StyleRuleChanged(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
   void StyleRuleAdded(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
   void StyleRuleRemoved(StyleSheet* aStyleSheet, css::Rule* aStyleRule);
@@ -3812,18 +3814,16 @@ class Document : public nsINode,
       mChildDocumentUseCounters[aUseCounter] = true;
     }
   }
 
   bool GetChildDocumentUseCounter(UseCounter aUseCounter) {
     return mChildDocumentUseCounters[aUseCounter];
   }
 
-  void UpdateDocumentStates(EventStates);
-
   void RemoveDocStyleSheetsFromStyleSets();
   void RemoveStyleSheetsFromStyleSets(
       const nsTArray<RefPtr<StyleSheet>>& aSheets, SheetType aType);
   void ResetStylesheetsToURI(nsIURI* aURI);
   void FillStyleSet();
   void FillStyleSetUserAndUASheets();
   void FillStyleSetDocumentSheets();
   void CompatibilityModeChanged();
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1907,24 +1907,25 @@ void Element::UnbindFromTree(bool aDeep,
     if (presContext) {
       MOZ_ASSERT(this != presContext->GetViewportScrollStylesOverrideElement(),
                  "Leaving behind a raw pointer to this element (as having "
                  "propagated scrollbar styles) - that's dangerous...");
     }
   }
 #endif
 
-  ClearInDocument();
-  SetIsConnected(false);
-
   // Ensure that CSS transitions don't continue on an element at a
   // different place in the tree (even if reinserted before next
   // animation refresh).
+  //
   // We need to delete the properties while we're still in document
-  // (if we were in document).
+  // (if we were in document) so that they can look up the
+  // PendingAnimationTracker on the document and remove their animations,
+  // and so they can find their pres context for dispatching cancel events.
+  //
   // FIXME (Bug 522599): Need a test for this.
   if (MayHaveAnimations()) {
     DeleteProperty(nsGkAtoms::transitionsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::transitionsOfAfterProperty);
     DeleteProperty(nsGkAtoms::transitionsOfMarkerProperty);
     DeleteProperty(nsGkAtoms::transitionsProperty);
     DeleteProperty(nsGkAtoms::animationsOfBeforeProperty);
     DeleteProperty(nsGkAtoms::animationsOfAfterProperty);
@@ -1935,16 +1936,19 @@ void Element::UnbindFromTree(bool aDeep,
         // We have to clear all pending restyle requests for the animations on
         // this element to avoid unnecessary restyles when we re-attached this
         // element.
         presContext->EffectCompositor()->ClearRestyleRequestsFor(this);
       }
     }
   }
 
+  ClearInDocument();
+  SetIsConnected(false);
+
   if (aNullParent || !mParent->IsInShadowTree()) {
     UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
     // Begin keeping track of our subtree root.
     SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
   }
 
   bool clearBindingParent = true;
--- a/dom/base/SelectionChangeEventDispatcher.h
+++ b/dom/base/SelectionChangeEventDispatcher.h
@@ -13,16 +13,17 @@
 #include "nsCOMPtr.h"
 
 class nsINode;
 class nsRange;
 
 namespace mozilla {
 
 namespace dom {
+class Document;
 class Selection;
 }
 
 class SelectionChangeEventDispatcher final {
  public:
   // SelectionChangeEventDispatcher has to participate in cycle collection
   // because it holds strong references to nsINodes in its mOldRanges array.
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -6716,17 +6716,17 @@ void nsGlobalWindowOuter::ActivateOrDeac
     if (topLevelWindow) {
       topLevelWindow->SetActive(aActivate);
     }
   }
 }
 
 static bool NotifyDocumentTree(Document* aDocument, void* aData) {
   aDocument->EnumerateSubDocuments(NotifyDocumentTree, nullptr);
-  aDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_WINDOW_INACTIVE);
+  aDocument->UpdateDocumentStates(NS_DOCUMENT_STATE_WINDOW_INACTIVE, true);
   return true;
 }
 
 void nsGlobalWindowOuter::SetActive(bool aActive) {
   nsPIDOMWindowOuter::SetActive(aActive);
   if (mDoc) {
     NotifyDocumentTree(mDoc, nullptr);
   }
--- a/dom/base/nsIDocumentObserver.h
+++ b/dom/base/nsIDocumentObserver.h
@@ -68,25 +68,16 @@ class nsIDocumentObserver : public nsIMu
    * (the other notifications are used for that).
    *
    * @param aDocument The document being observed
    * @param aContent the piece of content that changed
    */
   virtual void ContentStateChanged(mozilla::dom::Document*,
                                    nsIContent* aContent,
                                    mozilla::EventStates aStateMask) = 0;
-
-  /**
-   * Notification that the state of the document has changed.
-   *
-   * @param aDocument The document being observed
-   * @param aStateMask the state that changed
-   */
-  virtual void DocumentStatesChanged(mozilla::dom::Document*,
-                                     mozilla::EventStates aStateMask) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentObserver, NS_IDOCUMENT_OBSERVER_IID)
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE \
   virtual void BeginUpdate(mozilla::dom::Document*) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE \
@@ -98,42 +89,34 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumen
 #define NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD \
   virtual void EndLoad(mozilla::dom::Document*) override;
 
 #define NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED     \
   virtual void ContentStateChanged(mozilla::dom::Document*, \
                                    nsIContent* aContent,    \
                                    mozilla::EventStates aStateMask) override;
 
-#define NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED \
-  virtual void DocumentStatesChanged(                     \
-      mozilla::dom::Document*, mozilla::EventStates aStateMask) override;
-
 #define NS_DECL_NSIDOCUMENTOBSERVER                 \
   NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE           \
   NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE             \
   NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD             \
   NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD               \
   NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATECHANGED   \
-  NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED \
   NS_DECL_NSIMUTATIONOBSERVER
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_CORE_STUB(_class)  \
   void _class::BeginUpdate(mozilla::dom::Document*) {} \
   void _class::EndUpdate(mozilla::dom::Document*) {}   \
   NS_IMPL_NSIMUTATIONOBSERVER_CORE_STUB(_class)
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_LOAD_STUB(_class) \
   void _class::BeginLoad(mozilla::dom::Document*) {}  \
   void _class::EndLoad(mozilla::dom::Document*) {}
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_STATE_STUB(_class)                 \
   void _class::ContentStateChanged(mozilla::dom::Document*,            \
                                    nsIContent* aContent,               \
-                                   mozilla::EventStates aStateMask) {} \
-                                                                       \
-  void _class::DocumentStatesChanged(mozilla::dom::Document*,          \
-                                     mozilla::EventStates aStateMask) {}
+                                   mozilla::EventStates aStateMask) {}
 
 #define NS_IMPL_NSIDOCUMENTOBSERVER_CONTENT(_class) \
   NS_IMPL_NSIMUTATIONOBSERVER_CONTENT(_class)
 
 #endif /* nsIDocumentObserver_h___ */
--- a/dom/base/test/chrome/test_chromeOuterWindowID.xul
+++ b/dom/base/test/chrome/test_chromeOuterWindowID.xul
@@ -5,17 +5,16 @@
 https://bugzilla.mozilla.org/show_bug.cgi?id=1474662
 Test that the chromeOuterWindowID on the MessageManager interface
 works, and that it properly updates when swapping frameloaders between
 windows.
 -->
 <window title="Mozilla Bug 1474662"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1474662"
      target="_blank">Mozilla Bug 1474662</a>
   </body>
 
   <!-- test code goes here -->
--- a/dom/base/test/chrome/window_swapFrameLoaders.xul
+++ b/dom/base/test/chrome/window_swapFrameLoaders.xul
@@ -2,20 +2,19 @@
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1242644
 Test swapFrameLoaders with different frame types and remoteness
 -->
 <window title="Mozilla Bug 1242644"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
 
   <script type="application/javascript"><![CDATA[
-  ["SimpleTest", "SpecialPowers", "info", "is", "ok"].forEach(key => {
+  ["SimpleTest", "SpecialPowers", "info", "is", "ok", "add_task"].forEach(key => {
     window[key] = window.opener[key];
   })
 
   const NS = {
     xul: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
     html: "http://www.w3.org/1999/xhtml",
   }
 
--- a/dom/base/test/test_blockParsing.html
+++ b/dom/base/test/test_blockParsing.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for document.blockParsing</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body>
 <script>
 const {TestUtils} = ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
 async function runTest(url, initialHTML, finalHTML) {
   let iframe = document.createElement("iframe");
--- a/dom/base/test/test_bug1281963.html
+++ b/dom/base/test/test_bug1281963.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/1281963
 -->
 <head>
   <meta http-equiv="content-type" content="text/html; charset=utf-8">
   <title>Test for Bug 1281963</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content"></div>
 
 <script class="testbody" type="application/javascript">
 
--- a/dom/base/test/test_bug1384661.html
+++ b/dom/base/test/test_bug1384661.html
@@ -29,20 +29,24 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(document.childNodes.length, 0,
        "childNodes.length of overwritten document should be 0");
     is(document.childNodes[0], undefined,
        "childNodes[0] of overwritten document should be undefined");
 
     SimpleTest.finish();
   }
 
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.xmldocument.load.enabled", true],
+  ]}, runTest);
+
 
   </script>
 </head>
-<body onload="runTest()">
+<body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1384661">Mozilla Bug 1384661</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 </body>
--- a/dom/base/test/test_copypaste.html
+++ b/dom/base/test/test_copypaste.html
@@ -1,17 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 -->
 <head>
   <title>Test for copy/paste</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
-  <script src="/tests/SimpleTest/AddTask.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>
--- a/dom/base/test/test_copypaste.xhtml
+++ b/dom/base/test/test_copypaste.xhtml
@@ -11,17 +11,16 @@ 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 src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="copypaste.js"></script>
-  <script src="/tests/SimpleTest/AddTask.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">
--- a/dom/base/test/test_explicit_user_agent.html
+++ b/dom/base/test/test_explicit_user_agent.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test for XMLHttpRequest.GetResponseHeader(foo) byte-inflates the output</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta charset="utf-8">
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
--- a/dom/base/test/test_fragment_sanitization.xul
+++ b/dom/base/test/test_fragment_sanitization.xul
@@ -2,17 +2,16 @@
 <?xml-stylesheet type="text/css" href="chrome://global/skin"?>
 <?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1432966
 -->
 <window title="Mozilla Bug 1432966"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"/>
 
   <script type="application/javascript"><![CDATA[
 
 const NS_HTML = "http://www.w3.org/1999/xhtml";
 
 function awaitLoad(frame) {
   return new Promise(resolve => {
     frame.addEventListener("load", resolve, {once: true});
--- a/dom/base/test/test_meta_viewport0.html
+++ b/dom/base/test/test_meta_viewport0.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>No &lt;meta name="viewport"&gt; tag</p>
   <script type="application/javascript">
     "use strict";
 
--- a/dom/base/test/test_meta_viewport1.html
+++ b/dom/base/test/test_meta_viewport1.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width, initial-scale=1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport2.html
+++ b/dom/base/test/test_meta_viewport2.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport3.html
+++ b/dom/base/test/test_meta_viewport3.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=320">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=320</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport4.html
+++ b/dom/base/test/test_meta_viewport4.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale=1.0, user-scalable=no</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport5.html
+++ b/dom/base/test/test_meta_viewport5.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="user-scalable=NO">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>user-scalable=NO</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport6.html
+++ b/dom/base/test/test_meta_viewport6.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=2000, minimum-scale=0.75">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=2000, minimum-scale=0.75</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport7.html
+++ b/dom/base/test/test_meta_viewport7.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=320">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>Dynamic viewport updates</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_device_height.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_device_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>device-height enables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="height=device-height">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>height=device-height</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_device_width.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_device_width.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>device-width enables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_fixed_height_and_initial_scale_1.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_height_and_initial_scale_1.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale=1 with fixed height enable autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="height=400, initial-scale=1">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>height=400, initial-scale=1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_device_height.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_device_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Fixed width and device-height disables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=400,height=device-height">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=400, height=device-height</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_initial_scale_1.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_fixed_width_and_initial_scale_1.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale=1 with fixed width disables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=400, initial-scale=1">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=400, initial-scale=1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_0_5.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_0_5.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale!=1 without width disables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=0.5">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale!=1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_1.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_initial_scale_1.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale=1 without width enables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=1">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale=1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_invalid_width.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_invalid_width.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>invalid width enables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=-1">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=-1</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_auto_size_by_invalid_width_and_fixed_height.html
+++ b/dom/base/test/test_meta_viewport_auto_size_by_invalid_width_and_fixed_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>invalid width but with fixed height disables autoSize</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=-1,height=200">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=-1,height=200</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_device_width.html
+++ b/dom/base/test/test_meta_viewport_device_width.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>device-width in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_device_width_with_initial_scale_0_5.html
+++ b/dom/base/test/test_meta_viewport_device_width_with_initial_scale_0_5.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>device-width with initial-scale=0.5 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width, initial-scale=0.5">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width, initial-scale=0.5</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_device_width_with_initial_scale_2.html
+++ b/dom/base/test/test_meta_viewport_device_width_with_initial_scale_2.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>device-width with initial-scale=2 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width, initial-scale=2">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width, initial-scale=2</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_fixed_width_and_zero_display_width.html
+++ b/dom/base/test/test_meta_viewport_fixed_width_and_zero_display_width.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Fixed meta viewport width, zero display width</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=100">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>Fixed meta viewport width, zero display width</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_initial_scale_0_5.html
+++ b/dom/base/test/test_meta_viewport_initial_scale_0_5.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale=0.5 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=0.5">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale=0.5</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_initial_scale_2.html
+++ b/dom/base/test/test_meta_viewport_initial_scale_2.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>initial-scale=2 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="initial-scale=2">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>initial-scale=2</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_maximum_scale_0.html
+++ b/dom/base/test/test_meta_viewport_maximum_scale_0.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=0">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width, minimum-scale=1, maximum-scale=0</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_maximum_scale_0_5.html
+++ b/dom/base/test/test_meta_viewport_maximum_scale_0_5.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>maximum-scale=0.5 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="maximum-scale=0.5">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>maximum-scale=0.5</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_maximum_scale_2.html
+++ b/dom/base/test/test_meta_viewport_maximum_scale_2.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>maximum-scale=2 in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="maximum-scale=2">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>maximum-scale=2</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_negative_width_and_negative_height.html
+++ b/dom/base/test/test_meta_viewport_negative_width_and_negative_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>negative width and height in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=-400, height=-240">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=-400, height=-240</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_negative_width_and_no_height.html
+++ b/dom/base/test/test_meta_viewport_negative_width_and_no_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>negative width in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=-400">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=-400</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_negative_width_and_valid_height.html
+++ b/dom/base/test/test_meta_viewport_negative_width_and_valid_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>negative width and valid height in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=-300,height=240">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=-400, height=240</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_no_width_and_negative_height.html
+++ b/dom/base/test/test_meta_viewport_no_width_and_negative_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>negative height in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="height=-200">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>height=-200</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_no_width_and_valid_height.html
+++ b/dom/base/test/test_meta_viewport_no_width_and_valid_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>valid height in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="height=240">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>height=240</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_tiny_display_size.html
+++ b/dom/base/test/test_meta_viewport_tiny_display_size.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>meta viewport test</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=device-width">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=device-width</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_valid_width_and_negative_height.html
+++ b/dom/base/test/test_meta_viewport_valid_width_and_negative_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>valid width and negative height in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=400, height=-200">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=400, height=-200</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_meta_viewport_valid_width_and_no_height.html
+++ b/dom/base/test/test_meta_viewport_valid_width_and_no_height.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>valid width in meta viewport</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <meta name="viewport" content="width=400">
   <script src="viewport_helpers.js"></script>
 </head>
 <body>
   <p>width=400</p>
   <script type="application/javascript">
     "use strict";
--- a/dom/base/test/test_mozbrowser_apis_allowed.html
+++ b/dom/base/test/test_mozbrowser_apis_allowed.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Verify mozbrowser APIs are allowed with browser permission</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="mozbrowser_api_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <script type="application/javascript">
   add_task(async function() {
     await SpecialPowers.pushPrefEnv(
--- a/dom/base/test/test_mozbrowser_apis_blocked.html
+++ b/dom/base/test/test_mozbrowser_apis_blocked.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Verify mozbrowser APIs are blocked without browser permission</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="mozbrowser_api_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <script type="application/javascript">
   add_task(async function() {
     await SpecialPowers.pushPrefEnv(
--- a/dom/base/test/test_postMessage_originAttributes.html
+++ b/dom/base/test/test_postMessage_originAttributes.html
@@ -1,14 +1,13 @@
 <!DOCTYPE html>
 <html>
 <head>
   <title>Test window.postMessages from system principal to window with non-default originAttributes</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <iframe id="target-iframe"></iframe>
 <script type="application/javascript">
 
 add_task(async function() {
--- a/dom/chrome-webidl/JSWindowActor.webidl
+++ b/dom/chrome-webidl/JSWindowActor.webidl
@@ -18,16 +18,28 @@ interface JSWindowActor {
 interface JSWindowActorParent {
   readonly attribute WindowGlobalParent manager;
 };
 JSWindowActorParent implements JSWindowActor;
 
 [ChromeOnly, ChromeConstructor]
 interface JSWindowActorChild {
   readonly attribute WindowGlobalChild manager;
+
+  [Throws]
+  readonly attribute Document? document;
+
+  [Throws]
+  readonly attribute BrowsingContext? browsingContext;
+
+  // NOTE: As this returns a window proxy, it may not be currently referencing
+  // the document associated with this JSWindowActor. Generally prefer using
+  // `document`.
+  [Throws]
+  readonly attribute WindowProxy? contentWindow;
 };
 JSWindowActorChild implements JSWindowActor;
 
 // WebIDL callback interface version of the nsIObserver interface for use when
 // calling the observe method on JSWindowActors.
 //
 // NOTE: This isn't marked as ChromeOnly, as it has no interface object, and
 // thus cannot be conditionally exposed.
--- a/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html
+++ b/dom/credentialmanagement/tests/mochitest/test_credman_iframes.html
@@ -1,13 +1,12 @@
 <!DOCTYPE html>
 <head>
   <title>Credential Management: Prohibit use in cross-origin iframes</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta charset=utf-8>
 </head>
 <body>
 <h1>Credential Management: Prohibit use in cross-origin iframes</h1>
 <ul>
   <li><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1407789">Mozilla Bug 1407789</a></li>
 </ul>
--- a/dom/events/test/pointerevents/test_multiple_touches.html
+++ b/dom/events/test/pointerevents/test_multiple_touches.html
@@ -1,15 +1,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test for Multiple Touches</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <p id="display"></p>
 <div id="target0" style="width: 100px; height: 100px; background: green"></div>
 <div id="target1" style="width: 100px; height: 100px; background: red"></div>
 <script type="text/javascript">
--- a/dom/events/test/test_DataTransferItemList.html
+++ b/dom/events/test/test_DataTransferItemList.html
@@ -1,14 +1,13 @@
 <html>
 <head>
   <title>Tests for the DataTransferItemList object</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
   <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <script src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
 </head>
 <body style="height: 300px; overflow: auto;">
 <p id="display"> </p>
 <img id="image" draggable="true" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82">
 <div id="over" style="width: 100px; height: 100px; border: 2px black dashed;">
   drag over here
--- a/dom/gamepad/GamepadRemapping.cpp
+++ b/dom/gamepad/GamepadRemapping.cpp
@@ -42,16 +42,26 @@ enum CanonicalButtonIndex {
 enum CanonicalAxisIndex {
   AXIS_INDEX_LEFT_STICK_X,
   AXIS_INDEX_LEFT_STICK_Y,
   AXIS_INDEX_RIGHT_STICK_X,
   AXIS_INDEX_RIGHT_STICK_Y,
   AXIS_INDEX_COUNT
 };
 
+bool AxisNegativeAsButton(float input) {
+  const float value = (input < -0.5f) ? 1.f : 0.f;
+  return value > 0.1f;
+}
+
+bool AxisPositiveAsButton(float input) {
+  const float value = (input > 0.5f) ? 1.f : 0.f;
+  return value > 0.1f;
+}
+
 void FetchDpadFromAxis(uint32_t aIndex, double dir) {
   bool up = false;
   bool right = false;
   bool down = false;
   bool left = false;
 
   // Dpad is mapped as a direction on one axis, where -1 is up and it
   // increases clockwise to 1, which is up + left. It's set to a large (> 1.f)
@@ -85,16 +95,20 @@ class DefaultRemapper final : public Gam
   virtual void SetAxisCount(uint32_t aAxisCount) override {
     numAxes = aAxisCount;
   }
 
   virtual void SetButtonCount(uint32_t aButtonCount) override {
     numButtons = aButtonCount;
   }
 
+  virtual GamepadMappingType GetMappingType() const override {
+    return GamepadMappingType::_empty;
+  }
+
   virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
                                   double aValue) const override {
     RefPtr<GamepadPlatformService> service =
         GamepadPlatformService::GetParentService();
     if (!service) {
       return;
     }
     service->NewAxisMoveEvent(aIndex, aAxis, aValue);
@@ -110,28 +124,253 @@ class DefaultRemapper final : public Gam
     service->NewButtonEvent(aIndex, aButton, aPressed);
   }
 
  private:
   uint32_t numAxes;
   uint32_t numButtons;
 };
 
+class ADT1Remapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in ADT1Remapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in ADT1Remapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {12, BUTTON_INDEX_META},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class TwoAxesEightKeysRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return 0;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT - 1;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_LEFT, AxisNegativeAsButton(aValue));
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_RIGHT, AxisPositiveAsButton(aValue));
+          break;
+        case 1:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_UP, AxisNegativeAsButton(aValue));
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_DOWN, AxisPositiveAsButton(aValue));
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in TwoAxesEightKeysRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in TwoAxesEightKeysRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {0, BUTTON_INDEX_QUATERNARY},
+        {2, BUTTON_INDEX_PRIMARY},
+        {3, BUTTON_INDEX_TERTIARY}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class StadiaControllerRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return STADIA_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER, aValue);
+          break;
+        case 5:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in StadiaControllerRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (STADIA_BUTTON_COUNT <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in StadiaControllerRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      service->NewButtonEvent(aIndex, aButton, aPressed);
+    }
+
+ private:
+  enum STADIAButtons {
+    STADIA_BUTTON_EXTRA1 = BUTTON_INDEX_COUNT,
+    STADIA_BUTTON_EXTRA2,
+    STADIA_BUTTON_COUNT
+  };
+};
+
 class Dualshock4Remapper final : public GamepadRemapper {
  public:
   virtual uint32_t GetAxisCount() const override { return AXIS_INDEX_COUNT; }
 
   virtual uint32_t GetButtonCount() const override {
     return DUALSHOCK_BUTTON_COUNT;
   }
 
-  virtual GamepadMappingType GetMappingType() const override {
-    return GamepadMappingType::_empty;
-  }
-
   virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
                                   double aValue) const override {
     RefPtr<GamepadPlatformService> service =
         GamepadPlatformService::GetParentService();
     if (!service) {
       return;
     }
 
@@ -204,22 +443,1089 @@ class Dualshock4Remapper final : public 
 
  private:
   enum Dualshock4Buttons {
     DUALSHOCK_BUTTON_TOUCHPAD = BUTTON_INDEX_COUNT,
     DUALSHOCK_BUTTON_COUNT
   };
 };
 
+class LogitechDInputRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      // The Logitech button (BUTTON_INDEX_META) is not accessible through the
+      // device's D-mode.
+      return BUTTON_INDEX_COUNT - 1;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in LogitechDInputRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in LogitechDInputRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {0, BUTTON_INDEX_TERTIARY},
+        {1, BUTTON_INDEX_PRIMARY},
+        {2, BUTTON_INDEX_SECONDARY}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class SwitchJoyConRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return 2;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      service->NewButtonEvent(aIndex, aButton, aPressed);
+    }
+};
+
+class SwitchProRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      // The Switch Pro controller has a Capture button that has no equivalent in
+      // the Standard Gamepad.
+      return SWITCHPRO_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      service->NewButtonEvent(aIndex, aButton, aPressed);
+    }
+
+private:
+  enum SwitchProButtons {
+    SWITCHPRO_BUTTON_EXTRA = BUTTON_INDEX_COUNT,
+    SWITCHPRO_BUTTON_COUNT
+  };
+};
+
+class NvShieldRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return SHIELD_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in NvShieldRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in NvShieldRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {2, BUTTON_INDEX_META},
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {5, SHIELD_BUTTON_CIRCLE},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {9, BUTTON_INDEX_BACK_SELECT},
+        {11, BUTTON_INDEX_START},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+
+private:
+  enum ShieldButtons {
+    SHIELD_BUTTON_CIRCLE = BUTTON_INDEX_COUNT,
+    SHIELD_BUTTON_COUNT
+  };
+};
+
+class NvShield2017Remapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return SHIELD2017_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in NvShield2017Remapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in NvShield2017Remapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {2, BUTTON_INDEX_META},
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {5, BUTTON_INDEX_START},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {8, BUTTON_INDEX_BACK_SELECT},
+        {11, SHIELD2017_BUTTON_PLAYPAUSE},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+
+private:
+  enum Shield2017Buttons {
+    SHIELD2017_BUTTON_PLAYPAUSE = BUTTON_INDEX_COUNT,
+    SHIELD2017_BUTTON_COUNT
+  };
+};
+
+class IBuffaloRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return 2;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT - 1; /* no meta */
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_LEFT, AxisNegativeAsButton(aValue));
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_RIGHT, AxisPositiveAsButton(aValue));
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_UP, AxisNegativeAsButton(aValue));
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_DPAD_DOWN, AxisPositiveAsButton(aValue));
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in IBuffaloRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in IBuffaloRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {0, BUTTON_INDEX_SECONDARY},
+        {1, BUTTON_INDEX_PRIMARY},
+        {2, BUTTON_INDEX_QUATERNARY},
+        {3, BUTTON_INDEX_TERTIARY},
+        {5, BUTTON_INDEX_RIGHT_TRIGGER},
+        {6, BUTTON_INDEX_BACK_SELECT},
+        {7, BUTTON_INDEX_START}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class XSkillsRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return GAMECUBE_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in XSkillsRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in XSkillsRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {0, BUTTON_INDEX_PRIMARY},  // A
+        {1, BUTTON_INDEX_TERTIARY}, // B
+        {2, BUTTON_INDEX_SECONDARY},  // X
+        {3, BUTTON_INDEX_QUATERNARY}, // Y
+        {4, GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK},
+        {5, GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK},
+        {6, BUTTON_INDEX_RIGHT_SHOULDER},
+        {7, BUTTON_INDEX_START},
+        {8, BUTTON_INDEX_DPAD_LEFT},
+        {9, BUTTON_INDEX_DPAD_RIGHT},
+        {10, BUTTON_INDEX_DPAD_DOWN},
+        {11, BUTTON_INDEX_DPAD_UP}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+
+private:
+  enum GamecubeButtons {
+    GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK = BUTTON_INDEX_COUNT,
+    GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK,
+    GAMECUBE_BUTTON_COUNT
+  };
+};
+
+class BoomN64PsxRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT - 1;  // no meta
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in BoomN64PsxRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      const std::vector<uint32_t> buttonMapping = {
+        BUTTON_INDEX_QUATERNARY,
+        BUTTON_INDEX_SECONDARY,
+        BUTTON_INDEX_PRIMARY,
+        BUTTON_INDEX_TERTIARY,
+        BUTTON_INDEX_LEFT_TRIGGER,
+        BUTTON_INDEX_RIGHT_TRIGGER,
+        BUTTON_INDEX_LEFT_SHOULDER,
+        BUTTON_INDEX_RIGHT_SHOULDER,
+        BUTTON_INDEX_BACK_SELECT,
+        BUTTON_INDEX_LEFT_THUMBSTICK,
+        BUTTON_INDEX_RIGHT_THUMBSTICK,
+        BUTTON_INDEX_START,
+        BUTTON_INDEX_DPAD_UP,
+        BUTTON_INDEX_DPAD_RIGHT,
+        BUTTON_INDEX_DPAD_DOWN,
+        BUTTON_INDEX_DPAD_LEFT
+      };
+
+      if (buttonMapping.size() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in BoomN64PsxRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      service->NewButtonEvent(aIndex, buttonMapping[aButton], aPressed);
+    }
+
+private:
+  enum GamecubeButtons {
+    GAMECUBE_BUTTON_LEFT_TRIGGER_CLICK = BUTTON_INDEX_COUNT,
+    GAMECUBE_BUTTON_RIGHT_TRIGGER_CLICK,
+    GAMECUBE_BUTTON_COUNT
+  };
+};
+
+class AnalogGamepadRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return ANALOG_GAMEPAD_BUTTON_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in AnalogGamepadRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in AnalogGamepadRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {10, BUTTON_INDEX_BACK_SELECT},
+        {11, BUTTON_INDEX_META},
+        {12, BUTTON_INDEX_START},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK},
+        {16, ANALOG_GAMEPAD_BUTTON_EXTRA},
+        {17, ANALOG_GAMEPAD_BUTTON_EXTRA2}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+
+private:
+  enum AnalogGamepadButtons {
+    ANALOG_GAMEPAD_BUTTON_EXTRA = BUTTON_INDEX_COUNT,
+    ANALOG_GAMEPAD_BUTTON_EXTRA2,
+    ANALOG_GAMEPAD_BUTTON_COUNT
+  };
+};
+
+class RazerServalRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT - 1; /* no meta */
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in RazerServalRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in RazerServalRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {10, BUTTON_INDEX_BACK_SELECT},
+        {11, BUTTON_INDEX_START},
+        {12, BUTTON_INDEX_START},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class MogaProRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT - 1; /* no meta */
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                  double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 3:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 4:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER,
+                                  aValue > 0.1f);
+          break;
+        case 5:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in MogaProRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in MogaProRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {11, BUTTON_INDEX_START},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class OnLiveWirelessRemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                    double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER, aValue > 0.1f);
+          break;
+        case 3:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 4:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 5:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER, aValue > 0.1f);
+          break;
+        case 9:
+          FetchDpadFromAxis(aIndex, aValue);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in OnLiveWirelessRemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in OnLiveWirelessRemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {3, BUTTON_INDEX_TERTIARY},
+        {4, BUTTON_INDEX_QUATERNARY},
+        {6, BUTTON_INDEX_LEFT_SHOULDER},
+        {7, BUTTON_INDEX_RIGHT_SHOULDER},
+        {12, BUTTON_INDEX_META},
+        {13, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {14, BUTTON_INDEX_RIGHT_THUMBSTICK}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+
+class OUYARemapper final : public GamepadRemapper {
+  public:
+    virtual uint32_t GetAxisCount() const override {
+      return AXIS_INDEX_COUNT;
+    }
+
+    virtual uint32_t GetButtonCount() const override {
+      return BUTTON_INDEX_COUNT;
+    }
+
+    virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
+                                    double aValue) const override {
+      RefPtr<GamepadPlatformService> service =
+        GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      switch (aAxis) {
+        case 0:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_X, aValue);
+          break;
+        case 1:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_LEFT_STICK_Y, aValue);
+          break;
+        case 2:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_LEFT_TRIGGER, aValue > 0.1f);
+          break;
+        case 3:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_X, aValue);
+          break;
+        case 4:
+          service->NewAxisMoveEvent(aIndex, AXIS_INDEX_RIGHT_STICK_Y, aValue);
+          break;
+        case 5:
+          service->NewButtonEvent(aIndex, BUTTON_INDEX_RIGHT_TRIGGER, aValue > 0.1f);
+          break;
+        default:
+          NS_WARNING(
+              nsPrintfCString(
+                  "Axis idx '%d' doesn't support in OUYARemapper().", aAxis)
+                  .get());
+          break;
+      }
+    }
+
+    virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,
+                                  bool aPressed) const override {
+      RefPtr<GamepadPlatformService> service =
+          GamepadPlatformService::GetParentService();
+      if (!service) {
+        return;
+      }
+
+      if (GetButtonCount() <= aIndex) {
+        NS_WARNING(
+              nsPrintfCString(
+                  "Button idx '%d' doesn't support in OUYARemapper().",
+                  aButton)
+                  .get());
+        return;
+      }
+
+      const std::map<uint32_t, uint32_t> buttonMapping = {
+        {1, BUTTON_INDEX_TERTIARY},
+        {2, BUTTON_INDEX_QUATERNARY},
+        {3, BUTTON_INDEX_SECONDARY},
+        {6, BUTTON_INDEX_LEFT_THUMBSTICK},
+        {7, BUTTON_INDEX_RIGHT_THUMBSTICK},
+        {8, BUTTON_INDEX_DPAD_UP},
+        {9, BUTTON_INDEX_DPAD_DOWN},
+        {10, BUTTON_INDEX_DPAD_LEFT},
+        {11, BUTTON_INDEX_DPAD_RIGHT},
+        {15, BUTTON_INDEX_META}
+      };
+
+      auto find = buttonMapping.find(aButton);
+      if (find != buttonMapping.end()) {
+        service->NewButtonEvent(aIndex, find->second, aPressed);
+      } else {
+        service->NewButtonEvent(aIndex, aButton, aPressed);
+      }
+    }
+};
+      
 already_AddRefed<GamepadRemapper> GetGamepadRemapper(
     const uint16_t aVendorId, const uint16_t aProductId) {
   const std::vector<GamepadRemappingData> remappingRules = {
-      {GamepadId::kSonyDualshock4, new Dualshock4Remapper()},
-      {GamepadId::kSonyDualshock4Slim, new Dualshock4Remapper()},
-      {GamepadId::kSonyDualshock4USBReceiver, new Dualshock4Remapper()}};
+      {GamepadId::kAsusTekProduct4500, new ADT1Remapper()},
+      {GamepadId::kDragonRiseProduct0011, new TwoAxesEightKeysRemapper()},
+      {GamepadId::kGoogleProduct2c40, new ADT1Remapper()},
+      {GamepadId::kGoogleProduct9400, new StadiaControllerRemapper()},
+      {GamepadId::kLogitechProductc216, new LogitechDInputRemapper()},
+      {GamepadId::kLogitechProductc218, new LogitechDInputRemapper()},
+      {GamepadId::kLogitechProductc219, new LogitechDInputRemapper()},
+      {GamepadId::kNintendoProduct2006, new SwitchJoyConRemapper()},
+      {GamepadId::kNintendoProduct2007, new SwitchJoyConRemapper()},
+      {GamepadId::kNintendoProduct2009, new SwitchProRemapper()},
+      {GamepadId::kNintendoProduct200e, new SwitchProRemapper()},
+      {GamepadId::kNvidiaProduct7210, new NvShieldRemapper()},
+      {GamepadId::kNvidiaProduct7214, new NvShield2017Remapper()},
+      {GamepadId::kPadixProduct2060, new IBuffaloRemapper()},
+      {GamepadId::kPlayComProduct0005, new XSkillsRemapper()},
+      {GamepadId::kPrototypeVendorProduct0667, new BoomN64PsxRemapper()},
+      {GamepadId::kPrototypeVendorProduct9401, new AnalogGamepadRemapper()},
+      {GamepadId::kRazer1532Product0900, new RazerServalRemapper()},
+      {GamepadId::kSonyProduct05c4, new Dualshock4Remapper()},
+      {GamepadId::kSonyProduct09cc, new Dualshock4Remapper()},
+      {GamepadId::kSonyProduct0ba0, new Dualshock4Remapper()},
+      {GamepadId::kVendor20d6Product6271, new MogaProRemapper()},
+      {GamepadId::kVendor2378Product1008, new OnLiveWirelessRemapper()},
+      {GamepadId::kVendor2378Product100a, new OnLiveWirelessRemapper()},
+      {GamepadId::kVendor2836Product0001, new OUYARemapper()}
+  };
   const GamepadId id = static_cast<GamepadId>((aVendorId << 16) | aProductId);
 
   for (uint32_t i = 0; i < remappingRules.size(); ++i) {
     if (id == remappingRules[i].id) {
       return do_AddRef(remappingRules[i].remapping.get());
     }
   }
 
--- a/dom/gamepad/GamepadRemapping.h
+++ b/dom/gamepad/GamepadRemapping.h
@@ -7,19 +7,66 @@
 #ifndef mozilla_dom_GamepadRemapping_h_
 #define mozilla_dom_GamepadRemapping_h_
 
 namespace mozilla {
 namespace dom {
 
 // GamepadId is (vendorId << 16) | productId)
 enum class GamepadId : uint32_t {
-  kSonyDualshock4 = 0x054c05c4,
-  kSonyDualshock4Slim = 0x054c09cc,
-  kSonyDualshock4USBReceiver = 0x054c0ba0,
+  // Nexus Player Controller
+  kAsusTekProduct4500 = 0x0b054500,
+  // 2Axes 8Keys Game Pad
+  kDragonRiseProduct0011 = 0x00790011,
+  // ADT-1 Controller
+  kGoogleProduct2c40 = 0x18d12c40,
+  // Stadia Controller
+  kGoogleProduct9400 = 0x18d19400,
+  // Logitech F310, D-mode
+  kLogitechProductc216 = 0x046dc216,
+  // Logitech F510, D-mode
+  kLogitechProductc218 = 0x046dc218,
+  // Logitech F710, D-mode
+  kLogitechProductc219 = 0x046dc219,
+  // Switch Joy-Con L
+  kNintendoProduct2006 = 0x057e2006,
+  // Switch Joy-Con R
+  kNintendoProduct2007 = 0x057e2007,
+  // Switch Pro Controller
+  kNintendoProduct2009 = 0x057e2009,
+  // Switch Charging Grip
+  kNintendoProduct200e = 0x057e200e,
+  // Nvidia Shield gamepad (2015)
+  kNvidiaProduct7210 = 0x09557210,
+  // Nvidia Shield gamepad (2017)
+  kNvidiaProduct7214 = 0x09557214,
+  // iBuffalo Classic
+  kPadixProduct2060 = 0x05832060,
+  // XSkills Gamecube USB adapter
+  kPlayComProduct0005 = 0x0b430005,
+  // boom PSX+N64 USB Converter
+  kPrototypeVendorProduct0667 = 0x66660667,
+  // Analog game controller
+  kPrototypeVendorProduct9401 = 0x66669401,
+  // Razer Serval Controller
+  kRazer1532Product0900 = 0x15320900,
+  // Playstation Dualshock 4
+  kSonyProduct05c4 = 0x054c05c4,
+  // Dualshock 4 (PS4 Slim)
+  kSonyProduct09cc = 0x054c09cc,
+  // Dualshock 4 USB receiver
+  kSonyProduct0ba0 = 0x054c0ba0,
+  // Moga Pro Controller (HID mode)
+  kVendor20d6Product6271 = 0x20d66271,
+  // OnLive Controller (Bluetooth)
+  kVendor2378Product1008 = 0x23781008,
+  // OnLive Controller (Wired)
+  kVendor2378Product100a = 0x2378100a,
+  // OUYA Controller
+  kVendor2836Product0001 = 0x28360001,
 };
 
 class GamepadRemapper {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GamepadRemapper)
 
  public:
   virtual uint32_t GetAxisCount() const = 0;
   virtual uint32_t GetButtonCount() const = 0;
--- a/dom/grid/GridLine.cpp
+++ b/dom/grid/GridLine.cpp
@@ -55,10 +55,14 @@ void GridLine::SetLineValues(const nsTAr
   mNames = aNames;
   mStart = aStart;
   mBreadth = aBreadth;
   mNumber = aNumber;
   mNegativeNumber = aNegativeNumber;
   mType = aType;
 }
 
+void GridLine::SetLineNames(const nsTArray<nsString>& aNames) {
+  mNames = aNames;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/grid/GridLine.h
+++ b/dom/grid/GridLine.h
@@ -39,16 +39,18 @@ class GridLine : public nsISupports, pub
   GridDeclaration Type() const;
   uint32_t Number() const;
   int32_t NegativeNumber() const;
 
   void SetLineValues(const nsTArray<nsString>& aNames, double aStart,
                      double aBreadth, uint32_t aNumber, int32_t aNegativeNumber,
                      GridDeclaration aType);
 
+  void SetLineNames(const nsTArray<nsString>& aNames);
+
  protected:
   RefPtr<GridLines> mParent;
   nsTArray<nsString> mNames;
   double mStart;
   double mBreadth;
   GridDeclaration mType;
   uint32_t mNumber;
   int32_t mNegativeNumber;
--- a/dom/grid/GridLines.cpp
+++ b/dom/grid/GridLines.cpp
@@ -204,16 +204,92 @@ void GridLines::SetLineInfo(const Comput
           nsPresContext::AppUnitsToDoubleCSSPixels(startOfNextTrack -
                                                    lastTrackEdge),
           lineNumber, lineNegativeNumber, lineType);
 
       if (i < aTrackInfo->mEndFragmentTrack) {
         lastTrackEdge = aTrackInfo->mPositions[i] + aTrackInfo->mSizes[i];
       }
     }
+
+    // Define a function that gets the mLines index for a given line number.
+    // This is necessary since it's possible for a line number to not be
+    // represented in mLines. If this is the case, then return  -1.
+    const int32_t lineCount = mLines.Length();
+    const uint32_t lastLineNumber = mLines[lineCount - 1]->Number();
+    auto IndexForLineNumber =
+        [lineCount, lastLineNumber](uint32_t aLineNumber) -> int32_t {
+      if (lastLineNumber == 0) {
+        // None of the lines have addressable numbers, so none of them can have
+        // aLineNumber
+        return -1;
+      }
+
+      int32_t possibleIndex = (int32_t)aLineNumber - 1;
+      if (possibleIndex < 0 || possibleIndex > lineCount - 1) {
+        // aLineNumber is not represented in mLines.
+        return -1;
+      }
+
+      return possibleIndex;
+    };
+
+    // Post-processing loop for implicit grid areas.
+    for (const auto& area : aAreas) {
+      if (area->Type() == GridDeclaration::Implicit) {
+        // Get the appropriate indexes for the area's start and end lines as
+        // they are represented in mLines.
+        int32_t startIndex =
+            IndexForLineNumber(aIsRow ? area->RowStart() : area->ColumnStart());
+        int32_t endIndex =
+            IndexForLineNumber(aIsRow ? area->RowEnd() : area->ColumnEnd());
+
+        // If both start and end indexes are -1, then stop here since we cannot
+        // reason about the naming for either lines.
+        if (startIndex < 0 && endIndex < 0) {
+          break;
+        }
+
+        // Get the "-start" and "-end" line names of the grid area.
+        nsAutoString startLineName;
+        area->GetName(startLineName);
+        startLineName.AppendLiteral("-start");
+        nsAutoString endLineName;
+        area->GetName(endLineName);
+        endLineName.AppendLiteral("-end");
+
+        // Get the list of existing line names for the start and end of the grid
+        // area. In the case where one of the start or end indexes are -1, use a
+        // dummy line as a substitute for the start/end line.
+        RefPtr<GridLine> dummyLine = new GridLine(this);
+        RefPtr<GridLine> areaStartLine =
+            startIndex > -1 ? mLines[startIndex] : dummyLine;
+        nsTArray<nsString> startLineNames;
+        areaStartLine->GetNames(startLineNames);
+
+        RefPtr<GridLine> areaEndLine =
+            endIndex > -1 ? mLines[endIndex] : dummyLine;
+        nsTArray<nsString> endLineNames;
+        areaEndLine->GetNames(endLineNames);
+
+        if (startLineNames.Contains(endLineName) ||
+            endLineNames.Contains(startLineName)) {
+          // Add the reversed line names.
+          AddLineNameIfNotPresent(startLineNames, endLineName);
+          AddLineNameIfNotPresent(endLineNames, startLineName);
+        } else {
+          // Add the normal line names.
+          AddLineNameIfNotPresent(startLineNames, startLineName);
+          AddLineNameIfNotPresent(endLineNames, endLineName);
+        }
+
+        areaStartLine->SetLineNames(startLineNames);
+        areaEndLine->SetLineNames(endLineNames);
+      }
+    }
   }
 }
 
 uint32_t GridLines::AppendRemovedAutoFits(
     const ComputedGridTrackInfo* aTrackInfo,
     const ComputedGridLineInfo* aLineInfo, nscoord aLastTrackEdge,
     uint32_t& aRepeatIndex, uint32_t aNumRepeatTracks,
     uint32_t aNumLeadingTracks, nsTArray<nsString>& aLineNames) {
--- a/dom/grid/test/chrome/test_grid_implicit.html
+++ b/dom/grid/test/chrome/test_grid_implicit.html
@@ -33,16 +33,24 @@ body {
   grid-template-rows: [areaA-end areaB-start areaC-end] 50px [areaA-start areaB-end areaC-start];
 }
 
 .template4 {
   grid-template-columns: 100px 50px 100px;
   grid-template-rows: 50px;
 }
 
+.template5 {
+  grid-template-columns: [a-end] 100px [b];
+}
+
+.template6 {
+  grid-template-columns: [foo-end] 100px [foo-start];
+}
+
 .box {
   background-color: #444;
   color: #fff;
 }
 .a {
   grid-area: areaA;
 }
 .b {
@@ -53,16 +61,25 @@ body {
   grid-area: areaC;
 }
 .d {
   grid-area: areaD;
 }
 .e {
   grid-column: -7 / 5;
 }
+.f {
+  grid-column: a-start / a-end;
+}
+.g {
+  grid-column: a-start / 2 a-end;
+}
+.h {
+  grid-column: foo-start;
+}
 </style>
 
 <script>
 'use strict';
 
 SimpleTest.waitForExplicitFinish();
 
 function runTests() {
@@ -99,23 +116,22 @@ function runTests() {
       "Grid row line 1 has the name 'got-this-name-implicitly'."
     );
 
     // test that row line 3 gets its explicit name
     isnot(grid.rows.lines[2].names.indexOf("middle"), -1,
       "Grid row line 3 has the name 'middle'."
     );
 
-    // test that implicit column line names are not assigned for
-    // implicit area 'areaD'
-    is(grid.cols.lines[4].names.indexOf("areaD-start"), -1,
-      "Grid column line 5 doesn't have the name 'areaD-start'."
+    // test the names of the implicit column lines that were created for area 'areaD'
+    isnot(grid.cols.lines[4].names.indexOf("areaD-start"), -1,
+      "Grid column line 5 has the name 'areaD-start'."
     );
-    is(grid.cols.lines[5].names.indexOf("areaD-end"), -1,
-      "Grid column line 6 doesn't have the name 'areaD-end'."
+    isnot(grid.cols.lines[5].names.indexOf("areaD-end"), -1,
+      "Grid column line 6 has the name 'areaD-end'."
     );
   }
 
   // test column and row track counts
   is(grid.cols.tracks.length, 5,
     "Grid.cols.tracks property has length that respects implicit expansion."
   );
   is(grid.rows.tracks.length, 3,
@@ -283,16 +299,64 @@ function runTests() {
     for (let i = 0; i < grid.cols.lines.length; i++) {
       let line = grid.cols.lines[i];
       is(line.type, expectedType[i], "Line index " + i + " has expected type.");
       is(line.number, expectedNumber[i], "Line index " + i + " has expected number.");
       is(line.negativeNumber, expectedNegativeNumber[i], "Line index " + i + " has expected negative number.");
     }
   }
 
+  // Test the fifth grid wrapper
+  wrapper = document.getElementById("wrapper5");
+  grid = wrapper.getGridFragments()[0];
+
+  // Test that lineName-reversed implicit areas have the correct names.
+  for (var i = 0; i < grid.areas.length; i++) {
+    var area = grid.areas[i];
+    // test the resulting start/end lines of the areas has:
+    // Start line: [a-end]
+    // End Line: [a-start]
+    if (area.name == "a") {
+      const startLineNames = grid.cols.lines[area.columnStart - 1].names;
+      const endLineNames = grid.cols.lines[area.columnEnd - 1].names;
+
+      isnot(startLineNames.indexOf("a-end"), -1,
+        `Grid column line ${area.columnStart} has the name a-end`);
+      is(startLineNames.indexOf("a-start"), -1,
+        `Grid column line ${area.columnStart} does not have the name a-start`);
+
+      isnot(endLineNames.indexOf("a-start"), -1,
+        `Grid column line ${area.columnEnd} has the name a-start`);
+      todo_isnot(endLineNames.indexOf("a-end"), -1,
+        `Grid column line ${area.columnEnd} has the name a-end`);
+
+      // Also test that the fourth line has the name a-end.
+      todo_isnot(grid.cols.lines[3].names.indexOf("a-end"), -1,
+        "Grid column line 4 has the line name a-end");
+    }
+  }
+
+  // Test the sixth grid wrapper
+  wrapper = document.getElementById("wrapper6");
+  grid = wrapper.getGridFragments()[0];
+
+  // Test that the grid has two explicit lines: [foo-end][foo-start] and a third implicit
+  // line that follows.
+  is(grid.cols.lines.length, 3, "Grid should have three lines.");
+
+  isnot(grid.cols.lines[0].names.indexOf("foo-end"), -1,
+    "Grid column line 1 has the name 'foo-end'",
+  );
+
+  isnot(grid.cols.lines[1].names.indexOf("foo-start"), -1,
+    "Grid column line 2 has the name 'foo-start'",
+  );
+
+  is(grid.cols.lines[2].type, "implicit", "Grid column line 3 is implicit");
+
   SimpleTest.finish();
 }
 </script>
 </head>
 <body onLoad="runTests();">
 
   <div id="wrapper1" class="wrapper template1">
     <div id="boxA" class="box a">A</div>
@@ -312,10 +376,17 @@ function runTests() {
   <div id="wrapper3" class="wrapper template3">
     <div id="boxC" class="box c">C</div>
   </div>
 
   <div id="wrapper4" class="wrapper template4">
     <div id="boxE" class="box e">E</div>
   </div>
 
+  <div id="wrapper5" class="wrapper template5">
+    <div id="boxF" class="box f">F</div>
+    <div id="boxG" class="box g">G</div>
+  </div>
+  <div id="wrapper6" class="wrapper template6">
+    <div id="boxH" class="box h">H</div>
+  </div>
 </body>
 </html>
--- a/dom/html/test/forms/test_autocompleteinfo.html
+++ b/dom/html/test/forms/test_autocompleteinfo.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 Test getAutocompleteInfo() on <input> and <select>
 -->
 <head>
   <title>Test for getAutocompleteInfo()</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
   <form>
     <input id="input"/>
--- a/dom/html/test/test_bug1166138.html
+++ b/dom/html/test/test_bug1166138.html
@@ -2,17 +2,16 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1166138
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1166138</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.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=1166138">Mozilla Bug 1166138</a>
   <p id="display"></p>
   <div id="content" style="display: none">
   </div>
 
--- a/dom/html/test/test_bug1322678.html
+++ b/dom/html/test/test_bug1322678.html
@@ -1,17 +1,16 @@
 <!DOCTYPE html>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1322678
 -->
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>Test for Bug 1322678</title>
-<script src="/tests/SimpleTest/AddTask.js"></script>
 <script src="/tests/SimpleTest/EventUtils.js"></script>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <script type="text/javascript">
 
 const CUSTOM_TITLE = "Custom Title";
--- a/dom/html/test/test_bug347174_xslp.html
+++ b/dom/html/test/test_bug347174_xslp.html
@@ -8,54 +8,61 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script 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=347174">Mozilla Bug 347174</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-  
+
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 347174 **/
 // verifies that documents created with createDocument are born in "complete" state
 // (so we don't accidentally leave them in "interactive" state)
-window.readyStateText = [];
+function runTest() {
+  window.readyStateText = [];
+
+  function xmlLoaded(e) {
+      var xslDoc = document.implementation.createDocument("", "test", null);
+      xslDoc.async=false;
+      xslDoc.load("347174transform.xsl");
+
+      var processor = new XSLTProcessor();
+      processor.importStylesheet(xslDoc);
+
+      window.transformedDoc = processor.transformToDocument(xmlDoc);
 
-function xmlLoaded(e) {
-    var xslDoc = document.implementation.createDocument("", "test", null);
-    xslDoc.async=false;
-    xslDoc.load("347174transform.xsl");
-      
-    var processor = new XSLTProcessor();
-    processor.importStylesheet(xslDoc);
-    
-    window.transformedDoc = processor.transformToDocument(xmlDoc);
-      
-    showMessage("loaded: " + xmlDoc.readyState);
-    is(xmlDoc.readyState, "complete", "XML document.readyState should be 'complete' after transform");
-    SimpleTest.finish();
+      showMessage("loaded: " + xmlDoc.readyState);
+      is(xmlDoc.readyState, "complete", "XML document.readyState should be 'complete' after transform");
+      SimpleTest.finish();
+  }
+
+  var xmlDoc = document.implementation.createDocument("", "test", null);
+  showMessage("createDocument: " + xmlDoc.readyState);
+  is(xmlDoc.readyState, "complete", "created document readyState should be 'complete' before being associated with a parser");
+  xmlDoc.async=true;
+  xmlDoc.addEventListener("load", xmlLoaded);
+  xmlDoc.load("347174transformable.xml");
+  showMessage("load called: " + xmlDoc.readyState);
+  isnot(xmlDoc.readyState, "complete", "created document readyState should not be 'complete' after load called");
 }
 
-var xmlDoc = document.implementation.createDocument("", "test", null);
-showMessage("createDocument: " + xmlDoc.readyState);
-is(xmlDoc.readyState, "complete", "created document readyState should be 'complete' before being associated with a parser");
-xmlDoc.async=true;
-xmlDoc.addEventListener("load", xmlLoaded);
-xmlDoc.load("347174transformable.xml");
-showMessage("load called: " + xmlDoc.readyState);
-isnot(xmlDoc.readyState, "complete", "created document readyState should not be 'complete' after load called");
-
 
 function showMessage(msg) {
     window.readyStateText.push(msg);
     $("display").innerHTML = readyStateText.join("<br>");
 }
 
 SimpleTest.waitForExplicitFinish();
 
+SpecialPowers.pushPrefEnv({"set": [
+  ["dom.xmldocument.async.enabled", true],
+  ["dom.xmldocument.load.enabled", true],
+]}, runTest);
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -44,22 +44,16 @@ function clearAllDatabases(callback) {
 var testHarnessGenerator = testHarnessSteps();
 testHarnessGenerator.next();
 
 function* testHarnessSteps() {
   function nextTestHarnessStep(val) {
     testHarnessGenerator.next(val);
   }
 
-  let script = document.createElement("script");
-  script.src = "/tests/SimpleTest/AddTask.js";
-  script.onload = nextTestHarnessStep;
-  document.head.appendChild(script);
-  yield undefined;
-
   let testScriptPath;
   let testScriptFilename;
 
   let scripts = document.getElementsByTagName("script");
   for (let i = 0; i < scripts.length; i++) {
     let src = scripts[i].src;
     let match = src.match(/indexedDB\/test\/unit\/(test_[^\/]+\.js)$/);
     if (match && match.length == 2) {
--- a/dom/ipc/JSWindowActorChild.cpp
+++ b/dom/ipc/JSWindowActorChild.cpp
@@ -4,16 +4,18 @@
  * 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 "mozilla/dom/JSWindowActorBinding.h"
 #include "mozilla/dom/JSWindowActorChild.h"
 #include "mozilla/dom/WindowGlobalChild.h"
 #include "mozilla/dom/WindowGlobalParent.h"
 #include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "nsGlobalWindowInner.h"
 
 namespace mozilla {
 namespace dom {
 
 JSObject* JSWindowActorChild::WrapObject(JSContext* aCx,
                                          JS::Handle<JSObject*> aGivenProto) {
   return JSWindowActorChild_Binding::Wrap(aCx, this, aGivenProto);
 }
@@ -61,17 +63,17 @@ class AsyncMessageToParent : public Runn
 }  // anonymous namespace
 
 void JSWindowActorChild::SendAsyncMessage(JSContext* aCx,
                                           const nsAString& aMessageName,
                                           JS::Handle<JS::Value> aObj,
                                           JS::Handle<JS::Value> aTransfers,
                                           ErrorResult& aRv) {
   // If we've closed our channel already, just raise an exception.
-  if (NS_WARN_IF(mManager->IsClosed())) {
+  if (NS_WARN_IF(!mManager || mManager->IsClosed())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Serialize our object out to a StructuredCloneData.
   ipc::StructuredCloneData data;
   if (!aObj.isUndefined() && !nsFrameMessageManager::GetParamsForMessage(
                                  aCx, aObj, aTransfers, data)) {
@@ -103,16 +105,43 @@ void JSWindowActorChild::SendAsyncMessag
 
   if (!mManager->SendAsyncMessage(mName, PromiseFlatString(aMessageName),
                                   msgData)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 }
 
+Document* JSWindowActorChild::GetDocument(ErrorResult& aRv) {
+  if (!mManager) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  nsGlobalWindowInner* window = mManager->WindowGlobal();
+  return window ? window->GetDocument() : nullptr;
+}
+
+BrowsingContext* JSWindowActorChild::GetBrowsingContext(ErrorResult& aRv) {
+  if (!mManager) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
+  }
+
+  return mManager->BrowsingContext();
+}
+
+Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
+    ErrorResult& aRv) {
+  if (BrowsingContext* bc = GetBrowsingContext(aRv)) {
+    return WindowProxyHolder(bc);
+  }
+  return nullptr;
+}
+
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorChild)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActorChild)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActorChild)
 
--- a/dom/ipc/JSWindowActorChild.h
+++ b/dom/ipc/JSWindowActorChild.h
@@ -12,17 +12,22 @@
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
 namespace mozilla {
 namespace dom {
 
+template <typename>
+struct Nullable;
+
+class Document;
 class WindowGlobalChild;
+class WindowProxyHolder;
 
 }  // namespace dom
 }  // namespace mozilla
 
 namespace mozilla {
 namespace dom {
 
 class JSWindowActorChild final : public nsISupports, public nsWrapperCache {
@@ -45,16 +50,20 @@ class JSWindowActorChild final : public 
   }
 
   WindowGlobalChild* Manager() const;
   void Init(const nsAString& aName, WindowGlobalChild* aManager);
   void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
                         JS::Handle<JS::Value> aObj,
                         JS::Handle<JS::Value> aTransfers, ErrorResult& aRv);
 
+  Document* GetDocument(ErrorResult& aRv);
+  BrowsingContext* GetBrowsingContext(ErrorResult& aRv);
+  Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv);
+
  private:
   nsString mName;
   RefPtr<WindowGlobalChild> mManager;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/dom/ipc/JSWindowActorParent.cpp
+++ b/dom/ipc/JSWindowActorParent.cpp
@@ -60,17 +60,17 @@ class AsyncMessageToChild : public Runna
 }  // anonymous namespace
 
 void JSWindowActorParent::SendAsyncMessage(JSContext* aCx,
                                            const nsAString& aMessageName,
                                            JS::Handle<JS::Value> aObj,
                                            JS::Handle<JS::Value> aTransfers,
                                            ErrorResult& aRv) {
   // If we've closed our channel already, just raise a warning.
-  if (NS_WARN_IF(mManager->IsClosed())) {
+  if (NS_WARN_IF(!mManager || mManager->IsClosed())) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   // Serialize our object out to a StructuredCloneData.
   ipc::StructuredCloneData data;
   if (!aObj.isUndefined() && !nsFrameMessageManager::GetParamsForMessage(
                                  aCx, aObj, aTransfers, data)) {
--- a/dom/ipc/tests/test_JSWindowActor.xul
+++ b/dom/ipc/tests/test_JSWindowActor.xul
@@ -1,16 +1,15 @@
 <?xml version="1.0"?>
 <?xml-stylesheet href="chrome://global/skin" type="text/css"?>
 <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
                  type="text/css"?>
 <window title="Test JSWindowActor"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
 
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
   const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
--- a/dom/ipc/tests/test_force_oop_iframe.html
+++ b/dom/ipc/tests/test_force_oop_iframe.html
@@ -1,13 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/ChromeTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 
 <script type="application/javascript">
 "use strict";
 /* eslint-env mozilla/frame-script */
--- a/dom/media/doctor/gtest/TestMultiWriterQueue.cpp
+++ b/dom/media/doctor/gtest/TestMultiWriterQueue.cpp
@@ -151,16 +151,17 @@ static void TestMultiWriterQueueMT(int a
       "alloc=%d (w %d)\n",
       aPrintPrefix, aWriterThreads, aReaderThreads, loops, pushes, duration,
       pushes / duration, q.LiveBuffersStats().mCount,
       q.LiveBuffersStats().mWatermark, q.ReusableBuffersStats().mCount,
       q.ReusableBuffersStats().mWatermark, q.AllocatedBuffersStats().mCount,
       q.AllocatedBuffersStats().mWatermark);
 }
 
+#if !defined(_M_ARM64)
 TEST(MultiWriterQueue, MultiWriterSingleReader)
 {
   // Small BufferSize, to exercize the buffer management code.
   TestMultiWriterQueueMT<
       MultiWriterQueue<int, 10, MultiWriterQueueReaderLocking_None>>(
       1, 0, 2 * 1024 * 1024, "MultiWriterQueue<int, 10, Locking_None>");
   TestMultiWriterQueueMT<
       MultiWriterQueue<int, 10, MultiWriterQueueReaderLocking_None>>(
@@ -210,17 +211,19 @@ TEST(MultiWriterQueue, MultiWriterSingle
       "MultiWriterQueue<int, DefaultBufferSize, Locking_None>");
 
   // DEBUG-mode thread-safety checks should make the following (multi-reader
   // with no locking) crash; uncomment to verify.
   // TestMultiWriterQueueMT<
   //   MultiWriterQueue<int, MultiWriterQueueDefaultBufferSize,
   //   MultiWriterQueueReaderLocking_None>>(64, 2, 2*1024*1024);
 }
+#endif
 
+#if !defined(_M_ARM64)
 TEST(MultiWriterQueue, MultiWriterMultiReader)
 {
   static_assert(
       mozilla::IsSame<MultiWriterQueue<int, 10>,
                       MultiWriterQueue<
                           int, 10, MultiWriterQueueReaderLocking_Mutex>>::value,
       "MultiWriterQueue reader locking should use Mutex by default");
 
@@ -267,16 +270,17 @@ TEST(MultiWriterQueue, MultiWriterMultiR
 
   // A more real-life buffer size.
   TestMultiWriterQueueMT<
       MultiWriterQueue<int, MultiWriterQueueDefaultBufferSize,
                        MultiWriterQueueReaderLocking_Mutex>>(
       64, 32, 1024 * 1024,
       "MultiWriterQueue<int, DefaultBufferSize, Locking_Mutex>");
 }
+#endif
 
 // Single-threaded use only.
 struct DequeWrapperST {
   nsDeque mDQ;
 
   bool Push(int i) {
     mDQ.PushFront(reinterpret_cast<void*>(static_cast<uintptr_t>(i)));
     return true;
--- a/dom/media/ipc/RemoteDecoderParent.cpp
+++ b/dom/media/ipc/RemoteDecoderParent.cpp
@@ -154,19 +154,16 @@ mozilla::ipc::IPCResult RemoteDecoderPar
 
 void RemoteDecoderParent::ActorDestroy(ActorDestroyReason aWhy) {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(OnManagerThread());
   if (mDecoder) {
     mDecoder->Shutdown();
     mDecoder = nullptr;
   }
-  if (mDecodeTaskQueue) {
-    mDecodeTaskQueue->BeginShutdown();
-  }
 }
 
 void RemoteDecoderParent::Error(const MediaResult& aError) {
   MOZ_ASSERT(OnManagerThread());
   if (!mDestroyed) {
     Unused << SendError(aError);
   }
 }
--- a/dom/media/test/test_cloneElementVisually_ended_video.html
+++ b/dom/media/test/test_cloneElementVisually_ended_video.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>Clone</h1>
 </div>
--- a/dom/media/test/test_cloneElementVisually_mediastream.html
+++ b/dom/media/test/test_cloneElementVisually_mediastream.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>MediaStream</h1>
   <video id="streamTarget"></video>
--- a/dom/media/test/test_cloneElementVisually_no_suspend.html
+++ b/dom/media/test/test_cloneElementVisually_no_suspend.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>MediaStream</h1>
   <video id="streamTarget"></video>
--- a/dom/media/test/test_cloneElementVisually_paused.html
+++ b/dom/media/test/test_cloneElementVisually_paused.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>Clone</h1>
 </div>
--- a/dom/media/test/test_cloneElementVisually_poster.html
+++ b/dom/media/test/test_cloneElementVisually_poster.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>Clone</h1>
 </div>
--- a/dom/media/test/test_cloneElementVisually_resource_change.html
+++ b/dom/media/test/test_cloneElementVisually_resource_change.html
@@ -1,16 +1,15 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <meta charset="utf-8">
   <title>Test cloneElementVisually</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="https://example.com:443/tests/dom/media/test/cloneElementVisually_helpers.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
 </head>
 <body>
 <div id="content">
   <h1>Original</h1>
   <video id="original"></video>
   <h1>MediaStream</h1>
   <video id="streamTarget"></video>
--- a/dom/network/tests/test_tcpsocket_client_and_server_basics.html
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -6,17 +6,16 @@ separate xpcshell incarnations.  This mi
 of bug 1084245 in order to get coverage of the tests from content.
 
 https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 1084245</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
     function createServer(port, options, backlog) {
       return new TCPServerSocket(port, options, backlog);
     }
 
     function createSocket(host, port, options) {
       return new TCPSocket(host, port, options);
--- a/dom/network/tests/test_tcpsocket_jsm.html
+++ b/dom/network/tests/test_tcpsocket_jsm.html
@@ -1,17 +1,16 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for 1207090</title>
   <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/AddTask.js"></script>
   <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
     ChromeUtils.import("chrome://mochitests/content/chrome/dom/network/tests/tcpsocket_test.jsm", window);
   </script>
   <script type="application/javascript" src="test_tcpsocket_client_and_server_basics.js"></script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1207090">Mozilla Bug 1207090</a>
--- a/dom/notification/NotificationDB.jsm
+++ b/dom/notification/NotificationDB.jsm
@@ -4,45 +4,51 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = [];
 
 const DEBUG = false;
 function debug(s) { dump("-*- NotificationDB component: " + s + "\n"); }
 
-const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
-
-ChromeUtils.defineModuleGetter(this, "Services",
-                               "resource://gre/modules/Services.jsm");
-
-const NOTIFICATION_STORE_DIR = OS.Constants.Path.profileDir;
-const NOTIFICATION_STORE_PATH =
-        OS.Path.join(NOTIFICATION_STORE_DIR, "notificationstore.json");
+ChromeUtils.defineModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "KeyValueService", "resource://gre/modules/kvstore.jsm");
+ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
+ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
 
 const kMessages = [
   "Notification:Save",
   "Notification:Delete",
   "Notification:GetAll",
 ];
 
+// Given its origin and ID, produce the key that uniquely identifies
+// a notification.
+function makeKey(origin, id) {
+  return origin.concat("\t", id);
+}
+
 var NotificationDB = {
 
   // Ensure we won't call init() while xpcom-shutdown is performed
   _shutdownInProgress: false,
 
+  // A handle to the kvstore, retrieved lazily when we load the data.
+  _store: null,
+
+  // A promise that resolves once the store has been loaded.
+  // The promise doesn't resolve to a value; it merely captures the state
+  // of the load via its resolution.
+  _loadPromise: null,
+
   init() {
     if (this._shutdownInProgress) {
       return;
     }
 
-    this.notifications = {};
-    this.byTag = {};
-    this.loaded = false;
-
     this.tasks = []; // read/write operation queue
     this.runningTask = null;
 
     Services.obs.addObserver(this, "xpcom-shutdown");
     this.registerListeners();
   },
 
   registerListeners() {
@@ -80,78 +86,77 @@ var NotificationDB = {
         if (DEBUG) debug("Origin " + origin + " is not linked to an app manifest, deleting.");
         delete notifications[origin];
       }
     }
 
     return notifications;
   },
 
-  // Attempt to read notification file, if it's not there we will create it.
-  load() {
-    var promise = OS.File.read(NOTIFICATION_STORE_PATH, { encoding: "utf-8"});
-    return promise.then(
-      data => {
-        if (data.length > 0) {
-          // Preprocessing phase intends to cleanly separate any migration-related
-          // tasks.
-          this.notifications = this.filterNonAppNotifications(JSON.parse(data));
-        }
+  async maybeMigrateData() {
+    // We avoid using OS.File until we know we're going to migrate data
+    // to avoid the performance cost of loading that module.
+    const oldStore = FileUtils.getFile("ProfD", ["notificationstore.json"]);
+
+    if (!oldStore.exists()) {
+      if (DEBUG) { debug("Old store doesn't exist; not migrating data."); }
+      return;
+    }
 
-        // populate the list of notifications by tag
-        if (this.notifications) {
-          for (var origin in this.notifications) {
-            this.byTag[origin] = {};
-            for (var id in this.notifications[origin]) {
-              var curNotification = this.notifications[origin][id];
-              if (curNotification.tag) {
-                this.byTag[origin][curNotification.tag] = curNotification;
-              }
-            }
-          }
+    let data;
+    try {
+      data = await OS.File.read(oldStore.path, { encoding: "utf-8"});
+    } catch (ex) {
+      // If read failed, we assume we have no notifications to migrate.
+      if (DEBUG) { debug("Failed to read old store; not migrating data."); }
+      return;
+    } finally {
+      // Finally, delete the old file so we don't try to migrate it again.
+      await OS.File.remove(oldStore.path);
+    }
+
+    if (data.length > 0) {
+      // Preprocessing phase intends to cleanly separate any migration-related
+      // tasks.
+      //
+      // NB: This code existed before we migrated the data to a kvstore,
+      // and the "migration-related tasks" it references are from an earlier
+      // migration.  We used to do it every time we read the JSON file;
+      // now we do it once, when migrating the JSON file to the kvstore.
+      const notifications = this.filterNonAppNotifications(JSON.parse(data));
+
+      // Copy the data from the JSON file to the kvstore.
+      // TODO: use a transaction to improve the performance of these operations
+      // once the kvstore API supports it (bug 1515096).
+      for (const origin in notifications) {
+        for (const id in notifications[origin]) {
+          await this._store.put(makeKey(origin, id),
+            JSON.stringify(notifications[origin][id]));
         }
-
-        this.loaded = true;
-      },
-
-      // If read failed, we assume we have no notifications to load.
-      reason => {
-        this.loaded = true;
-        return this.createStore();
       }
-    );
+    }
   },
 
-  // Creates the notification directory.
-  createStore() {
-    var promise = OS.File.makeDir(NOTIFICATION_STORE_DIR, {
-      ignoreExisting: true,
-    });
-    return promise.then(
-      this.createFile.bind(this)
-    );
-  },
+  // Attempt to read notification file, if it's not there we will create it.
+  async load() {
+    // Get and cache a handle to the kvstore.
+    const dir = FileUtils.getDir("ProfD", ["notificationstore"], true);
+    this._store = await KeyValueService.getOrCreate(dir.path, "notifications");
 
-  // Creates the notification file once the directory is created.
-  createFile() {
-    return OS.File.writeAtomic(NOTIFICATION_STORE_PATH, "");
-  },
-
-  // Save current notifications to the file.
-  save() {
-    var data = JSON.stringify(this.notifications);
-    return OS.File.writeAtomic(NOTIFICATION_STORE_PATH, data, { encoding: "utf-8"});
+    // Migrate data from the old JSON file to the new kvstore if the old file
+    // is present in the user's profile directory.
+    await this.maybeMigrateData();
   },
 
   // Helper function: promise will be resolved once file exists and/or is loaded.
   ensureLoaded() {
-    if (!this.loaded) {
-      return this.load();
+    if (!this._loadPromise) {
+      this._loadPromise = this.load();
     }
-      return Promise.resolve();
+    return this._loadPromise;
   },
 
   receiveMessage(message) {
     if (DEBUG) { debug("Received message:" + message.name); }
 
     // sendAsyncMessage can fail if the child process exits during a
     // notification storage operation, so always wrap it in a try/catch.
     function returnMessage(name, data) {
@@ -212,21 +217,17 @@ var NotificationDB = {
 
   // We need to make sure any read/write operations are atomic,
   // so use a queue to run each operation sequentially.
   queueTask(operation, data) {
     if (DEBUG) { debug("Queueing task: " + operation); }
 
     var defer = {};
 
-    this.tasks.push({
-      operation,
-      data,
-      defer,
-    });
+    this.tasks.push({ operation, data, defer });
 
     var promise = new Promise(function(resolve, reject) {
       defer.resolve = resolve;
       defer.reject = reject;
     });
 
     // Only run immediately if we aren't currently running another task.
     if (!this.runningTask) {
@@ -254,21 +255,19 @@ var NotificationDB = {
         case "getall":
           return this.taskGetAll(task.data);
 
         case "save":
           return this.taskSave(task.data);
 
         case "delete":
           return this.taskDelete(task.data);
+      }
 
-        default:
-          return Promise.reject(
-            new Error(`Found a task with unknown operation ${task.operation}`));
-      }
+      throw new Error(`Unknown task operation: ${task.operation}`);
     })
     .then(payload => {
       if (DEBUG) {
         debug("Finishing task: " + this.runningTask.operation);
       }
       this.runningTask.defer.resolve(payload);
     })
     .catch(err => {
@@ -277,76 +276,60 @@ var NotificationDB = {
       }
       this.runningTask.defer.reject(err);
     })
     .then(() => {
       this.runNextTask();
     });
   },
 
-  taskGetAll(data) {
+  enumerate(origin) {
+    // The "from" and "to" key parameters to nsIKeyValueStore.enumerate()
+    // are inclusive and exclusive, respectively, and keys are tuples
+    // of origin and ID joined by a tab (\t), which is character code 9;
+    // so enumerating ["origin", "origin\n"), where the line feed (\n)
+    // is character code 10, enumerates all pairs with the given origin.
+    return this._store.enumerate(origin, `${origin}\n`);
+  },
+
+  async taskGetAll(data) {
     if (DEBUG) { debug("Task, getting all"); }
     var origin = data.origin;
     var notifications = [];
-    // Grab only the notifications for specified origin.
-    if (this.notifications[origin]) {
-      if (data.tag) {
-        let n;
-        if ((n = this.byTag[origin][data.tag])) {
-          notifications.push(n);
-        }
-      } else {
-        for (var i in this.notifications[origin]) {
-          notifications.push(this.notifications[origin][i]);
-        }
-      }
+
+    for (const {value} of await this.enumerate(origin)) {
+      notifications.push(JSON.parse(value));
     }
-    return Promise.resolve(notifications);
+
+    if (data.tag) {
+      notifications = notifications.filter(n => n.tag === data.tag);
+    }
+
+    return notifications;
   },
 
-  taskSave(data) {
+  async taskSave(data) {
     if (DEBUG) { debug("Task, saving"); }
     var origin = data.origin;
     var notification = data.notification;
-    if (!this.notifications[origin]) {
-      this.notifications[origin] = {};
-      this.byTag[origin] = {};
-    }
 
     // We might have existing notification with this tag,
     // if so we need to remove it before saving the new one.
     if (notification.tag) {
-      var oldNotification = this.byTag[origin][notification.tag];
-      if (oldNotification) {
-        delete this.notifications[origin][oldNotification.id];
+      for (const {key, value} of await this.enumerate(origin)) {
+        const oldNotification = JSON.parse(value);
+        if (oldNotification.tag === notification.tag) {
+          await this._store.delete(key);
+        }
       }
-      this.byTag[origin][notification.tag] = notification;
     }
 
-    this.notifications[origin][notification.id] = notification;
-    return this.save();
+    await this._store.put(makeKey(origin, notification.id),
+      JSON.stringify(notification));
   },
 
-  taskDelete(data) {
+  async taskDelete(data) {
     if (DEBUG) { debug("Task, deleting"); }
-    var origin = data.origin;
-    var id = data.id;
-    if (!this.notifications[origin]) {
-      if (DEBUG) { debug("No notifications found for origin: " + origin); }
-      return Promise.resolve();
-    }
-
-    // Make sure we can find the notification to delete.
-    var oldNotification = this.notifications[origin][id];
-    if (!oldNotification) {
-      if (DEBUG) { debug("No notification found with id: " + id); }
-      return Promise.resolve();
-    }
-
-    if (oldNotification.tag) {
-      delete this.byTag[origin][oldNotification.tag];
-    }
-    delete this.notifications[origin][id];
-    return this.save();
+    await this._store.delete(makeKey(data.origin, data.id));
   },
 };
 
 NotificationDB.init();
--- a/dom/notification/test/unit/head_notificationdb.js
+++ b/dom/notification/test/unit/head_notificationdb.js
@@ -1,23 +1,25 @@
 "use strict";
 
 var {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-function getNotificationObject(app, id, tag) {
+function getNotificationObject(app, id, tag, includeScope) {
+  const origin = `https://${app}.gaiamobile.org/`;
   return {
-    origin: "https://" + app + ".gaiamobile.org/",
+    origin,
     id,
     title: app + "Notification:" + Date.now(),
     dir: "auto",
     lang: "",
     body: app + " notification body",
     tag: tag || "",
     icon: "icon.png",
+    serviceWorkerRegistrationScope: includeScope ? origin : undefined,
   };
 }
 
 var systemNotification =
   getNotificationObject("system", "{2bc883bf-2809-4432-b0f4-f54e10372764}");
 
 var calendarNotification =
   getNotificationObject("calendar", "{d8d11299-a58e-429b-9a9a-57c562982fbf}");
--- a/dom/notification/test/unit/test_notificationdb_bug1024090.js
+++ b/dom/notification/test/unit/test_notificationdb_bug1024090.js
@@ -4,18 +4,18 @@ function run_test() {
   do_get_profile();
   run_next_test();
 }
 
 // For bug 1024090: test edge case of notificationstore.json
 add_test(function test_bug1024090_purge() {
   const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
   const NOTIFICATION_STORE_PATH =
-    OS.Path.join(OS.Constants.Path.profileDir, "notificationstore.json");
-  let cleanup = OS.File.writeAtomic(NOTIFICATION_STORE_PATH, "");
+    OS.Path.join(OS.Constants.Path.profileDir, "notificationstore");
+  let cleanup = OS.File.removeDir(NOTIFICATION_STORE_PATH);
   cleanup.then(
     function onSuccess() {
       ok(true, "Notification database cleaned.");
     },
     function onError(reason) {
       ok(false, "Notification database error when cleaning: " + reason);
     }
   ).then(function next() {
new file mode 100644
--- /dev/null
+++ b/dom/notification/test/unit/test_notificationdb_migration.js
@@ -0,0 +1,87 @@
+"use strict";
+
+const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
+
+const fooNotification =
+  getNotificationObject("foo", "a4f1d54a-98b7-4231-9120-5afc26545bad", null, true);
+const barNotification =
+  getNotificationObject("bar", "a4f1d54a-98b7-4231-9120-5afc26545bad", "baz", true);
+const msg = "Notification:GetAll";
+const msgReply = "Notification:GetAll:Return:OK";
+
+do_get_profile();
+const OLD_STORE_PATH =
+    OS.Path.join(OS.Constants.Path.profileDir, "notificationstore.json");
+
+let nextRequestID = 0;
+
+async function createOldDatastore() {
+  const notifications = {
+    [fooNotification.origin]: {
+      [fooNotification.id]: fooNotification,
+    },
+    [barNotification.origin]: {
+      [barNotification.id]: barNotification,
+    },
+  };
+
+  await OS.File.writeAtomic(OLD_STORE_PATH, JSON.stringify(notifications));
+}
+
+// Create the old datastore and populate it with data before we initialize
+// the notification database so it has data to migrate.  This is a setup step,
+// not a test, but it seems like we need to do it in a test function
+// rather than in run_test() because the test runner doesn't handle async steps
+// in run_test().
+add_task(async function test_create_old_datastore() {
+  await createOldDatastore();
+  startNotificationDB();
+});
+
+add_test(function test_get_system_notification() {
+  const requestID = nextRequestID++;
+  const msgHandler = function(message) {
+    Assert.equal(requestID, message.data.requestID);
+    Assert.equal(0, message.data.notifications.length);
+  };
+
+  addAndSend(msg, msgReply, msgHandler, {
+    origin: systemNotification.origin,
+    requestID,
+  });
+});
+
+add_test(function test_get_foo_notification() {
+  const requestID = nextRequestID++;
+  const msgHandler = function(message) {
+    Assert.equal(requestID, message.data.requestID);
+    Assert.equal(1, message.data.notifications.length);
+    Assert.deepEqual(fooNotification, message.data.notifications[0],
+      "Notification data migrated");
+  };
+
+  addAndSend(msg, msgReply, msgHandler, {
+    origin: fooNotification.origin,
+    requestID,
+  });
+});
+
+add_test(function test_get_bar_notification() {
+  const requestID = nextRequestID++;
+  const msgHandler = function(message) {
+    Assert.equal(requestID, message.data.requestID);
+    Assert.equal(1, message.data.notifications.length);
+    Assert.deepEqual(barNotification, message.data.notifications[0],
+      "Notification data migrated");
+  };
+
+  addAndSend(msg, msgReply, msgHandler, {
+    origin: barNotification.origin,
+    requestID,
+  });
+});
+
+add_task(async function test_old_datastore_deleted() {
+    Assert.ok(!await OS.File.exists(OLD_STORE_PATH),
+      "old datastore no longer exists");
+});
--- a/dom/notification/test/unit/xpcshell.ini
+++ b/dom/notification/test/unit/xpcshell.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 head = head_notificationdb.js
 skip-if = toolkit == 'android'
 
 [test_notificationdb.js]
 [test_notificationdb_bug1024090.js]
+[test_notificationdb_migration.js]
--- a/dom/plugins/test/mochitest/test_bug1028200-1.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-1.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Cancelled, div[F] -> iframe -> iframe -> plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-2.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-2.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Remains, div[F];plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-3.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-3.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Cancelled, iframe[F] -> iframe -> div -> plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-4.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-4.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Cancelled, iframe[F]  -> iframe -> div -> plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-5.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-5.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Cancelled, div[F] -> plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-6.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-6.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Remains, iframe[F];plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/plugins/test/mochitest/test_bug1028200-7.html
+++ b/dom/plugins/test/mochitest/test_bug1028200-7.html
@@ -1,12 +1,11 @@
 <head>
   <meta charset="utf-8">
   <title>Plugin Crash, FullScreenElement Remains, iframe -> div[F]; iframe -> iframe -> plugin</title>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="plugin-utils.js"></script>
 </head>
 
 <body onLoad="load()">
   <script class="testbody" type="application/javascript">
     setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
     SimpleTest.expectChildProcessCrash();
--- a/dom/prototype/PrototypeDocumentContentSink.cpp
+++ b/dom/prototype/PrototypeDocumentContentSink.cpp
@@ -319,17 +319,19 @@ nsresult PrototypeDocumentContentSink::P
 
   // Add the root element
   rv = CreateElementFromPrototype(proto, getter_AddRefs(root), true);
   if (NS_FAILED(rv)) return rv;
 
   rv = mDocument->AppendChildTo(root, false);
   if (NS_FAILED(rv)) return rv;
 
-  mDocument->DocumentStatesChanged(NS_DOCUMENT_STATE_RTL_LOCALE);
+  // TODO(emilio): Should this really notify? We don't notify of appends anyhow,
+  // and we just appended the root so no styles can possibly depend on it.
+  mDocument->UpdateDocumentStates(NS_DOCUMENT_STATE_RTL_LOCALE, true);
 
   nsContentUtils::AddScriptRunner(
       new nsDocElementCreatedNotificationRunner(mDocument));
 
   // There'd better not be anything on the context stack at this
   // point! This is the basis case for our "induction" in
   // ResumeWalk(), below, which'll assume that there's always a
   // content element on the context stack if we're in the document.
--- a/dom/push/test/test_data.html
+++ b/dom/push/test/test_data.html
@@ -5,17 +5,16 @@ Bug 1185544: Add data delivery to the We
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1185544</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/webpush.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1185544">Mozilla Bug 1185544</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/push/test/test_error_reporting.html
+++ b/dom/push/test/test_error_reporting.html
@@ -5,17 +5,16 @@ Bug 1246341: Report message delivery fai
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1246341</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1246341">Mozilla Bug 1246341</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_has_permissions.html
+++ b/dom/push/test/test_has_permissions.html
@@ -5,17 +5,16 @@ Bug 1038811: Push tests.
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1038811</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_permissions.html
+++ b/dom/push/test/test_permissions.html
@@ -5,17 +5,16 @@ Bug 1038811: Push tests.
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1038811</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_register.html
+++ b/dom/push/test/test_register.html
@@ -5,17 +5,16 @@ Bug 1038811: Push tests.
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1038811</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1038811">Mozilla Bug 1038811</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_register_key.html
+++ b/dom/push/test/test_register_key.html
@@ -5,17 +5,16 @@ Bug 1247685: Implement `applicationServe
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1247685</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1247685">Mozilla Bug 1247685</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_subscription_change.html
+++ b/dom/push/test/test_subscription_change.html
@@ -5,17 +5,16 @@ Bug 1205109: Make `pushsubscriptionchang
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1205109</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1205109">Mozilla Bug 1205109</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/push/test/test_unregister.html
+++ b/dom/push/test/test_unregister.html
@@ -5,17 +5,16 @@ Bug 1170817: Push tests.
 
 Any copyright is dedicated to the Public Domain.
 http://creativecommons.org/licenses/publicdomain/
 
 -->
 <head>
   <title>Test for Bug 1170817</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script type="text/javascript" src="/tests/dom/push/test/test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1170817">Mozilla Bug 1170817</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/quota/test/helpers.js
+++ b/dom/quota/test/helpers.js
@@ -156,18 +156,16 @@ function* testHarnessSteps()
   yield* loadScript("head-shared.js");
 
   // Now run the test script in the main thread.
   if (testSteps.constructor.name === "AsyncFunction") {
     SimpleTest.registerCleanupFunction(async function() {
       await requestFinished(clearAllDatabases());
     });
 
-    yield* loadScript("/tests/SimpleTest/AddTask.js");
-
     add_task(testSteps);
   } else {
     testGenerator.next();
 
     yield undefined;
   }
 }
 
--- a/dom/serviceworkers/test/test_async_waituntil.html
+++ b/dom/serviceworkers/test/test_async_waituntil.html
@@ -5,17 +5,16 @@
   1. waitUntil() waits for each individual promise separately, even if
      one of them was rejected.
   2. waitUntil() can be called asynchronously as long as there is still
      a pending extension promise.
   -->
 <head>
   <title>Test for Bug 1263304</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1263304">Mozilla Bug 1263304</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
--- a/dom/serviceworkers/test/test_bug1408734.html
+++ b/dom/serviceworkers/test/test_bug1408734.html
@@ -3,17 +3,16 @@
   http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Bug 1408734</title>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
   <script src="utils.js"></script>
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
--- a/dom/serviceworkers/test/test_devtools_bypass_serviceworker.html
+++ b/dom/serviceworkers/test/test_devtools_bypass_serviceworker.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title> Verify devtools can utilize nsIChannel::LOAD_BYPASS_SERVICE_WORKER to bypass the service worker </title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <body>
 <div id="content" style="display: none"></div>
 <script src="utils.js"></script>
 <script type="text/javascript">
--- a/dom/serviceworkers/test/test_error_reporting.html
+++ b/dom/serviceworkers/test/test_error_reporting.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Error Reporting of Service Worker Failures</title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
   <script src="utils.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">
 </head>
 <body>
 
 <script type="text/javascript">
--- a/dom/serviceworkers/test/test_fetch_event.html
+++ b/dom/serviceworkers/test/test_fetch_event.html
@@ -69,15 +69,16 @@
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.xmldocument.load.enabled", true],
     ["javascript.options.streams", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/serviceworkers/test/test_fetch_event_with_thirdpartypref.html
+++ b/dom/serviceworkers/test/test_fetch_event_with_thirdpartypref.html
@@ -77,16 +77,17 @@
 
   const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
+    ["dom.xmldocument.load.enabled", true],
     ["javascript.options.streams", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/serviceworkers/test/test_fetch_integrity.html
+++ b/dom/serviceworkers/test/test_fetch_integrity.html
@@ -1,14 +1,13 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title> Test fetch.integrity on console report for serviceWorker and sharedWorker </title>
   <script src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="/tests/SimpleTest/AddTask.js"></script>
   <script src="error_reporting_helpers.js"></script>
   <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
   <meta http-equiv="Content-type" content="text/html;charset=UTF-8">