Merge autoland to mozilla-central. a=merge
authorTiberius Oros <toros@mozilla.com>
Thu, 01 Mar 2018 19:10:44 +0200
changeset 461054 bf8a7dceffc4d68aca26b4ffc0577b1797f93601
parent 460786 426ef843d356879b3d1079d0ea7d867ff5a71b8c (current diff)
parent 461015 22c153aa809b6daa02728795044b3506f4f1aba5 (diff)
child 461055 40ba247350dad945cccd29680ee53d578c006420
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone60.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 autoland to mozilla-central. a=merge
browser/extensions/shield-recipe-client/bootstrap.js
browser/extensions/shield-recipe-client/content/AboutPages.jsm
browser/extensions/shield-recipe-client/content/about-studies/about-studies.css
browser/extensions/shield-recipe-client/content/about-studies/about-studies.html
browser/extensions/shield-recipe-client/content/about-studies/about-studies.js
browser/extensions/shield-recipe-client/content/about-studies/common.js
browser/extensions/shield-recipe-client/content/about-studies/img/shield-logo.png
browser/extensions/shield-recipe-client/content/about-studies/shield-studies.js
browser/extensions/shield-recipe-client/content/shield-content-frame.js
browser/extensions/shield-recipe-client/content/shield-content-process.js
browser/extensions/shield-recipe-client/docs/data-collection.rst
browser/extensions/shield-recipe-client/docs/index.rst
browser/extensions/shield-recipe-client/install.rdf.in
browser/extensions/shield-recipe-client/jar.mn
browser/extensions/shield-recipe-client/lib/ActionSandboxManager.jsm
browser/extensions/shield-recipe-client/lib/AddonStudies.jsm
browser/extensions/shield-recipe-client/lib/Addons.jsm
browser/extensions/shield-recipe-client/lib/CleanupManager.jsm
browser/extensions/shield-recipe-client/lib/ClientEnvironment.jsm
browser/extensions/shield-recipe-client/lib/EventEmitter.jsm
browser/extensions/shield-recipe-client/lib/FilterExpressions.jsm
browser/extensions/shield-recipe-client/lib/Heartbeat.jsm
browser/extensions/shield-recipe-client/lib/LogManager.jsm
browser/extensions/shield-recipe-client/lib/NormandyApi.jsm
browser/extensions/shield-recipe-client/lib/NormandyDriver.jsm
browser/extensions/shield-recipe-client/lib/PreferenceExperiments.jsm
browser/extensions/shield-recipe-client/lib/PreferenceFilters.jsm
browser/extensions/shield-recipe-client/lib/RecipeRunner.jsm
browser/extensions/shield-recipe-client/lib/Sampling.jsm
browser/extensions/shield-recipe-client/lib/SandboxManager.jsm
browser/extensions/shield-recipe-client/lib/ShieldPreferences.jsm
browser/extensions/shield-recipe-client/lib/ShieldRecipeClient.jsm
browser/extensions/shield-recipe-client/lib/Storage.jsm
browser/extensions/shield-recipe-client/lib/TelemetryEvents.jsm
browser/extensions/shield-recipe-client/lib/Uptake.jsm
browser/extensions/shield-recipe-client/lib/Utils.jsm
browser/extensions/shield-recipe-client/moz.build
browser/extensions/shield-recipe-client/skin/osx/Heartbeat.css
browser/extensions/shield-recipe-client/skin/shared/Heartbeat.css
browser/extensions/shield-recipe-client/skin/shared/heartbeat-icon.svg
browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-lit.svg
browser/extensions/shield-recipe-client/skin/shared/heartbeat-star-off.svg
browser/extensions/shield-recipe-client/test/.eslintrc.js
browser/extensions/shield-recipe-client/test/browser/.eslintrc.js
browser/extensions/shield-recipe-client/test/browser/action_server.sjs
browser/extensions/shield-recipe-client/test/browser/browser.ini
browser/extensions/shield-recipe-client/test/browser/browser_ActionSandboxManager.js
browser/extensions/shield-recipe-client/test/browser/browser_AddonStudies.js
browser/extensions/shield-recipe-client/test/browser/browser_Addons.js
browser/extensions/shield-recipe-client/test/browser/browser_CleanupManager.js
browser/extensions/shield-recipe-client/test/browser/browser_ClientEnvironment.js
browser/extensions/shield-recipe-client/test/browser/browser_EventEmitter.js
browser/extensions/shield-recipe-client/test/browser/browser_FilterExpressions.js
browser/extensions/shield-recipe-client/test/browser/browser_Heartbeat.js
browser/extensions/shield-recipe-client/test/browser/browser_LogManager.js
browser/extensions/shield-recipe-client/test/browser/browser_NormandyDriver.js
browser/extensions/shield-recipe-client/test/browser/browser_PreferenceExperiments.js
browser/extensions/shield-recipe-client/test/browser/browser_RecipeRunner.js
browser/extensions/shield-recipe-client/test/browser/browser_ShieldPreferences.js
browser/extensions/shield-recipe-client/test/browser/browser_ShieldRecipeClient.js
browser/extensions/shield-recipe-client/test/browser/browser_Storage.js
browser/extensions/shield-recipe-client/test/browser/browser_about_preferences.js
browser/extensions/shield-recipe-client/test/browser/browser_about_studies.js
browser/extensions/shield-recipe-client/test/browser/browser_bootstrap.js
browser/extensions/shield-recipe-client/test/browser/fixtures/addon-fixture/manifest.json
browser/extensions/shield-recipe-client/test/browser/fixtures/normandy.xpi
browser/extensions/shield-recipe-client/test/browser/head.js
browser/extensions/shield-recipe-client/test/unit/.eslintrc.js
browser/extensions/shield-recipe-client/test/unit/echo_server.sjs
browser/extensions/shield-recipe-client/test/unit/head_xpc.js
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/index.json
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/api/v1/recipe/signed/index.json
browser/extensions/shield-recipe-client/test/unit/invalid_recipe_signature_api/normandy.content-signature.mozilla.org-20210705.dev.chain
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/implementation/sha384-RGx3rydrSq53UfmW9kFcK0mQYra67XIvZvr4MhmAe--ljiiMQOtgM7Cmca48um3v
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/console-log/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/implementation/sha384-HM_avYcD00o27ufwU1V7PIBtiuMAXML6MMwlYrDEqDX-XzGVuOfL52RCM680JExN
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/opt-out-study/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/implementation/sha384-KQgG38GQ7KZAb2VIB48ANQO6nBcxZoLm2ORzUviRT5nAvSywyPjZ5cJIElw6iXIt
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/preference-experiment/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/implementation/sha384-dEGiyKPEln8Ns5cQHzGpMIGdirSAAX0X-Kwlu-U3sJ05yNbO-ANij_a6c5SyL7G4
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/show-heartbeat/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/action/signed/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/classify_client/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/api/v1/recipe/signed/index.json
browser/extensions/shield-recipe-client/test/unit/mock_api/normandy.content-signature.mozilla.org-20210705.dev.chain
browser/extensions/shield-recipe-client/test/unit/query_server.sjs
browser/extensions/shield-recipe-client/test/unit/test_NormandyApi.js
browser/extensions/shield-recipe-client/test/unit/test_Sampling.js
browser/extensions/shield-recipe-client/test/unit/test_SandboxManager.js
browser/extensions/shield-recipe-client/test/unit/test_Utils.js
browser/extensions/shield-recipe-client/test/unit/utils.js
browser/extensions/shield-recipe-client/test/unit/xpcshell.ini
browser/extensions/shield-recipe-client/vendor/LICENSE_THIRDPARTY
browser/extensions/shield-recipe-client/vendor/PropTypes.js
browser/extensions/shield-recipe-client/vendor/React.js
browser/extensions/shield-recipe-client/vendor/ReactDOM.js
browser/extensions/shield-recipe-client/vendor/classnames.js
browser/extensions/shield-recipe-client/vendor/mozjexl.js
devtools/client/inspector/fonts/test/browser_fontinspector_expand-details.js
devtools/client/netmonitor/src/utils/create-store.js
dom/events/test/test_bug742376.html
taskcluster/ci/push-apk-breakpoint/kind.yml
taskcluster/taskgraph/transforms/push_apk_breakpoint.py
taskcluster/taskgraph/util/push_apk.py
third_party/rust/itertools/src/adaptors/multipeek.rs
third_party/rust/itertools/src/lib.rs
third_party/rust/itertools/tests/tests.rs
--- a/accessible/base/XULMap.h
+++ b/accessible/base/XULMap.h
@@ -1,43 +1,49 @@
 /* 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/. */
 
+XULMAP_TYPE(browser, OuterDocAccessible)
+XULMAP_TYPE(button, XULButtonAccessible)
 XULMAP_TYPE(checkbox, XULCheckboxAccessible)
 XULMAP_TYPE(dropMarker, XULDropmarkerAccessible)
+XULMAP_TYPE(editor, OuterDocAccessible)
 XULMAP_TYPE(findbar, XULToolbarAccessible)
 XULMAP_TYPE(groupbox, XULGroupboxAccessible)
+XULMAP_TYPE(iframe, OuterDocAccessible)
 XULMAP_TYPE(listbox, XULListboxAccessibleWrap)
 XULMAP_TYPE(listhead, XULColumAccessible)
 XULMAP_TYPE(listheader, XULColumnItemAccessible)
 XULMAP_TYPE(listitem, XULListitemAccessible)
 XULMAP_TYPE(menu, XULMenuitemAccessibleWrap)
 XULMAP_TYPE(menubar, XULMenubarAccessible)
 XULMAP_TYPE(menucaption, XULMenuitemAccessibleWrap)
 XULMAP_TYPE(menuitem, XULMenuitemAccessibleWrap)
 XULMAP_TYPE(menulist, XULComboboxAccessible)
 XULMAP_TYPE(menuseparator, XULMenuSeparatorAccessible)
 XULMAP_TYPE(notification, XULAlertAccessible)
 XULMAP_TYPE(progressmeter, XULProgressMeterAccessible)
 XULMAP_TYPE(radio, XULRadioButtonAccessible)
 XULMAP_TYPE(radiogroup, XULRadioGroupAccessible)
 XULMAP_TYPE(richlistbox, XULListboxAccessibleWrap)
 XULMAP_TYPE(richlistitem, XULListitemAccessible)
+XULMAP_TYPE(scale, XULSliderAccessible)
 XULMAP_TYPE(statusbar, XULStatusBarAccessible)
 XULMAP_TYPE(tab, XULTabAccessible)
 XULMAP_TYPE(tabpanels, XULTabpanelsAccessible)
 XULMAP_TYPE(tabs, XULTabsAccessible)
 XULMAP_TYPE(toolbarseparator, XULToolbarSeparatorAccessible)
 XULMAP_TYPE(toolbarspacer, XULToolbarSeparatorAccessible)
 XULMAP_TYPE(toolbarspring, XULToolbarSeparatorAccessible)
 XULMAP_TYPE(treecol, XULColumnItemAccessible)
 XULMAP_TYPE(treecolpicker, XULButtonAccessible)
 XULMAP_TYPE(treecols, XULTreeColumAccessible)
 XULMAP_TYPE(toolbar, XULToolbarAccessible)
+XULMAP_TYPE(toolbarbutton, XULToolbarButtonAccessible)
 XULMAP_TYPE(tooltip, XULTooltipAccessible)
 
 XULMAP(
   image,
   [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
     if (aContent->IsElement() &&
         aContent->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::onclick)) {
       return new XULToolbarButtonAccessible(aContent, aContext->Document());
@@ -69,16 +75,54 @@ XULMAP(
       }
     }
 
     return nullptr;
   }
 )
 
 XULMAP(
+  menupopup,
+  [](nsIContent* aContent, Accessible* aContext) {
+    return CreateMenupopupAccessible(aContent, aContext);
+  }
+)
+
+XULMAP(
+  popup,
+  [](nsIContent* aContent, Accessible* aContext) {
+    return CreateMenupopupAccessible(aContent, aContext);
+  }
+)
+
+XULMAP(
+  textbox,
+  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+    if (aContent->IsElement() &&
+        aContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
+                                           nsGkAtoms::autocomplete, eIgnoreCase)) {
+      return new XULComboboxAccessible(aContent, aContext->Document());
+    }
+
+    return new EnumRoleAccessible<roles::SECTION>(aContent, aContext->Document());
+  }
+)
+
+XULMAP(
+  thumb,
+  [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
+    if (aContent->IsElement() &&
+        aContent->AsElement()->ClassList()->Contains(NS_LITERAL_STRING("scale-thumb"))) {
+      return new XULThumbAccessible(aContent, aContext->Document());
+    }
+    return nullptr;
+  }
+)
+
+XULMAP(
   tree,
   [](nsIContent* aContent, Accessible* aContext) -> Accessible* {
     nsIContent* child = nsTreeUtils::GetDescendantChild(aContent,
                                                         nsGkAtoms::treechildren);
     if (!child)
       return nullptr;
 
     nsTreeBodyFrame* treeFrame = do_QueryFrame(child->GetPrimaryFrame());
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -18,16 +18,17 @@
 #include "HTMLListAccessible.h"
 #include "HTMLSelectAccessible.h"
 #include "HTMLTableAccessibleWrap.h"
 #include "HyperTextAccessibleWrap.h"
 #include "RootAccessible.h"
 #include "nsAccUtils.h"
 #include "nsArrayUtils.h"
 #include "nsAttrName.h"
+#include "nsDOMTokenList.h"
 #include "nsEventShell.h"
 #include "nsIURI.h"
 #include "nsTextFormatter.h"
 #include "OuterDocAccessible.h"
 #include "Role.h"
 #ifdef MOZ_ACCESSIBILITY_ATK
 #include "RootAccessibleWrap.h"
 #endif
@@ -138,16 +139,38 @@ MustBeAccessible(nsIContent* aContent, D
   // for it.
   nsAutoString id;
   if (nsCoreUtils::GetID(aContent, id) && !id.IsEmpty())
     return aDocument->IsDependentID(id);
 
   return false;
 }
 
+/**
+ * Used by XULMap.h to map both menupopup and popup elements
+ */
+#ifdef MOZ_XUL
+Accessible*
+CreateMenupopupAccessible(nsIContent* aContent, Accessible* aContext)
+{
+#ifdef MOZ_ACCESSIBILITY_ATK
+    // ATK considers this node to be redundant when within menubars, and it makes menu
+    // navigation with assistive technologies more difficult
+    // XXX In the future we will should this for consistency across the nsIAccessible
+    // implementations on each platform for a consistent scripting environment, but
+    // then strip out redundant accessibles in the AccessibleWrap class for each platform.
+    nsIContent *parent = aContent->GetParent();
+    if (parent && parent->IsXULElement(nsGkAtoms::menu))
+      return nullptr;
+#endif
+
+    return new XULMenupopupAccessible(aContent, aContext->Document());
+}
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible constructors
 
 static Accessible*
 New_HTMLLink(nsIContent* aContent, Accessible* aContext)
 {
   // Only some roles truly enjoy life as HTMLLinkAccessibles, for details
   // see closed bug 494807.
@@ -1444,83 +1467,46 @@ nsAccessibilityService::Shutdown()
 }
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
                                                DocAccessible* aDoc)
 {
   nsAutoString role;
   nsCoreUtils::XBLBindingRole(aContent, role);
-  if (role.IsEmpty() || role.EqualsLiteral("none"))
+  if (role.IsEmpty())
     return nullptr;
 
-  if (role.EqualsLiteral("outerdoc")) {
-    RefPtr<Accessible> accessible = new OuterDocAccessible(aContent, aDoc);
-    return accessible.forget();
-  }
-
   RefPtr<Accessible> accessible;
 #ifdef MOZ_XUL
   // XUL controls
-  if (role.EqualsLiteral("xul:button")) {
-    accessible = new XULButtonAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:colorpicker")) {
+  if (role.EqualsLiteral("xul:colorpicker")) {
     accessible = new XULColorPickerAccessible(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:colorpickertile")) {
     accessible = new XULColorPickerTileAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:combobox")) {
-    accessible = new XULComboboxAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:link")) {
     accessible = new XULLinkAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:menupopup")) {
-#ifdef MOZ_ACCESSIBILITY_ATK
-    // ATK considers this node to be redundant when within menubars, and it makes menu
-    // navigation with assistive technologies more difficult
-    // XXX In the future we will should this for consistency across the nsIAccessible
-    // implementations on each platform for a consistent scripting environment, but
-    // then strip out redundant accessibles in the AccessibleWrap class for each platform.
-    nsIContent *parent = aContent->GetParent();
-    if (parent && parent->IsXULElement(nsGkAtoms::menu))
-      return nullptr;
-#endif
-
-    accessible = new XULMenupopupAccessible(aContent, aDoc);
-
   } else if(role.EqualsLiteral("xul:pane")) {
     accessible = new EnumRoleAccessible<roles::PANE>(aContent, aDoc);
 
   } else if (role.EqualsLiteral("xul:panel")) {
     if (aContent->IsElement() &&
         aContent->AsElement()->AttrValueIs(kNameSpaceID_None,
                                            nsGkAtoms::noautofocus,
                                            nsGkAtoms::_true, eCaseMatters))
       accessible = new XULAlertAccessible(aContent, aDoc);
     else
       accessible = new EnumRoleAccessible<roles::PANE>(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:scale")) {
-    accessible = new XULSliderAccessible(aContent, aDoc);
-
   } else if (role.EqualsLiteral("xul:text")) {
     accessible = new XULLabelAccessible(aContent, aDoc);
 
-  } else if (role.EqualsLiteral("xul:textbox")) {
-    accessible = new EnumRoleAccessible<roles::SECTION>(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:thumb")) {
-    accessible = new XULThumbAccessible(aContent, aDoc);
-
-  } else if (role.EqualsLiteral("xul:toolbarbutton")) {
-    accessible = new XULToolbarButtonAccessible(aContent, aDoc);
-
   }
 #endif // MOZ_XUL
 
   return accessible.forget();
 }
 
 already_AddRefed<Accessible>
 nsAccessibilityService::CreateAccessibleByFrameType(nsIFrame* aFrame,
--- a/accessible/ipc/win/HandlerProvider.cpp
+++ b/accessible/ipc/win/HandlerProvider.cpp
@@ -459,16 +459,23 @@ HandlerProvider::MarshalAs(REFIID aIid)
       aIid == IID_IAccessible2_3) {
     // This should always be the newest IA2 interface ID
     return NEWEST_IA2_IID;
   }
   // Otherwise we juse return the identity.
   return aIid;
 }
 
+HRESULT
+HandlerProvider::DisconnectHandlerRemotes()
+{
+  IUnknown* unk = static_cast<IGeckoBackChannel*>(this);
+  return ::CoDisconnectObject(unk, 0);
+}
+
 REFIID
 HandlerProvider::GetEffectiveOutParamIid(REFIID aCallIid,
                                          ULONG aCallMethod)
 {
   if (aCallIid == IID_IAccessibleTable ||
       aCallIid == IID_IAccessibleTable2 ||
       aCallIid == IID_IAccessibleDocument ||
       aCallIid == IID_IAccessibleTableCell ||
--- a/accessible/ipc/win/HandlerProvider.h
+++ b/accessible/ipc/win/HandlerProvider.h
@@ -42,16 +42,17 @@ public:
 
   // IHandlerProvider
   STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
   STDMETHODIMP GetHandlerPayloadSize(NotNull<mscom::IInterceptor*> aInterceptor,
                                      NotNull<DWORD*> aOutPayloadSize) override;
   STDMETHODIMP WriteHandlerPayload(NotNull<mscom::IInterceptor*> aInterceptor,
                                    NotNull<IStream*> aStream) override;
   STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
+  STDMETHODIMP DisconnectHandlerRemotes() override;
   STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
                                                 ULONG aCallMethod) override;
   STDMETHODIMP NewInstance(REFIID aIid,
                            mscom::InterceptorTargetPtr<IUnknown> aTarget,
                            NotNull<mscom::IHandlerProvider**> aOutNewPayload) override;
 
   // IGeckoBackChannel
   STDMETHODIMP put_HandlerControl(long aPid, IHandlerControl* aCtrl) override;
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -41,16 +41,17 @@
 #include "nsView.h"
 #include "nsViewManager.h"
 #include "nsEventMap.h"
 #include "nsArrayUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ReverseIterator.h"
 #include "nsIXULRuntime.h"
 #include "mozilla/mscom/AsyncInvoker.h"
+#include "mozilla/mscom/Interceptor.h"
 
 #include "oleacc.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 const uint32_t USE_ROLE_STRING = 0;
 
@@ -98,16 +99,34 @@ AccessibleWrap::Shutdown()
     auto doc = static_cast<DocAccessibleWrap*>(mDoc.get());
     MOZ_ASSERT(doc);
     if (doc) {
       doc->RemoveID(mID);
       mID = kNoID;
     }
   }
 
+  if (XRE_IsContentProcess()) {
+    // Bug 1434822: To improve performance for cross-process COM, we disable COM
+    // garbage collection. However, this means we never receive Release calls
+    // from clients, so defunct accessibles can never be deleted. Since we
+    // know when an accessible is shutting down, we can work around this by
+    // forcing COM to disconnect this object from all of its remote clients,
+    // which will cause associated references to be released.
+    IUnknown* unk = static_cast<IAccessible*>(this);
+    mscom::Interceptor::DisconnectRemotesForTarget(unk);
+    // If an accessible was retrieved via IAccessibleHypertext::hyperlink*,
+    // it will have a different Interceptor that won't be matched by the above
+    // call, even though it's the same object. Therefore, call it again with
+    // the IAccessibleHyperlink pointer. We can remove this horrible hack once
+    // bug 1440267 is fixed.
+    unk = static_cast<IAccessibleHyperlink*>(this);
+    mscom::Interceptor::DisconnectRemotesForTarget(unk);
+  }
+
   Accessible::Shutdown();
 }
 
 //-----------------------------------------------------
 // IUnknown interface methods - see iunknown.h for documentation
 //-----------------------------------------------------
 
 // Microsoft COM QueryInterface
--- a/accessible/xul/XULFormControlAccessible.cpp
+++ b/accessible/xul/XULFormControlAccessible.cpp
@@ -164,30 +164,26 @@ XULButtonAccessible::ContainerWidget() c
 
 bool
 XULButtonAccessible::IsAcceptableChild(nsIContent* aEl) const
 {
   // In general XUL button has not accessible children. Nevertheless menu
   // buttons can have button (@type="menu-button") and popup accessibles
   // (@type="menu-button", @type="menu" or columnpicker.
 
-  // XXX: no children until the button is menu button. Probably it's not
-  // totally correct but in general AT wants to have leaf buttons.
-  nsAutoString role;
-  nsCoreUtils::XBLBindingRole(aEl, role);
-
-  // Get an accessible for menupopup or panel elements.
-  if (role.EqualsLiteral("xul:menupopup")) {
+  // Get an accessible for menupopup or popup elements.
+  if (aEl->IsXULElement(nsGkAtoms::menupopup) ||
+      aEl->IsXULElement(nsGkAtoms::popup)) {
     return true;
   }
 
-  // Button type="menu-button" contains a real button. Get an accessible
+  // Button and toolbarbutton are real buttons. Get an accessible
   // for it. Ignore dropmarker button which is placed as a last child.
-  if ((!role.EqualsLiteral("xul:button") &&
-       !role.EqualsLiteral("xul:toolbarbutton")) ||
+  if ((!aEl->IsXULElement(nsGkAtoms::button) &&
+       !aEl->IsXULElement(nsGkAtoms::toolbarbutton)) ||
       aEl->IsXULElement(nsGkAtoms::dropMarker)) {
     return false;
   }
 
   return mContent->AsElement()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
                                             nsGkAtoms::menuButton, eCaseMatters);
 }
 
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -207,16 +207,17 @@
                     <menuitem id="menu_bookmarksSidebar"
                               key="viewBookmarksSidebarKb"
                               observes="viewBookmarksSidebar"/>
                     <menuitem id="menu_historySidebar"
                               key="key_gotoHistory"
                               observes="viewHistorySidebar"
                               label="&historyButton.label;"/>
                     <menuitem id="menu_tabsSidebar"
+                              class="sync-ui-item"
                               observes="viewTabsSidebar"
                               label="&syncedTabs.sidebar.label;"/>
                   </menupopup>
                 </menu>
                 <menuseparator/>
                 <menu id="viewFullZoomMenu" label="&fullZoom.label;"
                       accesskey="&fullZoom.accesskey;"
                       onpopupshowing="FullZoom.updateMenu();">
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -293,17 +293,17 @@
                      class="subviewbutton subviewbutton-iconic"
                      key="key_gotoHistory"
                      observes="viewHistorySidebar"
                      oncommand="SidebarUI.show('viewHistorySidebar');">
         <observes element="viewHistorySidebar" attribute="checked"/>
       </toolbarbutton>
       <toolbarbutton id="sidebar-switcher-tabs"
                      label="&syncedTabs.sidebar.label;"
-                     class="subviewbutton subviewbutton-iconic"
+                     class="subviewbutton subviewbutton-iconic sync-ui-item"
                      observes="viewTabsSidebar"
                      oncommand="SidebarUI.show('viewTabsSidebar');">
         <observes element="viewTabsSidebar" attribute="checked"/>
       </toolbarbutton>
       <toolbarseparator/>
       <!-- Extension toolbarbuttons go here. -->
       <toolbarseparator id="sidebar-extensions-separator"/>
       <toolbarbutton id="sidebar-reverse-position"
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -196,17 +196,17 @@ class TabBrowser {
     // set up the shared autoscroll popup
     this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
     this._autoScrollPopup.id = "autoscroller";
     this.appendChild(this._autoScrollPopup);
     this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
     this.mCurrentBrowser.droppedLinkHandler = handleDroppedLink;
 
     // Hook up the event listeners to the first browser
-    var tabListener = this.mTabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
+    var tabListener = new TabProgressListener(this.mCurrentTab, this.mCurrentBrowser, true, false);
     const nsIWebProgress = Ci.nsIWebProgress;
     const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
       .createInstance(nsIWebProgress);
     filter.addProgressListener(tabListener, nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(this.mCurrentTab, tabListener);
     this._tabFilters.set(this.mCurrentTab, filter);
     this.webProgress.addProgressListener(filter, nsIWebProgress.NOTIFY_ALL);
 
@@ -759,447 +759,16 @@ class TabBrowser {
       ).URI;
       return resolvedURI.schemeIs("jar") || resolvedURI.schemeIs("file");
     } catch (ex) {
       // aURI might be invalid.
       return false;
     }
   }
 
-  /**
-   * A web progress listener object definition for a given tab.
-   */
-  mTabProgressListener(aTab, aBrowser, aStartsBlank, aWasPreloadedBrowser, aOrigStateFlags) {
-    let stateFlags = aOrigStateFlags || 0;
-    // Initialize mStateFlags to non-zero e.g. when creating a progress
-    // listener for preloaded browsers as there was no progress listener
-    // around when the content started loading. If the content didn't
-    // quite finish loading yet, mStateFlags will very soon be overridden
-    // with the correct value and end up at STATE_STOP again.
-    if (aWasPreloadedBrowser) {
-      stateFlags = Ci.nsIWebProgressListener.STATE_STOP |
-        Ci.nsIWebProgressListener.STATE_IS_REQUEST;
-    }
-
-    return ({
-      mTabBrowser: this,
-      mTab: aTab,
-      mBrowser: aBrowser,
-      mBlank: aStartsBlank,
-
-      // cache flags for correct status UI update after tab switching
-      mStateFlags: stateFlags,
-      mStatus: 0,
-      mMessage: "",
-      mTotalProgress: 0,
-
-      // count of open requests (should always be 0 or 1)
-      mRequestCount: 0,
-
-      destroy() {
-        delete this.mTab;
-        delete this.mBrowser;
-        delete this.mTabBrowser;
-      },
-
-      _callProgressListeners() {
-        Array.unshift(arguments, this.mBrowser);
-        return this.mTabBrowser._callProgressListeners.apply(this.mTabBrowser, arguments);
-      },
-
-      _shouldShowProgress(aRequest) {
-        if (this.mBlank)
-          return false;
-
-        // Don't show progress indicators in tabs for about: URIs
-        // pointing to local resources.
-        if ((aRequest instanceof Ci.nsIChannel) &&
-            this.mTabBrowser._isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
-          return false;
-        }
-
-        return true;
-      },
-
-      _isForInitialAboutBlank(aWebProgress, aStateFlags, aLocation) {
-        if (!this.mBlank || !aWebProgress.isTopLevel) {
-          return false;
-        }
-
-        // If the state has STATE_STOP, and no requests were in flight, then this
-        // must be the initial "stop" for the initial about:blank document.
-        const nsIWebProgressListener = Ci.nsIWebProgressListener;
-        if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
-            this.mRequestCount == 0 &&
-            !aLocation) {
-          return true;
-        }
-
-        let location = aLocation ? aLocation.spec : "";
-        return location == "about:blank";
-      },
-
-      onProgressChange(aWebProgress, aRequest,
-        aCurSelfProgress, aMaxSelfProgress,
-        aCurTotalProgress, aMaxTotalProgress) {
-        this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
-
-        if (!this._shouldShowProgress(aRequest))
-          return;
-
-        if (this.mTotalProgress && this.mTab.hasAttribute("busy"))
-          this.mTab.setAttribute("progress", "true");
-
-        this._callProgressListeners("onProgressChange",
-                                    [aWebProgress, aRequest,
-                                     aCurSelfProgress, aMaxSelfProgress,
-                                     aCurTotalProgress, aMaxTotalProgress]);
-      },
-
-      onProgressChange64(aWebProgress, aRequest,
-        aCurSelfProgress, aMaxSelfProgress,
-        aCurTotalProgress, aMaxTotalProgress) {
-        return this.onProgressChange(aWebProgress, aRequest,
-          aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
-          aMaxTotalProgress);
-      },
-
-      /* eslint-disable complexity */
-      onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
-        if (!aRequest)
-          return;
-
-        const nsIWebProgressListener = Ci.nsIWebProgressListener;
-        const nsIChannel = Ci.nsIChannel;
-        let location, originalLocation;
-        try {
-          aRequest.QueryInterface(nsIChannel);
-          location = aRequest.URI;
-          originalLocation = aRequest.originalURI;
-        } catch (ex) {}
-
-        let ignoreBlank = this._isForInitialAboutBlank(aWebProgress, aStateFlags,
-          location);
-
-        // If we were ignoring some messages about the initial about:blank, and we
-        // got the STATE_STOP for it, we'll want to pay attention to those messages
-        // from here forward. Similarly, if we conclude that this state change
-        // is one that we shouldn't be ignoring, then stop ignoring.
-        if ((ignoreBlank &&
-            aStateFlags & nsIWebProgressListener.STATE_STOP &&
-            aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) ||
-            !ignoreBlank && this.mBlank) {
-          this.mBlank = false;
-        }
-
-        if (aStateFlags & nsIWebProgressListener.STATE_START) {
-          this.mRequestCount++;
-        } else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
-          const NS_ERROR_UNKNOWN_HOST = 2152398878;
-          if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
-            // to prevent bug 235825: wait for the request handled
-            // by the automatic keyword resolver
-            return;
-          }
-          // since we (try to) only handle STATE_STOP of the last request,
-          // the count of open requests should now be 0
-          this.mRequestCount = 0;
-        }
-
-        if (aStateFlags & nsIWebProgressListener.STATE_START &&
-            aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
-          if (aWebProgress.isTopLevel) {
-            // Need to use originalLocation rather than location because things
-            // like about:home and about:privatebrowsing arrive with nsIRequest
-            // pointing to their resolved jar: or file: URIs.
-            if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
-                originalLocation != "about:blank" &&
-                this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec &&
-                this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
-              // Indicating that we started a load will allow the location
-              // bar to be cleared when the load finishes.
-              // In order to not overwrite user-typed content, we avoid it
-              // (see if condition above) in a very specific case:
-              // If the load is of an 'initial' page (e.g. about:privatebrowsing,
-              // about:newtab, etc.), was not explicitly typed in the location
-              // bar by the user, is not about:blank (because about:blank can be
-              // loaded by websites under their principal), and the current
-              // page in the browser is about:blank (indicating it is a newly
-              // created or re-created browser, e.g. because it just switched
-              // remoteness or is a new tab/window).
-              this.mBrowser.urlbarChangeTracker.startedLoad();
-            }
-            delete this.mBrowser.initialPageLoadedFromURLBar;
-            // If the browser is loading it must not be crashed anymore
-            this.mTab.removeAttribute("crashed");
-          }
-
-          if (this._shouldShowProgress(aRequest)) {
-            if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING) &&
-                aWebProgress && aWebProgress.isTopLevel) {
-              this.mTab.setAttribute("busy", "true");
-              this.mTab._notselectedsinceload = !this.mTab.selected;
-              SchedulePressure.startMonitoring(window, {
-                highPressureFn() {
-                  // Only switch back to the SVG loading indicator after getting
-                  // three consecutive low pressure callbacks. Used to prevent
-                  // switching quickly between the SVG and APNG loading indicators.
-                  gBrowser.tabContainer._schedulePressureCount = gBrowser.schedulePressureDefaultCount;
-                  gBrowser.tabContainer.setAttribute("schedulepressure", "true");
-                },
-                lowPressureFn() {
-                  if (!gBrowser.tabContainer._schedulePressureCount ||
-                    --gBrowser.tabContainer._schedulePressureCount <= 0) {
-                    gBrowser.tabContainer.removeAttribute("schedulepressure");
-                  }
-
-                  // If tabs are closed while they are loading we need to
-                  // stop monitoring schedule pressure. We don't stop monitoring
-                  // during high pressure times because we want to eventually
-                  // return to the SVG tab loading animations.
-                  let continueMonitoring = true;
-                  if (!document.querySelector(".tabbrowser-tab[busy]")) {
-                    SchedulePressure.stopMonitoring(window);
-                    continueMonitoring = false;
-                  }
-                  return { continueMonitoring };
-                },
-              });
-              this.mTabBrowser.syncThrobberAnimations(this.mTab);
-            }
-
-            if (this.mTab.selected) {
-              this.mTabBrowser.mIsBusy = true;
-            }
-          }
-        } else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
-                   aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
-
-          if (this.mTab.hasAttribute("busy")) {
-            this.mTab.removeAttribute("busy");
-            if (!document.querySelector(".tabbrowser-tab[busy]")) {
-              SchedulePressure.stopMonitoring(window);
-              this.mTabBrowser.tabContainer.removeAttribute("schedulepressure");
-            }
-
-            // Only animate the "burst" indicating the page has loaded if
-            // the top-level page is the one that finished loading.
-            if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
-                Components.isSuccessCode(aStatus) &&
-                !this.mTabBrowser.tabAnimationsInProgress &&
-                Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
-              if (this.mTab._notselectedsinceload) {
-                this.mTab.setAttribute("notselectedsinceload", "true");
-              } else {
-                this.mTab.removeAttribute("notselectedsinceload");
-              }
-
-              this.mTab.setAttribute("bursting", "true");
-            }
-
-            this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
-            if (!this.mTab.selected)
-              this.mTab.setAttribute("unread", "true");
-          }
-          this.mTab.removeAttribute("progress");
-
-          if (aWebProgress.isTopLevel) {
-            let isSuccessful = Components.isSuccessCode(aStatus);
-            if (!isSuccessful && !isTabEmpty(this.mTab)) {
-              // Restore the current document's location in case the
-              // request was stopped (possibly from a content script)
-              // before the location changed.
-
-              this.mBrowser.userTypedValue = null;
-
-              let inLoadURI = this.mBrowser.inLoadURI;
-              if (this.mTab.selected && gURLBar && !inLoadURI) {
-                URLBarSetURI();
-              }
-            } else if (isSuccessful) {
-              this.mBrowser.urlbarChangeTracker.finishedLoad();
-            }
-
-            // Ignore initial about:blank to prevent flickering.
-            if (!this.mBrowser.mIconURL && !ignoreBlank) {
-              // Don't switch to the default icon on about:home or about:newtab,
-              // since these pages get their favicon set in browser code to
-              // improve perceived performance.
-              let isNewTab = originalLocation &&
-                (originalLocation.spec == "about:newtab" ||
-                  originalLocation.spec == "about:privatebrowsing" ||
-                  originalLocation.spec == "about:home");
-              if (!isNewTab) {
-                this.mTabBrowser.useDefaultIcon(this.mTab);
-              }
-            }
-          }
-
-          // For keyword URIs clear the user typed value since they will be changed into real URIs
-          if (location.scheme == "keyword")
-            this.mBrowser.userTypedValue = null;
-
-          if (this.mTab.selected)
-            this.mTabBrowser.mIsBusy = false;
-        }
-
-        if (ignoreBlank) {
-          this._callProgressListeners("onUpdateCurrentBrowser",
-                                      [aStateFlags, aStatus, "", 0],
-                                      true, false);
-        } else {
-          this._callProgressListeners("onStateChange",
-                                      [aWebProgress, aRequest, aStateFlags, aStatus],
-                                      true, false);
-        }
-
-        this._callProgressListeners("onStateChange",
-                                    [aWebProgress, aRequest, aStateFlags, aStatus],
-                                    false);
-
-        if (aStateFlags & (nsIWebProgressListener.STATE_START |
-            nsIWebProgressListener.STATE_STOP)) {
-          // reset cached temporary values at beginning and end
-          this.mMessage = "";
-          this.mTotalProgress = 0;
-        }
-        this.mStateFlags = aStateFlags;
-        this.mStatus = aStatus;
-      },
-      /* eslint-enable complexity */
-
-      onLocationChange(aWebProgress, aRequest, aLocation,
-        aFlags) {
-        // OnLocationChange is called for both the top-level content
-        // and the subframes.
-        let topLevel = aWebProgress.isTopLevel;
-
-        if (topLevel) {
-          let isSameDocument = !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
-          // We need to clear the typed value
-          // if the document failed to load, to make sure the urlbar reflects the
-          // failed URI (particularly for SSL errors). However, don't clear the value
-          // if the error page's URI is about:blank, because that causes complete
-          // loss of urlbar contents for invalid URI errors (see bug 867957).
-          // Another reason to clear the userTypedValue is if this was an anchor
-          // navigation initiated by the user.
-          if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
-              ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
-                aLocation.spec != "about:blank") ||
-              (isSameDocument && this.mBrowser.inLoadURI)) {
-            this.mBrowser.userTypedValue = null;
-          }
-
-          // If the tab has been set to "busy" outside the stateChange
-          // handler below (e.g. by sessionStore.navigateAndRestore), and
-          // the load results in an error page, it's possible that there
-          // isn't any (STATE_IS_NETWORK & STATE_STOP) state to cause busy
-          // attribute being removed. In this case we should remove the
-          // attribute here.
-          if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
-              this.mTab.hasAttribute("busy")) {
-            this.mTab.removeAttribute("busy");
-            this.mTabBrowser._tabAttrModified(this.mTab, ["busy"]);
-          }
-
-          // If the browser was playing audio, we should remove the playing state.
-          if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
-            clearTimeout(this.mTab._soundPlayingAttrRemovalTimer);
-            this.mTab._soundPlayingAttrRemovalTimer = 0;
-            this.mTab.removeAttribute("soundplaying");
-            this.mTabBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
-          }
-
-          // If the browser was previously muted, we should restore the muted state.
-          if (this.mTab.hasAttribute("muted")) {
-            this.mTab.linkedBrowser.mute();
-          }
-
-          if (this.mTabBrowser.isFindBarInitialized(this.mTab)) {
-            let findBar = this.mTabBrowser.getFindBar(this.mTab);
-
-            // Close the Find toolbar if we're in old-style TAF mode
-            if (findBar.findMode != findBar.FIND_NORMAL) {
-              findBar.close();
-            }
-          }
-
-          this.mTabBrowser.setTabTitle(this.mTab);
-
-          // Don't clear the favicon if this tab is in the pending
-          // state, as SessionStore will have set the icon for us even
-          // though we're pointed at an about:blank. Also don't clear it
-          // if onLocationChange was triggered by a pushState or a
-          // replaceState (bug 550565) or a hash change (bug 408415).
-          if (!this.mTab.hasAttribute("pending") &&
-              aWebProgress.isLoadingDocument &&
-              !isSameDocument) {
-            this.mBrowser.mIconURL = null;
-          }
-
-          let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
-          if (this.mBrowser.registeredOpenURI) {
-            this.mTabBrowser._unifiedComplete
-              .unregisterOpenPage(this.mBrowser.registeredOpenURI,
-                userContextId);
-            delete this.mBrowser.registeredOpenURI;
-          }
-          // Tabs in private windows aren't registered as "Open" so
-          // that they don't appear as switch-to-tab candidates.
-          if (!isBlankPageURL(aLocation.spec) &&
-              (!PrivateBrowsingUtils.isWindowPrivate(window) ||
-                PrivateBrowsingUtils.permanentPrivateBrowsing)) {
-            this.mTabBrowser._unifiedComplete
-              .registerOpenPage(aLocation, userContextId);
-            this.mBrowser.registeredOpenURI = aLocation;
-          }
-        }
-
-        if (!this.mBlank) {
-          this._callProgressListeners("onLocationChange",
-                                      [aWebProgress, aRequest, aLocation, aFlags]);
-        }
-
-        if (topLevel) {
-          this.mBrowser.lastURI = aLocation;
-          this.mBrowser.lastLocationChange = Date.now();
-        }
-      },
-
-      onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
-        if (this.mBlank)
-          return;
-
-        this._callProgressListeners("onStatusChange",
-                                    [aWebProgress, aRequest, aStatus, aMessage]);
-
-        this.mMessage = aMessage;
-      },
-
-      onSecurityChange(aWebProgress, aRequest, aState) {
-        this._callProgressListeners("onSecurityChange",
-                                    [aWebProgress, aRequest, aState]);
-      },
-
-      onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
-        return this._callProgressListeners("onRefreshAttempted",
-                                           [aWebProgress, aURI, aDelay, aSameURI]);
-      },
-
-      QueryInterface(aIID) {
-        if (aIID.equals(Ci.nsIWebProgressListener) ||
-          aIID.equals(Ci.nsIWebProgressListener2) ||
-          aIID.equals(Ci.nsISupportsWeakReference) ||
-          aIID.equals(Ci.nsISupports))
-          return this;
-        throw Cr.NS_NOINTERFACE;
-      }
-    });
-  }
-
   storeIcon(aBrowser, aURI, aLoadingPrincipal, aRequestContextID) {
     try {
       if (!(aURI instanceof Ci.nsIURI)) {
         aURI = makeURI(aURI);
       }
       PlacesUIUtils.loadFavicon(aBrowser, aLoadingPrincipal, aURI, aRequestContextID);
     } catch (ex) {
       Cu.reportError(ex);
@@ -2110,17 +1679,17 @@ class TabBrowser {
     // As frameLoaders start out with an active docShell we have to
     // deactivate it if this is not the selected tab's browser or the
     // browser window is minimized.
     aBrowser.docShellIsActive = this.shouldActivateDocShell(aBrowser);
 
     // Create a new tab progress listener for the new browser we just injected,
     // since tab progress listeners have logic for handling the initial about:blank
     // load
-    listener = this.mTabProgressListener(tab, aBrowser, true, false);
+    listener = new TabProgressListener(tab, aBrowser, true, false);
     this._tabListeners.set(tab, listener);
     filter.addProgressListener(listener, Ci.nsIWebProgress.NOTIFY_ALL);
 
     // Restore the progress listener.
     aBrowser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
 
     // Restore the securityUI state.
     let securityUI = aBrowser.securityUI;
@@ -2511,17 +2080,17 @@ class TabBrowser {
       // browser element, which fires off a bunch of notifications. Some
       // of those notifications can cause code to run that inspects our
       // state, so it is important that the tab element is fully
       // initialized by this point.
       this.mPanelContainer.appendChild(notificationbox);
     }
 
     // wire up a progress listener for the new browser object.
-    let tabListener = this.mTabProgressListener(aTab, browser, uriIsAboutBlank, usingPreloadedContent);
+    let tabListener = new TabProgressListener(aTab, browser, uriIsAboutBlank, usingPreloadedContent);
     const filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
       .createInstance(Ci.nsIWebProgress);
     filter.addProgressListener(tabListener, Ci.nsIWebProgress.NOTIFY_ALL);
     browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
     this._tabListeners.set(aTab, tabListener);
     this._tabFilters.set(aTab, filter);
 
     browser.droppedLinkHandler = handleDroppedLink;
@@ -3596,17 +3165,17 @@ class TabBrowser {
     let tabListener = otherTabBrowser._tabListeners.get(aOtherTab);
     otherBrowser.webProgress.removeProgressListener(filter);
     filter.removeProgressListener(tabListener);
 
     // Perform the docshell swap through the common mechanism.
     this._swapBrowserDocShells(aOurTab, otherBrowser, aFlags);
 
     // Restore the listeners for the swapped in tab.
-    tabListener = otherTabBrowser.mTabProgressListener(aOtherTab, otherBrowser, false, false);
+    tabListener = new otherTabBrowser.ownerGlobal.TabProgressListener(aOtherTab, otherBrowser, false, false);
     otherTabBrowser._tabListeners.set(aOtherTab, tabListener);
 
     const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
     filter.addProgressListener(tabListener, notifyAll);
     otherBrowser.webProgress.addProgressListener(filter, notifyAll);
   }
 
   _swapBrowserDocShells(aOurTab, aOtherBrowser, aFlags, aStateFlags) {
@@ -3662,18 +3231,17 @@ class TabBrowser {
         let otherTab = remoteBrowser.getTabForBrowser(aOtherBrowser);
         if (otherTab) {
           otherTab.permanentKey = aOtherBrowser.permanentKey;
         }
       }
     }
 
     // Restore the progress listener
-    tabListener = this.mTabProgressListener(aOurTab, ourBrowser, false, false,
-      aStateFlags);
+    tabListener = new TabProgressListener(aOurTab, ourBrowser, false, false, aStateFlags);
     this._tabListeners.set(aOurTab, tabListener);
 
     const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
     filter.addProgressListener(tabListener, notifyAll);
     ourBrowser.webProgress.addProgressListener(filter, notifyAll);
   }
 
   _swapRegisteredOpenURIs(aOurBrowser, aOtherBrowser) {
@@ -5730,8 +5298,433 @@ class TabBrowser {
         this._tabAttrModified(tab, ["activemedia-blocked"]);
         let hist = Services.telemetry.getHistogramById("TAB_AUDIO_INDICATOR_USED");
         hist.add(2 /* unblockByVisitingTab */ );
         tab.finishMediaBlockTimer();
       }
     });
   }
 }
+
+/**
+ * A web progress listener object definition for a given tab.
+ */
+class TabProgressListener {
+  constructor(aTab, aBrowser, aStartsBlank, aWasPreloadedBrowser, aOrigStateFlags) {
+    let stateFlags = aOrigStateFlags || 0;
+    // Initialize mStateFlags to non-zero e.g. when creating a progress
+    // listener for preloaded browsers as there was no progress listener
+    // around when the content started loading. If the content didn't
+    // quite finish loading yet, mStateFlags will very soon be overridden
+    // with the correct value and end up at STATE_STOP again.
+    if (aWasPreloadedBrowser) {
+      stateFlags = Ci.nsIWebProgressListener.STATE_STOP |
+        Ci.nsIWebProgressListener.STATE_IS_REQUEST;
+    }
+
+    this.mTab = aTab;
+    this.mBrowser = aBrowser;
+    this.mBlank = aStartsBlank;
+
+    // cache flags for correct status UI update after tab switching
+    this.mStateFlags = stateFlags;
+    this.mStatus = 0;
+    this.mMessage = "";
+    this.mTotalProgress = 0;
+
+    // count of open requests (should always be 0 or 1)
+    this.mRequestCount = 0;
+  }
+
+  destroy() {
+    delete this.mTab;
+    delete this.mBrowser;
+  }
+
+  _callProgressListeners() {
+    Array.unshift(arguments, this.mBrowser);
+    return gBrowser._callProgressListeners.apply(gBrowser, arguments);
+  }
+
+  _shouldShowProgress(aRequest) {
+    if (this.mBlank)
+      return false;
+
+    // Don't show progress indicators in tabs for about: URIs
+    // pointing to local resources.
+    if ((aRequest instanceof Ci.nsIChannel) &&
+        gBrowser._isLocalAboutURI(aRequest.originalURI, aRequest.URI)) {
+      return false;
+    }
+
+    return true;
+  }
+
+  _isForInitialAboutBlank(aWebProgress, aStateFlags, aLocation) {
+    if (!this.mBlank || !aWebProgress.isTopLevel) {
+      return false;
+    }
+
+    // If the state has STATE_STOP, and no requests were in flight, then this
+    // must be the initial "stop" for the initial about:blank document.
+    const nsIWebProgressListener = Ci.nsIWebProgressListener;
+    if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
+        this.mRequestCount == 0 &&
+        !aLocation) {
+      return true;
+    }
+
+    let location = aLocation ? aLocation.spec : "";
+    return location == "about:blank";
+  }
+
+  onProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress,
+                   aCurTotalProgress, aMaxTotalProgress) {
+    this.mTotalProgress = aMaxTotalProgress ? aCurTotalProgress / aMaxTotalProgress : 0;
+
+    if (!this._shouldShowProgress(aRequest))
+      return;
+
+    if (this.mTotalProgress && this.mTab.hasAttribute("busy"))
+      this.mTab.setAttribute("progress", "true");
+
+    this._callProgressListeners("onProgressChange",
+                                [aWebProgress, aRequest,
+                                 aCurSelfProgress, aMaxSelfProgress,
+                                 aCurTotalProgress, aMaxTotalProgress]);
+  }
+
+  onProgressChange64(aWebProgress, aRequest, aCurSelfProgress,
+                     aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress) {
+    return this.onProgressChange(aWebProgress, aRequest,
+      aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
+      aMaxTotalProgress);
+  }
+
+  /* eslint-disable complexity */
+  onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+    if (!aRequest)
+      return;
+
+    const nsIWebProgressListener = Ci.nsIWebProgressListener;
+    const nsIChannel = Ci.nsIChannel;
+    let location, originalLocation;
+    try {
+      aRequest.QueryInterface(nsIChannel);
+      location = aRequest.URI;
+      originalLocation = aRequest.originalURI;
+    } catch (ex) {}
+
+    let ignoreBlank = this._isForInitialAboutBlank(aWebProgress, aStateFlags,
+      location);
+
+    // If we were ignoring some messages about the initial about:blank, and we
+    // got the STATE_STOP for it, we'll want to pay attention to those messages
+    // from here forward. Similarly, if we conclude that this state change
+    // is one that we shouldn't be ignoring, then stop ignoring.
+    if ((ignoreBlank &&
+        aStateFlags & nsIWebProgressListener.STATE_STOP &&
+        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) ||
+        !ignoreBlank && this.mBlank) {
+      this.mBlank = false;
+    }
+
+    if (aStateFlags & nsIWebProgressListener.STATE_START) {
+      this.mRequestCount++;
+    } else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+      const NS_ERROR_UNKNOWN_HOST = 2152398878;
+      if (--this.mRequestCount > 0 && aStatus == NS_ERROR_UNKNOWN_HOST) {
+        // to prevent bug 235825: wait for the request handled
+        // by the automatic keyword resolver
+        return;
+      }
+      // since we (try to) only handle STATE_STOP of the last request,
+      // the count of open requests should now be 0
+      this.mRequestCount = 0;
+    }
+
+    if (aStateFlags & nsIWebProgressListener.STATE_START &&
+        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+      if (aWebProgress.isTopLevel) {
+        // Need to use originalLocation rather than location because things
+        // like about:home and about:privatebrowsing arrive with nsIRequest
+        // pointing to their resolved jar: or file: URIs.
+        if (!(originalLocation && gInitialPages.includes(originalLocation.spec) &&
+            originalLocation != "about:blank" &&
+            this.mBrowser.initialPageLoadedFromURLBar != originalLocation.spec &&
+            this.mBrowser.currentURI && this.mBrowser.currentURI.spec == "about:blank")) {
+          // Indicating that we started a load will allow the location
+          // bar to be cleared when the load finishes.
+          // In order to not overwrite user-typed content, we avoid it
+          // (see if condition above) in a very specific case:
+          // If the load is of an 'initial' page (e.g. about:privatebrowsing,
+          // about:newtab, etc.), was not explicitly typed in the location
+          // bar by the user, is not about:blank (because about:blank can be
+          // loaded by websites under their principal), and the current
+          // page in the browser is about:blank (indicating it is a newly
+          // created or re-created browser, e.g. because it just switched
+          // remoteness or is a new tab/window).
+          this.mBrowser.urlbarChangeTracker.startedLoad();
+        }
+        delete this.mBrowser.initialPageLoadedFromURLBar;
+        // If the browser is loading it must not be crashed anymore
+        this.mTab.removeAttribute("crashed");
+      }
+
+      if (this._shouldShowProgress(aRequest)) {
+        if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING) &&
+            aWebProgress && aWebProgress.isTopLevel) {
+          this.mTab.setAttribute("busy", "true");
+          this.mTab._notselectedsinceload = !this.mTab.selected;
+          SchedulePressure.startMonitoring(window, {
+            highPressureFn() {
+              // Only switch back to the SVG loading indicator after getting
+              // three consecutive low pressure callbacks. Used to prevent
+              // switching quickly between the SVG and APNG loading indicators.
+              gBrowser.tabContainer._schedulePressureCount = gBrowser.schedulePressureDefaultCount;
+              gBrowser.tabContainer.setAttribute("schedulepressure", "true");
+            },
+            lowPressureFn() {
+              if (!gBrowser.tabContainer._schedulePressureCount ||
+                --gBrowser.tabContainer._schedulePressureCount <= 0) {
+                gBrowser.tabContainer.removeAttribute("schedulepressure");
+              }
+
+              // If tabs are closed while they are loading we need to
+              // stop monitoring schedule pressure. We don't stop monitoring
+              // during high pressure times because we want to eventually
+              // return to the SVG tab loading animations.
+              let continueMonitoring = true;
+              if (!document.querySelector(".tabbrowser-tab[busy]")) {
+                SchedulePressure.stopMonitoring(window);
+                continueMonitoring = false;
+              }
+              return { continueMonitoring };
+            },
+          });
+          gBrowser.syncThrobberAnimations(this.mTab);
+        }
+
+        if (this.mTab.selected) {
+          gBrowser.mIsBusy = true;
+        }
+      }
+    } else if (aStateFlags & nsIWebProgressListener.STATE_STOP &&
+               aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+
+      if (this.mTab.hasAttribute("busy")) {
+        this.mTab.removeAttribute("busy");
+        if (!document.querySelector(".tabbrowser-tab[busy]")) {
+          SchedulePressure.stopMonitoring(window);
+          gBrowser.tabContainer.removeAttribute("schedulepressure");
+        }
+
+        // Only animate the "burst" indicating the page has loaded if
+        // the top-level page is the one that finished loading.
+        if (aWebProgress.isTopLevel && !aWebProgress.isLoadingDocument &&
+            Components.isSuccessCode(aStatus) &&
+            !gBrowser.tabAnimationsInProgress &&
+            Services.prefs.getBoolPref("toolkit.cosmeticAnimations.enabled")) {
+          if (this.mTab._notselectedsinceload) {
+            this.mTab.setAttribute("notselectedsinceload", "true");
+          } else {
+            this.mTab.removeAttribute("notselectedsinceload");
+          }
+
+          this.mTab.setAttribute("bursting", "true");
+        }
+
+        gBrowser._tabAttrModified(this.mTab, ["busy"]);
+        if (!this.mTab.selected)
+          this.mTab.setAttribute("unread", "true");
+      }
+      this.mTab.removeAttribute("progress");
+
+      if (aWebProgress.isTopLevel) {
+        let isSuccessful = Components.isSuccessCode(aStatus);
+        if (!isSuccessful && !isTabEmpty(this.mTab)) {
+          // Restore the current document's location in case the
+          // request was stopped (possibly from a content script)
+          // before the location changed.
+
+          this.mBrowser.userTypedValue = null;
+
+          let inLoadURI = this.mBrowser.inLoadURI;
+          if (this.mTab.selected && gURLBar && !inLoadURI) {
+            URLBarSetURI();
+          }
+        } else if (isSuccessful) {
+          this.mBrowser.urlbarChangeTracker.finishedLoad();
+        }
+
+        // Ignore initial about:blank to prevent flickering.
+        if (!this.mBrowser.mIconURL && !ignoreBlank) {
+          // Don't switch to the default icon on about:home or about:newtab,
+          // since these pages get their favicon set in browser code to
+          // improve perceived performance.
+          let isNewTab = originalLocation &&
+            (originalLocation.spec == "about:newtab" ||
+              originalLocation.spec == "about:privatebrowsing" ||
+              originalLocation.spec == "about:home");
+          if (!isNewTab) {
+            gBrowser.useDefaultIcon(this.mTab);
+          }
+        }
+      }
+
+      // For keyword URIs clear the user typed value since they will be changed into real URIs
+      if (location.scheme == "keyword")
+        this.mBrowser.userTypedValue = null;
+
+      if (this.mTab.selected)
+        gBrowser.mIsBusy = false;
+    }
+
+    if (ignoreBlank) {
+      this._callProgressListeners("onUpdateCurrentBrowser",
+                                  [aStateFlags, aStatus, "", 0],
+                                  true, false);
+    } else {
+      this._callProgressListeners("onStateChange",
+                                  [aWebProgress, aRequest, aStateFlags, aStatus],
+                                  true, false);
+    }
+
+    this._callProgressListeners("onStateChange",
+                                [aWebProgress, aRequest, aStateFlags, aStatus],
+                                false);
+
+    if (aStateFlags & (nsIWebProgressListener.STATE_START |
+        nsIWebProgressListener.STATE_STOP)) {
+      // reset cached temporary values at beginning and end
+      this.mMessage = "";
+      this.mTotalProgress = 0;
+    }
+    this.mStateFlags = aStateFlags;
+    this.mStatus = aStatus;
+  }
+  /* eslint-enable complexity */
+
+  onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
+    // OnLocationChange is called for both the top-level content
+    // and the subframes.
+    let topLevel = aWebProgress.isTopLevel;
+
+    if (topLevel) {
+      let isSameDocument = !!(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT);
+      // We need to clear the typed value
+      // if the document failed to load, to make sure the urlbar reflects the
+      // failed URI (particularly for SSL errors). However, don't clear the value
+      // if the error page's URI is about:blank, because that causes complete
+      // loss of urlbar contents for invalid URI errors (see bug 867957).
+      // Another reason to clear the userTypedValue is if this was an anchor
+      // navigation initiated by the user.
+      if (this.mBrowser.didStartLoadSinceLastUserTyping() ||
+          ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
+            aLocation.spec != "about:blank") ||
+          (isSameDocument && this.mBrowser.inLoadURI)) {
+        this.mBrowser.userTypedValue = null;
+      }
+
+      // If the tab has been set to "busy" outside the stateChange
+      // handler below (e.g. by sessionStore.navigateAndRestore), and
+      // the load results in an error page, it's possible that there
+      // isn't any (STATE_IS_NETWORK & STATE_STOP) state to cause busy
+      // attribute being removed. In this case we should remove the
+      // attribute here.
+      if ((aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) &&
+          this.mTab.hasAttribute("busy")) {
+        this.mTab.removeAttribute("busy");
+        gBrowser._tabAttrModified(this.mTab, ["busy"]);
+      }
+
+      // If the browser was playing audio, we should remove the playing state.
+      if (this.mTab.hasAttribute("soundplaying") && !isSameDocument) {
+        clearTimeout(this.mTab._soundPlayingAttrRemovalTimer);
+        this.mTab._soundPlayingAttrRemovalTimer = 0;
+        this.mTab.removeAttribute("soundplaying");
+        gBrowser._tabAttrModified(this.mTab, ["soundplaying"]);
+      }
+
+      // If the browser was previously muted, we should restore the muted state.
+      if (this.mTab.hasAttribute("muted")) {
+        this.mTab.linkedBrowser.mute();
+      }
+
+      if (gBrowser.isFindBarInitialized(this.mTab)) {
+        let findBar = gBrowser.getFindBar(this.mTab);
+
+        // Close the Find toolbar if we're in old-style TAF mode
+        if (findBar.findMode != findBar.FIND_NORMAL) {
+          findBar.close();
+        }
+      }
+
+      gBrowser.setTabTitle(this.mTab);
+
+      // Don't clear the favicon if this tab is in the pending
+      // state, as SessionStore will have set the icon for us even
+      // though we're pointed at an about:blank. Also don't clear it
+      // if onLocationChange was triggered by a pushState or a
+      // replaceState (bug 550565) or a hash change (bug 408415).
+      if (!this.mTab.hasAttribute("pending") &&
+          aWebProgress.isLoadingDocument &&
+          !isSameDocument) {
+        this.mBrowser.mIconURL = null;
+      }
+
+      let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
+      if (this.mBrowser.registeredOpenURI) {
+        gBrowser._unifiedComplete
+          .unregisterOpenPage(this.mBrowser.registeredOpenURI, userContextId);
+        delete this.mBrowser.registeredOpenURI;
+      }
+      // Tabs in private windows aren't registered as "Open" so
+      // that they don't appear as switch-to-tab candidates.
+      if (!isBlankPageURL(aLocation.spec) &&
+          (!PrivateBrowsingUtils.isWindowPrivate(window) ||
+            PrivateBrowsingUtils.permanentPrivateBrowsing)) {
+        gBrowser._unifiedComplete.registerOpenPage(aLocation, userContextId);
+        this.mBrowser.registeredOpenURI = aLocation;
+      }
+    }
+
+    if (!this.mBlank) {
+      this._callProgressListeners("onLocationChange",
+                                  [aWebProgress, aRequest, aLocation, aFlags]);
+    }
+
+    if (topLevel) {
+      this.mBrowser.lastURI = aLocation;
+      this.mBrowser.lastLocationChange = Date.now();
+    }
+  }
+
+  onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
+    if (this.mBlank)
+      return;
+
+    this._callProgressListeners("onStatusChange",
+                                [aWebProgress, aRequest, aStatus, aMessage]);
+
+    this.mMessage = aMessage;
+  }
+
+  onSecurityChange(aWebProgress, aRequest, aState) {
+    this._callProgressListeners("onSecurityChange",
+                                [aWebProgress, aRequest, aState]);
+  }
+
+  onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
+    return this._callProgressListeners("onRefreshAttempted",
+                                       [aWebProgress, aURI, aDelay, aSameURI]);
+  }
+
+  QueryInterface(aIID) {
+    if (aIID.equals(Ci.nsIWebProgressListener) ||
+        aIID.equals(Ci.nsIWebProgressListener2) ||
+        aIID.equals(Ci.nsISupportsWeakReference) ||
+        aIID.equals(Ci.nsISupports))
+      return this;
+    throw Cr.NS_NOINTERFACE;
+  }
+}
+
--- a/browser/components/places/tests/browser/browser.ini
+++ b/browser/components/places/tests/browser/browser.ini
@@ -14,16 +14,18 @@ support-files =
 [browser_0_library_left_pane_migration.js]
 [browser_addBookmarkForFrame.js]
 [browser_bookmark_add_tags.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmark_change_location.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmark_folder_moveability.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
+[browser_bookmark_load_in_sidebar.js]
+skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmark_private_window.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmark_remove_tags.js]
 skip-if = (os == 'win' && ccov) # Bug 1423667
 [browser_bookmarklet_windowOpen.js]
 support-files =
   bookmarklet_windowOpen_dummy.html
 [browser_bookmarks_change_title.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/browser/browser_bookmark_load_in_sidebar.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Test that a bookmark can be loaded inside the Bookmarks sidebar.
+ */
+"use strict";
+
+const TEST_URL = "about:buildconfig";
+
+// Cleanup.
+registerCleanupFunction(async () => {
+  await PlacesUtils.bookmarks.eraseEverything();
+});
+
+add_task(async function test_load_in_sidebar() {
+  let bm = await PlacesUtils.bookmarks.insert({
+    parentGuid: PlacesUtils.bookmarks.unfiledGuid,
+    url: TEST_URL,
+    title: TEST_URL,
+  });
+
+  await withSidebarTree("bookmarks", async function(tree) {
+    tree.selectItems([bm.guid]);
+    await withBookmarksDialog(
+      false,
+      function openPropertiesDialog() {
+        tree.controller.doCommand("placesCmd_show:info");
+      },
+
+      // Check the "Load this bookmark in the sidebar" option.
+      async function test(dialogWin) {
+        let loadInSidebar = dialogWin.document.getElementById("editBMPanel_loadInSidebarCheckbox");
+        let promiseCheckboxChanged = PlacesTestUtils.waitForNotification(
+          "onItemChanged",
+          (id, parentId, checked) => checked === true
+        );
+
+        loadInSidebar.click();
+
+        EventUtils.synthesizeKey("VK_RETURN", {}, dialogWin);
+        await promiseCheckboxChanged;
+      }
+    );
+
+    let sidebar = document.getElementById("sidebar");
+
+    let sidebarLoadedPromise = new Promise(resolve => {
+      sidebar.addEventListener("load", function() {
+        executeSoon(resolve);
+      }, {capture: true, once: true});
+    });
+
+    // Select and open the bookmark in the sidebar.
+    tree.selectItems([bm.guid]);
+    tree.controller.doCommand("placesCmd_open");
+
+    await sidebarLoadedPromise;
+
+    let sidebarTitle = document.getElementById("sidebar-title");
+    let sidebarBrowser = sidebar.contentDocument.getElementById("web-panels-browser");
+
+    await BrowserTestUtils.browserLoaded(sidebarBrowser, false, TEST_URL);
+
+    let h1Elem = sidebarBrowser.contentDocument.getElementsByTagName("h1")[0];
+
+    // Check that the title and the content of the page are loaded successfully.
+    Assert.equal(sidebarTitle.value, TEST_URL, "The sidebar title is successfully loaded.");
+    Assert.equal(h1Elem.textContent, TEST_URL, "The sidebar content is successfully loaded.");
+  });
+});
--- a/browser/components/preferences/connection.js
+++ b/browser/components/preferences/connection.js
@@ -256,16 +256,19 @@ var gConnectionsDialog = {
     let isLocked = API_PROXY_PREFS.some(
       pref => Services.prefs.prefIsLocked(pref));
 
     function setInputsDisabledState(isControlled) {
       let disabled = isLocked || isControlled;
       for (let element of gConnectionsDialog.getProxyControls()) {
         element.disabled = disabled;
       }
+      if (!isControlled) {
+        gConnectionsDialog.proxyTypeChanged();
+      }
     }
 
     if (isLocked) {
       // An extension can't control this setting if any pref is locked.
       hideControllingExtension(PROXY_KEY);
       setInputsDisabledState(false);
     } else {
       handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY)
--- a/browser/components/preferences/in-content/preferences.xul
+++ b/browser/components/preferences/in-content/preferences.xul
@@ -83,16 +83,17 @@
 
 <page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
       xmlns:html="http://www.w3.org/1999/xhtml"
       disablefastfind="true"
       data-l10n-id="pref-page"
       data-l10n-attrs="title">
 
   <link rel="localization" href="branding/brand.ftl"/>
+  <link rel="localization" href="browser/branding/sync-brand.ftl"/>
   <link rel="localization" href="browser/preferences/preferences.ftl"/>
   <script type="text/javascript" src="chrome://global/content/l10n.js"></script>
 
   <html:link rel="shortcut icon"
               href="chrome://browser/skin/settings.svg"/>
 
   <script type="application/javascript"
           src="chrome://browser/content/utilityOverlay.js"/>
--- a/browser/components/preferences/in-content/tests/browser_connection.js
+++ b/browser/components/preferences/in-content/tests/browser_connection.js
@@ -52,16 +52,20 @@ function runConnectionTests(win) {
   let networkProxyTypePref = win.Preferences.get("network.proxy.type");
 
   // make sure the networkProxyNone textbox is formatted properly
   is(networkProxyNone.getAttribute("multiline"), "true",
      "networkProxyNone textbox is multiline");
   is(networkProxyNone.getAttribute("rows"), "2",
      "networkProxyNone textbox has two rows");
 
+  // make sure manual proxy controls are disabled when the window is opened
+  let networkProxyHTTP = doc.getElementById("networkProxyHTTP");
+  is(networkProxyHTTP.disabled, true, "networkProxyHTTP textbox is disabled");
+
   // check if sanitizing the given input for the no_proxies_on pref results in
   // expected string
   function testSanitize(input, expected, errorMessage) {
     networkProxyNonePref.value = input;
     win.gConnectionsDialog.sanitizeNoProxiesPref();
     is(networkProxyNonePref.value, expected, errorMessage);
   }
 
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -668,27 +668,40 @@ add_task(async function testExtensionCon
       if (isControlled) {
         let controlledDesc = controlledSection.querySelector("description");
         // There are two spaces before "set_proxy" because it's " <image /> set_proxy".
         is(controlledDesc.textContent, `An extension,  set_proxy, is controlling how ${brandShortName} connects to the internet.`,
           "The user is notified that an extension is controlling proxy settings.");
       }
       function getProxyControls() {
         let controlGroup = doc.getElementById("networkProxyType");
-        return [
-          ...controlGroup.querySelectorAll(":scope > radio"),
-          ...controlGroup.querySelectorAll("label"),
-          ...controlGroup.querySelectorAll("textbox"),
-          ...controlGroup.querySelectorAll("checkbox"),
-          ...doc.querySelectorAll("#networkProxySOCKSVersion > radio"),
-          ...doc.querySelectorAll("#ConnectionsDialogPane > checkbox"),
-        ];
+        let manualControlContainer = controlGroup.querySelector("grid");
+        return {
+          manualControls: [
+            ...manualControlContainer.querySelectorAll("label"),
+            ...manualControlContainer.querySelectorAll("textbox"),
+            ...manualControlContainer.querySelectorAll("checkbox"),
+            ...doc.querySelectorAll("#networkProxySOCKSVersion > radio")],
+          pacControls: [doc.getElementById("networkProxyAutoconfigURL")],
+          otherControls: [
+            ...controlGroup.querySelectorAll(":scope > radio"),
+            ...doc.querySelectorAll("#ConnectionsDialogPane > checkbox")],
+        };
       }
       let controlState = isControlled ? "disabled" : "enabled";
-      for (let element of getProxyControls()) {
+      let controls = getProxyControls();
+      for (let element of controls.manualControls) {
+        let disabled = isControlled || proxyType !== proxySvc.PROXYCONFIG_MANUAL;
+        is(element.disabled, disabled, `Proxy controls are ${controlState}.`);
+      }
+      for (let element of controls.pacControls) {
+        let disabled = isControlled || proxyType !== proxySvc.PROXYCONFIG_PAC;
+        is(element.disabled, disabled, `Proxy controls are ${controlState}.`);
+      }
+      for (let element of controls.otherControls) {
         is(element.disabled, isControlled, `Proxy controls are ${controlState}.`);
       }
     } else {
       is(doc.getElementById(CONNECTION_SETTINGS_DESC_ID).textContent,
          expectedConnectionSettingsMessage(doc, isControlled),
          "The connection settings description is as expected.");
     }
   }
@@ -745,17 +758,17 @@ add_task(async function testExtensionCon
   let panelDoc = panelObj.panel.document;
 
   verifyState(panelDoc, false);
 
   await closeProxyPanel(panelObj);
 
   verifyState(mainDoc, false);
 
-  // Install an extension that sets Tracking Protection.
+  // Install an extension that controls proxy settings.
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       name: "set_proxy",
       applications: {gecko: {id: EXTENSION_ID}},
       permissions: ["browserSettings"],
     },
     background,
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -617,17 +617,17 @@ var pktApi = (function() {
 
     /**
      * Helper function to get a user's pocket stories
      * @return {Boolean} Returns Boolean whether the api call started sucessfully
      */
     function retrieve(data = {}, options = {}) {
         const requestData = Object.assign({}, data, {access_token: getAccessToken()});
         return apiRequest({
-            path: "/get",
+            path: "/firefox/get",
             data: requestData,
             success: options.success,
             error: options.error
         });
     }
 
     /**
      * Helper function to get current signup AB group the user is in
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -96,16 +96,17 @@ webextPerms.optionalPermsDeny.label=Deny
 webextPerms.optionalPermsDeny.accessKey=D
 
 webextPerms.description.bookmarks=Read and modify bookmarks
 webextPerms.description.browserSettings=Read and modify browser settings
 webextPerms.description.browsingData=Clear recent browsing history, cookies, and related data
 webextPerms.description.clipboardRead=Get data from the clipboard
 webextPerms.description.clipboardWrite=Input data to the clipboard
 webextPerms.description.devtools=Extend developer tools to access your data in open tabs
+webextPerms.description.dns=Access IP address and hostname information
 webextPerms.description.downloads=Download files and read and modify the browser’s download history
 webextPerms.description.downloads.open=Open files downloaded to your computer
 webextPerms.description.find=Read the text of all open tabs
 webextPerms.description.geolocation=Access your location
 webextPerms.description.history=Access browsing history
 webextPerms.description.management=Monitor extension usage and manage themes
 # LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
 # %S will be replaced with the name of the application
--- a/browser/themes/windows/places/organizer.css
+++ b/browser/themes/windows/places/organizer.css
@@ -156,37 +156,21 @@
     border: 0;
     border-inline-end: 1px solid #A9B7C9;
     min-width: 0;
     width: 3px;
     background-color: transparent;
     margin-inline-start: -3px;
     position: relative;
   }
+
+  @media (-moz-os-version: windows-win7) {
+    #detailsDeck {
+      border-top-color: #A9B7C9;
+    }
+  }
 }
 
 @media (-moz-windows-glass) {
   #placesToolbox {
     border-top: none;
   }
 }
-
-@media (-moz-windows-default-theme) and (-moz-os-version: windows-win7) {
-  #placesView,
-  #infoPane,
-  #placesList,
-  #placeContent {
-    background-color: #EEF3FA;
-  }
-
-  #detailsDeck {
-    border-top-color: #A9B7C9;
-  }
-
-  #searchFilter {
-    -moz-appearance: none;
-    padding: 2px;
-    padding-inline-start: 4px;
-    background-clip: padding-box;
-    border: 1px solid rgba(0,0,0,.32);
-    border-radius: 2px;
-  }
-}
--- a/build/moz.configure/rust.configure
+++ b/build/moz.configure/rust.configure
@@ -62,17 +62,17 @@ def rust_compiler(rustc_info, cargo_info
         die(dedent('''\
         Rust compiler not found.
         To compile rust language sources, you must have 'rustc' in your path.
         See https://www.rust-lang.org/ for more information.
 
         You can install rust by running './mach bootstrap'
         or by directly running the installer from https://rustup.rs/
         '''))
-    rustc_min_version = Version('1.23.0')
+    rustc_min_version = Version('1.24.0')
     cargo_min_version = Version('0.{}'.format(rustc_min_version.minor + 1))
 
     version = rustc_info.version
     if version < rustc_min_version:
         die(dedent('''\
         Rust compiler {} is too old.
 
         To compile Rust language sources please install at least
--- a/devtools/client/inspector/fonts/components/Font.js
+++ b/devtools/client/inspector/fonts/components/Font.js
@@ -21,58 +21,39 @@ class Font extends PureComponent {
       onPreviewFonts: PropTypes.func.isRequired,
     };
   }
 
   constructor(props) {
     super(props);
 
     this.state = {
-      isFontExpanded: false,
       isFontFaceRuleExpanded: false,
     };
 
-    this.onFontToggle = this.onFontToggle.bind(this);
     this.onFontFaceRuleToggle = this.onFontFaceRuleToggle.bind(this);
   }
 
   componentWillReceiveProps(newProps) {
     if (this.props.font.name === newProps.font.name) {
       return;
     }
 
     this.setState({
-      isFontExpanded: false,
       isFontFaceRuleExpanded: false,
     });
   }
 
-  onFontToggle(event) {
-    this.setState({
-      isFontExpanded: !this.state.isFontExpanded
-    });
-    event.stopPropagation();
-  }
-
   onFontFaceRuleToggle(event) {
     this.setState({
       isFontFaceRuleExpanded: !this.state.isFontFaceRuleExpanded
     });
     event.stopPropagation();
   }
 
-  renderFontCSS(cssFamilyName) {
-    return dom.p(
-      {
-        className: "font-css-name"
-      },
-      `${getStr("fontinspector.usedAs")} "${cssFamilyName}"`
-    );
-  }
-
   renderFontCSSCode(rule, ruleText) {
     if (!rule) {
       return null;
     }
 
     // Cut the rule text in 3 parts: the selector, the declarations, the closing brace.
     // This way we can collapse the declarations by default and display an expander icon
     // to expand them again.
@@ -84,24 +65,23 @@ class Font extends PureComponent {
 
     return dom.pre(
       {
         className: "font-css-code",
       },
       this.renderFontCSSCodeTwisty(),
       leading,
       isFontFaceRuleExpanded ?
-        null
-        :
+        body :
         dom.span(
           {
-            className: "font-css-code-expander"
+            className: "font-css-code-expander",
+            onClick: this.onFontFaceRuleToggle,
           }
         ),
-      isFontFaceRuleExpanded ? body : null,
       trailing
     );
   }
 
   renderFontTypeAndURL(url, format) {
     if (!url) {
       return dom.p(
         {
@@ -124,78 +104,59 @@ class Font extends PureComponent {
         format
       )
     );
   }
 
   renderFontName(name) {
     return dom.h1(
       {
-        className: "font-name",
-        onClick: this.onFontToggle,
+        className: "font-name"
       },
       name
     );
   }
 
-  renderFontTwisty() {
-    let { isFontExpanded } = this.state;
-    return this.renderTwisty(isFontExpanded, this.onFontToggle);
-  }
-
   renderFontCSSCodeTwisty() {
     let { isFontFaceRuleExpanded } = this.state;
-    return this.renderTwisty(isFontFaceRuleExpanded, this.onFontFaceRuleToggle);
-  }
 
-  renderTwisty(isExpanded, onClick) {
     let attributes = {
       className: "theme-twisty",
-      onClick,
+      onClick: this.onFontFaceRuleToggle,
     };
-    if (isExpanded) {
+    if (isFontFaceRuleExpanded) {
       attributes.open = "true";
     }
 
     return dom.span(attributes);
   }
 
   render() {
     let {
       font,
       fontOptions,
       onPreviewFonts,
     } = this.props;
 
     let { previewText } = fontOptions;
 
     let {
-      CSSFamilyName,
       format,
       name,
       previewUrl,
       rule,
       ruleText,
       URI,
     } = font;
 
-    let { isFontExpanded } = this.state;
-
     return dom.li(
       {
-        className: "font" + (isFontExpanded ? " expanded" : ""),
+        className: "font",
       },
-      this.renderFontTwisty(),
       this.renderFontName(name),
       FontPreview({ previewText, previewUrl, onPreviewFonts }),
-      dom.div(
-        {
-          className: "font-details"
-        },
-        this.renderFontTypeAndURL(URI, format),
-        this.renderFontCSSCode(rule, ruleText),
-        this.renderFontCSS(CSSFamilyName)
-      )
+      this.renderFontTypeAndURL(URI, format),
+      this.renderFontCSSCode(rule, ruleText)
     );
   }
 }
 
 module.exports = Font;
--- a/devtools/client/inspector/fonts/components/FontPreview.js
+++ b/devtools/client/inspector/fonts/components/FontPreview.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 const { PureComponent } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 const Types = require("../types");
+const { getStr } = require("../utils/l10n");
 
 class FontPreview extends PureComponent {
   static get propTypes() {
     return {
       previewText: Types.fontOptions.previewText.isRequired,
       previewUrl: Types.font.previewUrl.isRequired,
       onPreviewFonts: PropTypes.func.isRequired,
     };
@@ -80,15 +81,16 @@ class FontPreview extends PureComponent 
         )
         :
         null,
       dom.img(
         {
           className: "font-preview",
           src: previewUrl,
           onClick: this.onClick,
+          title: !isFocused ? getStr("fontinspector.editPreview") : "",
         }
       )
     );
   }
 }
 
 module.exports = FontPreview;
--- a/devtools/client/inspector/fonts/test/browser.ini
+++ b/devtools/client/inspector/fonts/test/browser.ini
@@ -12,12 +12,11 @@ support-files =
   !/devtools/client/inspector/test/head.js
   !/devtools/client/inspector/test/shared-head.js
   !/devtools/client/shared/test/test-actor.js
   !/devtools/client/shared/test/test-actor-registry.js
 
 [browser_fontinspector.js]
 [browser_fontinspector_edit-previews.js]
 [browser_fontinspector_expand-css-code.js]
-[browser_fontinspector_expand-details.js]
 [browser_fontinspector_other-fonts.js]
 [browser_fontinspector_text-node.js]
 [browser_fontinspector_theme-change.js]
--- a/devtools/client/inspector/fonts/test/browser_fontinspector.js
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector.js
@@ -50,53 +50,43 @@ function getFormat(fontLi) {
   let link = fontLi.querySelector(".font-format-url a");
   if (!link) {
     return null;
   }
 
   return link.textContent;
 }
 
-function getCSSName(fontLi) {
-  let text = fontLi.querySelector(".font-css-name").textContent;
-
-  return text.substring(text.indexOf('"') + 1, text.lastIndexOf('"'));
-}
-
 function* testBodyFonts(inspector, viewDoc) {
   let lis = getUsedFontsEls(viewDoc);
   is(lis.length, 5, "Found 5 fonts");
 
   for (let i = 0; i < FONTS.length; i++) {
     let li = lis[i];
     let font = FONTS[i];
 
     is(getName(li), font.name, "font " + i + " right font name");
     is(isRemote(li), font.remote, "font " + i + " remote value correct");
     is(li.querySelector(".font-url").href, font.url, "font " + i + " url correct");
     is(getFormat(li), font.format, "font " + i + " format correct");
-    is(getCSSName(li), font.cssName, "font " + i + " css name correct");
   }
 
   // test that the bold and regular fonts have different previews
   let regSrc = lis[0].querySelector(".font-preview").src;
   let boldSrc = lis[1].querySelector(".font-preview").src;
   isnot(regSrc, boldSrc, "preview for bold font is different from regular");
 
   // test system font
   let localFontName = getName(lis[4]);
-  let localFontCSSName = getCSSName(lis[4]);
 
   // On Linux test machines, the Arial font doesn't exist.
   // The fallback is "Liberation Sans"
   ok((localFontName == "Arial") || (localFontName == "Liberation Sans"),
      "local font right font name");
   ok(!isRemote(lis[4]), "local font is local");
-  ok((localFontCSSName == "Arial") || (localFontCSSName == "Liberation Sans"),
-     "Arial", "local font has right css name");
 }
 
 function* testDivFonts(inspector, viewDoc) {
   let updated = inspector.once("fontinspector-updated");
   yield selectNode("div", inspector);
   yield updated;
 
   let lis = getUsedFontsEls(viewDoc);
--- a/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
+++ b/devtools/client/inspector/fonts/test/browser_fontinspector_expand-css-code.js
@@ -7,30 +7,45 @@
 // Test that the font-face css rule code is collapsed by default, and can be expanded.
 
 const TEST_URI = URL_ROOT + "browser_fontinspector.html";
 
 add_task(function* () {
   let { view } = yield openFontInspectorForURL(TEST_URI);
   let viewDoc = view.document;
 
-  info("Expanding the details section of the first font");
+  info("Checking that the css font-face rule is collapsed by default");
   let fontEl = getUsedFontsEls(viewDoc)[0];
-  yield expandFontDetails(fontEl);
-
-  info("Checking that the css font-face rule is collapsed by default");
   let codeEl = fontEl.querySelector(".font-css-code");
   is(codeEl.textContent, "@font-face {}", "The font-face rule is collapsed");
 
   info("Expanding the rule by clicking on the expander icon");
   let onExpanded = BrowserTestUtils.waitForCondition(() => {
     return codeEl.textContent === `@font-face {
   font-family: "bar";
   src: url("bad/font/name.ttf"), url("ostrich-regular.ttf") format("truetype");
 }`;
   }, "Waiting for the font-face rule");
 
   let expander = fontEl.querySelector(".font-css-code .theme-twisty");
   expander.click();
   yield onExpanded;
 
   ok(true, "Font-face rule is now expanded");
+
+  info("Expanding another rule by clicking on the [...] icon instead");
+  fontEl = getUsedFontsEls(viewDoc)[1];
+  codeEl = fontEl.querySelector(".font-css-code");
+
+  onExpanded = BrowserTestUtils.waitForCondition(() => {
+    return codeEl.textContent === `@font-face {
+  font-family: "bar";
+  font-weight: bold;
+  src: url("ostrich-black.ttf");
+}`;
+  }, "Waiting for the font-face rule");
+
+  expander = fontEl.querySelector(".font-css-code .font-css-code-expander");
+  expander.click();
+  yield onExpanded;
+
+  ok(true, "Font-face rule is now expanded too");
 });
deleted file mode 100644
--- a/devtools/client/inspector/fonts/test/browser_fontinspector_expand-details.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* vim: set ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-// Test that fonts are collapsed by default, and can be expanded.
-
-const TEST_URI = URL_ROOT + "browser_fontinspector.html";
-
-add_task(function* () {
-  let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
-  let viewDoc = view.document;
-
-  info("Checking all font are collapsed by default");
-  let fonts = getUsedFontsEls(viewDoc);
-  checkAllFontsCollapsed(fonts);
-
-  info("Clicking on the first one to expand the font details");
-  yield expandFontDetails(fonts[0]);
-
-  ok(fonts[0].querySelector(".theme-twisty").hasAttribute("open"), `Twisty is open`);
-  ok(isFontDetailsVisible(fonts[0]), `Font details is shown`);
-
-  info("Selecting a node with different fonts and checking that all fonts are collapsed");
-  yield selectNode(".black-text", inspector);
-  fonts = getUsedFontsEls(viewDoc);
-  checkAllFontsCollapsed(fonts);
-});
-
-function checkAllFontsCollapsed(fonts) {
-  fonts.forEach((el, i) => {
-    let twisty = el.querySelector(".theme-twisty");
-    ok(twisty, `Twisty ${i} exists`);
-    ok(!twisty.hasAttribute("open"), `Twisty ${i} is closed`);
-    ok(!isFontDetailsVisible(el), `Font details ${i} is hidden`);
-  });
-}
--- a/devtools/client/inspector/fonts/test/head.js
+++ b/devtools/client/inspector/fonts/test/head.js
@@ -83,31 +83,16 @@ function* updatePreviewText(view, text) 
     let update = waitForNEvents(view.inspector, "fontinspector-updated", text.length);
     EventUtils.sendString(text, doc.defaultView);
     yield update;
   }
 
   is(input.value, text, "The input now contains the correct text.");
 }
 
-async function expandFontDetails(fontEl) {
-  info("Expanding a font details section");
-
-  let onExpanded = BrowserTestUtils.waitForCondition(() => isFontDetailsVisible(fontEl),
-                                                     "Waiting for font details");
-  let twisty = fontEl.querySelector(".theme-twisty");
-  twisty.click();
-  await onExpanded;
-}
-
-function isFontDetailsVisible(fontEl) {
-  return [...fontEl.querySelectorAll(".font-css-name, .font-css-code, .font-format-url")]
-         .every(el => el.getBoxQuads().length);
-}
-
 /**
  * Get all of the <li> elements for the fonts used on the currently selected element.
  *
  * @param  {document} viewDoc
  * @return {Array}
  */
 function getUsedFontsEls(viewDoc) {
   return viewDoc.querySelectorAll("#font-container > .fonts-list > li");
--- a/devtools/client/inspector/fonts/types.js
+++ b/devtools/client/inspector/fonts/types.js
@@ -5,19 +5,16 @@
 "use strict";
 
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 
 /**
  * A single font.
  */
 const font = exports.font = {
-  // The name of the font family
-  CSSFamilyName: PropTypes.string,
-
   // The format of the font
   format: PropTypes.string,
 
   // The name of the font
   name: PropTypes.string,
 
   // URL for the font preview
   previewUrl: PropTypes.string,
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -703,20 +703,24 @@ Inspector.prototype = {
     if (this.isSplitRuleViewEnabled && defaultTab === "ruleview") {
       defaultTab = "computedview";
     }
 
     // Append all side panels
 
     await this.addRuleView(defaultTab);
 
-    this.sidebar.addExistingTab(
-      "computedview",
-      INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
-      defaultTab == "computedview");
+    // If the 3 Pane Inspector feature is disabled, use the old order:
+    // Rules, Computed, Layout, etc.
+    if (!this.showSplitSidebarToggle) {
+      this.sidebar.addExistingTab(
+        "computedview",
+        INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
+        defaultTab == "computedview");
+    }
 
     // Inject a lazy loaded react tab by exposing a fake React object
     // with a lazy defined Tab thanks to `panel` being a function
     let layoutId = "layoutview";
     let layoutTitle = INSPECTOR_L10N.getStr("inspector.sidebar.layoutViewTitle2");
     this.sidebar.addTab(
       layoutId,
       layoutTitle,
@@ -732,16 +736,25 @@ Inspector.prototype = {
             this.layoutview = new LayoutView(this, this.panelWin);
           }
 
           return this.layoutview.provider;
         }
       },
       defaultTab == layoutId);
 
+    // If the 3 Pane Inspector feature is enabled, use the new order:
+    // Rules, Layout, Computed, etc.
+    if (this.showSplitSidebarToggle) {
+      this.sidebar.addExistingTab(
+        "computedview",
+        INSPECTOR_L10N.getStr("inspector.sidebar.computedViewTitle"),
+        defaultTab == "computedview");
+    }
+
     if (Services.prefs.getBoolPref("devtools.changesview.enabled")) {
       // Inject a lazy loaded react tab by exposing a fake React object
       // with a lazy defined Tab thanks to `panel` being a function
       let changesId = "changesview";
       let changesTitle = INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle");
       this.sidebar.addTab(
         changesId,
         changesTitle,
--- a/devtools/client/locales/en-US/font-inspector.properties
+++ b/devtools/client/locales/en-US/font-inspector.properties
@@ -1,26 +1,27 @@
 # 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/.
 
 # LOCALIZATION NOTE This file contains the Font Inspector strings.
 # The Font Inspector is a panel accessible in the Inspector sidebar.
 
-# LOCALIZATION NOTE (fontinspector.usedAs) This label introduces the name used to refer to
-# the font in a stylesheet.
-fontinspector.usedAs=Used as:
-
 # LOCALIZATION NOTE (fontinspector.system) This label indicates that the font is a local
 # system font.
 fontinspector.system=system
 
 # LOCALIZATION NOTE (fontinspector.remote) This label indicates that the font is a remote
 # font.
 fontinspector.remote=remote
 
 # LOCALIZATION NOTE (fontinspector.noFontsOnSelectedElement): This label is shown when
 # no fonts found on the selected element.
 fontinspector.noFontsOnSelectedElement=No fonts were found for the current element.
 
 # LOCALIZATION NOTE (fontinspector.otherFontsInPageHeader): This is the text for the
 # header of a collapsible section containing other fonts used in the page.
-fontinspector.otherFontsInPageHeader=Other fonts in page
\ No newline at end of file
+fontinspector.otherFontsInPageHeader=Other fonts in page
+
+# LOCALIZATION NOTE (fontinspector.editPreview): This is the text that appears in a
+# tooltip on hover of a font preview string. Clicking on the string opens a text input
+# where users can type to change the preview text.
+fontinspector.editPreview=Click to edit preview
--- a/devtools/client/netmonitor/initializer.js
+++ b/devtools/client/netmonitor/initializer.js
@@ -17,17 +17,17 @@ const require = window.windowRequire = B
 }).require;
 
 const EventEmitter = require("devtools/shared/old-event-emitter");
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const { render, unmountComponentAtNode } = require("devtools/client/shared/vendor/react-dom");
 const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
 const { bindActionCreators } = require("devtools/client/shared/vendor/redux");
 const { Connector } = require("./src/connector/index");
-const { configureStore } = require("./src/utils/create-store");
+const { configureStore } = require("./src/create-store");
 const App = createFactory(require("./src/components/App"));
 const { EVENTS } = require("./src/constants");
 const {
   getDisplayedRequestById,
   getSortedRequests
 } = require("./src/selectors/index");
 
 // Inject EventEmitter into global window.
--- a/devtools/client/netmonitor/launchpad.js
+++ b/devtools/client/netmonitor/launchpad.js
@@ -38,17 +38,17 @@ pref("devtools.netmonitor.har.enableAuto
 pref("devtools.netmonitor.persistlog", false);
 pref("devtools.styleeditor.enabled", true);
 
 require("./src/assets/styles/netmonitor.css");
 
 const EventEmitter = require("devtools-modules/src/utils/event-emitter");
 EventEmitter.decorate(window);
 
-const { configureStore } = require("./src/utils/create-store");
+const { configureStore } = require("./src/create-store");
 const App = require("./src/components/App");
 const { Connector } = require("./src/connector/index");
 const connector = new Connector();
 const store = configureStore(connector);
 const actions = bindActionCreators(require("./src/actions"), store.dispatch);
 
 // Inject to global window for testing
 window.store = store;
--- a/devtools/client/netmonitor/src/components/App.js
+++ b/devtools/client/netmonitor/src/components/App.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 
 // Components
 loader.lazyGetter(this, "MonitorPanel", function () {
   return createFactory(require("./MonitorPanel"));
 });
 loader.lazyGetter(this, "StatisticsPanel", function () {
   return createFactory(require("./StatisticsPanel"));
 });
--- a/devtools/client/netmonitor/src/components/CustomRequestPanel.js
+++ b/devtools/client/netmonitor/src/components/CustomRequestPanel.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { L10N } = require("../utils/l10n");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
 const Actions = require("../actions/index");
 const { getSelectedRequest } = require("../selectors/index");
 const {
   getUrlQuery,
   parseQueryString,
   writeHeaderText,
--- a/devtools/client/netmonitor/src/components/MonitorPanel.js
+++ b/devtools/client/netmonitor/src/components/MonitorPanel.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const { div } = dom;
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { findDOMNode } = require("devtools/client/shared/vendor/react-dom");
 const Actions = require("../actions/index");
 const { updateFormDataSections } = require("../utils/request-utils");
 const {
   getSelectedRequest,
   isSelectedRequestVisible,
 } = require("../selectors/index");
 
--- a/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
+++ b/devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { createFactory } = require("devtools/client/shared/vendor/react");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const Actions = require("../actions/index");
 const { getSelectedRequest } = require("../selectors/index");
 
 // Components
 loader.lazyGetter(this, "CustomRequestPanel", function () {
   return createFactory(require("./CustomRequestPanel"));
 });
 loader.lazyGetter(this, "TabboxPanel", function () {
--- a/devtools/client/netmonitor/src/components/ParamsPanel.js
+++ b/devtools/client/netmonitor/src/components/ParamsPanel.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { L10N } = require("../utils/l10n");
 const {
   fetchNetworkUpdatePacket,
   getUrlQuery,
   parseQueryString,
   parseFormData,
 } = require("../utils/request-utils");
 const { sortObjectKeys } = require("../utils/sort-utils");
--- a/devtools/client/netmonitor/src/components/RequestListContent.js
+++ b/devtools/client/netmonitor/src/components/RequestListContent.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { HTMLTooltip } = require("devtools/client/shared/widgets/tooltip/HTMLTooltip");
 
 const Actions = require("../actions/index");
 const { formDataURI } = require("../utils/request-utils");
 const {
   getDisplayedRequests,
   getSelectedRequest,
   getSortedRequests,
--- a/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
+++ b/devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const Actions = require("../actions/index");
 const { ACTIVITY_TYPE } = require("../constants");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 
 // Components
 const MDNLink = createFactory(require("./MdnLink"));
 const RequestListHeader = createFactory(require("./RequestListHeader"));
--- a/devtools/client/netmonitor/src/components/RequestListHeader.js
+++ b/devtools/client/netmonitor/src/components/RequestListHeader.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { Component } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { getTheme, addThemeObserver, removeThemeObserver } =
   require("devtools/client/shared/theme");
 const Actions = require("../actions/index");
 const { HEADERS, REQUESTS_WATERFALL } = require("../constants");
 const { getWaterfallScale } = require("../selectors/index");
 const { getFormattedTime } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const RequestListHeaderContextMenu = require("../widgets/RequestListHeaderContextMenu");
--- a/devtools/client/netmonitor/src/components/StatisticsPanel.js
+++ b/devtools/client/netmonitor/src/components/StatisticsPanel.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { FILTER_TAGS } = require("../constants");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { Chart } = require("devtools/client/shared/widgets/Chart");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
 const { Filters } = require("../utils/filter-predicates");
 const { getSizeWithDecimals, getTimeWithDecimals } = require("../utils/format-utils");
 const { L10N } = require("../utils/l10n");
 const { getPerformanceAnalysisURL } = require("../utils/mdn-utils");
 const { fetchNetworkUpdatePacket } = require("../utils/request-utils");
--- a/devtools/client/netmonitor/src/components/StatusBar.js
+++ b/devtools/client/netmonitor/src/components/StatusBar.js
@@ -1,17 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 const { PluralForm } = require("devtools/shared/plural-form");
 const Actions = require("../actions/index");
 const {
   getDisplayedRequestsSummary,
   getDisplayedTimingMarker,
 } = require("../selectors/index");
 const {
   getFormattedSize,
--- a/devtools/client/netmonitor/src/components/Toolbar.js
+++ b/devtools/client/netmonitor/src/components/Toolbar.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { Component, createFactory } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
-const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { connect } = require("../utils/redux-connect");
 
 const Actions = require("../actions/index");
 const { FILTER_SEARCH_DELAY, FILTER_TAGS } = require("../constants");
 const {
   getRecordingState,
   getTypeFilteredRequests,
   isNetworkDetailsToggleButtonDisabled,
 } = require("../selectors/index");
rename from devtools/client/netmonitor/src/utils/create-store.js
rename to devtools/client/netmonitor/src/create-store.js
--- a/devtools/client/netmonitor/src/utils/create-store.js
+++ b/devtools/client/netmonitor/src/create-store.js
@@ -3,28 +3,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const Services = require("Services");
 const { applyMiddleware, createStore } = require("devtools/client/shared/vendor/redux");
 
 // Middleware
-const batching = require("../middleware/batching");
-const prefs = require("../middleware/prefs");
-const thunk = require("../middleware/thunk");
-const recording = require("../middleware/recording");
+const batching = require("./middleware/batching");
+const prefs = require("./middleware/prefs");
+const thunk = require("./middleware/thunk");
+const recording = require("./middleware/recording");
 
 // Reducers
-const rootReducer = require("../reducers/index");
-const { FilterTypes, Filters } = require("../reducers/filters");
-const { Requests } = require("../reducers/requests");
-const { Sort } = require("../reducers/sort");
-const { TimingMarkers } = require("../reducers/timing-markers");
-const { UI, Columns } = require("../reducers/ui");
+const rootReducer = require("./reducers/index");
+const { FilterTypes, Filters } = require("./reducers/filters");
+const { Requests } = require("./reducers/requests");
+const { Sort } = require("./reducers/sort");
+const { TimingMarkers } = require("./reducers/timing-markers");
+const { UI, Columns } = require("./reducers/ui");
 
 /**
  * Configure state and middleware for the Network monitor tool.
  */
 function configureStore(connector) {
   // Prepare initial state.
   const initialState = {
     filters: new Filters({
--- a/devtools/client/netmonitor/src/moz.build
+++ b/devtools/client/netmonitor/src/moz.build
@@ -11,9 +11,10 @@ DIRS += [
     'reducers',
     'selectors',
     'utils',
     'widgets',
 ]
 
 DevToolsModules(
     'constants.js',
+    'create-store.js',
 )
--- a/devtools/client/netmonitor/src/utils/moz.build
+++ b/devtools/client/netmonitor/src/utils/moz.build
@@ -3,23 +3,23 @@
 # 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/.
 
 DIRS += [
     'firefox',
 ]
 
 DevToolsModules(
-    'create-store.js',
     'filter-autocomplete-provider.js',
     'filter-predicates.js',
     'filter-text-utils.js',
     'format-utils.js',
     'headers-provider.js',
     'l10n.js',
     'mdn-utils.js',
     'menu.js',
     'open-request-in-tab.js',
     'prefs.js',
+    'redux-connect.js',
     'request-utils.js',
     'sort-predicates.js',
     'sort-utils.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/utils/redux-connect.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
+const VisibilityHandler = createFactory(require("devtools/client/shared/components/VisibilityHandler"));
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+
+/**
+ * This helper is wrapping Redux's connect() method and applying
+ * HOC (VisibilityHandler component) on whatever component is
+ * originally passed in. The HOC is responsible for not causing
+ * rendering if the owner panel runs in the background.
+ */
+function connectWrapper() {
+  let args = [].slice.call(arguments);
+  return component => {
+    return connect(...args)(props => {
+      return VisibilityHandler(null, createElement(component, props));
+    });
+  };
+}
+
+module.exports = {
+  connect: connectWrapper
+};
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -55,16 +55,17 @@ support-files =
   !/devtools/client/framework/test/shared-head.js
   xhr_bundle.js
   xhr_bundle.js.map
   xhr_original.js
 
 [browser_net_accessibility-01.js]
 [browser_net_accessibility-02.js]
 [browser_net_api-calls.js]
+[browser_net_background_update.js]
 [browser_net_autoscroll.js]
 [browser_net_cached-status.js]
 [browser_net_cause.js]
 [browser_net_cause_redirect.js]
 [browser_net_cause_source_map.js]
 [browser_net_service-worker-status.js]
 [browser_net_charts-01.js]
 [browser_net_charts-02.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_background_update.js
@@ -0,0 +1,56 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Check that network logs created when the Net panel is not visible
+ * are displayed when the user shows the panel again.
+ */
+add_task(async () => {
+  let { tab, monitor, toolbox } = await initNetMonitor(CUSTOM_GET_URL);
+  info("Starting test... ");
+
+  let { document, store, windowRequire } = monitor.panelWin;
+  let Actions = windowRequire("devtools/client/netmonitor/src/actions/index");
+
+  store.dispatch(Actions.batchEnable(false));
+
+  // Execute two requests
+  await performRequests(monitor, tab, 2);
+
+  // Wait for two logs
+  await waitUntil(() => document.querySelectorAll(".request-list-item").length == 2);
+
+  info("Select the inspector");
+  await toolbox.selectTool("inspector");
+
+  info("Wait for Net panel to be hidden");
+  await waitUntil(() => (document.visibilityState == "hidden"));
+
+  // Execute another two requests
+  await performRequests(monitor, tab, 2);
+
+  // The number of rendered requests should be the same since
+  // requests shouldn't be rendered while the net panel is in
+  // background
+  is(document.querySelectorAll(".request-list-item").length, 2,
+    "There should be expected number of requests");
+
+  info("Select the Net panel again");
+  await toolbox.selectTool("netmonitor");
+
+  // Wait for another two logs to be rendered since the panel
+  // is selected now.
+  await waitUntil(() => document.querySelectorAll(".request-list-item").length == 4);
+
+  return teardown(monitor);
+});
+
+async function performRequests(monitor, tab, count) {
+  let wait = waitForNetworkEvents(monitor, count);
+  await ContentTask.spawn(tab.linkedBrowser, count, requestCount => {
+    content.wrappedJSObject.performRequests(requestCount);
+  });
+  await wait;
+}
--- a/devtools/client/shared/browser-loader.js
+++ b/devtools/client/shared/browser-loader.js
@@ -14,16 +14,17 @@ const BROWSER_BASED_DIRS = [
   "resource://devtools/client/inspector/changes",
   "resource://devtools/client/inspector/computed",
   "resource://devtools/client/inspector/events",
   "resource://devtools/client/inspector/flexbox",
   "resource://devtools/client/inspector/fonts",
   "resource://devtools/client/inspector/grids",
   "resource://devtools/client/inspector/layout",
   "resource://devtools/client/jsonview",
+  "resource://devtools/client/netmonitor/src/utils",
   "resource://devtools/client/shared/source-map",
   "resource://devtools/client/shared/redux",
   "resource://devtools/client/shared/vendor",
 ];
 
 const COMMON_LIBRARY_DIRS = [
   "resource://devtools/client/shared/vendor",
 ];
--- a/devtools/client/shared/components/SidebarToggle.js
+++ b/devtools/client/shared/components/SidebarToggle.js
@@ -39,16 +39,17 @@ class SidebarToggle extends Component {
     };
 
     this.onClick = this.onClick.bind(this);
   }
 
   // Events
 
   onClick(event) {
+    event.stopPropagation();
     this.props.onClick(event);
   }
 
   // Rendering
 
   render() {
     let title = this.state.collapsed ?
       this.props.expandPaneTitle :
--- a/devtools/client/sourceeditor/codemirror/old-debugger.css
+++ b/devtools/client/sourceeditor/codemirror/old-debugger.css
@@ -1,17 +1,15 @@
 :root {
   --breakpoint-background: url("chrome://devtools/skin/images/breakpoint.svg#light");
-  --breakpoint-hover-background: url("chrome://devtools/skin/images/breakpoint.svg#light-hover");
   --breakpoint-conditional-background: url("chrome://devtools/skin/images/breakpoint.svg#light-conditional");
 }
 
 .theme-dark:root {
   --breakpoint-background: url("chrome://devtools/skin/images/breakpoint.svg#dark");
-  --breakpoint-hover-background: url("chrome://devtools/skin/images/breakpoint.svg#dark-hover");
   --breakpoint-conditional-background: url("chrome://devtools/skin/images/breakpoint.svg#dark-conditional");
 }
 
 .breakpoint .CodeMirror-linenumber:before {
   background-image: var(--breakpoint-background) !important;
 }
 
 .conditional .CodeMirror-linenumber:before {
--- a/devtools/client/themes/fonts.css
+++ b/devtools/client/themes/fonts.css
@@ -20,32 +20,29 @@
   margin: 0;
   list-style: none;
 }
 
 .font {
   border: 1px solid var(--theme-splitter-color);
   border-width: 0 1px 1px 0;
   display: grid;
-  grid-template-columns: 14px auto 1fr;
-  grid-template-rows: 50px;
-  grid-column-gap: 10px;
-  padding: 0 10px 0 5px;
+  grid-template-columns: 1fr auto;
+  padding: 10px 20px;
 }
 
 #font-container .theme-twisty {
   display: inline-block;
   cursor: pointer;
-  place-self: center;
-  vertical-align: text-top;
+  vertical-align: bottom;
 }
 
 .font-preview-container {
-  grid-column: 3 / -1;
-  grid-row: 1;
+  grid-column: 2;
+  grid-row: 1 / span 2;
   overflow: hidden;
   display: grid;
   place-items: center end;
   position: relative;
 }
 
 .font-preview {
   height: 50px;
@@ -56,54 +53,49 @@
   cursor: text;
   background-image: linear-gradient(to right,
     var(--grey-40) 3px, transparent 3px, transparent);
   background-size: 6px 1px;
   background-repeat: repeat-x;
   background-position-y: 45px;
 }
 
-.font-preview-input {
+#font-container .font-preview-input {
   position: absolute;
-  top: 0;
+  top: 5px;
   left: 0;
   width: calc(100% - 5px);
-  height: calc(100% - 2px);
+  height: calc(100% - 10px);
   background: transparent;
   color: transparent;
+  border-radius: 0;
+  padding: 0;
 }
 
 .font-preview-input::-moz-selection {
   background: transparent;
   color: transparent;
 }
 
 .font-name {
   margin: 0;
-  font-size: 1em;
+  font-size: 1.2em;
+  font-weight: normal;
   white-space: nowrap;
-  grid-column: 2;
-  place-self: center start;
-}
-
-.font-details {
-  grid-column: 2 / 4;
-  padding-inline-end: 14px;
-  width: 100%;
 }
 
 .font-css-code {
   direction: ltr;
-  padding: 5px;
   margin: 0;
-  border: 1px solid var(--theme-splitter-color);
-  border-radius: 3px;
   overflow: hidden;
   text-overflow: ellipsis;
   color: var(--theme-toolbar-color);
+  grid-column: span 2;
+  position: relative;
+  offset-inline-start: -4px;
 }
 
 .font-css-code-expander::before {
   content: "\2026";
   display: inline-block;
   width: 12px;
   height: 8px;
   margin: 0 2px;
@@ -113,22 +105,22 @@
   border-style: solid;
   border-width: 1px;
   text-align: center;
   vertical-align: middle;
 }
 
 .font-format-url {
   text-transform: capitalize;
-  margin-block-start: 0;
+  margin-top: .2em;
+  color: var(--grey-50);
 }
 
 .font-url {
-  margin-inline-start: 1em;
-  text-transform: uppercase;
+  margin-inline-start: .5em;
   text-decoration: underline;
   color: var(--theme-highlight-blue);
   background: transparent;
   border: none;
   cursor: pointer;
 }
 
 .font-url::after {
@@ -140,14 +132,11 @@
   vertical-align: middle;
   background-image: url(chrome://devtools-shim/content/aboutdevtools/images/external-link.svg);
   background-repeat: no-repeat;
   background-size: 13px 13px;
   -moz-context-properties: fill;
   fill: var(--blue-60);
 }
 
-
-.font:not(.expanded) .font-css-name,
-.font:not(.expanded) .font-css-code,
-.font:not(.expanded) .font-format-url {
-  display: none;
+#font-container .devtools-sidepanel-no-result + .accordion {
+  border-block-start: 1px solid var(--theme-splitter-color);
 }
--- a/devtools/client/themes/webconsole.css
+++ b/devtools/client/themes/webconsole.css
@@ -756,17 +756,16 @@ a.learn-more-link.webconsole-learn-more-
 }
 
 .webconsole-output-wrapper {
   display: flex;
   flex-direction: column;
   height: 100%;
   -moz-user-focus: normal;
   color: var(--console-output-color);
-  --console-output-indent-width: 1rem;
   --console-output-indent-border-color: var(--theme-selection-background);
   --icon-top-margin: 3px;
   --object-inspector-hover-background: transparent;
   --attachment-margin-block-end: 3px;
   --primary-toolbar-height: 29px;
   --close-button-image: url(chrome://devtools/skin/images/close.svg);
 }
 
@@ -932,17 +931,17 @@ a.learn-more-link.webconsole-learn-more-
 .theme-dark .webconsole-output-wrapper .message.warn .objectLeftBrace,
 .theme-dark .webconsole-output-wrapper .message.warn .objectRightBrace,
 .theme-dark .webconsole-output-wrapper .message.warn .arrayLeftBracket,
 .theme-dark .webconsole-output-wrapper .message.warn .arrayRightBracket {
   color: var(--theme-body-color);
 }
 .theme-dark .webconsole-output-wrapper .message.error .tree.object-inspector,
 .theme-dark .webconsole-output-wrapper .message.warn .tree.object-inspector {
-  --tree-indent-border-color: var(--theme-body-color);
+  --console-output-indent-border-color: var(--theme-body-color);
 }
 
 .webconsole-output-wrapper .message-flex-body > .message-body {
   display: inline-block;
   max-width: 100%;
 }
 
 .webconsole-output-wrapper .message-body > * {
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser.ini
@@ -171,17 +171,16 @@ skip-if = true # Bug 1437807
 [browser_console_clear_method.js]
 skip-if = true # Bug 1437843
 [browser_console_consolejsm_output.js]
 skip-if = true # Bug 1437844
 [browser_console_context_menu_entries.js]
 [browser_console_dead_objects.js]
 skip-if = true # Bug 1437845
 [browser_console_error_source_click.js]
-skip-if = true # Bug 1437847
 [browser_console_filters.js]
 [browser_console_nsiconsolemessage.js]
 [browser_console_open_or_focus.js]
 [browser_console_restore.js]
 [browser_console_webconsole_ctrlw_close_tab.js]
 [browser_console_webconsole_iframe_messages.js]
 [browser_console_webconsole_private_browsing.js]
 skip-if = true #	Bug 1403188
--- a/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js
+++ b/devtools/client/webconsole/new-console-output/test/mochitest/browser_console_error_source_click.js
@@ -1,79 +1,58 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+/* import-globals-from head.js */
+
 // Check that JS errors and CSS warnings open view source when their source link
-// is clicked in the Browser Console. See bug 877778.
+// is clicked in the Browser Console.
 
 "use strict";
 
-const TEST_URI = "data:text/html;charset=utf8,<p>hello world from bug 877778 " +
+const TEST_URI = "data:text/html;charset=utf8,<p>hello world" +
                  "<button onclick='foobar.explode()' " +
                  "style='test-color: green-please'>click!</button>";
 
-add_task(function* () {
-  yield new Promise(resolve => {
-    SpecialPowers.pushPrefEnv({"set": [
-      ["devtools.browserconsole.filter.cssparser", true]
-    ]}, resolve);
+add_task(async function () {
+  await addTab(TEST_URI);
+  let hud = await HUDService.toggleBrowserConsole();
+  ok(hud, "browser console opened");
+
+  // Enable CSS warnings and errors.
+  await setFilterState(hud, {
+    css: true
   });
 
-  yield loadTab(TEST_URI);
-  let hud = yield HUDService.toggleBrowserConsole();
-  ok(hud, "browser console opened");
-
   // On e10s, the exception is triggered in child process
   // and is ignored by test harness
   if (!Services.appinfo.browserTabsRemoteAutostart) {
     expectUncaughtException();
   }
 
   info("generate exception and wait for the message");
-  ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
+  ContentTask.spawn(gBrowser.selectedBrowser, {}, () => {
     let button = content.document.querySelector("button");
     button.click();
   });
 
-  let results = yield waitForMessages({
-    webconsole: hud,
-    messages: [
-      {
-        text: "ReferenceError: foobar is not defined",
-        category: CATEGORY_JS,
-        severity: SEVERITY_ERROR,
-      },
-      {
-        text: "Unknown property \u2018test-color\u2019",
-        category: CATEGORY_CSS,
-        severity: SEVERITY_WARNING,
-      },
-    ],
-  });
-
-  let viewSourceCalled = false;
+  await waitForMessageAndViewSource(hud,
+    "ReferenceError: foobar is not defined");
+  await waitForMessageAndViewSource(hud,
+    "Unknown property \u2018test-color\u2019.");
+  await resetFilters(hud);
+});
 
-  let viewSource = hud.viewSource;
-  hud.viewSource = () => {
-    viewSourceCalled = true;
-  };
+async function waitForMessageAndViewSource(hud, message) {
+  let msg = await waitFor(() => findMessage(hud, message));
+  ok(msg, `Message found: "${message}"`);
 
-  for (let result of results) {
-    viewSourceCalled = false;
+  let locationNode = msg.querySelector(".message-location .frame-link-source");
+  ok(locationNode, "Message location link element found");
 
-    let msg = [...result.matched][0];
-    ok(msg, "message element found for: " + result.text);
-    ok(!msg.classList.contains("filtered-by-type"), "message element is not filtered");
-    let selector = ".message .message-location .frame-link-source";
-    let locationNode = msg.querySelector(selector);
-    ok(locationNode, "message location element found");
-
-    locationNode.click();
-
-    ok(viewSourceCalled, "view source opened");
-  }
-
-  hud.viewSource = viewSource;
-
-  yield finishTest();
-});
+  let onTabOpen = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
+  locationNode.click();
+  let newTab = await onTabOpen;
+  ok(true, "The view source tab was opened in response to clicking the link");
+  await BrowserTestUtils.removeTab(newTab);
+}
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -119,54 +119,61 @@ ExplicitChildIterator::GetNextChild()
 
   return mChild;
 }
 
 void
 FlattenedChildIterator::Init(bool aIgnoreXBL)
 {
   if (aIgnoreXBL) {
+    mXBLInvolved = Some(false);
     return;
   }
 
   // TODO(emilio): I think it probably makes sense to only allow constructing
   // FlattenedChildIterators with Element.
   if (mParent->IsElement()) {
     if (ShadowRoot* shadow = mParent->AsElement()->GetShadowRoot()) {
       mParent = shadow;
-      mXBLInvolved = true;
+      mXBLInvolved = Some(true);
       return;
     }
   }
 
   nsXBLBinding* binding =
     mParent->OwnerDoc()->BindingManager()->GetBindingWithContent(mParent);
 
   if (binding) {
     MOZ_ASSERT(binding->GetAnonymousContent());
     mParent = binding->GetAnonymousContent();
-    mXBLInvolved = true;
+    mXBLInvolved = Some(true);
+  }
+}
+
+bool
+FlattenedChildIterator::ComputeWhetherXBLIsInvolved() const
+{
+  MOZ_ASSERT(mXBLInvolved.isNothing());
+  // We set mXBLInvolved to true if either the node we're iterating has a
+  // binding with content attached to it (in which case it is handled in Init),
+  // or the node is generated XBL content and has an <xbl:children> child.
+  if (!mParent->GetBindingParent()) {
+    return false;
   }
 
-  // We set mXBLInvolved to true if either:
-  // - The node we're iterating has a binding with content attached to it.
-  // - The node is generated XBL content and has an <xbl:children> child.
-  //
-  // FIXME(emilio): This is very slow :(
-  if (!mXBLInvolved && mParent->GetBindingParent()) {
-    for (nsIContent* child = mParent->GetFirstChild();
-         child;
-         child = child->GetNextSibling()) {
-      if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
-        MOZ_ASSERT(child->GetBindingParent());
-        mXBLInvolved = true;
-        break;
-      }
+  for (nsIContent* child = mParent->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child->NodeInfo()->Equals(nsGkAtoms::children, kNameSpaceID_XBL)) {
+      MOZ_ASSERT(child->GetBindingParent());
+      return true;
     }
   }
+
+  return false;
 }
 
 bool
 ExplicitChildIterator::Seek(const nsIContent* aChildToFind)
 {
   if (aChildToFind->GetParent() == mParent &&
       !aChildToFind->IsRootOfAnonymousSubtree()) {
     // Fast path: just point ourselves to aChildToFind, which is a
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -129,60 +129,69 @@ protected:
 // at the end by providing false for aStartAtBeginning in order to start
 // iterating in reverse from the last child.
 class FlattenedChildIterator : public ExplicitChildIterator
 {
 public:
   explicit FlattenedChildIterator(const nsIContent* aParent,
                                   bool aStartAtBeginning = true)
     : ExplicitChildIterator(aParent, aStartAtBeginning)
-    , mXBLInvolved(false)
     , mOriginalContent(aParent)
   {
     Init(false);
   }
 
   FlattenedChildIterator(FlattenedChildIterator&& aOther)
     : ExplicitChildIterator(Move(aOther))
+    , mOriginalContent(aOther.mOriginalContent)
     , mXBLInvolved(aOther.mXBLInvolved)
-    , mOriginalContent(aOther.mOriginalContent)
   {}
 
   FlattenedChildIterator(const FlattenedChildIterator& aOther)
     : ExplicitChildIterator(aOther)
+    , mOriginalContent(aOther.mOriginalContent)
     , mXBLInvolved(aOther.mXBLInvolved)
-    , mOriginalContent(aOther.mOriginalContent)
   {}
 
-  bool XBLInvolved() { return mXBLInvolved; }
+  bool XBLInvolved() {
+    if (mXBLInvolved.isNothing()) {
+      mXBLInvolved = Some(ComputeWhetherXBLIsInvolved());
+    }
+    return *mXBLInvolved;
+  }
 
   const nsIContent* Parent() const { return mOriginalContent; }
 
+private:
+  bool ComputeWhetherXBLIsInvolved() const;
+
+  void Init(bool aIgnoreXBL);
+
 protected:
   /**
    * This constructor is a hack to help AllChildrenIterator which sometimes
    * doesn't want to consider XBL.
    */
   FlattenedChildIterator(const nsIContent* aParent, uint32_t aFlags,
                          bool aStartAtBeginning = true)
     : ExplicitChildIterator(aParent, aStartAtBeginning)
-    , mXBLInvolved(false)
     , mOriginalContent(aParent)
   {
     bool ignoreXBL = aFlags & nsIContent::eAllButXBL;
     Init(ignoreXBL);
   }
 
-  void Init(bool aIgnoreXBL);
+  const nsIContent* mOriginalContent;
 
-  // For certain optimizations, nsCSSFrameConstructor needs to know if the
-  // child list of the element that we're iterating matches its .childNodes.
-  bool mXBLInvolved;
-
-  const nsIContent* mOriginalContent;
+private:
+  // For certain optimizations, nsCSSFrameConstructor needs to know if the child
+  // list of the element that we're iterating matches its .childNodes.
+  //
+  // This is lazily computed when asked for it.
+  Maybe<bool> mXBLInvolved;
 };
 
 /**
  * AllChildrenIterator traverses the children of an element including before /
  * after content and optionally XBL children.  The iterator can be initialized
  * to start at the end by providing false for aStartAtBeginning in order to
  * start iterating in reverse from the last child.
  *
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -37,8 +37,13 @@ DEPRECATED_OPERATION(PrefixedImageSmooth
 DEPRECATED_OPERATION(PrefixedFullscreenAPI)
 DEPRECATED_OPERATION(LenientSetter)
 DEPRECATED_OPERATION(FileLastModifiedDate)
 DEPRECATED_OPERATION(ImageBitmapRenderingContext_TransferImageBitmap)
 DEPRECATED_OPERATION(URLCreateObjectURL_MediaStream)
 DEPRECATED_OPERATION(XMLBaseAttribute)
 DEPRECATED_OPERATION(WindowContentUntrusted)
 DEPRECATED_OPERATION(RegisterProtocolHandlerInsecure)
+DEPRECATED_OPERATION(MixedDisplayObjectSubrequest)
+DEPRECATED_OPERATION(MotionEvent)
+DEPRECATED_OPERATION(OrientationEvent)
+DEPRECATED_OPERATION(ProximityEvent)
+DEPRECATED_OPERATION(AmbientLightEvent)
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -906,29 +906,35 @@ nsGenericDOMDataNode::TextIsOnlyWhitespa
 
 bool
 nsGenericDOMDataNode::ThreadSafeTextIsOnlyWhitespace() const
 {
   // FIXME: should this method take content language into account?
   if (mText.Is2b()) {
     // The fragment contains non-8bit characters and such characters
     // are never considered whitespace.
+    //
+    // FIXME(emilio): This is not quite true in presence of the
+    // NS_MAYBE_MODIFIED_FREQUENTLY flag... But looks like we only set that on
+    // anonymous nodes, so should be fine...
     return false;
   }
 
   if (HasFlag(NS_CACHED_TEXT_IS_ONLY_WHITESPACE)) {
     return HasFlag(NS_TEXT_IS_ONLY_WHITESPACE);
   }
 
   const char* cp = mText.Get1b();
   const char* end = cp + mText.GetLength();
 
   while (cp < end) {
     char ch = *cp;
 
+    // NOTE(emilio): If you ever change the definition of "whitespace" here, you
+    // need to change it too in RestyleManager::CharacterDataChanged.
     if (!dom::IsSpaceCharacter(ch)) {
       return false;
     }
 
     ++cp;
   }
 
   return true;
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -3027,16 +3027,22 @@ nsGlobalWindowInner::IsRequestIdleCallba
 
 /* static */ bool
 nsGlobalWindowInner::RegisterProtocolHandlerAllowedForContext(JSContext* aCx, JSObject* aObj)
 {
   return IsSecureContextOrObjectIsFromSecureContext(aCx, aObj) ||
          Preferences::GetBool("dom.registerProtocolHandler.insecure.enabled");
 }
 
+/* static */ bool
+nsGlobalWindowInner::DeviceSensorsEnabled(JSContext* aCx, JSObject* aObj)
+{
+  return Preferences::GetBool("device.sensors.enabled");
+}
+
 nsIDOMOfflineResourceList*
 nsGlobalWindowInner::GetApplicationCache(ErrorResult& aError)
 {
   if (!mApplicationCache) {
     nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(GetDocShell()));
     if (!webNav || !mDoc) {
       aError.Throw(NS_ERROR_FAILURE);
       return nullptr;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -397,16 +397,18 @@ public:
   static bool OfflineCacheAllowedForContext(JSContext* /* unused */, JSObject* aObj);
 
   static bool IsRequestIdleCallbackEnabled(JSContext* aCx, JSObject* /* unused */);
 
   static bool IsWindowPrintEnabled(JSContext* /* unused */, JSObject* /* unused */);
 
   static bool RegisterProtocolHandlerAllowedForContext(JSContext* /* unused */, JSObject* aObj);
 
+  static bool DeviceSensorsEnabled(JSContext* /* unused */, JSObject* aObj);
+
   bool DoResolve(JSContext* aCx, JS::Handle<JSObject*> aObj,
                  JS::Handle<jsid> aId,
                  JS::MutableHandle<JS::PropertyDescriptor> aDesc);
   // The return value is whether DoResolve might end up resolving the given id.
   // If in doubt, return true.
   static bool MayResolve(jsid aId);
 
   void GetOwnPropertyNames(JSContext* aCx, JS::AutoIdVector& aNames,
new file mode 100644
--- /dev/null
+++ b/dom/canvas/crashtests/1441613.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<iframe id="iframe" src="about:blank"></iframe>
+<script>
+iframe.onload = function() {
+  let doc = iframe.contentDocument;
+  let canvas = doc.createElement('canvas');
+  doc.body.appendChild(canvas);
+  let ctx = canvas.getContext('2d');
+  document.body.offsetTop;
+  iframe.style.display = 'none';
+  try {
+    ctx.font = '10px serif';
+  } finally {
+    document.documentElement.className = "";
+  }
+};
+</script>
+</html>
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -45,8 +45,9 @@ load 1299062-1.html
 load 1305085-1.html
 load 1305312-1.html
 load 1305850.html
 load 1334366-1.html
 load 1334647-1.html
 load 1349067.html
 pref(gfx.offscreencanvas.enabled,true) load 1348976-1.html
 load 1357092.html
+load 1441613.html
--- a/dom/events/test/mochitest.ini
+++ b/dom/events/test/mochitest.ini
@@ -112,17 +112,17 @@ skip-if = toolkit == 'android' #CRASH_DU
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_bug684208.html]
 [test_bug689564.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_bug698929.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_bug704423.html]
 [test_bug741666.html]
-[test_bug742376.html]
+[test_deviceSensor.html]
 [test_bug812744.html]
 [test_bug822898.html]
 [test_bug855741.html]
 [test_bug864040.html]
 [test_bug924087.html]
 [test_bug930374-content.html]
 [test_bug944011.html]
 [test_bug944847.html]
@@ -162,16 +162,18 @@ skip-if = toolkit == 'android' #CRASH_DU
 [test_dom_wheel_event.html]
 [test_draggableprop.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
 [test_dragstart.html]
 [test_error_events.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_eventctors.html]
 skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM
+[test_eventctors_sensors.html]
+[test_disabled_events.html]
 [test_eventhandler_scoping.html]
 [test_eventTimeStamp.html]
 [test_focus_disabled.html]
 [test_focus_abspos.html]
 [test_legacy_event.html]
 [test_messageEvent.html]
 [test_messageEvent_init.html]
 [test_moz_mouse_pixel_scroll_event.html]
rename from dom/events/test/test_bug742376.html
rename to dom/events/test/test_deviceSensor.html
--- a/dom/events/test/test_bug742376.html
+++ b/dom/events/test/test_deviceSensor.html
@@ -10,65 +10,127 @@ https://bugzilla.mozilla.org/show_bug.cg
   <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=742376">Mozilla Bug 742376</a>
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 742376 **/
-
-function hasListeners() {
+let Cc = SpecialPowers.Cc;
+let Ci = SpecialPowers.Ci;
+let dss = Cc["@mozilla.org/devicesensors;1"].getService(Ci.nsIDeviceSensors);
 
-  var Cc = SpecialPowers.Cc;
-  var Ci = SpecialPowers.Ci;
-  var dss = Cc["@mozilla.org/devicesensors;1"].getService(Ci.nsIDeviceSensors);
+function hasLightListeners() {
+  return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_LIGHT, window);
+}
 
+function hasOrientationListeners() {
   return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ORIENTATION, window) ||
          dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ROTATION_VECTOR, window) ||
          dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GAME_ROTATION_VECTOR, window);
 }
 
-is(hasListeners(), false, "Must not have listeners before tests start");
+function hasProximityListeners() {
+  return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_PROXIMITY, window);
+}
+
+function hasMotionListeners() {
+  return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ACCELERATION, window) ||
+         dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_LINEAR_ACCELERATION, window) ||
+         dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GYROSCOPE, window);
+}
 
-function dumbListener(event) {}
-function dumbListener2(event) {}
-function dumbListener3(event) {}
+async function test_event_presence(prefName, eventCheck, eventName) {
+  function dumbListener(event) {}
+  function dumbListener2(event) {}
+  function dumbListener3(event) {}
+
+  await SpecialPowers.pushPrefEnv({"set": [
+    [prefName, true]
+  ]});
+
+  is(eventCheck(), false, "Must not have listeners before tests start");
 
-window.addEventListener("deviceorientation", dumbListener);
-window.addEventListener("random_event_name", function() {});
-window.addEventListener("deviceorientation", dumbListener2);
+  window.addEventListener(eventName, dumbListener);
+  window.addEventListener("random_event_name", function() {});
+  window.addEventListener(eventName, dumbListener2);
 
-is(hasListeners(), true, "Listeners should have been added");
+  is(eventCheck(), true, `Should have listeners when ${eventName} sensor is enabled`);
+
+  window.removeEventListener(eventName, dumbListener);
+  window.removeEventListener(eventName, dumbListener2);
+
+  is(eventCheck(), false, "Must not have listeners when removed");
 
-window.setTimeout(function() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    [prefName, false]
+  ]});
 
-  window.removeEventListener("deviceorientation", dumbListener);
-  is(hasListeners(), true, "Only some listeners should have been removed");
-  window.setTimeout(function() {
+  window.addEventListener(eventName, dumbListener);
+  window.addEventListener("random_event_name", function() {});
+  window.addEventListener(eventName, dumbListener2);
+
+  is(eventCheck(), false, "Must not have listeners when sensor is disabled");
+}
 
-    window.removeEventListener("deviceorientation", dumbListener2);
-    window.setTimeout(function() {
-      is(hasListeners(), false, "Listeners should have been removed");
-      testEventHandler();
-    }, 0);
-  }, 0);
-}, 0);
+async function start() {
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["device.sensors.enabled", true],
+    ["device.sensors.orientation.enabled", true]
+  ]});
+
+  is(hasOrientationListeners(), false, "Must not have listeners before tests start");
+
+  function dumbListener(event) {}
+  function dumbListener2(event) {}
+  function dumbListener3(event) {}
+
+  window.addEventListener("deviceorientation", dumbListener);
+  window.addEventListener("random_event_name", function() {});
+  window.addEventListener("deviceorientation", dumbListener2);
+
+  is(hasOrientationListeners(), true, "Listeners should have been added");
 
-function testEventHandler() {
-  window.ondeviceorientation = function() {}
-  window.setTimeout(function() {
-    is(hasListeners(), true, "Handler should have been added");
-    window.ondeviceorientation = null;
+  await new Promise(resolve => {
+    window.setTimeout(function() {
+      window.removeEventListener("deviceorientation", dumbListener);
+      is(hasOrientationListeners(), true, "Only some listeners should have been removed");
+      window.setTimeout(function() {
+        window.removeEventListener("deviceorientation", dumbListener2);
+        window.setTimeout(function() {
+          is(hasOrientationListeners(), false, "Listeners should have been removed");
+          resolve();
+        }, 0);
+      }, 0);
+    }, 0);
+  });
+
+  await new Promise(resolve => {
+    window.ondeviceorientation = function() {}
     window.setTimeout(function() {
-      is(hasListeners(), false, "Handler should have been removed");
-      SimpleTest.finish();
+      is(hasOrientationListeners(), true, "Handler should have been added");
+      window.ondeviceorientation = null;
+      window.setTimeout(function() {
+        is(hasOrientationListeners(), false, "Handler should have been removed");
+        resolve();
+      }, 0);
     }, 0);
-  }, 0)
+  });
+
+  await test_event_presence("device.sensors.ambientLight.enabled", hasLightListeners, "devicelight");
+  await test_event_presence("device.sensors.proximity.enabled", hasProximityListeners, "deviceproximity");
+  await test_event_presence("device.sensors.motion.enabled", hasMotionListeners, "devicemotion");
+  await test_event_presence("device.sensors.orientation.enabled", hasOrientationListeners, "deviceorientation");
+
+  SimpleTest.finish();
+
 }
 
 SimpleTest.waitForExplicitFinish();
 
+start();
+
 </script>
 </pre>
 </body>
 </html>
 
copy from dom/events/test/test_eventctors.html
copy to dom/events/test/test_disabled_events.html
--- a/dom/events/test/test_eventctors.html
+++ b/dom/events/test/test_disabled_events.html
@@ -1,946 +1,40 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=675884
+https://bugzilla.mozilla.org/show_bug.cgi?id=1359076
 -->
 <head>
   <title>Test for Bug 675884</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=675884">Mozilla Bug 675884</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1359076">Mozilla Bug 1359076</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 675884 **/
-
-var receivedEvent;
-document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
-
-function isMethodResultInitializer(aPropName)
-{
-  return aPropName.startsWith("modifier");
-}
-
-function getPropValue(aEvent, aPropName)
-{
-  if (aPropName.startsWith("modifier")) {
-    return aEvent.getModifierState(aPropName.substr("modifier".length));
-  }
-  return aEvent[aPropName];
-}
-
-// Event
-var e;
-var ex = false;
-try {
-  e = new Event();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-try {
-  e = new Event("foo", 123);
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-try {
-  e = new Event("foo", "asdf");
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-
-try {
-  e = new Event("foo", false);
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-
-e = new Event("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-e.isTrusted = true;
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-
-try {
-  e.__defineGetter__("isTrusted", function() { return true });
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
-ex = false;
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-
-ok(!("isTrusted" in Object.getPrototypeOf(e)))
-
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", null);
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", undefined);
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", {});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", { bubbles: true, cancelable: true });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// CustomEvent
-
-try {
-  e = new CustomEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new CustomEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CustomEvent("hello", { bubbles: true, cancelable: true, detail: window });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, window , "Wrong event.detail!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CustomEvent("hello", { cancelable: true, detail: window });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, window , "Wrong event.detail!");
-
-e = new CustomEvent("hello", { detail: 123 });
-is(e.detail, 123, "Wrong event.detail!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-
-var dict = { get detail() { return document.body } };
-e = new CustomEvent("hello", dict);
-is(e.detail, dict.detail, "Wrong event.detail!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-
-var dict = { get detail() { throw "foo"; } };
-
-try {
-  e = new CustomEvent("hello", dict);
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Should have thrown an exception!");
-ex = false;
-
-// BlobEvent
-
-try {
-  e = new BlobEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new BlobEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-try {
-  e.__defineGetter__("isTrusted", function() { return true });
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
-ex = false;
-ok(!e.isTrusted, "BlobEvent shouldn't be trusted!");
-
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-var blob = new Blob();
-e = new BlobEvent("hello", { bubbles: true, cancelable: true, data: blob });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.data, blob , "Wrong event.data!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-e = new BlobEvent("hello", {data: blob});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event should be cancelable1!");
-is(e.data, blob , "Wrong event.data!");
-
-e = new BlobEvent("hello", { data: null });
-is(e.data, null, "Wrong event.data!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-blob = null;
-// CloseEvent
-
-try {
-  e = new CloseEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new CloseEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.wasClean, false, "wasClean should be false!");
-is(e.code, 0, "code should be 0!");
-is(e.reason, "", "reason should be ''!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CloseEvent("hello",
-  { bubbles: true, cancelable: true, wasClean: true, code: 1, reason: "foo" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.wasClean, true, "wasClean should be true!");
-is(e.code, 1, "code should be 1!");
-is(e.reason, "foo", "reason should be 'foo'!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CloseEvent("hello",
-  { bubbles: true, cancelable: true, wasClean: true, code: 1 });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.wasClean, true, "wasClean should be true!");
-is(e.code, 1, "code should be 1!");
-is(e.reason, "", "reason should be ''!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-// HashChangeEvent
-
-try {
-  e = new HashChangeEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new HashChangeEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.oldURL, "", "oldURL should be ''");
-is(e.newURL, "", "newURL should be ''");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new HashChangeEvent("hello",
-  { bubbles: true, cancelable: true, oldURL: "old", newURL: "new" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.oldURL, "old", "oldURL should be 'old'");
-is(e.newURL, "new", "newURL should be 'new'");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new HashChangeEvent("hello",
-  { bubbles: true, cancelable: true, newURL: "new" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.oldURL, "", "oldURL should be ''");
-is(e.newURL, "new", "newURL should be 'new'");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// InputEvent
-
-e = new InputEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 0, "detail should be 0");
-ok(!e.isComposing, "isComposing should be false");
-
-e = new InputEvent("hi!", { bubbles: true, detail: 5, isComposing: false });
-is(e.type, "hi!", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 5, "detail should be 5");
-ok(!e.isComposing, "isComposing should be false");
-
-e = new InputEvent("hi!", { cancelable: true, detail: 0, isComposing: true });
-is(e.type, "hi!", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, 0, "detail should be 0");
-ok(e.isComposing, "isComposing should be true");
-
-// KeyboardEvent
-
-try {
-  e = new KeyboardEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "KeyboardEvent: First parameter is required!");
-ex = false;
-
-e = new KeyboardEvent("hello");
-ok(e.type, "hello", "KeyboardEvent: Wrong event type!");
-ok(!e.isTrusted, "KeyboardEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "KeyboardEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "KeyboardEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "KeyboardEvent: Wrong event!");
-
-var keyboardEventProps =
-[
-  { bubbles: false },
-  { cancelable: false },
-  { view: null },
-  { detail: 0 },
-  { key: "" },
-  { code: "" },
-  { location: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { repeat: false },
-  { isComposing: false },
-  { charCode: 0 },
-  { keyCode: 0 },
-  { which: 0 },
-];
-
-var testKeyboardProps =
-[
-  { bubbles: true },
-  { cancelable: true },
-  { view: window },
-  { detail: 1 },
-  { key: "CustomKey" },
-  { code: "CustomCode" },
-  { location: 1 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { repeat: true },
-  { isComposing: true },
-  { charCode: 2 },
-  { keyCode: 3 },
-  { which: 4 },
-  { charCode: 5, which: 6 },
-  { keyCode: 7, which: 8 },
-  { keyCode: 9, charCode: 10 },
-  { keyCode: 11, charCode: 12, which: 13 },
-];
-
-var defaultKeyboardEventValues = {};
-for (var i = 0; i < keyboardEventProps.length; ++i) {
-  for (prop in keyboardEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "keyboardEvent: KeyboardEvent doesn't have property " + prop + "!");
-    }
-    defaultKeyboardEventValues[prop] = keyboardEventProps[i][prop]; 
-  }
-}
-
-while (testKeyboardProps.length) {
-  var p = testKeyboardProps.shift();
-  e = new KeyboardEvent("foo", p);
-  for (var def in defaultKeyboardEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultKeyboardEventValues[def],
-         "KeyboardEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def],
-         "KeyboardEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// PageTransitionEvent
-
-try {
-  e = new PageTransitionEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new PageTransitionEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.persisted, false, "persisted should be false");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
+SimpleTest.waitForExplicitFinish();
 
-e = new PageTransitionEvent("hello",
-  { bubbles: true, cancelable: true, persisted: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.persisted, true, "persisted should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new PageTransitionEvent("hello", { persisted: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.persisted, true, "persisted should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// PopStateEvent
-
-try {
-  e = new PopStateEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new PopStateEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.state, null, "persisted should be null");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new PopStateEvent("hello",
-  { bubbles: true, cancelable: true, state: window});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.state, window, "persisted should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-e = new PopStateEvent("hello", { state: window});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.state, window, "persisted should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// UIEvent
-
-try {
-  e = new UIEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-try {
-  e = new UIEvent("foo", { view: {} });
-  e.view.onunload;
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "{} isn't a valid value.");
-ex = false;
-
-try {
-  e = new UIEvent("foo", { view: null });
-} catch(exp) {
-  ex = true;
-}
-ok(!ex, "null is a valid value.");
-is(e.view, null);
-ex = false;
-
-e = new UIEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 0, "detail should be 0");
-is(e.view, null, "view should be null");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new UIEvent("hello",
-  { bubbles: true, cancelable: true, view: window, detail: 1});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, 1, "detail should be 1");
-is(e.view, window, "view should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// StorageEvent
-
-e = document.createEvent("StorageEvent");
-ok(e, "Should have created an event!");
-
-try {
-  e = new StorageEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new StorageEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.key, null, "key should be null");
-is(e.oldValue, null, "oldValue should be null");
-is(e.newValue, null, "newValue should be null");
-is(e.url, "", "url should be ''");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new StorageEvent("hello",
-  { bubbles: true, cancelable: true, key: "key",
-    oldValue: "oldValue", newValue: "newValue", url: "url",
-    storageArea: localStorage });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.key, "key", "Wrong value");
-is(e.oldValue, "oldValue", "Wrong value");
-is(e.newValue, "newValue", "Wrong value");
-is(e.url, "url", "Wrong value");
-is(e.storageArea, localStorage, "Wrong value");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// DeviceProximityEvent
-e = new DeviceProximityEvent("hello", {min: 0, value: 1, max: 2});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-is(e.min, 0, "min should be 0");
-is(e.max, 2, "max should be 2");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceProximityEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be infinity");
-is(e.min, -Infinity, "Uninitialized min should be -infinity");
-is(e.max, Infinity, "Uninitialized max should be infinity");
-
-// UserProximityEvent
-e = new UserProximityEvent("hello", {near: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.near, true, "near should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// DeviceLightEvent
-e = new DeviceLightEvent("hello", {value: 1} );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceLightEvent("hello", {value: Infinity} );
-is(e.value, Infinity, "value should be positive infinity");
-e = new DeviceLightEvent("hello", {value: -Infinity} );
-is(e.value, -Infinity, "value should be negative infinity");
-e = new DeviceLightEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be positive infinity");
-
-// DeviceOrientationEvent
-e = new DeviceOrientationEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, null);
-is(e.beta, null);
-is(e.gamma, null);
-is(e.absolute, false);
-
-e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, 1);
-is(e.beta, 2);
-is(e.gamma, 3);
-is(e.absolute, true);
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// MouseEvent
-
-try {
-  e = new MouseEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "MouseEvent: First parameter is required!");
-ex = false;
-
-e = new MouseEvent("hello",  { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "MouseEvent: Wrong event type!");
-ok(!e.isTrusted, "MouseEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "MouseEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "MouseEvent: Event shouldn't be cancelable!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-document.dispatchEvent(e);
-is(receivedEvent, e, "MouseEvent: Wrong event!");
-
-var mouseEventProps =
-[ { screenX: 0 },
-  { screenY: 0 },
-  { clientX: 0 },
-  { clientY: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { button: 0 },
-  { buttons: 0 },
-  { relatedTarget: null },
-];
-
-var testProps =
-[
-  { screenX: 1 },
-  { screenY: 2 },
-  { clientX: 3 },
-  { clientY: 4 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { button: 5 },
-  { buttons: 6 },
-  { relatedTarget: window }
-];
-
-var defaultMouseEventValues = {};
-for (var i = 0; i < mouseEventProps.length; ++i) {
-  for (prop in mouseEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "MouseEvent: MouseEvent doesn't have property " + prop + "!");
-    }
-    defaultMouseEventValues[prop] = mouseEventProps[i][prop]; 
-  }
-}
-
-while (testProps.length) {
-  var p = testProps.shift();
-  e = new MouseEvent("foo", p);
-  for (var def in defaultMouseEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultMouseEventValues[def],
-         "MouseEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def], "MouseEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// PopupBlockedEvent
-
-try {
-  e = new PopupBlockedEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "PopupBlockedEvent: First parameter is required!");
-ex = false;
-
-e = new PopupBlockedEvent("hello");
-is(e.type, "hello", "PopupBlockedEvent: Wrong event type!");
-ok(!e.isTrusted, "PopupBlockedEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "PopupBlockedEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "PopupBlockedEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "PopupBlockedEvent: Wrong event!");
-
-e = new PopupBlockedEvent("hello",
-                          { requestingWindow: window,
-                            popupWindowFeatures: "features",
-                            popupWindowName: "name"
-                          });
-is(e.requestingWindow, window);
-is(e.popupWindowFeatures, "features");
-is(e.popupWindowName, "name");
-
-// WheelEvent
-
-try {
-  e = new WheelEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "WheelEvent: First parameter is required!");
-ex = false;
-
-e = new WheelEvent("hello",  { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "WheelEvent: Wrong event type!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-ok(!e.isTrusted, "WheelEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "WheelEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "WheelEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "WheelEvent: Wrong event!");
-
-var wheelEventProps =
-[ { screenX: 0 },
-  { screenY: 0 },
-  { clientX: 0 },
-  { clientY: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { button: 0 },
-  { buttons: 0 },
-  { relatedTarget: null },
-  { deltaX: 0.0 },
-  { deltaY: 0.0 },
-  { deltaZ: 0.0 },
-  { deltaMode: 0 }
-];
-
-var testWheelProps =
-[
-  { screenX: 1 },
-  { screenY: 2 },
-  { clientX: 3 },
-  { clientY: 4 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { button: 5 },
-  { buttons: 6 },
-  { relatedTarget: window },
-  { deltaX: 7.8 },
-  { deltaY: 9.1 },
-  { deltaZ: 2.3 },
-  { deltaMode: 4 }
-];
-
-var defaultWheelEventValues = {};
-for (var i = 0; i < wheelEventProps.length; ++i) {
-  for (prop in wheelEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "WheelEvent: WheelEvent doesn't have property " + prop + "!");
-    }
-    defaultWheelEventValues[prop] = wheelEventProps[i][prop]; 
-  }
-}
-
-while (testWheelProps.length) {
-  var p = testWheelProps.shift();
-  e = new WheelEvent("foo", p);
-  for (var def in defaultWheelEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultWheelEventValues[def],
-         "WheelEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def], "WheelEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// DragEvent
-
-try {
-  e = new DragEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "DragEvent: First parameter is required!");
-ex = false;
-
-e = new DragEvent("hello", { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "DragEvent: Wrong event type!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-document.dispatchEvent(e);
-is(receivedEvent, e, "DragEvent: Wrong event!");
-
-// TransitionEvent
-e = new TransitionEvent("hello", { propertyName: "color", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
-is("propertyName" in e, true, "Transition events have propertyName property");
-is("foobar" in e, false, "Transition events do not copy random properties from event init");
-is(e.propertyName, "color", "Transition event copies propertyName from TransitionEventInit");
-is(e.elapsedTime, 3.5, "Transition event copies elapsedTime from TransitionEventInit");
-is(e.pseudoElement, "", "Transition event copies pseudoElement from TransitionEventInit");
-is(e.bubbles, false, "Lack of bubbles property in TransitionEventInit");
-is(e.cancelable, false, "Lack of cancelable property in TransitionEventInit");
-is(e.type, "hello", "Wrong event type!");
-is(e.isTrusted, false, "Event shouldn't be trusted!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-
-// AnimationEvent
-e = new AnimationEvent("hello", { animationName: "bounce3", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
-is("animationName" in e, true, "Animation events have animationName property");
-is("foobar" in e, false, "Animation events do not copy random properties from event init");
-is(e.animationName, "bounce3", "Animation event copies animationName from AnimationEventInit");
-is(e.elapsedTime, 3.5, "Animation event copies elapsedTime from AnimationEventInit");
-is(e.pseudoElement, "", "Animation event copies pseudoElement from AnimationEventInit");
-is(e.bubbles, false, "Lack of bubbles property in AnimationEventInit");
-is(e.cancelable, false, "Lack of cancelable property in AnimationEventInit");
-is(e.type, "hello", "Wrong event type!");
-is(e.isTrusted, false, "Event shouldn't be trusted!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
+SpecialPowers.pushPrefEnv({"set": [
+  ["device.sensors.orientation.enabled", false],
+  ["device.sensors.motion.enabled", false],
+  ["device.sensors.proximity.enabled", false],
+  ["device.sensors.ambientLight.enabled", false],
+  ["dom.w3c_pointer_events.enabled", false]
+]}, () => {
+  is("DeviceProximityEvent" in window, false, "DeviceProximityEvent does not exist");
+  is("UserProximityEvent" in window, false, "UserProximityEvent does not exist");
+  is("DeviceLightEvent" in window, false, "DeviceLightEvent does not exist");
+  is("DeviceOrientationEvent" in window, false, "DeviceOrientationEvent does not exist");
+  is("DeviceMotionEvent" in window, false, "DeviceMotionEvent does not exist");
+  SimpleTest.finish();
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/events/test/test_eventctors.html
+++ b/dom/events/test/test_eventctors.html
@@ -619,71 +619,16 @@ ok(e.cancelable, "Event should be cancel
 is(e.key, "key", "Wrong value");
 is(e.oldValue, "oldValue", "Wrong value");
 is(e.newValue, "newValue", "Wrong value");
 is(e.url, "url", "Wrong value");
 is(e.storageArea, localStorage, "Wrong value");
 document.dispatchEvent(e);
 is(receivedEvent, e, "Wrong event!");
 
-// DeviceProximityEvent
-e = new DeviceProximityEvent("hello", {min: 0, value: 1, max: 2});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-is(e.min, 0, "min should be 0");
-is(e.max, 2, "max should be 2");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceProximityEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be infinity");
-is(e.min, -Infinity, "Uninitialized min should be -infinity");
-is(e.max, Infinity, "Uninitialized max should be infinity");
-
-// UserProximityEvent
-e = new UserProximityEvent("hello", {near: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.near, true, "near should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// DeviceLightEvent
-e = new DeviceLightEvent("hello", {value: 1} );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceLightEvent("hello", {value: Infinity} );
-is(e.value, Infinity, "value should be positive infinity");
-e = new DeviceLightEvent("hello", {value: -Infinity} );
-is(e.value, -Infinity, "value should be negative infinity");
-e = new DeviceLightEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be positive infinity");
-
-// DeviceOrientationEvent
-e = new DeviceOrientationEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, null);
-is(e.beta, null);
-is(e.gamma, null);
-is(e.absolute, false);
-
-e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, 1);
-is(e.beta, 2);
-is(e.gamma, 3);
-is(e.absolute, true);
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
 // MouseEvent
 
 try {
   e = new MouseEvent();
 } catch(exp) {
   ex = true;
 }
 ok(ex, "MouseEvent: First parameter is required!");
copy from dom/events/test/test_eventctors.html
copy to dom/events/test/test_eventctors_sensors.html
--- a/dom/events/test/test_eventctors.html
+++ b/dom/events/test/test_eventctors_sensors.html
@@ -12,935 +12,99 @@ https://bugzilla.mozilla.org/show_bug.cg
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=675884">Mozilla Bug 675884</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
-/** Test for Bug 675884 **/
-
-var receivedEvent;
-document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
-
-function isMethodResultInitializer(aPropName)
-{
-  return aPropName.startsWith("modifier");
-}
-
-function getPropValue(aEvent, aPropName)
-{
-  if (aPropName.startsWith("modifier")) {
-    return aEvent.getModifierState(aPropName.substr("modifier".length));
-  }
-  return aEvent[aPropName];
-}
-
-// Event
-var e;
-var ex = false;
-try {
-  e = new Event();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-try {
-  e = new Event("foo", 123);
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-try {
-  e = new Event("foo", "asdf");
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-
-try {
-  e = new Event("foo", false);
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "2nd parameter should be an object!");
-ex = false;
-
-
-e = new Event("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-e.isTrusted = true;
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-
-try {
-  e.__defineGetter__("isTrusted", function() { return true });
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
-ex = false;
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-
-ok(!("isTrusted" in Object.getPrototypeOf(e)))
-
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", null);
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", undefined);
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", {});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-document.dispatchEvent(e);
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-is(receivedEvent, e, "Wrong event!");
-
-e = new Event("hello", { bubbles: true, cancelable: true });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// CustomEvent
-
-try {
-  e = new CustomEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new CustomEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CustomEvent("hello", { bubbles: true, cancelable: true, detail: window });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, window , "Wrong event.detail!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CustomEvent("hello", { cancelable: true, detail: window });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, window , "Wrong event.detail!");
-
-e = new CustomEvent("hello", { detail: 123 });
-is(e.detail, 123, "Wrong event.detail!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-
-var dict = { get detail() { return document.body } };
-e = new CustomEvent("hello", dict);
-is(e.detail, dict.detail, "Wrong event.detail!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-
-var dict = { get detail() { throw "foo"; } };
-
-try {
-  e = new CustomEvent("hello", dict);
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Should have thrown an exception!");
-ex = false;
-
-// BlobEvent
-
-try {
-  e = new BlobEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new BlobEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-try {
-  e.__defineGetter__("isTrusted", function() { return true });
-} catch (exp) {
-  ex = true;
-}
-ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
-ex = false;
-ok(!e.isTrusted, "BlobEvent shouldn't be trusted!");
-
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-var blob = new Blob();
-e = new BlobEvent("hello", { bubbles: true, cancelable: true, data: blob });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.data, blob , "Wrong event.data!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-e = new BlobEvent("hello", {data: blob});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event should be cancelable1!");
-is(e.data, blob , "Wrong event.data!");
-
-e = new BlobEvent("hello", { data: null });
-is(e.data, null, "Wrong event.data!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-blob = null;
-// CloseEvent
+SimpleTest.waitForExplicitFinish();
 
-try {
-  e = new CloseEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new CloseEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.wasClean, false, "wasClean should be false!");
-is(e.code, 0, "code should be 0!");
-is(e.reason, "", "reason should be ''!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CloseEvent("hello",
-  { bubbles: true, cancelable: true, wasClean: true, code: 1, reason: "foo" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.wasClean, true, "wasClean should be true!");
-is(e.code, 1, "code should be 1!");
-is(e.reason, "foo", "reason should be 'foo'!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new CloseEvent("hello",
-  { bubbles: true, cancelable: true, wasClean: true, code: 1 });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.wasClean, true, "wasClean should be true!");
-is(e.code, 1, "code should be 1!");
-is(e.reason, "", "reason should be ''!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-// HashChangeEvent
-
-try {
-  e = new HashChangeEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new HashChangeEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.oldURL, "", "oldURL should be ''");
-is(e.newURL, "", "newURL should be ''");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new HashChangeEvent("hello",
-  { bubbles: true, cancelable: true, oldURL: "old", newURL: "new" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.oldURL, "old", "oldURL should be 'old'");
-is(e.newURL, "new", "newURL should be 'new'");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new HashChangeEvent("hello",
-  { bubbles: true, cancelable: true, newURL: "new" });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.oldURL, "", "oldURL should be ''");
-is(e.newURL, "new", "newURL should be 'new'");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// InputEvent
-
-e = new InputEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 0, "detail should be 0");
-ok(!e.isComposing, "isComposing should be false");
-
-e = new InputEvent("hi!", { bubbles: true, detail: 5, isComposing: false });
-is(e.type, "hi!", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 5, "detail should be 5");
-ok(!e.isComposing, "isComposing should be false");
-
-e = new InputEvent("hi!", { cancelable: true, detail: 0, isComposing: true });
-is(e.type, "hi!", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, 0, "detail should be 0");
-ok(e.isComposing, "isComposing should be true");
-
-// KeyboardEvent
+SpecialPowers.pushPrefEnv({"set": [
+  ["device.sensors.enabled", true],
+  ["device.sensors.orientation.enabled", true],
+  ["device.sensors.motion.enabled", true],
+  ["device.sensors.proximity.enabled", true],
+  ["device.sensors.ambientLight.enabled", true]
+]}, () => {
+  let receivedEvent;
+  document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
+  // DeviceProximityEvent
+  let e = new DeviceProximityEvent("hello", {min: 0, value: 1, max: 2});
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(e.value, 1, "value should be 1");
+  is(e.min, 0, "min should be 0");
+  is(e.max, 2, "max should be 2");
+  document.dispatchEvent(e);
+  is(receivedEvent, e, "Wrong event!");
+  e = new DeviceProximityEvent("hello");
+  is(e.value, Infinity, "Uninitialized value should be infinity");
+  is(e.min, -Infinity, "Uninitialized min should be -infinity");
+  is(e.max, Infinity, "Uninitialized max should be infinity");
 
-try {
-  e = new KeyboardEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "KeyboardEvent: First parameter is required!");
-ex = false;
-
-e = new KeyboardEvent("hello");
-ok(e.type, "hello", "KeyboardEvent: Wrong event type!");
-ok(!e.isTrusted, "KeyboardEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "KeyboardEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "KeyboardEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "KeyboardEvent: Wrong event!");
-
-var keyboardEventProps =
-[
-  { bubbles: false },
-  { cancelable: false },
-  { view: null },
-  { detail: 0 },
-  { key: "" },
-  { code: "" },
-  { location: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { repeat: false },
-  { isComposing: false },
-  { charCode: 0 },
-  { keyCode: 0 },
-  { which: 0 },
-];
-
-var testKeyboardProps =
-[
-  { bubbles: true },
-  { cancelable: true },
-  { view: window },
-  { detail: 1 },
-  { key: "CustomKey" },
-  { code: "CustomCode" },
-  { location: 1 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { repeat: true },
-  { isComposing: true },
-  { charCode: 2 },
-  { keyCode: 3 },
-  { which: 4 },
-  { charCode: 5, which: 6 },
-  { keyCode: 7, which: 8 },
-  { keyCode: 9, charCode: 10 },
-  { keyCode: 11, charCode: 12, which: 13 },
-];
-
-var defaultKeyboardEventValues = {};
-for (var i = 0; i < keyboardEventProps.length; ++i) {
-  for (prop in keyboardEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "keyboardEvent: KeyboardEvent doesn't have property " + prop + "!");
-    }
-    defaultKeyboardEventValues[prop] = keyboardEventProps[i][prop]; 
-  }
-}
-
-while (testKeyboardProps.length) {
-  var p = testKeyboardProps.shift();
-  e = new KeyboardEvent("foo", p);
-  for (var def in defaultKeyboardEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultKeyboardEventValues[def],
-         "KeyboardEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def],
-         "KeyboardEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// PageTransitionEvent
-
-try {
-  e = new PageTransitionEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new PageTransitionEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.persisted, false, "persisted should be false");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
+  // UserProximityEvent
+  e = new UserProximityEvent("hello", {near: true});
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(e.near, true, "near should be true");
+  document.dispatchEvent(e);
+  is(receivedEvent, e, "Wrong event!");
 
-e = new PageTransitionEvent("hello",
-  { bubbles: true, cancelable: true, persisted: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.persisted, true, "persisted should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new PageTransitionEvent("hello", { persisted: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.persisted, true, "persisted should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// PopStateEvent
-
-try {
-  e = new PopStateEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new PopStateEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.state, null, "persisted should be null");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new PopStateEvent("hello",
-  { bubbles: true, cancelable: true, state: window});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.state, window, "persisted should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-
-e = new PopStateEvent("hello", { state: window});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.state, window, "persisted should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// UIEvent
-
-try {
-  e = new UIEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-try {
-  e = new UIEvent("foo", { view: {} });
-  e.view.onunload;
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "{} isn't a valid value.");
-ex = false;
-
-try {
-  e = new UIEvent("foo", { view: null });
-} catch(exp) {
-  ex = true;
-}
-ok(!ex, "null is a valid value.");
-is(e.view, null);
-ex = false;
-
-e = new UIEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.detail, 0, "detail should be 0");
-is(e.view, null, "view should be null");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new UIEvent("hello",
-  { bubbles: true, cancelable: true, view: window, detail: 1});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.detail, 1, "detail should be 1");
-is(e.view, window, "view should be window");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// StorageEvent
-
-e = document.createEvent("StorageEvent");
-ok(e, "Should have created an event!");
+  // DeviceLightEvent
+  e = new DeviceLightEvent("hello", {value: 1} );
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(e.value, 1, "value should be 1");
+  document.dispatchEvent(e);
+  is(receivedEvent, e, "Wrong event!");
+  e = new DeviceLightEvent("hello", {value: Infinity} );
+  is(e.value, Infinity, "value should be positive infinity");
+  e = new DeviceLightEvent("hello", {value: -Infinity} );
+  is(e.value, -Infinity, "value should be negative infinity");
+  e = new DeviceLightEvent("hello");
+  is(e.value, Infinity, "Uninitialized value should be positive infinity");
 
-try {
-  e = new StorageEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "First parameter is required!");
-ex = false;
-
-e = new StorageEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(!e.bubbles, "Event shouldn't bubble!");
-ok(!e.cancelable, "Event shouldn't be cancelable!");
-is(e.key, null, "key should be null");
-is(e.oldValue, null, "oldValue should be null");
-is(e.newValue, null, "newValue should be null");
-is(e.url, "", "url should be ''");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-e = new StorageEvent("hello",
-  { bubbles: true, cancelable: true, key: "key",
-    oldValue: "oldValue", newValue: "newValue", url: "url",
-    storageArea: localStorage });
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event shouldn't be trusted!");
-ok(e.bubbles, "Event should bubble!");
-ok(e.cancelable, "Event should be cancelable!");
-is(e.key, "key", "Wrong value");
-is(e.oldValue, "oldValue", "Wrong value");
-is(e.newValue, "newValue", "Wrong value");
-is(e.url, "url", "Wrong value");
-is(e.storageArea, localStorage, "Wrong value");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// DeviceProximityEvent
-e = new DeviceProximityEvent("hello", {min: 0, value: 1, max: 2});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-is(e.min, 0, "min should be 0");
-is(e.max, 2, "max should be 2");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceProximityEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be infinity");
-is(e.min, -Infinity, "Uninitialized min should be -infinity");
-is(e.max, Infinity, "Uninitialized max should be infinity");
-
-// UserProximityEvent
-e = new UserProximityEvent("hello", {near: true});
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.near, true, "near should be true");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// DeviceLightEvent
-e = new DeviceLightEvent("hello", {value: 1} );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.value, 1, "value should be 1");
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-e = new DeviceLightEvent("hello", {value: Infinity} );
-is(e.value, Infinity, "value should be positive infinity");
-e = new DeviceLightEvent("hello", {value: -Infinity} );
-is(e.value, -Infinity, "value should be negative infinity");
-e = new DeviceLightEvent("hello");
-is(e.value, Infinity, "Uninitialized value should be positive infinity");
-
-// DeviceOrientationEvent
-e = new DeviceOrientationEvent("hello");
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, null);
-is(e.beta, null);
-is(e.gamma, null);
-is(e.absolute, false);
-
-e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
-is(e.type, "hello", "Wrong event type!");
-ok(!e.isTrusted, "Event should not be trusted");
-is(e.alpha, 1);
-is(e.beta, 2);
-is(e.gamma, 3);
-is(e.absolute, true);
-document.dispatchEvent(e);
-is(receivedEvent, e, "Wrong event!");
-
-// MouseEvent
-
-try {
-  e = new MouseEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "MouseEvent: First parameter is required!");
-ex = false;
-
-e = new MouseEvent("hello",  { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "MouseEvent: Wrong event type!");
-ok(!e.isTrusted, "MouseEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "MouseEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "MouseEvent: Event shouldn't be cancelable!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-document.dispatchEvent(e);
-is(receivedEvent, e, "MouseEvent: Wrong event!");
+  // DeviceOrientationEvent
+  e = new DeviceOrientationEvent("hello");
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(e.alpha, null);
+  is(e.beta, null);
+  is(e.gamma, null);
+  is(e.absolute, false);
 
-var mouseEventProps =
-[ { screenX: 0 },
-  { screenY: 0 },
-  { clientX: 0 },
-  { clientY: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { button: 0 },
-  { buttons: 0 },
-  { relatedTarget: null },
-];
-
-var testProps =
-[
-  { screenX: 1 },
-  { screenY: 2 },
-  { clientX: 3 },
-  { clientY: 4 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { button: 5 },
-  { buttons: 6 },
-  { relatedTarget: window }
-];
-
-var defaultMouseEventValues = {};
-for (var i = 0; i < mouseEventProps.length; ++i) {
-  for (prop in mouseEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "MouseEvent: MouseEvent doesn't have property " + prop + "!");
-    }
-    defaultMouseEventValues[prop] = mouseEventProps[i][prop]; 
-  }
-}
-
-while (testProps.length) {
-  var p = testProps.shift();
-  e = new MouseEvent("foo", p);
-  for (var def in defaultMouseEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultMouseEventValues[def],
-         "MouseEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def], "MouseEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// PopupBlockedEvent
-
-try {
-  e = new PopupBlockedEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "PopupBlockedEvent: First parameter is required!");
-ex = false;
-
-e = new PopupBlockedEvent("hello");
-is(e.type, "hello", "PopupBlockedEvent: Wrong event type!");
-ok(!e.isTrusted, "PopupBlockedEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "PopupBlockedEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "PopupBlockedEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "PopupBlockedEvent: Wrong event!");
-
-e = new PopupBlockedEvent("hello",
-                          { requestingWindow: window,
-                            popupWindowFeatures: "features",
-                            popupWindowName: "name"
-                          });
-is(e.requestingWindow, window);
-is(e.popupWindowFeatures, "features");
-is(e.popupWindowName, "name");
-
-// WheelEvent
-
-try {
-  e = new WheelEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "WheelEvent: First parameter is required!");
-ex = false;
-
-e = new WheelEvent("hello",  { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "WheelEvent: Wrong event type!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-ok(!e.isTrusted, "WheelEvent: Event shouldn't be trusted!");
-ok(!e.bubbles, "WheelEvent: Event shouldn't bubble!");
-ok(!e.cancelable, "WheelEvent: Event shouldn't be cancelable!");
-document.dispatchEvent(e);
-is(receivedEvent, e, "WheelEvent: Wrong event!");
+  e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(e.alpha, 1);
+  is(e.beta, 2);
+  is(e.gamma, 3);
+  is(e.absolute, true);
+  document.dispatchEvent(e);
+  is(receivedEvent, e, "Wrong event!");
 
-var wheelEventProps =
-[ { screenX: 0 },
-  { screenY: 0 },
-  { clientX: 0 },
-  { clientY: 0 },
-  { ctrlKey: false },
-  { shiftKey: false },
-  { altKey: false },
-  { metaKey: false },
-  { modifierAltGraph: false },
-  { modifierCapsLock: false },
-  { modifierFn: false },
-  { modifierFnLock: false },
-  { modifierNumLock: false },
-  { modifierOS: false },
-  { modifierScrollLock: false },
-  { modifierSymbol: false },
-  { modifierSymbolLock: false },
-  { button: 0 },
-  { buttons: 0 },
-  { relatedTarget: null },
-  { deltaX: 0.0 },
-  { deltaY: 0.0 },
-  { deltaZ: 0.0 },
-  { deltaMode: 0 }
-];
-
-var testWheelProps =
-[
-  { screenX: 1 },
-  { screenY: 2 },
-  { clientX: 3 },
-  { clientY: 4 },
-  { ctrlKey: true },
-  { shiftKey: true },
-  { altKey: true },
-  { metaKey: true },
-  { modifierAltGraph: true },
-  { modifierCapsLock: true },
-  { modifierFn: true },
-  { modifierFnLock: true },
-  { modifierNumLock: true },
-  { modifierOS: true },
-  { modifierScrollLock: true },
-  { modifierSymbol: true },
-  { modifierSymbolLock: true },
-  { button: 5 },
-  { buttons: 6 },
-  { relatedTarget: window },
-  { deltaX: 7.8 },
-  { deltaY: 9.1 },
-  { deltaZ: 2.3 },
-  { deltaMode: 4 }
-];
+  // DeviceMotionEvent
+  e = new DeviceMotionEvent("hello");
+  is(e.type, "hello", "Wrong event type!");
+  ok(!e.isTrusted, "Event should not be trusted");
+  is(typeof e.acceleration, "object");
+  is(e.acceleration.x, null);
+  is(e.acceleration.y, null);
+  is(e.acceleration.z, null);
+  is(typeof e.accelerationIncludingGravity, "object");
+  is(e.accelerationIncludingGravity.x, null);
+  is(e.accelerationIncludingGravity.y, null);
+  is(e.accelerationIncludingGravity.z, null);
+  is(typeof e.rotationRate, "object");
+  is(e.rotationRate.alpha, null);
+  is(e.rotationRate.beta, null);
+  is(e.rotationRate.gamma, null);
+  is(e.interval, null);
 
-var defaultWheelEventValues = {};
-for (var i = 0; i < wheelEventProps.length; ++i) {
-  for (prop in wheelEventProps[i]) {
-    if (!isMethodResultInitializer(prop)) {
-      ok(prop in e, "WheelEvent: WheelEvent doesn't have property " + prop + "!");
-    }
-    defaultWheelEventValues[prop] = wheelEventProps[i][prop]; 
-  }
-}
-
-while (testWheelProps.length) {
-  var p = testWheelProps.shift();
-  e = new WheelEvent("foo", p);
-  for (var def in defaultWheelEventValues) {
-    if (!(def in p)) {
-      is(getPropValue(e, def), defaultWheelEventValues[def],
-         "WheelEvent: Wrong default value for " + def + "!");
-    } else {
-      is(getPropValue(e, def), p[def], "WheelEvent: Wrong event init value for " + def + "!");
-    }
-  }
-}
-
-// DragEvent
-
-try {
-  e = new DragEvent();
-} catch(exp) {
-  ex = true;
-}
-ok(ex, "DragEvent: First parameter is required!");
-ex = false;
-
-e = new DragEvent("hello", { buttons: 1, movementX: 2, movementY: 3});
-is(e.type, "hello", "DragEvent: Wrong event type!");
-is(e.buttons, 1);
-is(e.movementX, 2);
-is(e.movementY, 3);
-document.dispatchEvent(e);
-is(receivedEvent, e, "DragEvent: Wrong event!");
-
-// TransitionEvent
-e = new TransitionEvent("hello", { propertyName: "color", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
-is("propertyName" in e, true, "Transition events have propertyName property");
-is("foobar" in e, false, "Transition events do not copy random properties from event init");
-is(e.propertyName, "color", "Transition event copies propertyName from TransitionEventInit");
-is(e.elapsedTime, 3.5, "Transition event copies elapsedTime from TransitionEventInit");
-is(e.pseudoElement, "", "Transition event copies pseudoElement from TransitionEventInit");
-is(e.bubbles, false, "Lack of bubbles property in TransitionEventInit");
-is(e.cancelable, false, "Lack of cancelable property in TransitionEventInit");
-is(e.type, "hello", "Wrong event type!");
-is(e.isTrusted, false, "Event shouldn't be trusted!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
-
-// AnimationEvent
-e = new AnimationEvent("hello", { animationName: "bounce3", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
-is("animationName" in e, true, "Animation events have animationName property");
-is("foobar" in e, false, "Animation events do not copy random properties from event init");
-is(e.animationName, "bounce3", "Animation event copies animationName from AnimationEventInit");
-is(e.elapsedTime, 3.5, "Animation event copies elapsedTime from AnimationEventInit");
-is(e.pseudoElement, "", "Animation event copies pseudoElement from AnimationEventInit");
-is(e.bubbles, false, "Lack of bubbles property in AnimationEventInit");
-is(e.cancelable, false, "Lack of cancelable property in AnimationEventInit");
-is(e.type, "hello", "Wrong event type!");
-is(e.isTrusted, false, "Event shouldn't be trusted!");
-is(e.eventPhase, Event.NONE, "Wrong event phase");
+  SimpleTest.finish();
+});
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1640,21 +1640,20 @@ HTMLMediaElement::MozDumpDebugInfo()
     promise->MaybeResolveWithUndefined();
   }
   return promise.forget();
 }
 
 void
 HTMLMediaElement::SetVisible(bool aVisible)
 {
-  if (!mDecoder) {
-    return;
-  }
-
-  mDecoder->SetForcedHidden(!aVisible);
+  mForcedHidden = !aVisible;
+  if (mDecoder) {
+    mDecoder->SetForcedHidden(!aVisible);
+  }
 }
 
 already_AddRefed<layers::Image>
 HTMLMediaElement::GetCurrentImage()
 {
   MarkAsTainted();
 
   // TODO: In bug 1345404, handle case when video decoder is already suspended.
@@ -3875,16 +3874,17 @@ HTMLMediaElement::HTMLMediaElement(alrea
     mMediaSecurityVerified(false),
     mCORSMode(CORS_NONE),
     mIsEncrypted(false),
     mWaitingForKey(NOT_WAITING_FOR_KEY),
     mDisableVideo(false),
     mFirstFrameLoaded(false),
     mDefaultPlaybackStartPosition(0.0),
     mHasSuspendTaint(false),
+    mForcedHidden(false),
     mMediaTracksConstructed(false),
     mVisibilityState(Visibility::UNTRACKED),
     mErrorSink(new ErrorSink(this)),
     mAudioChannelWrapper(new AudioChannelAgentCallback(this))
 {
   MOZ_ASSERT(mMainThreadEventTarget);
   MOZ_ASSERT(mAbstractMainThread);
 
@@ -7326,16 +7326,19 @@ void
 HTMLMediaElement::SetDecoder(MediaDecoder* aDecoder)
 {
   MOZ_ASSERT(aDecoder); // Use ShutdownDecoder() to clear.
   if (mDecoder) {
     ShutdownDecoder();
   }
   mDecoder = aDecoder;
   DDLINKCHILD("decoder", mDecoder.get());
+  if (mDecoder && mForcedHidden) {
+    mDecoder->SetForcedHidden(mForcedHidden);
+  }
 }
 
 float
 HTMLMediaElement::ComputedVolume() const
 {
   return mMuted ? 0.0f : mAudioChannelWrapper ?
     mAudioChannelWrapper->GetEffectiveVolume() : mVolume;
 }
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -1816,16 +1816,20 @@ private:
   // initially be set to zero seconds. This time is used to allow the element to
   // be seeked even before the media is loaded.
   double mDefaultPlaybackStartPosition;
 
   // True if media element has been marked as 'tainted' and can't
   // participate in video decoder suspending.
   bool mHasSuspendTaint;
 
+  // True if media element has been forced into being considered 'hidden'.
+  // For use by mochitests. Enabling pref "media.test.video-suspend"
+  bool mForcedHidden;
+
   // True if audio tracks and video tracks are constructed and added into the
   // track list, false if all tracks are removed from the track list.
   bool mMediaTracksConstructed;
 
   Visibility mVisibilityState;
 
   UniquePtr<ErrorSink> mErrorSink;
 
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2964,57 +2964,53 @@ nsGenericHTMLElement::NewURIFromString(c
   }
 
   return NS_OK;
 }
 
 static bool
 IsOrHasAncestorWithDisplayNone(Element* aElement, nsIPresShell* aPresShell)
 {
-  // FIXME(emilio): In the servo case it should suffice to do something like:
-  //
-  // return !aElement->HasServoData() || Servo_Element_IsDisplayNone(aElement);
-  //
-  // at least in the case the element is part of the flattened tree...
+  if (aPresShell->StyleSet()->IsServo()) {
+    return !aElement->HasServoData() || Servo_Element_IsDisplayNone(aElement);
+  }
+
+#ifdef MOZ_OLD_STYLE
   AutoTArray<Element*, 10> elementsToCheck;
   // Style and layout work on the flattened tree, so this is what we need to
   // check in order to figure out whether we're in a display: none subtree.
   for (Element* e = aElement; e; e = e->GetFlattenedTreeParentElement()) {
     if (e->GetPrimaryFrame()) {
       // e definitely isn't display:none and doesn't have a display:none
       // ancestor.
       break;
     }
     elementsToCheck.AppendElement(e);
   }
 
   if (elementsToCheck.IsEmpty()) {
     return false;
   }
 
-  StyleSetHandle styleSet = aPresShell->StyleSet();
-  RefPtr<nsStyleContext> sc;
+  nsStyleSet* styleSet = aPresShell->StyleSet()->AsGecko();
+  RefPtr<GeckoStyleContext> sc;
   for (auto* element : Reversed(elementsToCheck)) {
     if (sc) {
-      if (styleSet->IsGecko()) {
-        sc = styleSet->ResolveStyleFor(element, sc,
-                                       LazyComputeBehavior::Assert);
-      } else {
-        // Call ResolveStyleLazily to protect against stale element data in
-        // the tree when styled by Servo.
-        sc = styleSet->AsServo()->ResolveStyleLazily(
-            element, CSSPseudoElementType::NotPseudo);
-      }
+      sc = styleSet->ResolveStyleFor(element, sc, LazyComputeBehavior::Assert);
     } else {
-      sc = nsComputedDOMStyle::GetStyleContextNoFlush(element, nullptr);
+      sc = nsComputedDOMStyle::GetStyleContextNoFlush(element, nullptr)
+        .downcast<GeckoStyleContext>();
     }
     if (sc->StyleDisplay()->mDisplay == StyleDisplay::None) {
       return true;
     }
   }
+#else
+  MOZ_CRASH("Old style system disabled");
+#endif
 
   return false;
 }
 
 void
 nsGenericHTMLElement::GetInnerText(mozilla::dom::DOMString& aValue,
                                    mozilla::ErrorResult& aError)
 {
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -25,16 +25,17 @@ interface nsIPaymentCurrencyAmount : nsI
 };
 
 [scriptable, builtinclass, uuid(4f78a59f-b5ff-4fb5-ab48-3b37d0101b02)]
 interface nsIPaymentItem : nsISupports
 {
   readonly attribute AString label;
   readonly attribute nsIPaymentCurrencyAmount amount;
   readonly attribute boolean pending;
+  readonly attribute AString type;
 };
 
 [scriptable, builtinclass, uuid(74259861-c318-40e8-b3d5-518e701bed80)]
 interface nsIPaymentDetailsModifier : nsISupports
 {
   readonly attribute AString supportedMethods;
   readonly attribute nsIPaymentItem total;
   readonly attribute nsIArray additionalDisplayItems;
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -349,8 +349,13 @@ ModuleSourceMalformed=Module source URI is malformed: “%S”.
 ScriptSourceNotAllowed=<script> source URI is not allowed in this document: “%S”.
 ModuleSourceNotAllowed=Module source URI is not allowed in this document: “%S”.
 # LOCALIZATION NOTE: %1$S is the invalid property value and %2$S is the property name.
 InvalidKeyframePropertyValue=Keyframe property value “%1$S” is invalid according to the syntax for “%2$S”.
 # LOCALIZATION NOTE: Do not translate "ReadableStream".
 ReadableStreamReadingFailed=Failed to read data from the ReadableStream: “%S”.
 # LOCALIZATION NOTE: Do not translate "registerProtocolHandler".
 RegisterProtocolHandlerInsecureWarning=Use of the registerProtocolHandler for insecure connections will be removed in version 62.
+MixedDisplayObjectSubrequestWarning=Loading insecure content within a plugin embedded in a secure connection is going to be removed.
+MotionEventWarning=Use of the motion sensor is deprecated.
+OrientationEventWarning=Use of the orientation sensor is deprecated.
+ProximityEventWarning=Use of the proximity sensor is deprecated.
+AmbientLightEventWarning=Use of the ambient light sensor is deprecated.
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -20,16 +20,19 @@
 #include "nsAutoRef.h"
 #include "nsDebug.h"
 #include "nsIStringBundle.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "prdtoa.h"
 #include <algorithm>
 #include <stdint.h>
+#ifdef MOZ_WIDGET_ANDROID
+#include "GeneratedJNIWrappers.h"
+#endif
 
 #define PREF_VOLUME_SCALE "media.volume_scale"
 #define PREF_CUBEB_BACKEND "media.cubeb.backend"
 #define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
 #define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
 #define PREF_CUBEB_LOGGING_LEVEL "media.cubeb.logging_level"
 #define PREF_CUBEB_SANDBOX "media.cubeb.sandbox"
 
@@ -114,18 +117,18 @@ enum class CubebState {
   Uninitialized = 0,
   Initialized,
   Shutdown
 } sCubebState = CubebState::Uninitialized;
 cubeb* sCubebContext;
 double sVolumeScale = 1.0;
 uint32_t sCubebPlaybackLatencyInMilliseconds = 100;
 uint32_t sCubebMSGLatencyInFrames = 512;
-bool sCubebPlaybackLatencyPrefSet;
-bool sCubebMSGLatencyPrefSet;
+bool sCubebPlaybackLatencyPrefSet = false;
+bool sCubebMSGLatencyPrefSet = false;
 bool sAudioStreamInitEverSucceeded = false;
 #ifdef MOZ_CUBEB_REMOTING
 bool sCubebSandbox;
 #endif
 StaticAutoPtr<char> sBrandName;
 StaticAutoPtr<char> sCubebBackendName;
 
 const char kBrandBundleURL[]      = "chrome://branding/locale/brand.properties";
@@ -300,21 +303,25 @@ bool InitPreferredSampleRate()
   StaticMutexAutoLock lock(sMutex);
   if (sPreferredSampleRate != 0) {
     return true;
   }
   cubeb* context = GetCubebContextUnlocked();
   if (!context) {
     return false;
   }
+#ifdef MOZ_WIDGET_ANDROID
+  sPreferredSampleRate = AndroidGetAudioOutputSampleRate();
+#else
   if (cubeb_get_preferred_sample_rate(context,
                                       &sPreferredSampleRate) != CUBEB_OK) {
 
     return false;
   }
+#endif
   MOZ_ASSERT(sPreferredSampleRate);
   return true;
 }
 
 uint32_t PreferredSampleRate()
 {
   if (!InitPreferredSampleRate()) {
     return 44100;
@@ -522,24 +529,38 @@ bool CubebPlaybackLatencyPrefSet()
 }
 
 bool CubebMSGLatencyPrefSet()
 {
   StaticMutexAutoLock lock(sMutex);
   return sCubebMSGLatencyPrefSet;
 }
 
-Maybe<uint32_t> GetCubebMSGLatencyInFrames()
+uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params * params)
 {
   StaticMutexAutoLock lock(sMutex);
-  if (!sCubebMSGLatencyPrefSet) {
-    return Maybe<uint32_t>();
+  if (sCubebMSGLatencyPrefSet) {
+    MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
+    return sCubebMSGLatencyInFrames;
   }
-  MOZ_ASSERT(sCubebMSGLatencyInFrames > 0);
-  return Some(sCubebMSGLatencyInFrames);
+
+#ifdef MOZ_WIDGET_ANDROID
+  return AndroidGetAudioOutputFramesPerBuffer();
+#else
+  cubeb* context = GetCubebContextUnlocked();
+  if (!context) {
+    return sCubebMSGLatencyInFrames; // default 512
+  }
+  uint32_t latency_frames = 0;
+  if (cubeb_get_min_latency(context, params, &latency_frames) != CUBEB_OK) {
+    NS_WARNING("Could not get minimal latency from cubeb.");
+    return sCubebMSGLatencyInFrames; // default 512
+  }
+  return latency_frames;
+#endif
 }
 
 void InitLibrary()
 {
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_VOLUME_SCALE);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_MSG);
   Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_BACKEND);
@@ -736,10 +757,25 @@ void GetDeviceCollection(nsTArray<RefPtr
                               device.latency_lo);
         aDeviceInfos.AppendElement(info);
       }
     }
     cubeb_device_collection_destroy(context, &collection);
   }
 }
 
+#ifdef MOZ_WIDGET_ANDROID
+uint32_t AndroidGetAudioOutputSampleRate()
+{
+  int32_t sample_rate = java::GeckoAppShell::GetAudioOutputSampleRate();
+  MOZ_ASSERT(sample_rate > 0);
+  return sample_rate;
+}
+uint32_t AndroidGetAudioOutputFramesPerBuffer()
+{
+  int32_t frames = java::GeckoAppShell::GetAudioOutputFramesPerBuffer();
+  MOZ_ASSERT(frames > 0);
+  return frames;
+}
+#endif
+
 } // namespace CubebUtils
 } // namespace mozilla
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -39,20 +39,25 @@ enum Side {
 };
 
 double GetVolumeScale();
 bool GetFirstStream();
 cubeb* GetCubebContext();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebPlaybackLatencyInMilliseconds();
-Maybe<uint32_t> GetCubebMSGLatencyInFrames();
+uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params * params);
 bool CubebLatencyPrefSet();
 cubeb_channel_layout ConvertChannelMapToCubebLayout(uint32_t aChannelMap);
 void GetCurrentBackend(nsAString& aBackend);
 void GetPreferredChannelLayout(nsAString& aLayout);
 void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
                          Side aSide);
 cubeb_channel_layout GetPreferredChannelLayoutOrSMPTE(cubeb* context, uint32_t aChannels);
+
+#ifdef MOZ_WIDGET_ANDROID
+uint32_t AndroidGetAudioOutputSampleRate();
+uint32_t AndroidGetAudioOutputFramesPerBuffer();
+#endif
 } // namespace CubebUtils
 } // namespace mozilla
 
 #endif // CubebUtils_h_
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -594,17 +594,16 @@ AudioCallbackDriver::Init()
     if (!mFromFallback) {
       CubebUtils::ReportCubebStreamInitFailure(true);
     }
     return false;
   }
 
   cubeb_stream_params output;
   cubeb_stream_params input;
-  uint32_t latency_frames;
   bool firstStream = CubebUtils::GetFirstStream();
 
   MOZ_ASSERT(!NS_IsMainThread(),
       "This is blocking and should never run on the main thread.");
 
   mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
 
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
@@ -624,24 +623,17 @@ AudioCallbackDriver::Init()
 
   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannels);
   mScratchBuffer = SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannels);
 
   output.channels = mOutputChannels;
   output.layout = CubebUtils::GetPreferredChannelLayoutOrSMPTE(cubebContext, mOutputChannels);
   output.prefs = CUBEB_STREAM_PREF_NONE;
 
-  Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
-  if (latencyPref) {
-    latency_frames = latencyPref.value();
-  } else {
-    if (cubeb_get_min_latency(cubebContext, &output, &latency_frames) != CUBEB_OK) {
-      NS_WARNING("Could not get minimal latency from cubeb.");
-    }
-  }
+  uint32_t latency_frames = CubebUtils::GetCubebMSGLatencyInFrames(&output);
 
   // Macbook and MacBook air don't have enough CPU to run very low latency
   // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
   if (IsMacbookOrMacbookAir()) {
     latency_frames = std::max((uint32_t) 512, latency_frames);
   }
 
   input = output;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -3075,33 +3075,33 @@ void MediaDecoderStateMachine::SetVideoD
     aMode);
   OwnerThread()->DispatchStateChange(r.forget());
 }
 
 void MediaDecoderStateMachine::SetVideoDecodeModeInternal(VideoDecodeMode aMode)
 {
   MOZ_ASSERT(OnTaskQueue());
 
+  LOG("SetVideoDecodeModeInternal(), VideoDecodeMode=(%s->%s), mVideoDecodeSuspended=%c",
+      mVideoDecodeMode == VideoDecodeMode::Normal ? "Normal" : "Suspend",
+      aMode == VideoDecodeMode::Normal ? "Normal" : "Suspend",
+      mVideoDecodeSuspended ? 'T' : 'F');
+
   // Should not suspend decoding if we don't turn on the pref.
   if (!MediaPrefs::MDSMSuspendBackgroundVideoEnabled() &&
       aMode == VideoDecodeMode::Suspend) {
     LOG("SetVideoDecodeModeInternal(), early return because preference off and set to Suspend");
     return;
   }
 
   if (aMode == mVideoDecodeMode) {
     LOG("SetVideoDecodeModeInternal(), early return because the mode does not change");
     return;
   }
 
-  LOG("SetVideoDecodeModeInternal(), VideoDecodeMode=(%s->%s), mVideoDecodeSuspended=%c",
-      mVideoDecodeMode == VideoDecodeMode::Normal ? "Normal" : "Suspend",
-      aMode == VideoDecodeMode::Normal ? "Normal" : "Suspend",
-      mVideoDecodeSuspended ? 'T' : 'F');
-
   // Set new video decode mode.
   mVideoDecodeMode = aMode;
 
   // Start timer to trigger suspended video decoding.
   if (mVideoDecodeMode == VideoDecodeMode::Suspend) {
     TimeStamp target = TimeStamp::Now() + SuspendBackgroundVideoDelay();
 
     RefPtr<MediaDecoderStateMachine> self = this;
--- a/dom/media/test/background_video.js
+++ b/dom/media/test/background_video.js
@@ -1,39 +1,71 @@
 /* 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/. */
 /* jshint esversion: 6, -W097 */
-/* globals SimpleTest, SpecialPowers, info, is, ok */
+/* globals SimpleTest, SpecialPowers, document, info, is, manager, ok */
 
 "use strict";
 
 function startTest(test) {
   info(test.desc);
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({ 'set': test.prefs }, () => {
     manager.runTests(test.tests, test.runTest);
   });
 }
 
 /**
+ * @param {HTMLMediaElement} video target of interest.
+ * @param {string} eventName the event to wait on.
+ * @returns {Promise} A promise that is resolved when event happens.
+ */
+function nextEvent(video, eventName) {
+  return new Promise(function (resolve, reject) {
+    let f = function (event) {
+      ok(true, `${video.token} ${eventName}.`);
+      video.removeEventListener(eventName, f, false);
+      resolve(event);
+    };
+    video.addEventListener(eventName, f, false);
+  });
+}
+
+function nextVideoEnded(video) {
+  return nextEvent(video, 'ended');
+}
+
+function nextVideoPlaying(video) {
+  return nextEvent(video, 'playing');
+}
+
+function nextVideoResumes(video) {
+  return nextEvent(video, 'mozexitvideosuspend');
+}
+
+function nextVideoSuspends(video) {
+  return nextEvent(video, 'mozentervideosuspend');
+}
+
+/**
  * @param {string} url video src.
  * @returns {HTMLMediaElement} The created video element.
  */
 function appendVideoToDoc(url, token, width, height) {
   // Default size of (160, 120) is used by other media tests.
   if (width === undefined) { width = 160; }
-  if (height === undefined) { height = 3*width/4; }
+  if (height === undefined) { height = 3 * width / 4; }
 
   let v = document.createElement('video');
   v.token = token;
-  document.body.appendChild(v);
   v.width = width;
   v.height = height;
   v.src = url;
+  document.body.appendChild(v);
   return v;
 }
 
 /**
  * @param {HTMLMediaElement} video Video element under test.
  * @returns {Promise} Promise that is resolved when video 'playing' event fires.
  */
 function waitUntilPlaying(video) {
@@ -84,44 +116,44 @@ function testVideoSuspendsWhenHidden(vid
   return p;
 }
 
 /**
  * @param {HTMLMediaElement} video Video element under test.
  * @returns {Promise} Promise that is resolved when video decode resumes.
  */
 function testVideoResumesWhenShown(video) {
-  var p  = once(video, 'mozexitvideosuspend').then(() => {
+  var p = once(video, 'mozexitvideosuspend').then(() => {
     ok(true, `${video.token} resumes`);
   });
   Log(video.token, "Set visible");
   video.setVisible(true);
   return p;
 }
 
 /**
  * @param {HTMLMediaElement} video Video element under test.
  * @returns {Promise} Promise that is resolved when video decode resumes.
  */
 function testVideoOnlySeekCompletedWhenShown(video) {
-  var p  = once(video, 'mozvideoonlyseekcompleted').then(() => {
+  var p = once(video, 'mozvideoonlyseekcompleted').then(() => {
     ok(true, `${video.token} resumes`);
   });
   Log(video.token, "Set visible");
   video.setVisible(true);
   return p;
 }
 
 /**
  * @param {HTMLVideoElement} video Video element under test.
  * @returns {Promise} Promise that is resolved if video ends and rejects if video suspends.
  */
 function checkVideoDoesntSuspend(video) {
   let p = Promise.race([
-    waitUntilEnded(video).then(() => { ok(true, `${video.token} ended before decode was suspended`)}),
+    waitUntilEnded(video).then(() => { ok(true, `${video.token} ended before decode was suspended`) }),
     once(video, 'mozentervideosuspend', () => { Promise.reject(new Error(`${video.token} suspended`)) })
   ]);
   Log(video.token, "Set hidden.");
   video.setVisible(false);
   return p;
 }
 
 /**
--- a/dom/media/test/test_background_video_suspend.html
+++ b/dom/media/test/test_background_video_suspend.html
@@ -1,51 +1,74 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Test Background Video Suspends</title>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <script src="manifest.js"></script>
 <script src="background_video.js"></script>
-<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
 <script type="text/javascript">
-"use strict";
-
-var manager = new MediaTestManager;
+  "use strict";
 
-var MIN_DELAY = 100;
+  var manager = new MediaTestManager;
 
-function testDelay(v, start, min) {
-  let end = performance.now();
-  let delay = end - start;
-  ok(delay > min, `${v.token} suspended with a delay of ${delay} ms`);
-}
+  var MIN_DELAY = 100;
 
-startTest({
-  desc: 'Test Background Video Suspends',
-  prefs: [
-    [ "media.test.video-suspend", true ],
-    [ "media.suspend-bkgnd-video.enabled", true ],
-    // Use a short delay to ensure video decode suspend happens before end
-    // of video.
-    [ "media.suspend-bkgnd-video.delay-ms", MIN_DELAY ],
-    [ "privacy.reduceTimerPrecision", false ]
-  ],
-  tests: gDecodeSuspendTests,
-  runTest: (test, token) => {
-    let v = appendVideoToDoc(test.name, token);
+  function testDelay(v, start, min) {
+    let end = performance.now();
+    let delay = end - start;
+    ok(delay > min, `${v.token} suspended with a delay of ${delay} ms`);
+  }
+
+  async function runTest(test, token) {
+    let video = appendVideoToDoc(test.name, token);
     manager.started(token);
 
-    let start;
-    waitUntilPlaying(v)
-      .then(() => { start = performance.now(); })
-      .then(() => testVideoSuspendsWhenHidden(v))
-      .then(() => {
-        testDelay(v, start, MIN_DELAY);
-        return testVideoResumesWhenShown(v);
-      })
-      .then(() => waitUntilEnded(v))
-      .then(() => {
-        removeNodeAndSource(v);
-        manager.finished(token);
-      });
+    let ended = nextVideoEnded(video);
+    let playing = nextVideoPlaying(video);
+    let resumes = nextVideoResumes(video);
+    let suspends = nextVideoSuspends(video);
+
+    Log(token, "Start playing");
+    video.play();
+
+    Log(token, "Waiting for video playing");
+    await playing;
+
+    let start = performance.now();
+
+    Log(token, "Set hidden");
+    video.setVisible(false);
+
+    Log(token, "Waiting for video suspend");
+    await suspends;
+
+    testDelay(video, start, MIN_DELAY);
+
+    Log(token, "Set visible");
+    video.setVisible(true);
+
+    Log(token, "Waiting for video resume");
+    await resumes;
+
+    Log(token, "Waiting for ended");
+    await ended;
+
+    ok(video.currentTime >= video.duration, 'current time approximates duration.');
+
+    removeNodeAndSource(video);
+    manager.finished(token);
   }
-});
-</script>
+
+  startTest({
+    desc: 'Test Background Video Suspends',
+    prefs: [
+      ["media.test.video-suspend", true],
+      ["media.suspend-bkgnd-video.enabled", true],
+      // Use a short delay to ensure video decode suspend happens before end
+      // of video.
+      ["media.suspend-bkgnd-video.delay-ms", MIN_DELAY],
+      ["privacy.reduceTimerPrecision", false]
+    ],
+    tests: gDecodeSuspendTests,
+    runTest: runTest
+  });
+</script>
\ No newline at end of file
--- a/dom/media/test/test_background_video_suspend_ends.html
+++ b/dom/media/test/test_background_video_suspend_ends.html
@@ -1,39 +1,53 @@
 <!DOCTYPE html>
 <meta charset="utf-8">
 <title>Test Background Suspended Video Fires 'ended' Event</title>
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <script src="manifest.js"></script>
 <script src="background_video.js"></script>
-<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
 <script type="text/javascript">
-"use strict";
-
-var manager = new MediaTestManager;
+  "use strict";
 
-startTest({
-  desc: "Test Background Suspended Video Fires 'ended' Event",
-  prefs: [
-    [ "media.test.video-suspend", true ],
-    [ "media.suspend-bkgnd-video.enabled", true ],
-    // User a short delay to ensure video decode suspend happens before end
-    // of video.
-    [ "media.suspend-bkgnd-video.delay-ms", 1000 ]
-  ],
-  tests: gDecodeSuspendTests,
-  runTest: (test, token) => {
-    let v = appendVideoToDoc(test.name, token);
+  var manager = new MediaTestManager;
+
+  async function runTest(test, token) {
+    let video = appendVideoToDoc(test.name, token);
     manager.started(token);
 
     // This test checks that 'ended' event is received for videos with
     // suspended video decoding. This is important for looping video logic
     // handling in HTMLMediaElement.
-    waitUntilPlaying(v)
-      .then(() => testVideoSuspendsWhenHidden(v))
-      .then(() => waitUntilEnded(v))
-      .then(() => {
-        ok(v.currentTime >= v.duration, 'current time approximates duration.');
-        manager.finished(token);
-      });
+
+    let ended = nextVideoEnded(video);
+    let suspends = nextVideoSuspends(video);
+
+    Log(token, "Start playing");
+    video.play();
+
+    Log(token, "Set hidden");
+    video.setVisible(false);
+
+    Log(token, "Waiting for video suspend");
+    await suspends;
+
+    Log(token, "Waiting for ended");
+    await ended;
+
+    ok(video.currentTime >= video.duration, 'current time approximates duration.');
+
+    manager.finished(token);
   }
-});
-</script>
+
+  startTest({
+    desc: "Test Background Suspended Video Fires 'ended' Event",
+    prefs: [
+      ["media.test.video-suspend", true],
+      ["media.suspend-bkgnd-video.enabled", true],
+      // User a short delay to ensure video decode suspend happens before end
+      // of video.
+      ["media.suspend-bkgnd-video.delay-ms", 1000]
+    ],
+    tests: gDecodeSuspendTests,
+    runTest: runTest
+  });
+</script>
\ No newline at end of file
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -103,35 +103,37 @@ PaymentCurrencyAmount::GetValue(nsAStrin
 
 /* PaymentItem */
 
 NS_IMPL_ISUPPORTS(PaymentItem,
                   nsIPaymentItem)
 
 PaymentItem::PaymentItem(const nsAString& aLabel,
                          nsIPaymentCurrencyAmount* aAmount,
-                         const bool aPending)
+                         const bool aPending,
+                         const nsAString& aType)
   : mLabel(aLabel)
   , mAmount(aAmount)
   , mPending(aPending)
+  , mType(aType)
 {
 }
 
 nsresult
 PaymentItem::Create(const IPCPaymentItem& aIPCItem, nsIPaymentItem** aItem)
 {
   NS_ENSURE_ARG_POINTER(aItem);
   nsCOMPtr<nsIPaymentCurrencyAmount> amount;
   nsresult rv = PaymentCurrencyAmount::Create(aIPCItem.amount(),
                                               getter_AddRefs(amount));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   nsCOMPtr<nsIPaymentItem> item =
-    new PaymentItem(aIPCItem.label(), amount, aIPCItem.pending());
+    new PaymentItem(aIPCItem.label(), amount, aIPCItem.pending(), aIPCItem.type());
   item.forget(aItem);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PaymentItem::GetLabel(nsAString& aLabel)
 {
   aLabel = mLabel;
@@ -151,16 +153,23 @@ PaymentItem::GetAmount(nsIPaymentCurrenc
 NS_IMETHODIMP
 PaymentItem::GetPending(bool* aPending)
 {
   NS_ENSURE_ARG_POINTER(aPending);
   *aPending = mPending;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PaymentItem::GetType(nsAString& aType)
+{
+  aType = mType;
+  return NS_OK;
+}
+
 /* PaymentDetailsModifier */
 
 NS_IMPL_ISUPPORTS(PaymentDetailsModifier,
                   nsIPaymentDetailsModifier)
 
 PaymentDetailsModifier::PaymentDetailsModifier(const nsAString& aSupportedMethods,
                                                nsIPaymentItem* aTotal,
                                                nsIArray* aAdditionalDisplayItems,
--- a/dom/payments/PaymentRequestData.h
+++ b/dom/payments/PaymentRequestData.h
@@ -60,23 +60,25 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTITEM
 
   static nsresult Create(const IPCPaymentItem& aIPCItem, nsIPaymentItem** aItem);
 
 private:
   PaymentItem(const nsAString& aLabel,
               nsIPaymentCurrencyAmount* aAmount,
-              const bool aPending);
+              const bool aPending,
+              const nsAString& aType);
 
   ~PaymentItem() = default;
 
   nsString mLabel;
   nsCOMPtr<nsIPaymentCurrencyAmount> mAmount;
   bool mPending;
+  nsString mType;
 };
 
 class PaymentDetailsModifier final : public nsIPaymentDetailsModifier
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPAYMENTDETAILSMODIFIER
 
--- a/dom/payments/PaymentRequestManager.cpp
+++ b/dom/payments/PaymentRequestManager.cpp
@@ -46,19 +46,28 @@ ConvertCurrencyAmount(const PaymentCurre
                       IPCPaymentCurrencyAmount& aIPCCurrencyAmount)
 {
   aIPCCurrencyAmount = IPCPaymentCurrencyAmount(aAmount.mCurrency, aAmount.mValue);
 }
 
 void
 ConvertItem(const PaymentItem& aItem, IPCPaymentItem& aIPCItem)
 {
+  uint8_t typeIndex = UINT8_MAX;
+  if (aItem.mType.WasPassed()) {
+    typeIndex = static_cast<uint8_t>(aItem.mType.Value());
+  }
+  nsString type;
+  if (typeIndex < ArrayLength(PaymentItemTypeValues::strings)) {
+    type.AssignASCII(
+      PaymentItemTypeValues::strings[typeIndex].value);
+  }
   IPCPaymentCurrencyAmount amount;
   ConvertCurrencyAmount(aItem.mAmount, amount);
-  aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending);
+  aIPCItem = IPCPaymentItem(aItem.mLabel, amount, aItem.mPending, type);
 }
 
 nsresult
 ConvertModifier(JSContext* aCx,
                 const PaymentDetailsModifier& aModifier,
                 IPCPaymentDetailsModifier& aIPCModifier)
 {
   NS_ENSURE_ARG_POINTER(aCx);
--- a/dom/payments/ipc/PPaymentRequest.ipdl
+++ b/dom/payments/ipc/PPaymentRequest.ipdl
@@ -22,16 +22,17 @@ struct IPCPaymentCurrencyAmount
   nsString value;
 };
 
 struct IPCPaymentItem
 {
   nsString label;
   IPCPaymentCurrencyAmount amount;
   bool pending;
+  nsString type;
 };
 
 struct IPCPaymentDetailsModifier
 {
   nsString supportedMethods;
   IPCPaymentItem total;
   IPCPaymentItem[] additionalDisplayItems;
   nsString data;
--- a/dom/payments/test/ConstructorChromeScript.js
+++ b/dom/payments/test/ConstructorChromeScript.js
@@ -136,39 +136,58 @@ function checkComplexRequest(payRequest)
   if (details.totalItem.amount.value != "100.00") {
     emitTestFail("total item's value should be '100.00'.");
   }
 
   const displayItems = details.displayItems;
   if (!details.displayItems) {
     emitTestFail("details.displayItems should not be undefined.");
   }
-  if (displayItems.length != 2) {
-    emitTestFail("displayItems' length should be 2.")
+  if (displayItems.length != 3) {
+    emitTestFail("displayItems' length should be 3.")
   }
   let item = displayItems.queryElementAt(0, Ci.nsIPaymentItem);
   if (item.label != "First item") {
     emitTestFail("1st display item's label should be 'First item'.");
   }
   if (item.amount.currency != "USD") {
     emitTestFail("1st display item's currency should be 'USD'.");
   }
   if (item.amount.value != "60.00") {
     emitTestFail("1st display item's value should be '60.00'.");
   }
+  if (item.type != "") {
+    emitTestFail("1st display item's type should be ''.");
+  }
   item = displayItems.queryElementAt(1, Ci.nsIPaymentItem);
   if (item.label != "Second item") {
     emitTestFail("2nd display item's label should be 'Second item'.");
   }
   if (item.amount.currency != "USD") {
     emitTestFail("2nd display item's currency should be 'USD'.");
   }
   if (item.amount.value != "40.00") {
     emitTestFail("2nd display item's value should be '40.00'.");
   }
+  if (item.type != "") {
+    emitTestFail("2nd display item's type should be ''.");
+  }
+  item = displayItems.queryElementAt(2, Ci.nsIPaymentItem);
+  if (item.label != "Tax") {
+    emitTestFail("3rd display item's label should be 'Tax'.");
+  }
+  if (item.amount.currency != "USD") {
+    emitTestFail("3rd display item's currency should be 'USD'.");
+  }
+  if (item.amount.value != "5.00") {
+    emitTestFail("3rd display item's value should be '5.00'.");
+  }
+  if (item.type != "tax") {
+    emitTestFail("3rd display item's type should be 'tax'.");
+  }
 
   const modifiers = details.modifiers;
   if (!modifiers) {
     emitTestFail("details.displayItems should not be undefined.");
   }
   if (modifiers.length != 1) {
     emitTestFail("modifiers' length should be 1.");
   }
--- a/dom/payments/test/test_constructor.html
+++ b/dom/payments/test/test_constructor.html
@@ -70,16 +70,24 @@ https://bugzilla.mozilla.org/show_bug.cg
         }
       },
       {
         label: "Second item",
         amount: {
           currency: "USD",
           value: "40.00"
         }
+      },
+      {
+        label: "Tax",
+        amount: {
+          currency: "USD",
+          value: "5.00"
+        },
+        type: "tax"
       }
     ],
     modifiers: [
       {
         supportedMethods: "basic-card",
         total: {
           label: "Discounted Total",
           amount: {
--- a/dom/security/nsMixedContentBlocker.cpp
+++ b/dom/security/nsMixedContentBlocker.cpp
@@ -902,16 +902,19 @@ nsMixedContentBlocker::ShouldLoad(bool a
         cc->SendAccumulateMixedContentHSTS(uri, active,
                                            originAttributes);
       }
     }
   }
 
   // set hasMixedContentObjectSubrequest on this object if necessary
   if (aContentType == TYPE_OBJECT_SUBREQUEST) {
+    if (!sBlockMixedObjectSubrequest) {
+      rootDoc->WarnOnceAbout(nsIDocument::eMixedDisplayObjectSubrequest);
+    }
     rootDoc->SetHasMixedContentObjectSubrequest(true);
   }
 
   // If the content is display content, and the pref says display content should be blocked, block it.
   if (sBlockMixedDisplay && classification == eMixedDisplay) {
     if (allowMixedContent) {
       LogMixedContentMessage(classification, aContentLocation, rootDoc, eUserOverride);
       *aDecision = nsIContentPolicy::ACCEPT;
--- a/dom/system/nsDeviceSensors.cpp
+++ b/dom/system/nsDeviceSensors.cpp
@@ -32,16 +32,22 @@
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace hal;
 
 #undef near
 
 #define DEFAULT_SENSOR_POLL 100
 
+static bool gPrefSensorsEnabled = false;
+static bool gPrefMotionSensorEnabled = false;
+static bool gPrefOrientationSensorEnabled = false;
+static bool gPrefProximitySensorEnabled = false;
+static bool gPrefAmbientLightSensorEnabled = false;
+
 static const nsTArray<nsIDOMWindow*>::index_type NoIndex =
   nsTArray<nsIDOMWindow*>::NoIndex;
 
 class nsDeviceSensorData final : public nsIDeviceSensorData
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDEVICESENSORDATA
@@ -101,17 +107,31 @@ NS_IMETHODIMP nsDeviceSensorData::GetZ(d
 }
 
 NS_IMPL_ISUPPORTS(nsDeviceSensors, nsIDeviceSensors)
 
 nsDeviceSensors::nsDeviceSensors()
 {
   mIsUserProximityNear = false;
   mLastDOMMotionEventTime = TimeStamp::Now();
-  mEnabled = Preferences::GetBool("device.sensors.enabled", true);
+  Preferences::AddBoolVarCache(&gPrefSensorsEnabled,
+                              "device.sensors.enabled",
+                              true);
+  Preferences::AddBoolVarCache(&gPrefMotionSensorEnabled,
+                              "device.sensors.motion.enabled",
+                              true);
+  Preferences::AddBoolVarCache(&gPrefOrientationSensorEnabled,
+                              "device.sensors.orientation.enabled",
+                              true);
+  Preferences::AddBoolVarCache(&gPrefProximitySensorEnabled,
+                              "device.sensors.proximity.enabled",
+                              false);
+  Preferences::AddBoolVarCache(&gPrefAmbientLightSensorEnabled,
+                              "device.sensors.ambientLight.enabled",
+                              false);
 
   for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
     nsTArray<nsIDOMWindow*> *windows = new nsTArray<nsIDOMWindow*>();
     mWindowListeners.AppendElement(windows);
   }
 
   mLastDOMMotionEventTime = TimeStamp::Now();
 }
@@ -125,17 +145,17 @@ nsDeviceSensors::~nsDeviceSensors()
 
   for (int i = 0; i < NUM_SENSOR_TYPE; i++) {
     delete mWindowListeners[i];
   }
 }
 
 NS_IMETHODIMP nsDeviceSensors::HasWindowListener(uint32_t aType, nsIDOMWindow *aWindow, bool *aRetVal)
 {
-  if (AreSensorEventsDisabled(aWindow))
+  if (!IsSensorAllowedByPref(aType, aWindow))
     *aRetVal = false;
   else
     *aRetVal = mWindowListeners[aType]->IndexOf(aWindow) != NoIndex;
 
   return NS_OK;
 }
 
 class DeviceSensorTestEvent : public Runnable
@@ -166,17 +186,17 @@ private:
   RefPtr<nsDeviceSensors> mTarget;
   uint32_t mType;
 };
 
 static bool sTestSensorEvents = false;
 
 NS_IMETHODIMP nsDeviceSensors::AddWindowListener(uint32_t aType, nsIDOMWindow *aWindow)
 {
-  if (AreSensorEventsDisabled(aWindow))
+  if (!IsSensorAllowedByPref(aType, aWindow))
     return NS_OK;
 
   if (mWindowListeners[aType]->IndexOf(aWindow) != NoIndex)
     return NS_OK;
 
   if (!IsSensorEnabled(aType)) {
     RegisterSensorObserver((SensorType)aType, this);
   }
@@ -582,22 +602,69 @@ nsDeviceSensors::FireDOMMotionEvent(nsID
 
   mLastRotationRate.reset();
   mLastAccelerationIncludingGravity.reset();
   mLastAcceleration.reset();
   mLastDOMMotionEventTime = TimeStamp::Now();
 }
 
 bool
-nsDeviceSensors::AreSensorEventsDisabled(nsIDOMWindow* aWindow)
+nsDeviceSensors::IsSensorAllowedByPref(uint32_t aType, nsIDOMWindow* aWindow)
 {
-  if (!mEnabled) {
-    return true;
+  // checks "device.sensors.enabled" master pref
+  if (!gPrefSensorsEnabled) {
+    return false;
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aWindow);
+  nsCOMPtr<nsIDocument> doc;
+  if (window) {
+    doc = window->GetExtantDoc();
+  }
 
-  if (!window) {
+  switch (aType) {
+  case nsIDeviceSensorData::TYPE_LINEAR_ACCELERATION:
+  case nsIDeviceSensorData::TYPE_ACCELERATION:
+  case nsIDeviceSensorData::TYPE_GYROSCOPE:
+    // checks "device.sensors.motion.enabled" pref
+    if (!gPrefMotionSensorEnabled) {
+      return false;
+    } else if (doc) {
+      doc->WarnOnceAbout(nsIDocument::eMotionEvent);
+    }
+    break;
+  case nsIDeviceSensorData::TYPE_GAME_ROTATION_VECTOR:
+  case nsIDeviceSensorData::TYPE_ORIENTATION:
+  case nsIDeviceSensorData::TYPE_ROTATION_VECTOR:
+    // checks "device.sensors.orientation.enabled" pref
+    if (!gPrefOrientationSensorEnabled) {
+      return false;
+    } else if (doc) {
+      doc->WarnOnceAbout(nsIDocument::eOrientationEvent);
+    }
+    break;
+  case nsIDeviceSensorData::TYPE_PROXIMITY:
+    // checks "device.sensors.proximity.enabled" pref
+    if (!gPrefProximitySensorEnabled) {
+      return false;
+    } else if (doc) {
+      doc->WarnOnceAbout(nsIDocument::eProximityEvent, true);
+    }
+    break;
+  case nsIDeviceSensorData::TYPE_LIGHT:
+    // checks "device.sensors.ambientLight.enabled" pref
+    if (!gPrefAmbientLightSensorEnabled) {
+      return false;
+    } else if (doc) {
+      doc->WarnOnceAbout(nsIDocument::eAmbientLightEvent, true);
+    }
+    break;
+  default:
+    MOZ_ASSERT_UNREACHABLE("Device sensor type not recognised");
     return false;
   }
 
-  return nsContentUtils::ShouldResistFingerprinting(window->GetDocShell());
+  if (!window) {
+    return true;
+  }
+
+  return !nsContentUtils::ShouldResistFingerprinting(window->GetDocShell());
 }
--- a/dom/system/nsDeviceSensors.h
+++ b/dom/system/nsDeviceSensors.h
@@ -64,23 +64,21 @@ private:
   void FireDOMMotionEvent(nsIDocument* domDoc,
                           mozilla::dom::EventTarget* target,
                           uint32_t type,
                           PRTime timestamp,
                           double x,
                           double y,
                           double z);
 
-  bool mEnabled;
-
   inline bool IsSensorEnabled(uint32_t aType) {
     return mWindowListeners[aType]->Length() > 0;
   }
 
-  bool AreSensorEventsDisabled(nsIDOMWindow* aWindow);
+  bool IsSensorAllowedByPref(uint32_t aType, nsIDOMWindow* aWindow);
 
   mozilla::TimeStamp mLastDOMMotionEventTime;
   bool mIsUserProximityNear;
   mozilla::Maybe<DeviceAccelerationInit> mLastAcceleration;
   mozilla::Maybe<DeviceAccelerationInit> mLastAccelerationIncludingGravity;
   mozilla::Maybe<DeviceRotationRateInit> mLastRotationRate;
 };
 
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -274,23 +274,23 @@ var interfaceNamesInGlobalScope =
     {name: "DataTransfer", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DataTransferItem", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DataTransferItemList", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DelayNode", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "DeviceLightEvent", insecureContext: true},
+    {name: "DeviceLightEvent", insecureContext: true, release: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DeviceMotionEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DeviceOrientationEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "DeviceProximityEvent", insecureContext: true},
+    {name: "DeviceProximityEvent", insecureContext: true, release: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Directory", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Document", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DocumentFragment", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "DocumentTimeline", insecureContext: true, release: false},
@@ -1144,17 +1144,17 @@ var interfaceNamesInGlobalScope =
     {name: "U2F", insecureContext: true, disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "UIEvent", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "URL", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "URLSearchParams", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "UserProximityEvent", insecureContext: true},
+    {name: "UserProximityEvent", insecureContext: true, release: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "ValidityState", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VideoPlaybackQuality", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VideoStreamTrack", insecureContext: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRDisplay", insecureContext: true, releaseNonWindowsAndMac: false},
--- a/dom/webidl/DeviceLightEvent.webidl
+++ b/dom/webidl/DeviceLightEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional DeviceLightEventInit eventInitDict)]
+[Pref="device.sensors.ambientLight.enabled", Func="nsGlobalWindowInner::DeviceSensorsEnabled", Constructor(DOMString type, optional DeviceLightEventInit eventInitDict)]
 interface DeviceLightEvent : Event
 {
   readonly attribute unrestricted double value;
 };
 
 dictionary DeviceLightEventInit : EventInit
 {
   unrestricted double value = Infinity;
--- a/dom/webidl/DeviceMotionEvent.webidl
+++ b/dom/webidl/DeviceMotionEvent.webidl
@@ -13,17 +13,17 @@ interface DeviceAcceleration {
 
 [NoInterfaceObject]
 interface DeviceRotationRate {
   readonly attribute double? alpha;
   readonly attribute double? beta;
   readonly attribute double? gamma;
 };
 
-[Constructor(DOMString type, optional DeviceMotionEventInit eventInitDict)]
+[Pref="device.sensors.motion.enabled", Func="nsGlobalWindowInner::DeviceSensorsEnabled", Constructor(DOMString type, optional DeviceMotionEventInit eventInitDict)]
 interface DeviceMotionEvent : Event {
   readonly attribute DeviceAcceleration? acceleration;
   readonly attribute DeviceAcceleration? accelerationIncludingGravity;
   readonly attribute DeviceRotationRate? rotationRate;
   readonly attribute double? interval;
 };
 
 dictionary DeviceAccelerationInit {
--- a/dom/webidl/DeviceOrientationEvent.webidl
+++ b/dom/webidl/DeviceOrientationEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional DeviceOrientationEventInit eventInitDict), LegacyEventInit]
+[Pref="device.sensors.orientation.enabled", Func="nsGlobalWindowInner::DeviceSensorsEnabled", Constructor(DOMString type, optional DeviceOrientationEventInit eventInitDict), LegacyEventInit]
 interface DeviceOrientationEvent : Event
 {
   readonly attribute double? alpha;
   readonly attribute double? beta;
   readonly attribute double? gamma;
   readonly attribute boolean absolute;
 
   // initDeviceOrientationEvent is a Gecko specific deprecated method.
--- a/dom/webidl/DeviceProximityEvent.webidl
+++ b/dom/webidl/DeviceProximityEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional DeviceProximityEventInit eventInitDict)]
+[Pref="device.sensors.proximity.enabled", Func="nsGlobalWindowInner::DeviceSensorsEnabled", Constructor(DOMString type, optional DeviceProximityEventInit eventInitDict)]
 interface DeviceProximityEvent : Event
 {
   readonly attribute double value;
   readonly attribute double min;
   readonly attribute double max;
 };
 
 dictionary DeviceProximityEventInit : EventInit
--- a/dom/webidl/PaymentRequest.webidl
+++ b/dom/webidl/PaymentRequest.webidl
@@ -13,20 +13,25 @@ dictionary PaymentMethodData {
 };
 
 dictionary PaymentCurrencyAmount {
   required DOMString currency;
   required DOMString value;
            DOMString currencySystem = "urn:iso:std:iso:4217";
 };
 
+enum PaymentItemType {
+  "tax"
+};
+
 dictionary PaymentItem {
   required DOMString             label;
   required PaymentCurrencyAmount amount;
            boolean               pending = false;
+           PaymentItemType       type;
 };
 
 dictionary PaymentShippingOption {
   required DOMString             id;
   required DOMString             label;
   required PaymentCurrencyAmount amount;
            boolean               selected = false;
 };
--- a/dom/webidl/UserProximityEvent.webidl
+++ b/dom/webidl/UserProximityEvent.webidl
@@ -1,15 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-[Constructor(DOMString type, optional UserProximityEventInit eventInitDict)]
+[Pref="device.sensors.proximity.enabled", Func="nsGlobalWindowInner::DeviceSensorsEnabled", Constructor(DOMString type, optional UserProximityEventInit eventInitDict)]
 interface UserProximityEvent : Event
 {
   readonly attribute boolean near;
 };
 
 dictionary UserProximityEventInit : EventInit
 {
   boolean near = false;
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -1068,52 +1068,52 @@ XULDocument::ResolveForwardReferences()
 // nsIDOMDocument interface
 //
 
 already_AddRefed<nsINodeList>
 XULDocument::GetElementsByAttribute(const nsAString& aAttribute,
                                     const nsAString& aValue)
 {
     RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-    void* attrValue = new nsString(aValue);
+    nsAutoPtr<nsString> attrValue(new nsString(aValue));
     RefPtr<nsContentList> list = new nsContentList(this,
                                             MatchAttribute,
                                             nsContentUtils::DestroyMatchString,
-                                            attrValue,
+                                            attrValue.forget(),
                                             true,
                                             attrAtom,
                                             kNameSpaceID_Unknown);
 
     return list.forget();
 }
 
 already_AddRefed<nsINodeList>
 XULDocument::GetElementsByAttributeNS(const nsAString& aNamespaceURI,
                                       const nsAString& aAttribute,
                                       const nsAString& aValue,
                                       ErrorResult& aRv)
 {
     RefPtr<nsAtom> attrAtom(NS_Atomize(aAttribute));
-    void* attrValue = new nsString(aValue);
+    nsAutoPtr<nsString> attrValue(new nsString(aValue));
 
     int32_t nameSpaceId = kNameSpaceID_Wildcard;
     if (!aNamespaceURI.EqualsLiteral("*")) {
       nsresult rv =
         nsContentUtils::NameSpaceManager()->RegisterNameSpace(aNamespaceURI,
                                                               nameSpaceId);
       if (NS_FAILED(rv)) {
           aRv.Throw(rv);
           return nullptr;
       }
     }
 
     RefPtr<nsContentList> list = new nsContentList(this,
                                             MatchAttribute,
                                             nsContentUtils::DestroyMatchString,
-                                            attrValue,
+                                            attrValue.forget(),
                                             true,
                                             attrAtom,
                                             nameSpaceId);
     return list.forget();
 }
 
 void
 XULDocument::Persist(const nsAString& aID,
--- a/ipc/mscom/FastMarshaler.cpp
+++ b/ipc/mscom/FastMarshaler.cpp
@@ -98,17 +98,17 @@ FastMarshaler::InternalRelease()
 DWORD
 FastMarshaler::GetMarshalFlags(DWORD aDestContext, DWORD aMshlFlags)
 {
   // Only worry about local contexts.
   if (aDestContext != MSHCTX_LOCAL) {
     return aMshlFlags;
   }
 
-  if (!IsCallerExternalProcess()) {
+  if (IsCallerExternalProcess()) {
     return aMshlFlags;
   }
 
   // The caller is our parent main thread. Disable ping functionality.
   return aMshlFlags | MSHLFLAGS_NOPING;
 }
 
 HRESULT
--- a/ipc/mscom/IHandlerProvider.h
+++ b/ipc/mscom/IHandlerProvider.h
@@ -18,16 +18,17 @@ namespace mscom {
 struct IInterceptor;
 
 struct HandlerProvider
 {
   virtual STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) = 0;
   virtual STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor, NotNull<DWORD*> aOutPayloadSize) = 0;
   virtual STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor, NotNull<IStream*> aStream) = 0;
   virtual STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) = 0;
+  virtual STDMETHODIMP DisconnectHandlerRemotes() = 0;
 };
 
 struct IHandlerProvider : public IUnknown
                         , public HandlerProvider
 {
   virtual STDMETHODIMP_(REFIID) GetEffectiveOutParamIid(REFIID aCallIid,
                                                         ULONG aCallMethod) = 0;
   virtual STDMETHODIMP NewInstance(REFIID aIid,
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -423,16 +423,17 @@ HRESULT
 Interceptor::ReleaseMarshalData(IStream* pStm)
 {
   return mStdMarshal->ReleaseMarshalData(pStm);
 }
 
 HRESULT
 Interceptor::DisconnectObject(DWORD dwReserved)
 {
+  mEventSink->DisconnectHandlerRemotes();
   return mStdMarshal->DisconnectObject(dwReserved);
 }
 
 Interceptor::MapEntry*
 Interceptor::Lookup(REFIID aIid)
 {
   mInterceptorMapMutex.AssertCurrentThreadOwns();
 
@@ -825,10 +826,35 @@ Interceptor::AddRef()
 }
 
 ULONG
 Interceptor::Release()
 {
   return WeakReferenceSupport::Release();
 }
 
+/* static */ HRESULT
+Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget)
+{
+  MOZ_ASSERT(aTarget);
+
+  detail::LiveSetAutoLock lock(GetLiveSet());
+
+  // It is not an error if the interceptor doesn't exist, so we return
+  // S_FALSE instead of an error in that case.
+  RefPtr<IWeakReference> existingWeak(Move(GetLiveSet().Get(aTarget)));
+  if (!existingWeak) {
+    return S_FALSE;
+  }
+
+  RefPtr<IWeakReferenceSource> existingStrong;
+  if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
+    return S_FALSE;
+  }
+  // Since we now hold a strong ref on the interceptor, we may now release the
+  // lock.
+  lock.Unlock();
+
+  return ::CoDisconnectObject(existingStrong, 0);
+}
+
 } // namespace mscom
 } // namespace mozilla
--- a/ipc/mscom/Interceptor.h
+++ b/ipc/mscom/Interceptor.h
@@ -69,16 +69,36 @@ class Interceptor final : public WeakRef
                         , public IStdMarshalInfo
                         , public IMarshal
                         , public IInterceptor
 {
 public:
   static HRESULT Create(STAUniquePtr<IUnknown> aTarget, IInterceptorSink* aSink,
                         REFIID aInitialIid, void** aOutInterface);
 
+  /**
+   * Disconnect all remote clients for a given target.
+   * Because Interceptors disable COM garbage collection to improve
+   * performance, they never receive Release calls from remote clients. If
+   * the object can be shut down while clients still hold a reference, this
+   * function can be used to force COM to disconnect all remote connections
+   * (using CoDisconnectObject) and thus release the associated references to
+   * the Interceptor, its target and any objects associated with the
+   * HandlerProvider.
+   * Note that the specified target must be the same IUnknown pointer used to
+   * create the Interceptor. Where there is multiple inheritance, querying for
+   * IID_IUnknown and calling this function with that pointer alone will not
+   * disconnect remotes for all interfaces. If you expect that the same object
+   * may be fetched with different initial interfaces, you should call this
+   * function once for each possible IUnknown pointer.
+   * @return S_OK if there was an Interceptor for the given target,
+   *         S_FALSE if there was not.
+   */
+  static HRESULT DisconnectRemotesForTarget(IUnknown* aTarget);
+
   // IUnknown
   STDMETHODIMP QueryInterface(REFIID riid, void** ppv) override;
   STDMETHODIMP_(ULONG) AddRef() override;
   STDMETHODIMP_(ULONG) Release() override;
 
   // IStdMarshalInfo
   STDMETHODIMP GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
                                   CLSID* aHandlerClsid) override;
--- a/ipc/mscom/MainThreadHandoff.cpp
+++ b/ipc/mscom/MainThreadHandoff.cpp
@@ -585,16 +585,26 @@ MainThreadHandoff::MarshalAs(REFIID aIid
 {
   if (!mHandlerProvider) {
     return aIid;
   }
   return mHandlerProvider->MarshalAs(aIid);
 }
 
 HRESULT
+MainThreadHandoff::DisconnectHandlerRemotes()
+{
+  if (!mHandlerProvider) {
+    return E_NOTIMPL;
+  }
+
+  return mHandlerProvider->DisconnectHandlerRemotes();
+}
+
+HRESULT
 MainThreadHandoff::OnWalkInterface(REFIID aIid, PVOID* aInterface,
                                    BOOL aIsInParam, BOOL aIsOutParam)
 {
   MOZ_ASSERT(aInterface && aIsOutParam);
   if (!aInterface || !aIsOutParam) {
     return E_UNEXPECTED;
   }
 
--- a/ipc/mscom/MainThreadHandoff.h
+++ b/ipc/mscom/MainThreadHandoff.h
@@ -61,16 +61,17 @@ public:
   // IInterceptorSink
   STDMETHODIMP SetInterceptor(IWeakReference* aInterceptor) override;
   STDMETHODIMP GetHandler(NotNull<CLSID*> aHandlerClsid) override;
   STDMETHODIMP GetHandlerPayloadSize(NotNull<IInterceptor*> aInterceptor,
                                      NotNull<DWORD*> aOutPayloadSize) override;
   STDMETHODIMP WriteHandlerPayload(NotNull<IInterceptor*> aInterceptor,
                                    NotNull<IStream*> aStream) override;
   STDMETHODIMP_(REFIID) MarshalAs(REFIID aIid) override;
+  STDMETHODIMP DisconnectHandlerRemotes() override;
 
   // ICallFrameWalker
   STDMETHODIMP OnWalkInterface(REFIID aIid, PVOID* aInterface, BOOL aIsInParam,
                                BOOL aIsOutParam) override;
 
 private:
   explicit MainThreadHandoff(IHandlerProvider* aHandlerProvider);
   ~MainThreadHandoff();
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -4293,30 +4293,17 @@ PresShell::CharacterDataChanged(nsIDocum
                                 nsIContent* aContent,
                                 const CharacterDataChangeInfo& aInfo)
 {
   NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
   NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
 
   nsAutoCauseReflowNotifier crNotifier(this);
 
-  // Call this here so it only happens for real content mutations and
-  // not cases when the frame constructor calls its own methods to force
-  // frame reconstruction.
-  nsIContent *container = aContent->GetParent();
-  uint32_t selectorFlags =
-    container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
-  if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
-    Element* element = container->AsElement();
-    if (aInfo.mAppend && !aContent->GetNextSibling())
-      mPresContext->RestyleManager()->RestyleForAppend(element, aContent);
-    else
-      mPresContext->RestyleManager()->RestyleForInsertOrChange(element, aContent);
-  }
-
+  mPresContext->RestyleManager()->CharacterDataChanged(aContent, aInfo);
   mFrameConstructor->CharacterDataChanged(aContent, aInfo);
   VERIFY_STYLE_TREE;
 }
 
 void
 PresShell::ContentStateChanged(nsIDocument* aDocument,
                                nsIContent* aContent,
                                EventStates aStateMask)
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -16,16 +16,17 @@
 #include "nsIFrame.h"
 #include "nsIFrameInlines.h"
 #include "nsIPresShellInlines.h"
 #include "nsPlaceholderFrame.h"
 #include "nsStyleChangeList.h"
 #include "nsStyleUtil.h"
 #include "StickyScrollContainer.h"
 #include "mozilla/EffectSet.h"
+#include "mozilla/IntegerRange.h"
 #include "mozilla/ViewportFrame.h"
 #include "SVGObserverUtils.h"
 #include "SVGTextFrame.h"
 #include "ActiveLayerTracker.h"
 #include "nsSVGIntegrationUtils.h"
 
 using namespace mozilla::dom;
 
@@ -48,37 +49,16 @@ void
 RestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild)
 {
   RestyleForInsertOrChange(aContainer, aChild);
 }
 
 void
 RestyleManager::ContentAppended(nsIContent* aContainer, nsIContent* aFirstNewContent)
 {
-  RestyleForAppend(aContainer, aFirstNewContent);
-}
-
-void
-RestyleManager::RestyleForEmptyChange(Element* aContainer)
-{
-  // In some cases (:empty + E, :empty ~ E), a change in the content of
-  // an element requires restyling its parent's siblings.
-  nsRestyleHint hint = eRestyle_Subtree;
-  nsIContent* grandparent = aContainer->GetParent();
-  if (grandparent &&
-      (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
-    hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
-  }
-  PostRestyleEvent(aContainer, hint, nsChangeHint(0));
-}
-
-void
-RestyleManager::RestyleForAppend(nsIContent* aContainer,
-                                 nsIContent* aFirstNewContent)
-{
   // The container cannot be a document, but might be a ShadowRoot.
   if (!aContainer->IsElement()) {
     return;
   }
   Element* container = aContainer->AsElement();
 
 #ifdef DEBUG
   {
@@ -129,16 +109,72 @@ RestyleManager::RestyleForAppend(nsICont
       if (cur->IsElement()) {
         PostRestyleEvent(cur->AsElement(), eRestyle_Subtree, nsChangeHint(0));
         break;
       }
     }
   }
 }
 
+void
+RestyleManager::RestyleForEmptyChange(Element* aContainer)
+{
+  // In some cases (:empty + E, :empty ~ E), a change in the content of
+  // an element requires restyling its parent's siblings.
+  nsRestyleHint hint = eRestyle_Subtree;
+  nsIContent* grandparent = aContainer->GetParent();
+  if (grandparent &&
+      (grandparent->GetFlags() & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS)) {
+    hint = nsRestyleHint(hint | eRestyle_LaterSiblings);
+  }
+  PostRestyleEvent(aContainer, hint, nsChangeHint(0));
+}
+
+void
+RestyleManager::MaybeRestyleForEdgeChildChange(Element* aContainer,
+                                               nsIContent* aChangedChild)
+{
+  MOZ_ASSERT(aContainer->GetFlags() & NODE_HAS_EDGE_CHILD_SELECTOR);
+  MOZ_ASSERT(aChangedChild->GetParent() == aContainer);
+  // restyle the previously-first element child if it is after this node
+  bool passedChild = false;
+  for (nsIContent* content = aContainer->GetFirstChild();
+       content;
+       content = content->GetNextSibling()) {
+    if (content == aChangedChild) {
+      passedChild = true;
+      continue;
+    }
+    if (content->IsElement()) {
+      if (passedChild) {
+        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+                         nsChangeHint(0));
+      }
+      break;
+    }
+  }
+  // restyle the previously-last element child if it is before this node
+  passedChild = false;
+  for (nsIContent* content = aContainer->GetLastChild();
+       content;
+       content = content->GetPreviousSibling()) {
+    if (content == aChangedChild) {
+      passedChild = true;
+      continue;
+    }
+    if (content->IsElement()) {
+      if (passedChild) {
+        PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
+                         nsChangeHint(0));
+      }
+      break;
+    }
+  }
+}
+
 // Needed since we can't use PostRestyleEvent on non-elements (with
 // eRestyle_LaterSiblings or nsRestyleHint(eRestyle_Subtree |
 // eRestyle_LaterSiblings) as appropriate).
 static void
 RestyleSiblingsStartingWith(RestyleManager* aRestyleManager,
                             nsIContent* aStartingSibling /* may be null */)
 {
   for (nsIContent* sibling = aStartingSibling; sibling;
@@ -148,16 +184,142 @@ RestyleSiblingsStartingWith(RestyleManag
         PostRestyleEvent(sibling->AsElement(),
                          nsRestyleHint(eRestyle_Subtree | eRestyle_LaterSiblings),
                          nsChangeHint(0));
       break;
     }
   }
 }
 
+template<typename CharT>
+bool
+WhitespaceOnly(const CharT* aBuffer, size_t aUpTo)
+{
+  for (auto index : IntegerRange(aUpTo)) {
+    if (!dom::IsSpaceCharacter(aBuffer[index])) {
+      return false;
+    }
+  }
+  return true;
+}
+
+template<typename CharT>
+bool
+WhitespaceOnlyChangedOnAppend(const CharT* aBuffer,
+                              size_t aOldLength,
+                              size_t aNewLength)
+{
+  MOZ_ASSERT(aOldLength < aNewLength);
+  if (!WhitespaceOnly(aBuffer, aOldLength)) {
+    // The old text was already not whitespace-only.
+    return false;
+  }
+
+  return !WhitespaceOnly(aBuffer + aOldLength, aNewLength - aOldLength);
+}
+
+static bool
+HasAnySignificantSibling(Element* aContainer, nsIContent* aChild)
+{
+  MOZ_ASSERT(aChild->GetParent() == aContainer);
+  for (nsIContent* child = aContainer->GetFirstChild();
+       child;
+       child = child->GetNextSibling()) {
+    if (child == aChild) {
+      continue;
+    }
+    // We don't know whether we're testing :empty or :-moz-only-whitespace,
+    // so be conservative and assume :-moz-only-whitespace (i.e., make
+    // IsSignificantChild less likely to be true, and thus make us more
+    // likely to restyle).
+    if (nsStyleUtil::IsSignificantChild(child, true, false)) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void
+RestyleManager::CharacterDataChanged(nsIContent* aContent,
+                                     const CharacterDataChangeInfo& aInfo)
+{
+  nsINode* parent = aContent->GetParentNode();
+  MOZ_ASSERT(parent, "How were we notified of a stray node?");
+
+  uint32_t slowSelectorFlags = parent->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
+  if (!(slowSelectorFlags & (NODE_HAS_EMPTY_SELECTOR |
+                             NODE_HAS_EDGE_CHILD_SELECTOR))) {
+    // Nothing to do, no other slow selector can change as a result of this.
+    return;
+  }
+
+  if (!aContent->IsNodeOfType(nsINode::eTEXT)) {
+    // Doesn't matter to styling (could be a processing instruction or a
+    // comment), it can't change whether any selectors match or don't.
+    return;
+  }
+
+
+  if (MOZ_UNLIKELY(!parent->IsElement())) {
+    MOZ_ASSERT(parent->IsShadowRoot());
+    return;
+  }
+
+  if (MOZ_UNLIKELY(aContent->IsRootOfAnonymousSubtree())) {
+    // This is an anonymous node and thus isn't in child lists, so isn't taken
+    // into account for selector matching the relevant selectors here.
+    return;
+  }
+
+  // Handle appends specially since they're common and we can know both the old
+  // and the new text exactly.
+  //
+  // TODO(emilio): This could be made much more general if :-moz-only-whitespace
+  // / :-moz-first-node and :-moz-last-node didn't exist. In that case we only
+  // need to know whether we went from empty to non-empty, and that's trivial to
+  // know, with CharacterDataChangeInfo...
+  if (!aInfo.mAppend) {
+    // FIXME(emilio): This restyles unnecessarily if the text node is the only
+    // child of the parent element. Fortunately, it's uncommon to have such
+    // nodes and this not being an append.
+    //
+    // See the testcase in bug 1427625 for a test-case that triggers this.
+    RestyleForInsertOrChange(parent->AsElement(), aContent);
+    return;
+  }
+
+  const nsTextFragment* text = aContent->GetText();
+
+  const size_t oldLength = aInfo.mChangeStart;
+  const size_t newLength = text->GetLength();
+
+  const bool emptyChanged = !oldLength && newLength;
+
+  const bool whitespaceOnlyChanged = text->Is2b()
+    ? WhitespaceOnlyChangedOnAppend(text->Get2b(), oldLength, newLength)
+    : WhitespaceOnlyChangedOnAppend(text->Get1b(), oldLength, newLength);
+
+  if (!emptyChanged && !whitespaceOnlyChanged) {
+    return;
+  }
+
+  if (slowSelectorFlags & NODE_HAS_EMPTY_SELECTOR) {
+    if (!HasAnySignificantSibling(parent->AsElement(), aContent)) {
+      // We used to be empty, restyle the parent.
+      RestyleForEmptyChange(parent->AsElement());
+      return;
+    }
+  }
+
+  if (slowSelectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
+    MaybeRestyleForEdgeChildChange(parent->AsElement(), aContent);
+  }
+}
+
 // Restyling for a ContentInserted or CharacterDataChanged notification.
 // This could be used for ContentRemoved as well if we got the
 // notification before the removal happened (and sometimes
 // CharacterDataChanged is more like a removal than an addition).
 // The comments are written and variables are named in terms of it being
 // a ContentInserted notification.
 void
 RestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
@@ -171,33 +333,23 @@ RestyleManager::RestyleForInsertOrChange
 
   NS_ASSERTION(!aChild->IsRootOfAnonymousSubtree(),
                "anonymous nodes should not be in child lists");
   uint32_t selectorFlags = container->GetFlags() & NODE_ALL_SELECTOR_FLAGS;
   if (selectorFlags == 0)
     return;
 
   if (selectorFlags & NODE_HAS_EMPTY_SELECTOR) {
-    // see whether we need to restyle the container
-    bool wasEmpty = true; // :empty or :-moz-only-whitespace
-    for (nsIContent* child = container->GetFirstChild();
-         child;
-         child = child->GetNextSibling()) {
-      if (child == aChild)
-        continue;
-      // We don't know whether we're testing :empty or :-moz-only-whitespace,
-      // so be conservative and assume :-moz-only-whitespace (i.e., make
-      // IsSignificantChild less likely to be true, and thus make us more
-      // likely to restyle).
-      if (nsStyleUtil::IsSignificantChild(child, true, false)) {
-        wasEmpty = false;
-        break;
-      }
-    }
+    // See whether we need to restyle the container due to :empty /
+    // :-moz-only-whitespace.
+    const bool wasEmpty = !HasAnySignificantSibling(container, aChild);
     if (wasEmpty) {
+      // FIXME(emilio): When coming from CharacterDataChanged this can restyle
+      // unnecessarily. Also can restyle unnecessarily if aChild is not
+      // significant anyway, though that's more unlikely.
       RestyleForEmptyChange(container);
       return;
     }
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR) {
     PostRestyleEvent(container, eRestyle_Subtree, nsChangeHint(0));
     // Restyling the container is the most we can do here, so we're done.
@@ -205,50 +357,17 @@ RestyleManager::RestyleForInsertOrChange
   }
 
   if (selectorFlags & NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
     // Restyle all later siblings.
     RestyleSiblingsStartingWith(this, aChild->GetNextSibling());
   }
 
   if (selectorFlags & NODE_HAS_EDGE_CHILD_SELECTOR) {
-    // restyle the previously-first element child if it is after this node
-    bool passedChild = false;
-    for (nsIContent* content = container->GetFirstChild();
-         content;
-         content = content->GetNextSibling()) {
-      if (content == aChild) {
-        passedChild = true;
-        continue;
-      }
-      if (content->IsElement()) {
-        if (passedChild) {
-          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
-                           nsChangeHint(0));
-        }
-        break;
-      }
-    }
-    // restyle the previously-last element child if it is before this node
-    passedChild = false;
-    for (nsIContent* content = container->GetLastChild();
-         content;
-         content = content->GetPreviousSibling()) {
-      if (content == aChild) {
-        passedChild = true;
-        continue;
-      }
-      if (content->IsElement()) {
-        if (passedChild) {
-          PostRestyleEvent(content->AsElement(), eRestyle_Subtree,
-                           nsChangeHint(0));
-        }
-        break;
-      }
-    }
+    MaybeRestyleForEdgeChildChange(container, aChild);
   }
 }
 
 void
 RestyleManager::ContentRemoved(nsINode* aContainer,
                                nsIContent* aOldChild,
                                nsIContent* aFollowingSibling)
 {
--- a/layout/base/RestyleManager.h
+++ b/layout/base/RestyleManager.h
@@ -154,24 +154,23 @@ public:
   // following sibling in addition to the old child.  |aContainer| must be
   // non-null; when the container is null, no work is needed.  aFollowingSibling
   // is the sibling that used to come after aOldChild before the removal.
   void ContentRemoved(nsINode* aContainer,
                       nsIContent* aOldChild,
                       nsIContent* aFollowingSibling);
 
   // Restyling for a ContentInserted (notification after insertion) or
-  // for a CharacterDataChanged.  |aContainer| must be non-null; when
+  // for some CharacterDataChanged.  |aContainer| must be non-null; when
   // the container is null, no work is needed.
   void RestyleForInsertOrChange(nsINode* aContainer, nsIContent* aChild);
 
-  // Restyling for a ContentAppended (notification after insertion) or
-  // for a CharacterDataChanged.  |aContainer| must be non-null; when
-  // the container is null, no work is needed.
-  void RestyleForAppend(nsIContent* aContainer, nsIContent* aFirstNewContent);
+  // Restyle for a CharacterDataChanged notification. In practice this can only
+  // affect :empty / :-moz-only-whitespace / :-moz-first-node / :-moz-last-node.
+  void CharacterDataChanged(nsIContent*, const CharacterDataChangeInfo&);
 
   MOZ_DECL_STYLO_METHODS(GeckoRestyleManager, ServoRestyleManager)
 
   inline void PostRestyleEvent(dom::Element* aElement,
                                nsRestyleHint aRestyleHint,
                                nsChangeHint aMinChangeHint);
   inline void RebuildAllStyleData(nsChangeHint aExtraHint,
                                   nsRestyleHint aRestyleHint);
@@ -218,16 +217,17 @@ protected:
 
   virtual ~RestyleManager()
   {
     MOZ_ASSERT(!mAnimationsWithDestroyedFrame,
                "leaving dangling pointers from AnimationsWithDestroyedFrame");
   }
 
   void RestyleForEmptyChange(Element* aContainer);
+  void MaybeRestyleForEdgeChildChange(Element* aContainer, nsIContent* aChangedChild);
 
   void ContentStateChangedInternal(Element* aElement,
                                    EventStates aStateMask,
                                    nsChangeHint* aOutChangeHint);
 
   bool IsDisconnected() { return mPresContext == nullptr; }
 
   void IncrementHoverGeneration() {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -6832,17 +6832,17 @@ nsCSSFrameConstructor::IsValidSibling(ns
 }
 
 // FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
 // bit (no need to pass aTargetContent or aTargetContentDisplay, and the
 // adjust() calls can be responsibility of the caller).
 template<nsCSSFrameConstructor::SiblingDirection aDirection>
 nsIFrame*
 nsCSSFrameConstructor::FindSiblingInternal(
-  FlattenedChildIterator aIter,
+  FlattenedChildIterator& aIter,
   nsIContent* aTargetContent,
   StyleDisplay& aTargetContentDisplay)
 {
   auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
     return AdjustSiblingFrame(
       aPotentialSiblingFrame, aTargetContent, aTargetContentDisplay,
       aDirection);
   };
@@ -6944,18 +6944,19 @@ nsCSSFrameConstructor::FindNextSibling(c
 }
 
 template<nsCSSFrameConstructor::SiblingDirection aDirection>
 nsIFrame*
 nsCSSFrameConstructor::FindSibling(const FlattenedChildIterator& aIter,
                                    StyleDisplay& aTargetContentDisplay)
 {
   nsIContent* targetContent = aIter.Get();
-  nsIFrame* sibling =
-    FindSiblingInternal<aDirection>(aIter, targetContent, aTargetContentDisplay);
+  FlattenedChildIterator siblingIter = aIter;
+  nsIFrame* sibling = FindSiblingInternal<aDirection>(
+    siblingIter, targetContent, aTargetContentDisplay);
   if (sibling) {
     return sibling;
   }
 
   // Our siblings (if any) do not have a frame to guide us. The frame for the
   // target content should be inserted whereever a frame for the container would
   // be inserted. This is needed when inserting into display: contents nodes.
   const nsIContent* current = aIter.Parent();
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -2150,19 +2150,21 @@ private:
    *          points to if already known, UNSET_DISPLAY otherwise. It will be
    *          filled in if needed.
    */
   template<SiblingDirection>
   nsIFrame* FindSibling(const mozilla::dom::FlattenedChildIterator& aIter,
                         mozilla::StyleDisplay& aTargetContentDisplay);
 
   // Helper for the implementation of FindSibling.
+  //
+  // Beware that this function does mutate the iterator.
   template<SiblingDirection>
   nsIFrame* FindSiblingInternal(
-    mozilla::dom::FlattenedChildIterator,
+    mozilla::dom::FlattenedChildIterator&,
     nsIContent* aTargetContent,
     mozilla::StyleDisplay& aTargetContentDisplay);
 
   // An alias of FindSibling<SiblingDirection::Forward>.
   nsIFrame* FindNextSibling(const mozilla::dom::FlattenedChildIterator& aIter,
                             mozilla::StyleDisplay& aTargetContentDisplay);
   // An alias of FindSibling<SiblingDirection::Backwards>.
   nsIFrame* FindPreviousSibling(const mozilla::dom::FlattenedChildIterator& aIter,
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -249,31 +249,25 @@ PhysicalCoordFromFlexRelativeCoord(nscoo
                                    nscoord aContainerSize,
                                    AxisOrientationType aAxis) {
   if (AxisGrowsInPositiveDirection(aAxis)) {
     return aFlexRelativeCoord;
   }
   return aContainerSize - aFlexRelativeCoord;
 }
 
-// Helper-macro to let us pick one of two expressions to evaluate
-// (a width expression vs. a height expression), to get a main-axis or
-// cross-axis component.
-// For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent
-// and GetCrossComponent are cleaner; but in cases where we simply have
-// two separate expressions for width and height (which may be expensive to
-// evaluate), these macros will ensure that only the expression for the correct
-// axis gets evaluated.
-#define GET_MAIN_COMPONENT(axisTracker_, width_, height_)  \
-  (axisTracker_).IsMainAxisHorizontal() ? (width_) : (height_)
-
-#define GET_CROSS_COMPONENT(axisTracker_, width_, height_)  \
-  (axisTracker_).IsCrossAxisHorizontal() ? (width_) : (height_)
-
-// Logical versions of helper-macros above:
+// Helper-macros to let us pick one of two expressions to evaluate
+// (an inline-axis expression vs. a block-axis expression), to get a
+// main-axis or cross-axis component.
+// For code that has e.g. a LogicalSize object, the methods
+// FlexboxAxisTracker::GetMainComponent and GetCrossComponent are cleaner
+// than these macros. But in cases where we simply have two separate
+// expressions for ISize and BSize (which may be expensive to evaluate),
+// these macros can be used to ensure that only the needed expression is
+// evaluated.
 #define GET_MAIN_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_)  \
   wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
     (axisTracker_).IsRowOriented() ? (isize_) : (bsize_)
 
 #define GET_CROSS_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_)  \
   wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
     (axisTracker_).IsRowOriented() ? (bsize_) : (isize_)
 
@@ -295,26 +289,16 @@ public:
   FlexboxAxisTracker(const nsFlexContainerFrame* aFlexContainer,
                      const WritingMode& aWM,
                      AxisTrackerFlags aFlags = eNoFlags);
 
   // Accessors:
   // XXXdholbert [BEGIN DEPRECATED]
   AxisOrientationType GetMainAxis() const  { return mMainAxis;  }
   AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
-
-  bool IsMainAxisHorizontal() const {
-    // If we're row-oriented, and our writing mode is NOT vertical,
-    // or we're column-oriented and our writing mode IS vertical,
-    // then our main axis is horizontal. This handles all cases:
-    return mIsRowOriented != mWM.IsVertical();
-  }
-  bool IsCrossAxisHorizontal() const {
-    return !IsMainAxisHorizontal();
-  }
   // XXXdholbert [END DEPRECATED]
 
   // Returns the flex container's writing mode.
   WritingMode GetWritingMode() const { return mWM; }
 
   // Returns true if our main axis is in the reverse direction of our
   // writing mode's corresponding axis. (From 'flex-direction: *-reverse')
   bool IsMainAxisReversed() const {
@@ -324,50 +308,44 @@ public:
   // writing mode's corresponding axis. (From 'flex-wrap: *-reverse')
   bool IsCrossAxisReversed() const {
     return mIsCrossAxisReversed;
   }
 
   bool IsRowOriented() const { return mIsRowOriented; }
   bool IsColumnOriented() const { return !mIsRowOriented; }
 
-  nscoord GetMainComponent(const nsSize& aSize) const {
-    return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height);
+  // aSize is expected to match the flex container's WritingMode.
+  nscoord GetMainComponent(const LogicalSize& aSize) const {
+    return IsRowOriented() ? aSize.ISize(mWM) : aSize.BSize(mWM);
   }
   int32_t GetMainComponent(const LayoutDeviceIntSize& aIntSize) const {
-    return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height);
-  }
-
-  nscoord GetCrossComponent(const nsSize& aSize) const {
-    return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height);
+    return IsMainAxisHorizontal() ? aIntSize.width : aIntSize.height;
+  }
+
+  // aSize is expected to match the flex container's WritingMode.
+  nscoord GetCrossComponent(const LogicalSize& aSize) const {
+    return IsRowOriented() ? aSize.BSize(mWM) : aSize.ISize(mWM);
   }
   int32_t GetCrossComponent(const LayoutDeviceIntSize& aIntSize) const {
-    return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height);
-  }
-
-  nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const {
-    return IsMainAxisHorizontal() ?
-      aMargin.LeftRight() :
-      aMargin.TopBottom();
-  }
-  nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const {
-    return IsCrossAxisHorizontal() ?
-      aMargin.LeftRight() :
-      aMargin.TopBottom();
-  }
-
-  // Returns aFrame's computed value for 'height' or 'width' -- whichever is in
-  // the cross-axis. (NOTE: This is cross-axis-specific for now. If we need a
-  // main-axis version as well, we could generalize or clone this function.)
-  const nsStyleCoord& ComputedCrossSize(const nsIFrame* aFrame) const {
-    const nsStylePosition* stylePos = aFrame->StylePosition();
-
-    return IsCrossAxisHorizontal() ?
-      stylePos->mWidth :
-      stylePos->mHeight;
+    return IsMainAxisHorizontal() ? aIntSize.height : aIntSize.width;
+  }
+
+  // NOTE: aMargin is expected to use the flex container's WritingMode.
+  nscoord GetMarginSizeInMainAxis(const LogicalMargin& aMargin) const {
+    // If we're row-oriented, our main axis is the inline axis.
+    return IsRowOriented()
+      ? aMargin.IStartEnd(mWM)
+      : aMargin.BStartEnd(mWM);
+  }
+  nscoord GetMarginSizeInCrossAxis(const LogicalMargin& aMargin) const {
+    // If we're row-oriented, our cross axis is the block axis.
+    return IsRowOriented()
+      ? aMargin.BStartEnd(mWM)
+      : aMargin.IStartEnd(mWM);
   }
 
   /**
    * Converts a "flex-relative" point (a main-axis & cross-axis coordinate)
    * into a LogicalPoint, using the flex container's writing mode.
    *
    *  @arg aMainCoord  The main-axis coordinate -- i.e an offset from the
    *                   main-start edge of the flex container's content box.
@@ -419,16 +397,25 @@ public:
   }
 
 private:
   // Delete copy-constructor & reassignment operator, to prevent accidental
   // (unnecessary) copying.
   FlexboxAxisTracker(const FlexboxAxisTracker&) = delete;
   FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete;
 
+  // Private because callers shouldn't need to care about physical axes
+  // (but we do internally, to provide one API).
+  bool IsMainAxisHorizontal() const {
+    // If we're row-oriented, and our writing mode is NOT vertical,
+    // or we're column-oriented and our writing mode IS vertical,
+    // then our main axis is horizontal. This handles all cases:
+    return mIsRowOriented != mWM.IsVertical();
+  }
+
   // Helpers for constructor which determine the orientation of our axes, based
   // on legacy box properties (-webkit-box-orient, -webkit-box-direction) or
   // modern flexbox properties (flex-direction, flex-wrap) depending on whether
   // the flex container is a "legacy box" (as determined by IsLegacyBox).
   void InitAxesFromLegacyProps(const nsFlexContainerFrame* aFlexContainer);
   void InitAxesFromModernProps(const nsFlexContainerFrame* aFlexContainer);
 
   // XXXdholbert [BEGIN DEPRECATED]
@@ -548,38 +535,44 @@ public:
 
   bool HadMinViolation() const     { return mHadMinViolation; }
   bool HadMaxViolation() const     { return mHadMaxViolation; }
 
   // Indicates whether this item received a preliminary "measuring" reflow
   // before its actual reflow.
   bool HadMeasuringReflow() const  { return mHadMeasuringReflow; }
 
+  // Indicates whether this item's computed cross-size property is 'auto'.
+  bool IsCrossSizeAuto() const;
+
   // Indicates whether this item's cross-size has been stretched (from having
   // "align-self: stretch" with an auto cross-size and no auto margins in the
   // cross axis).
   bool IsStretched() const         { return mIsStretched; }
 
   // Indicates whether we need to resolve an 'auto' value for the main-axis
   // min-[width|height] property.
   bool NeedsMinSizeAutoResolution() const
     { return mNeedsMinSizeAutoResolution; }
 
   // Indicates whether this item is a "strut" left behind by an element with
   // visibility:collapse.
   bool IsStrut() const             { return mIsStrut; }
 
-  // Returns true if this item's inline axis is parallel (or antiparallel)
-  // to the container's main axis. Otherwise (i.e. if this item's inline axis
-  // is orthogonal to the container's main axis), this function returns false.
-  bool IsInlineAxisMainAxis() const { return mIsInlineAxisMainAxis; }
-
-  // Same as above, but for cross axis. Equivalent to !IsInlineAxisMainAxis().
-  // This just exists for convenience/readability at callsites.
+  // IsInlineAxisMainAxis() returns true if this item's inline axis is parallel
+  // (or antiparallel) to the container's main axis. Otherwise (i.e. if this
+  // item's inline axis is orthogonal to the container's main axis), this
+  // function returns false. The next 3 methods are all other ways of asking
+  // the same question, and only exist for readability at callsites (depending
+  // on which axes those callsites are reasoning about).
+  bool IsInlineAxisMainAxis() const  { return mIsInlineAxisMainAxis;  }
   bool IsInlineAxisCrossAxis() const { return !mIsInlineAxisMainAxis; }
+  bool IsBlockAxisMainAxis() const   { return !mIsInlineAxisMainAxis; }
+  bool IsBlockAxisCrossAxis() const  { return mIsInlineAxisMainAxis;  }
+
 
   WritingMode GetWritingMode() const { return mWM; }
   uint8_t GetAlignSelf() const     { return mAlignSelf; }
 
   // Returns the flex factor (flex-grow or flex-shrink), depending on
   // 'aIsUsingFlexGrow'.
   //
   // Asserts fatally if called on a frozen item (since frozen items are not
@@ -617,18 +610,20 @@ public:
       // regardless of mFlexShrink, we should just return 0.
       // (This is really a special-case for when mFlexShrink is infinity, to
       // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
       return 0.0f;
     }
     return mFlexShrink * mFlexBaseSize;
   }
 
-  const nsSize& IntrinsicRatio() const { return mIntrinsicRatio; }
-  bool HasIntrinsicRatio() const { return mIntrinsicRatio != nsSize(); }
+  // Returns a LogicalSize representing the flex item's logical intrinsic ratio
+  // (ISize:BSize), as expressed in the *flex container's* writing mode.
+  const LogicalSize& IntrinsicRatio() const { return mIntrinsicRatio; }
+  bool HasIntrinsicRatio() const { return !mIntrinsicRatio.IsAllZero(); }
 
   // Getters for margin:
   // ===================
   const nsMargin& GetMargin() const { return mMargin; }
 
   // Returns the margin component for a given mozilla::Side
   nscoord GetMarginComponentForSide(mozilla::Side aSide) const
   { return mMargin.Side(aSide); }
@@ -813,17 +808,17 @@ protected:
   // Helper called by the constructor, to set mNeedsMinSizeAutoResolution:
   void CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
                            const FlexboxAxisTracker& aAxisTracker);
 
   // Values that we already know in constructor (and are hence mostly 'const'):
   nsIFrame* const mFrame; // The flex item's frame.
   const float mFlexGrow;
   const float mFlexShrink;
-  const nsSize mIntrinsicRatio;
+  const LogicalSize mIntrinsicRatio;
   const nsMargin mBorderPadding;
   nsMargin mMargin; // non-const because we need to resolve auto margins
 
   // These are non-const so that we can lazily update them with the item's
   // intrinsic size (obtained via a "measuring" reflow), when necessary.
   // (e.g. for "flex-basis:auto;height:auto" & "min-height:auto")
   nscoord mFlexBaseSize;
   nscoord mMainMinSize;
@@ -1197,23 +1192,16 @@ nsFlexContainerFrame::CSSAlignmentForAbs
     alignment = NS_STYLE_ALIGN_START;
   } else if (alignment == NS_STYLE_ALIGN_LAST_BASELINE) {
     alignment = NS_STYLE_ALIGN_END;
   }
 
   return alignment;
 }
 
-bool
-nsFlexContainerFrame::IsHorizontal()
-{
-  const FlexboxAxisTracker axisTracker(this, GetWritingMode());
-  return axisTracker.IsMainAxisHorizontal();
-}
-
 UniquePtr<FlexItem>
 nsFlexContainerFrame::GenerateFlexItemForChild(
   nsPresContext* aPresContext,
   nsIFrame*      aChildFrame,
   const ReflowInput& aParentReflowInput,
   const FlexboxAxisTracker& aAxisTracker)
 {
   // Create temporary reflow state just for sizing -- to get hypothetical
@@ -1284,23 +1272,26 @@ nsFlexContainerFrame::GenerateFlexItemFo
 
     nscoord widgetMainMinSize =
       aPresContext->DevPixelsToAppUnits(
         aAxisTracker.GetMainComponent(widgetMinSize));
     nscoord widgetCrossMinSize =
       aPresContext->DevPixelsToAppUnits(
         aAxisTracker.GetCrossComponent(widgetMinSize));
 
-    // GMWS() returns border-box. We need content-box, so subtract
-    // borderPadding (but don't let that push our min sizes below 0).
-    nsMargin& bp = childRI.ComputedPhysicalBorderPadding();
-    widgetMainMinSize = std::max(widgetMainMinSize -
-                                 aAxisTracker.GetMarginSizeInMainAxis(bp), 0);
-    widgetCrossMinSize = std::max(widgetCrossMinSize -
-                                  aAxisTracker.GetMarginSizeInCrossAxis(bp), 0);
+    // GetMinimumWidgetSize() returns border-box. We need content-box, so
+    // subtract borderPadding.
+    const LogicalMargin bpInChildWM = childRI.ComputedLogicalBorderPadding();
+    const LogicalMargin bpInFlexWM =
+      bpInChildWM.ConvertTo(aAxisTracker.GetWritingMode(), childWM);
+    widgetMainMinSize -= aAxisTracker.GetMarginSizeInMainAxis(bpInFlexWM);
+    widgetCrossMinSize -= aAxisTracker.GetMarginSizeInCrossAxis(bpInFlexWM);
+    // ... (but don't let that push these min sizes below 0).
+    widgetMainMinSize = std::max(0, widgetMainMinSize);
+    widgetCrossMinSize = std::max(0, widgetCrossMinSize);
 
     if (!canOverride) {
       // Fixed-size widget: freeze our main-size at the widget's mandated size.
       // (Set min and max main-sizes to that size, too, to keep us from
       // clamping to any other size later on.)
       flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize;
       tentativeCrossSize = crossMinSize = crossMaxSize = widgetCrossMinSize;
       isFixedSizeWidget = true;
@@ -1403,32 +1394,29 @@ CrossSizeToUseWithRatio(const FlexItem& 
                                        aItemReflowInput.ComputedMinBSize());
   }
 
   // Indefinite cross-size.
   return NS_AUTOHEIGHT;
 }
 
 // Convenience function; returns a main-size, given a cross-size and an
-// intrinsic ratio. The intrinsic ratio must not have 0 in its cross-axis
-// component (or else we'll divide by 0).
+// intrinsic ratio. The caller is responsible for ensuring that the passed-in
+// intrinsic ratio must not have 0 in its cross-axis component (or else we'll
+// divide by 0).
 static nscoord
 MainSizeFromAspectRatio(nscoord aCrossSize,
-                        const nsSize& aIntrinsicRatio,
+                        const LogicalSize& aIntrinsicRatio,
                         const FlexboxAxisTracker& aAxisTracker)
 {
   MOZ_ASSERT(aAxisTracker.GetCrossComponent(aIntrinsicRatio) != 0,
              "Invalid ratio; will divide by 0! Caller should've checked...");
-
-  if (aAxisTracker.IsCrossAxisHorizontal()) {
-    // cross axis horiz --> aCrossSize is a width. Converting to height.
-    return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.height, aIntrinsicRatio.width);
-  }
-  // cross axis vert --> aCrossSize is a height. Converting to width.
-  return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.width, aIntrinsicRatio.height);
+  return NSCoordMulDiv(aCrossSize,
+                       aAxisTracker.GetMainComponent(aIntrinsicRatio),
+                       aAxisTracker.GetCrossComponent(aIntrinsicRatio));
 }
 
 // Partially resolves "min-[width|height]:auto" and returns the resulting value.
 // By "partially", I mean we don't consider the min-content size (but we do
 // consider flex-basis, main max-size, and the intrinsic aspect ratio).
 // The caller is responsible for computing & considering the min-content size
 // in combination with the partially-resolved value that this function returns.
 //
@@ -1800,17 +1788,18 @@ FlexItem::FlexItem(ReflowInput& aFlexIte
                    float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize,
                    nscoord aMainMinSize,  nscoord aMainMaxSize,
                    nscoord aTentativeCrossSize,
                    nscoord aCrossMinSize, nscoord aCrossMaxSize,
                    const FlexboxAxisTracker& aAxisTracker)
   : mFrame(aFlexItemReflowInput.mFrame),
     mFlexGrow(aFlexGrow),
     mFlexShrink(aFlexShrink),
-    mIntrinsicRatio(mFrame->GetIntrinsicRatio()),
+    // We store the intrinsic ratio in the *flex container's* WM:
+    mIntrinsicRatio(aAxisTracker.GetWritingMode(), mFrame->GetIntrinsicRatio()),
     mBorderPadding(aFlexItemReflowInput.ComputedPhysicalBorderPadding()),
     mMargin(aFlexItemReflowInput.ComputedPhysicalMargin()),
     mMainMinSize(aMainMinSize),
     mMainMaxSize(aMainMaxSize),
     mCrossMinSize(aCrossMinSize),
     mCrossMaxSize(aCrossMaxSize),
     mMainPosn(0),
     mCrossSize(aTentativeCrossSize),
@@ -1871,41 +1860,42 @@ FlexItem::FlexItem(ReflowInput& aFlexIte
         MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
                    "Someone else tried to resolve our auto margin");
       }
     }
   }
 #endif // DEBUG
 
   // Map align-self 'baseline' value to 'start' when baseline alignment
-  // is not possible because the FlexItem's writing mode is orthogonal to
-  // the main axis of the container. If that's the case, we just directly
+  // is not possible because the FlexItem's block axis is orthogonal to
+  // the cross axis of the container. If that's the case, we just directly
   // convert our align-self value here, so that we don't have to handle this
   // with special cases elsewhere.
   // We are treating this case as one where it is appropriate to use the
-  // fallback values defined at https://www.w3.org/TR/css-align-3/#baseline
-  if (aAxisTracker.IsRowOriented() ==
-      aAxisTracker.GetWritingMode().IsOrthogonalTo(mWM)) {
+  // fallback values defined at https://www.w3.org/TR/css-align/#baseline-values
+  // XXXdholbert That spec text actually says to fall back to 'start'/'end',
+  // not 'flex-start'/'flex-end'... Probably sort this out in bug 1207698.
+  if (!IsBlockAxisCrossAxis()) {
     if (mAlignSelf == NS_STYLE_ALIGN_BASELINE) {
       mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
     } else if (mAlignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
       mAlignSelf = NS_STYLE_ALIGN_FLEX_END;
     }
   }
 }
 
 // Simplified constructor for creating a special "strut" FlexItem, for a child
 // with visibility:collapse. The strut has 0 main-size, and it only exists to
 // impose a minimum cross size on whichever FlexLine it ends up in.
 FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize,
                    WritingMode aContainerWM)
   : mFrame(aChildFrame),
     mFlexGrow(0.0f),
     mFlexShrink(0.0f),
-    mIntrinsicRatio(),
+    mIntrinsicRatio(aContainerWM),
     // mBorderPadding uses default constructor,
     // mMargin uses default constructor,
     mFlexBaseSize(0),
     mMainMinSize(0),
     mMainMaxSize(0),
     mCrossMinSize(0),
     mCrossMaxSize(0),
     mMainSize(0),
@@ -1940,50 +1930,54 @@ FlexItem::FlexItem(nsIFrame* aChildFrame
 void
 FlexItem::CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
                               const FlexboxAxisTracker& aAxisTracker)
 {
   const nsStylePosition* pos = aFlexItemReflowInput.mStylePosition;
   const nsStyleDisplay* disp = aFlexItemReflowInput.mStyleDisplay;
 
   // We'll need special behavior for "min-[width|height]:auto" (whichever is in
-  // the main axis) iff:
+  // the flex container's main axis) iff:
   // (a) its computed value is "auto"
   // (b) the "overflow" sub-property in the same axis (the main axis) has a
   //     computed value of "visible"
-  const nsStyleCoord& minSize = GET_MAIN_COMPONENT(aAxisTracker,
-                                                   pos->mMinWidth,
-                                                   pos->mMinHeight);
-
-  const uint8_t overflowVal = GET_MAIN_COMPONENT(aAxisTracker,
-                                                 disp->mOverflowX,
-                                                 disp->mOverflowY);
-
-  mNeedsMinSizeAutoResolution = (minSize.GetUnit() == eStyleUnit_Auto &&
-                                 overflowVal == NS_STYLE_OVERFLOW_VISIBLE);
+  const nsStyleCoord& mainMinSize = aAxisTracker.IsRowOriented()
+    ? pos->MinISize(aAxisTracker.GetWritingMode())
+    : pos->MinBSize(aAxisTracker.GetWritingMode());
+
+  // NOTE: Technically we should be checking the 'overflow' subproperty in the
+  // main axis. But since we only care whether it's 'visible', we can check
+  // either subproperty -- because they must be BOTH 'visible' or BOTH
+  // non-'visible' due to the way the subproperties interact.
+  mNeedsMinSizeAutoResolution = (mainMinSize.GetUnit() == eStyleUnit_Auto &&
+                                 disp->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE);
 }
 
 nscoord
 FlexItem::GetBaselineOffsetFromOuterCrossEdge(
   AxisEdgeType aEdge,
   const FlexboxAxisTracker& aAxisTracker,
   bool aUseFirstLineBaseline) const
 {
-  // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
-  // measurement -- it's the distance from the border-top edge of this FlexItem
-  // to its baseline. So, we can really only do baseline alignment when the
-  // cross axis is vertical. (The FlexItem constructor enforces this when
-  // resolving the item's "mAlignSelf" value).
-  MOZ_ASSERT(!aAxisTracker.IsCrossAxisHorizontal(),
+  // NOTE:
+  //  * We only use baselines for aligning in the flex container's cross axis.
+  //  * Baselines are a measurement in the item's block axis.
+  // ...so we only expect to get here if the item's block axis is parallel (or
+  // antiparallel) to the container's cross axis.  (Otherwise, the FlexItem
+  // constructor should've resolved mAlignSelf with a fallback value, which
+  // would prevent this function from being called.)
+  MOZ_ASSERT(IsBlockAxisCrossAxis(),
              "Only expecting to be doing baseline computations when the "
-             "cross axis is vertical");
+             "cross axis is the block axis");
 
   AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
   mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge];
 
+  // XXXdholbert The "top"/"bottom" physical-axis dependencies below need to be
+  // logicalized -- see bug 1384266.
   nscoord marginTopToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
                                 mMargin.top;
 
   if (sideToMeasureFrom == eSideTop) {
     // Measuring from top (normal case): the distance from the margin-box top
     // edge to the baseline is just ascent + margin-top.
     return marginTopToBaseline;
   }
@@ -1993,16 +1987,28 @@ FlexItem::GetBaselineOffsetFromOuterCros
              "we're not using the top side, so that only leaves the bottom...");
 
   // Measuring from bottom: The distance from the margin-box bottom edge to the
   // baseline is just the margin-box cross size (i.e. outer cross size), minus
   // the already-computed distance from margin-top to baseline.
   return GetOuterCrossSize(crossAxis) - marginTopToBaseline;
 }
 
+bool
+FlexItem::IsCrossSizeAuto() const
+{
+  const nsStylePosition* stylePos = mFrame->StylePosition();
+  // Check whichever component is in the flex container's cross axis.
+  // (IsInlineAxisCrossAxis() tells us whether that's our ISize or BSize, in
+  // terms of our own WritingMode, mWM.)
+  return eStyleUnit_Auto == (IsInlineAxisCrossAxis()
+                             ? stylePos->ISize(mWM).GetUnit()
+                             : stylePos->BSize(mWM).GetUnit());
+}
+
 uint32_t
 FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const
 {
   uint32_t numAutoMargins = 0;
   const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
   for (uint32_t i = 0; i < eNumAxisEdges; i++) {
     mozilla::Side side = kAxisOrientationToSidesMap[aAxis][i];
     if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
@@ -3273,17 +3279,17 @@ FlexItem::ResolveStretchedCrossSize(nsco
                                     const FlexboxAxisTracker& aAxisTracker)
 {
   AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
   // We stretch IFF we are align-self:stretch, have no auto margins in
   // cross axis, and have cross-axis size property == "auto". If any of those
   // conditions don't hold up, we won't stretch.
   if (mAlignSelf != NS_STYLE_ALIGN_STRETCH ||
       GetNumAutoMarginsInAxis(crossAxis) != 0 ||
-      eStyleUnit_Auto != aAxisTracker.ComputedCrossSize(mFrame).GetUnit()) {
+      !IsCrossSizeAuto()) {
     return;
   }
 
   // If we've already been stretched, we can bail out early, too.
   // No need to redo the calculation.
   if (mIsStretched) {
     return;
   }
@@ -4516,20 +4522,20 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
 
         WritingMode wm = item->Frame()->GetWritingMode();
         LogicalSize availSize = aReflowInput.ComputedSize(wm);
         availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
         ReflowInput childReflowInput(aPresContext, aReflowInput,
                                      item->Frame(), availSize);
         if (!sizeOverride) {
           // Directly override the computed main-size, by tweaking reflow state:
-          if (aAxisTracker.IsMainAxisHorizontal()) {
-            childReflowInput.SetComputedWidth(item->GetMainSize());
+          if (item->IsInlineAxisMainAxis()) {
+            childReflowInput.SetComputedISize(item->GetMainSize());
           } else {
-            childReflowInput.SetComputedHeight(item->GetMainSize());
+            childReflowInput.SetComputedBSize(item->GetMainSize());
           }
         }
 
         SizeItemInCrossAxis(aPresContext, aAxisTracker,
                             childReflowInput, *item);
       }
     }
     // Now that we've finished with this line's items, size the line itself:
@@ -4859,71 +4865,79 @@ nsFlexContainerFrame::ReflowFlexItem(nsP
 {
   WritingMode outerWM = aReflowInput.GetWritingMode();
   WritingMode wm = aItem.Frame()->GetWritingMode();
   LogicalSize availSize = aReflowInput.ComputedSize(wm);
   availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
   ReflowInput childReflowInput(aPresContext, aReflowInput,
                                      aItem.Frame(), availSize);
 
-  // Keep track of whether we've overriden the child's computed height
-  // and/or width, so we can set its resize flags accordingly.
-  bool didOverrideComputedWidth = false;
-  bool didOverrideComputedHeight = false;
+  // Keep track of whether we've overriden the child's computed ISize
+  // and/or BSize, so we can set its resize flags accordingly.
+  bool didOverrideComputedISize = false;
+  bool didOverrideComputedBSize = false;
 
   // Override computed main-size
-  if (aAxisTracker.IsMainAxisHorizontal()) {
-    childReflowInput.SetComputedWidth(aItem.GetMainSize());
-    didOverrideComputedWidth = true;
+  if (aItem.IsInlineAxisMainAxis()) {
+    childReflowInput.SetComputedISize(aItem.GetMainSize());
+    didOverrideComputedISize = true;
   } else {
-    childReflowInput.SetComputedHeight(aItem.GetMainSize());
-    didOverrideComputedHeight = true;
+    childReflowInput.SetComputedBSize(aItem.GetMainSize());
+    didOverrideComputedBSize = true;
   }
 
   // Override reflow state's computed cross-size if either:
   // - the item was stretched (in which case we're imposing a cross size)
   // ...or...
   // - the item it has an aspect ratio (in which case the cross-size that's
   // currently in the reflow state is based on arithmetic involving a stale
   // main-size value that we just stomped on above). (Note that we could handle
   // this case using an AutoFlexItemMainSizeOverride, as we do elsewhere; but
   // given that we *already know* the correct cross size to use here, it's
   // cheaper to just directly set it instead of setting a frame property.)
   if (aItem.IsStretched() ||
       aItem.HasIntrinsicRatio()) {
-    if (aAxisTracker.IsCrossAxisHorizontal()) {
-      childReflowInput.SetComputedWidth(aItem.GetCrossSize());
-      didOverrideComputedWidth = true;
+    if (aItem.IsInlineAxisCrossAxis()) {
+      childReflowInput.SetComputedISize(aItem.GetCrossSize());
+      didOverrideComputedISize = true;
     } else {
-      childReflowInput.SetComputedHeight(aItem.GetCrossSize());
-      didOverrideComputedHeight = true;
+      childReflowInput.SetComputedBSize(aItem.GetCrossSize());
+      didOverrideComputedBSize = true;
     }
   }
-  if (aItem.IsStretched() && !aAxisTracker.IsCrossAxisHorizontal()) {
-    // If this item's height is stretched, it's a relative height.
+  if (aItem.IsStretched() && aItem.IsBlockAxisCrossAxis()) {
+    // This item is stretched (in the cross axis), and that axis is its block
+    // axis.  That stretching effectively gives it a relative BSize.
+    // XXXdholbert This flag only makes a difference if we use the flex items'
+    // frame-state when deciding whether to reflow them -- and we don't, as of
+    // the changes in bug 851607. So this has no effect right now, but it might
+    // make a difference if we optimize to use dirty bits in the
+    // future. (Reftests flexbox-resizeviewport-1.xhtml and -2.xhtml are
+    // intended to catch any regressions here, if we end up relying on this bit
+    // & neglecting to set it.)
     aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
   }
 
   // XXXdholbert Might need to actually set the correct margins in the
   // reflow state at some point, so that they can be saved on the frame for
   // UsedMarginProperty().  Maybe doesn't matter though...?
 
   // If we're overriding the computed width or height, *and* we had an
   // earlier "measuring" reflow, then this upcoming reflow needs to be
   // treated as a resize.
   if (aItem.HadMeasuringReflow()) {
-    if (didOverrideComputedWidth) {
-      // (This is somewhat redundant, since the reflow state already
-      // sets mHResize whenever our computed width has changed since the
-      // previous reflow. Still, it's nice for symmetry, and it may become
-      // necessary once we support orthogonal flows.)
-      childReflowInput.SetHResize(true);
+    if (didOverrideComputedISize) {
+      // (This is somewhat redundant, since ReflowInput::InitResizeFlags()
+      // already calls SetIResize() whenever our computed ISize has changed
+      // since the previous reflow. Still, it's nice for symmetry, and it might
+      // be necessary for some edge cases.)
+      childReflowInput.SetIResize(true);
     }
-    if (didOverrideComputedHeight) {
-      childReflowInput.SetVResize(true);
+    if (didOverrideComputedBSize) {
+      childReflowInput.SetBResize(true);
     }
   }
   // NOTE: Be very careful about doing anything else with childReflowInput
   // after this point, because some of its methods (e.g. SetComputedWidth)
   // internally call InitResizeFlags and stomp on mVResize & mHResize.
 
   ReflowOutput childDesiredSize(childReflowInput);
   nsReflowStatus childReflowStatus;
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -148,19 +148,16 @@ public:
     return true;
   }
 
   // nsContainerFrame overrides
   uint16_t CSSAlignmentForAbsPosChild(
             const ReflowInput& aChildRI,
             mozilla::LogicalAxis aLogicalAxis) const override;
 
-  // Flexbox-specific public methods
-  bool IsHorizontal();
-
   /**
    * Helper function to calculate packing space and initial offset of alignment
    * subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for
    * space-between, space-around, and space-evenly.
    *    * @param aNumThingsToPack             Number of alignment subjects.
    * @param aAlignVal                    Value for align-self or justify-self.
    * @param aFirstSubjectOffset          Outparam for first subject offset.
    * @param aNumPackingSpacesRemaining   Outparam for number of equal-sized
copy from layout/reftests/flexbox/flexbox-resizeviewport-1-helper.html
copy to layout/reftests/flexbox/flexbox-resizeviewport-2-helper.html
--- a/layout/reftests/flexbox/flexbox-resizeviewport-1-helper.html
+++ b/layout/reftests/flexbox/flexbox-resizeviewport-2-helper.html
@@ -1,21 +1,22 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
-<!-- Helper-file for reftest flexbox-resizeviewport-1.xhtml
+<!-- Helper-file for reftest flexbox-resizeviewport-2.xhtml
      I'm intentionally using quirks-mode (no doctype), so that
-     a 100% height will work. -->
+     a 100% width (block-size) will work. -->
 <html>
   <head>
     <style>
+      html { writing-mode: vertical-rl; }
       div.flexbox {
         display: flex;
-        height: 100%;
+        width: 100%;
         border: 2px dashed black;
       }
       div.a {
         flex: 1;
         background: pink;
       }
       div.b {
         flex: 1;
copy from layout/reftests/flexbox/flexbox-resizeviewport-1-ref.xhtml
copy to layout/reftests/flexbox/flexbox-resizeviewport-2-ref.xhtml
--- a/layout/reftests/flexbox/flexbox-resizeviewport-1-ref.xhtml
+++ b/layout/reftests/flexbox/flexbox-resizeviewport-2-ref.xhtml
@@ -8,15 +8,15 @@
     <style>
       iframe {
         width:  75px;
         height: 75px;
       }
     </style>
   </head>
   <body>
-    <iframe src="flexbox-resizeviewport-1-helper.html" style="width: 50px"/>
-    <iframe src="flexbox-resizeviewport-1-helper.html" style="width: 125px"/>
+    <iframe src="flexbox-resizeviewport-2-helper.html" style="width: 50px"/>
+    <iframe src="flexbox-resizeviewport-2-helper.html" style="width: 125px"/>
     <br/>
-    <iframe src="flexbox-resizeviewport-1-helper.html" style="height: 50px"/>
-    <iframe src="flexbox-resizeviewport-1-helper.html" style="height: 125px"/>
+    <iframe src="flexbox-resizeviewport-2-helper.html" style="height: 50px"/>
+    <iframe src="flexbox-resizeviewport-2-helper.html" style="height: 125px"/>
   </body>
 </html>
copy from layout/reftests/flexbox/flexbox-resizeviewport-1.xhtml
copy to layout/reftests/flexbox/flexbox-resizeviewport-2.xhtml
--- a/layout/reftests/flexbox/flexbox-resizeviewport-1.xhtml
+++ b/layout/reftests/flexbox/flexbox-resizeviewport-2.xhtml
@@ -1,15 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!-- Testcase to be sure a flex container gets reflowed properly when its
      iframe changes size. -->
+<!-- XXXdholbert This testcase can't actually test the scenario it's intended
+     to test right now (quirks-mode percent-BSize resolution in a vertical
+     writing mode), due to Bug 1441348.
+ -->
 <html xmlns="http://www.w3.org/1999/xhtml"
       class="reftest-wait">
   <head>
     <style>
       iframe {
         width:  75px;
         height: 75px;
       }
@@ -26,15 +30,15 @@
         setElementPropertyTo("c", "height", "50px");
         setElementPropertyTo("d", "height", "125px");
         document.documentElement.removeAttribute("class");
       }
       window.addEventListener("MozReftestInvalidate", tweak, false);
     </script>
   </head>
   <body>
-    <iframe id="a" src="flexbox-resizeviewport-1-helper.html"/>
-    <iframe id="b" src="flexbox-resizeviewport-1-helper.html"/>
+    <iframe id="a" src="flexbox-resizeviewport-2-helper.html"/>
+    <iframe id="b" src="flexbox-resizeviewport-2-helper.html"/>
     <br/>
-    <iframe id="c" src="flexbox-resizeviewport-1-helper.html"/>
-    <iframe id="d" src="flexbox-resizeviewport-1-helper.html"/>
+    <iframe id="c" src="flexbox-resizeviewport-2-helper.html"/>
+    <iframe id="d" src="flexbox-resizeviewport-2-helper.html"/>
   </body>
 </html>
--- a/layout/reftests/flexbox/reftest.list
+++ b/layout/reftests/flexbox/reftest.list
@@ -106,16 +106,17 @@ fails == flexbox-inlinecontent-horiz-1b.
 == flexbox-intrinsic-sizing-horiz-2a.xhtml flexbox-intrinsic-sizing-horiz-2-ref.xhtml
 == flexbox-intrinsic-sizing-horiz-2b.xhtml flexbox-intrinsic-sizing-horiz-2-ref.xhtml
 
 # Tests for invalidation after dynamic modifications
 == flexbox-invalidation-1.html flexbox-invalidation-1-ref.html
 
 # Tests for flexbox in an iframe that gets resized.
 fuzzy-if(skiaContent,1,5) == flexbox-resizeviewport-1.xhtml flexbox-resizeviewport-1-ref.xhtml
+fuzzy-if(skiaContent,1,5) == flexbox-resizeviewport-2.xhtml flexbox-resizeviewport-2-ref.xhtml
 
 # Tests for flexbox styling on things that don't support it
 == flexbox-styling-on-svg-1.svg flexbox-styling-on-svg-1-ref.svg
 
 # Tests with widgets as flex items
 fuzzy-if(gtkWidget,1,66) == flexbox-widget-flex-items-1.html flexbox-widget-flex-items-1-ref.html
 fuzzy-if(gtkWidget,1,74) == flexbox-widget-flex-items-2.html flexbox-widget-flex-items-2-ref.html
 skip-if(gtkWidget) == flexbox-widget-flex-items-3.html flexbox-widget-flex-items-3-ref.html # bug 1260965
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -1082,17 +1082,19 @@ function SynchronizeForSnapshot(flags)
         gCurrentTestType == TYPE_LOAD ||
         gCurrentTestType == TYPE_PRINT) {
         // Script, load-only, and PDF-print tests do not need any snapshotting.
         return;
     }
 
     if (flags & SYNC_ALLOW_DISABLE) {
         var docElt = content.document.documentElement;
-        if (docElt && docElt.hasAttribute("reftest-no-sync-layers")) {
+        if (docElt &&
+            (docElt.hasAttribute("reftest-no-sync-layers") ||
+             docElt.classList.contains("reftest-no-flush"))) {
             LogInfo("Test file chose to skip SynchronizeForSnapshot");
             return;
         }
     }
 
     windowUtils().updateLayerTree();
 
     // Setup async scroll offsets now, because any scrollable layers should
--- a/media/libcubeb/README_MOZILLA
+++ b/media/libcubeb/README_MOZILLA
@@ -1,8 +1,8 @@
 The source from this directory was copied from the cubeb 
 git repository using the update.sh script.  The only changes
 made were those applied by update.sh and the addition of
 Makefile.in build files for the Mozilla build system.
 
 The cubeb git repository is: git://github.com/kinetiknz/cubeb.git
 
-The git commit ID used was 1d53c3a3779cbeb860b16aa38cc7f51e196b9745 (2018-02-13 12:30:46 +1000)
+The git commit ID used was eb3409e201daaaf03401ff234de5e1f69ba10c8d (2018-03-01 13:31:19 +0100)
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/android/cubeb-output-latency.h
@@ -0,0 +1,76 @@
+#ifndef _CUBEB_OUTPUT_LATENCY_H_
+#define _CUBEB_OUTPUT_LATENCY_H_
+
+#include <stdbool.h>
+#include "cubeb_media_library.h"
+#include "../cubeb-jni.h"
+
+struct output_latency_function {
+  media_lib * from_lib;
+  cubeb_jni * from_jni;
+  int version;
+};
+
+typedef struct output_latency_function output_latency_function;
+
+const int ANDROID_JELLY_BEAN_MR1_4_2 = 17;
+
+output_latency_function *
+cubeb_output_latency_load_method(int version)
+{
+  output_latency_function * ol = NULL;
+  ol = calloc(1, sizeof(output_latency_function));
+
+  ol->version = version;
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    ol->from_jni = cubeb_jni_init();
+    return ol;
+  }
+
+  ol->from_lib = cubeb_load_media_library();
+  return ol;
+}
+
+bool
+cubeb_output_latency_method_is_loaded(output_latency_function * ol)
+{
+  assert(ol && (ol->from_jni || ol->from_lib));
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    return !!ol->from_jni;
+  }
+
+  return !!ol->from_lib;
+}
+
+void
+cubeb_output_latency_unload_method(output_latency_function * ol)
+{
+  if (!ol) {
+    return;
+  }
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_jni) {
+    cubeb_jni_destroy(ol->from_jni);
+  }
+
+  if (ol->version <= ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_lib) {
+    cubeb_close_media_library(ol->from_lib);
+  }
+
+  free(ol);
+}
+
+uint32_t
+cubeb_get_output_latency(output_latency_function * ol)
+{
+  assert(cubeb_output_latency_method_is_loaded(ol));
+
+  if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
+    return cubeb_get_output_latency_from_jni(ol->from_jni);
+  }
+
+  return cubeb_get_output_latency_from_media_library(ol->from_lib);
+}
+
+#endif // _CUBEB_OUTPUT_LATENCY_H_
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/android/cubeb_media_library.h
@@ -0,0 +1,62 @@
+#ifndef _CUBEB_MEDIA_LIBRARY_H_
+#define _CUBEB_MEDIA_LIBRARY_H_
+
+struct media_lib {
+  void * libmedia;
+  int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
+};
+
+typedef struct media_lib media_lib;
+
+media_lib *
+cubeb_load_media_library()
+{
+  media_lib ml = {0};
+  ml.libmedia = dlopen("libmedia.so", RTLD_LAZY);
+  if (!ml.libmedia) {
+    return NULL;
+  }
+
+  // Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
+  // status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
+  ml.get_output_latency =
+    dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
+  if (!ml.get_output_latency) {
+    // In case of failure, try the signature from legacy version.
+    // status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
+    ml.get_output_latency =
+      dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
+    if (!ml.get_output_latency) {
+      return NULL;
+    }
+  }
+
+  media_lib * rv = NULL;
+  rv = calloc(1, sizeof(media_lib));
+  assert(rv);
+  *rv = ml;
+  return rv;
+}
+
+void
+cubeb_close_media_library(media_lib * ml)
+{
+  dlclose(ml->libmedia);
+  ml->libmedia = NULL;
+  ml->get_output_latency = NULL;
+  free(ml);
+}
+
+uint32_t
+cubeb_get_output_latency_from_media_library(media_lib * ml)
+{
+  uint32_t latency = 0;
+  const int audio_stream_type_music = 3;
+  int32_t r = ml->get_output_latency(&latency, audio_stream_type_music);
+  if (r) {
+    return 0;
+  }
+  return latency;
+}
+
+#endif // _CUBEB_MEDIA_LIBRARY_H_
new file mode 100644
--- /dev/null
+++ b/media/libcubeb/src/cubeb-jni-instances.h
@@ -0,0 +1,34 @@
+#ifndef _CUBEB_JNI_INSTANCES_H_
+#define _CUBEB_JNI_INSTANCES_H_
+
+#include "GeneratedJNIWrappers.h"
+#include "mozilla/jni/Utils.h"
+
+/*
+ * The methods in this file offer a way to pass in the required
+ * JNI instances in the cubeb library. By default they return NULL.
+ * In this case part of the cubeb API that depends on JNI
+ * will return CUBEB_ERROR_NOT_SUPPORTED. Currently only one
+ * method depends on that:
+ *
+ * cubeb_stream_get_position()
+ *
+ * Users that want to use that cubeb API method must "override"
+ * the methods bellow to return a valid instance of JavaVM
+ * and application's Context object.
+ * */
+
+JNIEnv *
+cubeb_get_jni_env_for_thread()
+{