Merge mozilla-central to autoland. a=merge on a CLOSED TREE
authorAndreea Pavel <apavel@mozilla.com>
Thu, 26 Apr 2018 09:06:52 +0300
changeset 469243 4da66989752079b16519ad896cdf9267f4f17dac
parent 469242 c75b36672e2a6ec3e45609a2d76e60d2302c2521 (current diff)
parent 469227 b62ad926cf2a2d5759222f4e9b40c9e3bd3bdd31 (diff)
child 469244 20ffaafbae3c4f3c677ef12825207e4cf8fa5932
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland. a=merge on a CLOSED TREE
caps/tests/mochitest/test_extensionURL.html
hal/fallback/FallbackThreadPriority.cpp
js/src/jit/FlowAliasAnalysis.cpp
js/src/jit/FlowAliasAnalysis.h
layout/inspector/InspectorUtils.cpp
layout/style/ServoMediaList.cpp
layout/style/ServoMediaList.h
layout/style/moz.build
layout/style/nsCSSParser.h
servo/tests/unit/style/parsing/font.rs
toolkit/mozapps/extensions/nsBlocklistService.js
--- a/accessible/base/TextAttrs.cpp
+++ b/accessible/base/TextAttrs.cpp
@@ -651,19 +651,20 @@ TextAttrsMgr::FontWeightTextAttr::
     return FontWeight::Bold();
   }
 
   // On Windows, font->GetStyle()->weight will give the same weight as
   // fontEntry->Weight(), the weight of the first font in the font group,
   // which may not be the weight of the font face used to render the
   // characters. On Mac, font->GetStyle()->weight will just give the same
   // number as getComputedStyle(). fontEntry->Weight() will give the weight
-  // of the font face used.
+  // range supported by the font face used, so we clamp the weight that was
+  // requested by style to what is actually supported by the font.
   gfxFontEntry *fontEntry = font->GetFontEntry();
-  return fontEntry->Weight();
+  return fontEntry->Weight().Clamp(font->GetStyle()->weight);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // AutoGeneratedTextAttr
 ////////////////////////////////////////////////////////////////////////////////
 TextAttrsMgr::AutoGeneratedTextAttr::
   AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
                         Accessible* aAccessible) :
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -139,12 +139,18 @@ Accessible::InsertAfter(Accessible* aNew
     mDoc->UnbindFromDocument(aNewChild);
     return false;
   }
 
   return InsertChildAt(aRefChild ? aRefChild->IndexInParent() + 1 : 0,
                        aNewChild);
 }
 
+inline nsIntRect
+Accessible::Bounds() const
+{
+  return BoundsInAppUnits().ToNearestPixels(mDoc->PresContext()->AppUnitsPerDevPixel());
+}
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -665,42 +665,41 @@ Accessible::RelativeBounds(nsIFrame** aB
     return nsLayoutUtils::
       GetAllInFlowRectsUnion(frame, *aBoundingFrame,
                              nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   }
 
   return nsRect();
 }
 
-nsIntRect
-Accessible::Bounds() const
+nsRect
+Accessible::BoundsInAppUnits() const
 {
   nsIFrame* boundingFrame = nullptr;
   nsRect unionRectTwips = RelativeBounds(&boundingFrame);
-  if (!boundingFrame)
-    return nsIntRect();
-
-  nsIntRect screenRect;
-  nsPresContext* presContext = mDoc->PresContext();
-  screenRect.SetRect(presContext->AppUnitsToDevPixels(unionRectTwips.X()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Y()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Width()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Height()));
+  if (!boundingFrame) {
+    return nsRect();
+  }
 
   // We need to take into account a non-1 resolution set on the presshell.
   // This happens in mobile platforms with async pinch zooming. Here we
   // scale the bounds before adding the screen-relative offset.
-  screenRect.ScaleRoundOut(presContext->PresShell()->GetResolution());
+  unionRectTwips.ScaleRoundOut(mDoc->PresContext()->PresShell()->GetResolution());
   // We have the union of the rectangle, now we need to put it in absolute
   // screen coords.
-  nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
-    ToNearestPixels(presContext->AppUnitsPerDevPixel());
-  screenRect.MoveBy(orgRectPixels.X(), orgRectPixels.Y());
-
-  return screenRect;
+  nsRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits();
+  unionRectTwips.MoveBy(orgRectPixels.X(), orgRectPixels.Y());
+
+  return unionRectTwips;
+}
+
+nsIntRect
+Accessible::BoundsInCSSPixels() const
+{
+  return BoundsInAppUnits().ToNearestPixels(mDoc->PresContext()->AppUnitsPerCSSPixel());
 }
 
 void
 Accessible::SetSelected(bool aSelect)
 {
   if (!HasOwnContent())
     return;
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -505,19 +505,29 @@ public:
    *                        if missed entire text is appended
    * @param aLength       [in, optional] required length of text, if missed
    *                        then text form start offset till the end is appended
    */
   virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
                             uint32_t aLength = UINT32_MAX);
 
   /**
+   * Return boundaries in screen coordinates in app units.
+   */
+  virtual nsRect BoundsInAppUnits() const;
+
+  /**
    * Return boundaries in screen coordinates.
    */
-  virtual nsIntRect Bounds() const;
+  nsIntRect Bounds() const;
+
+  /**
+   * Return boundaries in screen coordinates in CSS pixels.
+   */
+  virtual nsIntRect BoundsInCSSPixels() const;
 
   /**
    * Return boundaries rect relative the bounding frame.
    */
   virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const;
 
   /**
    * Selects the accessible within its container if applicable.
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -111,20 +111,20 @@ ApplicationAccessible::FocusedChild()
 }
 
 Relation
 ApplicationAccessible::RelationByType(RelationType aRelationType)
 {
   return Relation();
 }
 
-nsIntRect
-ApplicationAccessible::Bounds() const
+nsRect
+ApplicationAccessible::BoundsInAppUnits() const
 {
-  return nsIntRect();
+  return nsRect();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public methods
 
 void
 ApplicationAccessible::Shutdown()
 {
--- a/accessible/generic/ApplicationAccessible.h
+++ b/accessible/generic/ApplicationAccessible.h
@@ -31,17 +31,17 @@ class ApplicationAccessible : public Acc
 public:
 
   ApplicationAccessible();
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(ApplicationAccessible, AccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual GroupPos GroupPosition() override;
   virtual ENameValueFlag Name(nsString& aName) override;
   virtual void ApplyARIAState(uint64_t* aState) const override;
   virtual void Description(nsString& aDescription) override;
   virtual void Value(nsString& aValue) override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual uint64_t State() override;
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -69,25 +69,25 @@ HTMLLIAccessible::NativeRole()
 }
 
 uint64_t
 HTMLLIAccessible::NativeState()
 {
   return HyperTextAccessibleWrap::NativeState() | states::READONLY;
 }
 
-nsIntRect
-HTMLLIAccessible::Bounds() const
+nsRect
+HTMLLIAccessible::BoundsInAppUnits() const
 {
-  nsIntRect rect = AccessibleWrap::Bounds();
-  if (rect.IsEmpty() || !mBullet || mBullet->IsInside())
+  nsRect rect = AccessibleWrap::BoundsInAppUnits();
+  if (rect.IsEmpty() || !mBullet || mBullet->IsInside()) {
     return rect;
+  }
 
-  nsIntRect bulletRect = mBullet->Bounds();
-
+  nsRect bulletRect = mBullet->BoundsInAppUnits();
   // Move x coordinate of list item over to cover bullet as well
   rect.SetLeftEdge(bulletRect.X());
   return rect;
 }
 
 bool
 HTMLLIAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
 {
--- a/accessible/html/HTMLListAccessible.h
+++ b/accessible/html/HTMLListAccessible.h
@@ -46,17 +46,17 @@ public:
   HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // nsISupports
   NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLLIAccessible,
                                        HyperTextAccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
 
   virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
 
   // HTMLLIAccessible
   HTMLListBulletAccessible* Bullet() const { return mBullet; }
   void UpdateBullet(bool aHasBullet);
--- a/accessible/interfaces/gecko/IGeckoCustom.idl
+++ b/accessible/interfaces/gecko/IGeckoCustom.idl
@@ -7,16 +7,20 @@
 import "objidl.idl";
 import "oaidl.idl";
 
 [object, uuid(7510360f-cdae-4de9-88c8-d167eda62afc)]
 interface IGeckoCustom : IUnknown
 {
   [propget] HRESULT ID([out, retval] unsigned __int64* aID);
   [propget] HRESULT anchorCount([out, retval] long* aCount);
+  [propget] HRESULT boundsInCSSPixels([out] __int32* aX,
+                                      [out] __int32* aY,
+                                      [out] __int32* aWidth,
+                                      [out, retval] __int32* aHeight);
   [propget] HRESULT DOMNodeID([out, retval] BSTR* aID);
   [propget] HRESULT minimumIncrement([out, retval] double* aIncrement);
   [propget] HRESULT mozState([out, retval] unsigned __int64* aState);
 }
 
 
 [
     uuid(55769d85-f830-4d76-9e39-3670914a28f7),
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -15,21 +15,21 @@ interface nsIAccessibleRelation;
 namespace mozilla {
 namespace a11y {
 class Accessible;
 }
 }
 %}
 
 /**
- * A cross-platform interface that supports platform-specific 
+ * A cross-platform interface that supports platform-specific
  * accessibility APIs like MSAA and ATK. Contains the sum of what's needed
  * to support IAccessible as well as ATK's generic accessibility objects.
  * Can also be used by in-process accessibility clients to get information
- * about objects in the accessible tree. The accessible tree is a subset of 
+ * about objects in the accessible tree. The accessible tree is a subset of
  * nodes in the DOM tree -- such as documents, focusable elements and text.
  * Mozilla creates the implementations of nsIAccessible on demand.
  * See http://www.mozilla.org/projects/ui/accessibility for more information.
  */
 [scriptable, builtinclass, uuid(de2869d9-563c-4943-996b-31a4daa4d097)]
 interface nsIAccessible : nsISupports
 {
   /**
@@ -51,17 +51,17 @@ interface nsIAccessible : nsISupports
    * First child in accessible tree
    */
   readonly attribute nsIAccessible firstChild;
 
   /**
    * Last child in accessible tree
    */
   readonly attribute nsIAccessible lastChild;
-  
+
   /**
    * Array of all this element's children.
    */
   readonly attribute nsIArray children;
 
   /**
    * Number of accessible children
    */
@@ -119,17 +119,17 @@ interface nsIAccessible : nsISupports
   /**
    * Accessible description -- long text associated with this node
    */
   readonly attribute AString description;
 
   /**
    * Provides localized string of accesskey name, such as Alt+D.
    * The modifier may be affected by user and platform preferences.
-   * Usually alt+letter, or just the letter alone for menu items. 
+   * Usually alt+letter, or just the letter alone for menu items.
    */
   readonly attribute AString accessKey;
 
   /**
    * Provides localized string of global keyboard accelerator for default
    * action, such as Ctrl+O for Open file
    */
   readonly attribute AString keyboardShortcut;
@@ -221,21 +221,27 @@ interface nsIAccessible : nsISupports
 
   /**
    * Returns multiple accessible relations for this object.
    */
   nsIArray getRelations();
 
   /**
    * Return accessible's x and y coordinates relative to the screen and
-   * accessible's width and height.
+   * accessible's width and height in Dev pixels.
    */
   void getBounds(out long x, out long y, out long width, out long height);
 
   /**
+   * Return accessible's x and y coordinates relative to the screen and
+   * accessible's width and height in CSS pixels.
+   */
+  void getBoundsInCSSPixels(out long aX, out long aY, out long aWidth, out long aHeight);
+
+  /**
    * Add or remove this accessible to the current selection
    */
   void setSelected(in boolean isSelected);
 
   /**
    * Select this accessible node only
    */
   void takeSelection();
--- a/accessible/ipc/ProxyAccessibleShared.h
+++ b/accessible/ipc/ProxyAccessibleShared.h
@@ -248,16 +248,17 @@ double MinValue();
 double MaxValue();
 double Step();
 
 void TakeFocus();
 ProxyAccessible* FocusedChild();
 ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
     Accessible::EWhichChildAtPoint aWhichChild);
 nsIntRect Bounds();
+nsIntRect BoundsInCSSPixels();
 
 void Language(nsString& aLocale);
 void DocType(nsString& aType);
 void Title(nsString& aTitle);
 void URL(nsString& aURL);
 void MimeType(nsString aMime);
 void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
     nsString& aMimeType);
--- a/accessible/ipc/other/DocAccessibleChild.cpp
+++ b/accessible/ipc/other/DocAccessibleChild.cpp
@@ -1979,16 +1979,40 @@ DocAccessibleChild::RecvExtents(const ui
       *aWidth = screenRect.width;
       *aHeight = screenRect.height;
     }
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+DocAccessibleChild::RecvExtentsInCSSPixels(const uint64_t& aID,
+                                           int32_t* aX,
+                                           int32_t* aY,
+                                           int32_t* aWidth,
+                                           int32_t* aHeight)
+{
+  *aX = 0;
+  *aY = 0;
+  *aWidth = 0;
+  *aHeight = 0;
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && !acc->IsDefunct()) {
+    nsIntRect screenRect = acc->BoundsInCSSPixels();
+    if (!screenRect.IsEmpty()) {
+      *aX = screenRect.x;
+      *aY = screenRect.y;
+      *aWidth = screenRect.width;
+      *aHeight = screenRect.height;
+    }
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 DocAccessibleChild::RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID)
 {
   Accessible* acc = IdToAccessible(aID);
   if (!acc) {
     return IPC_OK();
   }
 
   nsIContent* content = acc->GetContent();
--- a/accessible/ipc/other/DocAccessibleChild.h
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -465,16 +465,21 @@ public:
                                                         bool* aOk) override;
 
   virtual mozilla::ipc::IPCResult RecvExtents(const uint64_t& aID,
                                               const bool& aNeedsScreenCoords,
                                               int32_t* aX,
                                               int32_t* aY,
                                               int32_t* aWidth,
                                               int32_t* aHeight) override;
+  virtual mozilla::ipc::IPCResult RecvExtentsInCSSPixels(const uint64_t& aID,
+                                                         int32_t* aX,
+                                                         int32_t* aY,
+                                                         int32_t* aWidth,
+                                                         int32_t* aHeight) override;
   virtual mozilla::ipc::IPCResult RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   TextLeafAccessible* IdToTextLeafAccessible(const uint64_t& aID) const;
--- a/accessible/ipc/other/PDocAccessible.ipdl
+++ b/accessible/ipc/other/PDocAccessible.ipdl
@@ -260,13 +260,15 @@ child:
   nested(inside_sync) sync MimeType(uint64_t aID) returns(nsString aMime);
   nested(inside_sync) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 
   nested(inside_sync) sync AccessibleAtPoint(uint64_t aID, int32_t aX, int32_t aY, bool aNeedsScreenCoords, uint32_t aWhich)
     returns(uint64_t aResult, bool aOk);
 
   nested(inside_sync) sync Extents(uint64_t aID, bool aNeedsScreenCoords)
     returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+  nested(inside_sync) sync ExtentsInCSSPixels(uint64_t aID)
+    returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
   nested(inside_sync) sync DOMNodeID(uint64_t aID) returns(nsString aDOMNodeID);
 };
 
 }
 }
--- a/accessible/ipc/other/ProxyAccessible.cpp
+++ b/accessible/ipc/other/ProxyAccessible.cpp
@@ -1008,16 +1008,26 @@ ProxyAccessible::Bounds()
 {
   nsIntRect rect;
   Unused << mDoc->SendExtents(mID, false,
                               &(rect.x), &(rect.y),
                               &(rect.width), &(rect.height));
   return rect;
 }
 
+nsIntRect
+ProxyAccessible::BoundsInCSSPixels()
+{
+  nsIntRect rect;
+  Unused << mDoc->SendExtentsInCSSPixels(mID,
+                                         &rect.x, &rect.y,
+                                         &rect.width, &rect.height);
+  return rect;
+}
+
 void
 ProxyAccessible::Language(nsString& aLocale)
 {
   Unused << mDoc->SendLanguage(mID, &aLocale);
 }
 
 void
 ProxyAccessible::DocType(nsString& aType)
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -230,16 +230,29 @@ ProxyAccessible::Bounds()
   HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
   if (FAILED(hr)) {
     return rect;
   }
   rect.SetRect(left, top, width, height);
   return rect;
 }
 
+nsIntRect
+ProxyAccessible::BoundsInCSSPixels()
+{
+  RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+  if (!custom) {
+    return nsIntRect();
+  }
+
+  nsIntRect rect;
+  HRESULT hr = custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width, &rect.height);
+  return rect;
+}
+
 void
 ProxyAccessible::Language(nsString& aLocale)
 {
   aLocale.Truncate();
 
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
--- a/accessible/jsat/Constants.jsm
+++ b/accessible/jsat/Constants.jsm
@@ -1,13 +1,10 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-var EXPORTED_SYMBOLS = ["Roles", "Events", "Relations",
-                        "Filters", "States", "Prefilters", "AndroidEvents"];
-
 const AndroidEvents = {
   ANDROID_VIEW_CLICKED: 0x01,
   ANDROID_VIEW_LONG_CLICKED: 0x02,
   ANDROID_VIEW_SELECTED: 0x04,
   ANDROID_VIEW_FOCUSED: 0x08,
   ANDROID_VIEW_TEXT_CHANGED: 0x10,
   ANDROID_WINDOW_STATE_CHANGED: 0x20,
   ANDROID_VIEW_HOVER_ENTER: 0x80,
@@ -65,8 +62,11 @@ XPCOMUtils.defineLazyGetter(
   this, "States",
   function() {
     let statesMap = ConstantsMap(Ci.nsIAccessibleStates, "STATE_", {},
                                  (val) => { return { base: val, extended: 0 }; });
     ConstantsMap(Ci.nsIAccessibleStates, "EXT_STATE_", statesMap,
                  (val) => { return { base: 0, extended: val }; });
     return statesMap;
   });
+
+var EXPORTED_SYMBOLS = ["Roles", "Events", "Relations",
+                        "Filters", "States", "Prefilters", "AndroidEvents"];
--- a/accessible/jsat/ContentControl.jsm
+++ b/accessible/jsat/ContentControl.jsm
@@ -1,15 +1,13 @@
 /* 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/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "Services",
-  "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "Utils",
   "resource://gre/modules/accessibility/Utils.jsm");
 ChromeUtils.defineModuleGetter(this, "Logger",
   "resource://gre/modules/accessibility/Utils.jsm");
 ChromeUtils.defineModuleGetter(this, "Roles",
   "resource://gre/modules/accessibility/Constants.jsm");
 ChromeUtils.defineModuleGetter(this, "TraversalRules",
   "resource://gre/modules/accessibility/Traversal.jsm");
--- a/accessible/jsat/Presentation.jsm
+++ b/accessible/jsat/Presentation.jsm
@@ -2,24 +2,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* exported Presentation */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/accessibility/Utils.jsm");
-ChromeUtils.defineModuleGetter(this, "Logger", // jshint ignore:line
-  "resource://gre/modules/accessibility/Utils.jsm");
 ChromeUtils.defineModuleGetter(this, "PivotContext", // jshint ignore:line
   "resource://gre/modules/accessibility/Utils.jsm");
 ChromeUtils.defineModuleGetter(this, "UtteranceGenerator", // jshint ignore:line
   "resource://gre/modules/accessibility/OutputGenerator.jsm");
-ChromeUtils.defineModuleGetter(this, "Roles", // jshint ignore:line
-  "resource://gre/modules/accessibility/Constants.jsm");
 ChromeUtils.defineModuleGetter(this, "States", // jshint ignore:line
   "resource://gre/modules/accessibility/Constants.jsm");
 ChromeUtils.defineModuleGetter(this, "AndroidEvents", // jshint ignore:line
   "resource://gre/modules/accessibility/Constants.jsm");
 
 var EXPORTED_SYMBOLS = ["Presentation"]; // jshint ignore:line
 
 class AndroidPresentor {
--- a/accessible/tests/browser/bounds/browser_test_zoom.js
+++ b/accessible/tests/browser/bounds/browser_test_zoom.js
@@ -1,33 +1,36 @@
 /* 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";
 
 /* import-globals-from ../../mochitest/layout.js */
 
+/* global getContentDPR */
+
 async function getContentBoundsForDOMElm(browser, id) {
   return ContentTask.spawn(browser, id, contentId => {
     this.ok = ok;
     return getBoundsForDOMElm(contentId);
   });
 }
 
 async function testContentBounds(browser, acc) {
   let [expectedX, expectedY, expectedWidth, expectedHeight] =
     await getContentBoundsForDOMElm(browser, getAccessibleDOMNodeID(acc));
 
-  let [x, y, width, height] = getBounds(acc);
+  let contentDPR = await getContentDPR(browser);
+  let [x, y, width, height] = getBounds(acc, contentDPR);
   let prettyAccName = prettyName(acc);
   is(x, expectedX, "Wrong x coordinate of " + prettyAccName);
   is(y, expectedY, "Wrong y coordinate of " + prettyAccName);
   is(width, expectedWidth, "Wrong width of " + prettyAccName);
-  is(height, expectedHeight, "Wrong height of " + prettyAccName);
+  ok(height >= expectedHeight, "Wrong height of " + prettyAccName);
 }
 
 async function runTests(browser, accDoc) {
   loadFrameScripts(browser, { name: "layout.js", dir: MOCHITESTS_DIR });
 
   let p1 = findAccessibleChildByID(accDoc, "p1");
   let p2 = findAccessibleChildByID(accDoc, "p2");
   let imgmap = findAccessibleChildByID(accDoc, "imgmap");
--- a/accessible/tests/browser/bounds/browser_test_zoom_text.js
+++ b/accessible/tests/browser/bounds/browser_test_zoom_text.js
@@ -1,36 +1,39 @@
 /* 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";
 
 /* import-globals-from ../../mochitest/layout.js */
 
+/* global getContentDPR */
+
 async function runTests(browser, accDoc) {
-  function testTextNode(id) {
+  async function testTextNode(id) {
     let hyperTextNode = findAccessibleChildByID(accDoc, id);
     let textNode = hyperTextNode.firstChild;
 
-    let [x, y, width, height] = getBounds(textNode);
+    let contentDPR = await getContentDPR(browser);
+    let [x, y, width, height] = getBounds(textNode, contentDPR);
     testTextBounds(hyperTextNode, 0, -1, [x, y, width, height],
                    COORDTYPE_SCREEN_RELATIVE);
   }
 
   loadFrameScripts(browser, { name: "layout.js", dir: MOCHITESTS_DIR });
 
-  testTextNode("p1");
-  testTextNode("p2");
+  await testTextNode("p1");
+  await testTextNode("p2");
 
   await ContentTask.spawn(browser, {}, () => {
     zoomDocument(document, 2.0);
   });
 
-  testTextNode("p1");
+  await testTextNode("p1");
 
   await ContentTask.spawn(browser, {}, () => {
     zoomDocument(document, 1.0);
   });
 }
 
 /**
  * Test the text range boundary when page is zoomed
--- a/accessible/tests/browser/bounds/head.js
+++ b/accessible/tests/browser/bounds/head.js
@@ -1,16 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 // Load the shared-head file first.
 /* import-globals-from ../shared-head.js */
+
+/* exported getContentDPR */
+
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
   this);
 
 // Loading and common.js from accessible/tests/mochitest/ for all tests, as
 // well as events.js.
 loadScripts({ name: "common.js", dir: MOCHITESTS_DIR },
             { name: "layout.js", dir: MOCHITESTS_DIR }, "events.js");
+
+/**
+ * Get content window DPR that can be different from parent window DPR.
+ */
+async function getContentDPR(browser) {
+  return ContentTask.spawn(browser, null, () => content.window.devicePixelRatio);
+}
--- a/accessible/tests/mochitest/bounds/test_list.html
+++ b/accessible/tests/mochitest/bounds/test_list.html
@@ -41,18 +41,18 @@
       [xLI, yLI, widthLI, heightLI] = getBounds(li);
 
       ok(xLI < xLIElm,
          "Outside list item x=" + xLI + " should be lesser than list item element x=" + xLIElm);
       is(yLI, yLIElm,
          "Outside list item y should match to list item element y");
       ok(widthLI > widthLIElm,
          "Outside list item width=" + widthLI + " should be greater than list item element width=" + widthLIElm);
-      is(heightLI, heightLIElm,
-         "Outside list item height should match to list item element height");
+      ok(heightLI >= Math.trunc(heightLIElm),
+         "Outside list item height=" + heightLI + " should not be less than list item element height=" + heightLIElm);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/layout.js
+++ b/accessible/tests/mochitest/layout.js
@@ -194,22 +194,35 @@ function getPos(aID) {
   var accessible = getAccessible(aID);
   var x = {}, y = {};
   accessible.getBounds(x, y, {}, {});
   return [x.value, y.value];
 }
 
 /**
  * Return the accessible coordinates and size relative to the screen in device
- * pixels.
+ * pixels. This methods also retrieves coordinates in CSS pixels and ensures that they
+ * match Dev pixels with a given device pixel ratio.
  */
-function getBounds(aID) {
-  var accessible = getAccessible(aID);
-  var x = {}, y = {}, width = {}, height = {};
+function getBounds(aID, aDPR = window.devicePixelRatio) {
+  const accessible = getAccessible(aID);
+  let x = {}, y = {}, width = {}, height = {};
+  let xInCSS = {}, yInCSS = {}, widthInCSS = {}, heightInCSS = {};
   accessible.getBounds(x, y, width, height);
+  accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS);
+
+  isWithin(x.value / aDPR, xInCSS.value, 1,
+    "Heights in CSS pixels is calculated correctly");
+  isWithin(y.value / aDPR, yInCSS.value, 1,
+    "Heights in CSS pixels is calculated correctly");
+  isWithin(width.value / aDPR, widthInCSS.value, 1,
+    "Heights in CSS pixels is calculated correctly");
+  isWithin(height.value / aDPR, heightInCSS.value, 1,
+    "Heights in CSS pixels is calculated correctly");
+
   return [x.value, y.value, width.value, height.value];
 }
 
 function getRangeExtents(aID, aStartOffset, aEndOffset, aCoordOrigin) {
   var hyperText = getAccessible(aID, [nsIAccessibleText]);
   var x = {}, y = {}, width = {}, height = {};
   hyperText.getRangeExtents(aStartOffset, aEndOffset,
                             x, y, width, height, aCoordOrigin);
--- a/accessible/windows/msaa/GeckoCustom.cpp
+++ b/accessible/windows/msaa/GeckoCustom.cpp
@@ -16,16 +16,31 @@ IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAcc
 HRESULT
 GeckoCustom::get_anchorCount(long* aCount)
 {
   *aCount = mAcc->AnchorCount();
   return S_OK;
 }
 
 HRESULT
+GeckoCustom::get_boundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                   int32_t* aWidth, int32_t* aHeight)
+{
+  nsIntRect bounds = mAcc->BoundsInCSSPixels();
+  if (!bounds.IsEmpty()) {
+    *aX = bounds.X();
+    *aY = bounds.Y();
+    *aWidth = bounds.Width();
+    *aHeight = bounds.Height();
+  }
+
+  return S_OK;
+}
+
+HRESULT
 GeckoCustom::get_DOMNodeID(BSTR* aID)
 {
   nsIContent* content = mAcc->GetContent();
   if (!content) {
     return S_OK;
   }
 
   nsAtom* id = content->GetID();
--- a/accessible/windows/msaa/GeckoCustom.h
+++ b/accessible/windows/msaa/GeckoCustom.h
@@ -22,16 +22,18 @@ class GeckoCustom final : public IGeckoC
 {
 public:
   explicit GeckoCustom(AccessibleWrap* aAcc) : mAcc(aAcc) {}
 
   // IUnknown
   DECL_IUNKNOWN
 
   virtual STDMETHODIMP get_anchorCount(long* aCount);
+  virtual STDMETHODIMP get_boundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                             int32_t* aWidth, int32_t* aHeight);
   virtual STDMETHODIMP get_DOMNodeID(BSTR* aID);
   virtual STDMETHODIMP get_ID(uint64_t* aID);
   virtual STDMETHODIMP get_minimumIncrement(double* aIncrement);
   virtual STDMETHODIMP get_mozState(uint64_t* aState);
 
 private:
   GeckoCustom() = delete;
   GeckoCustom& operator =(const GeckoCustom&) = delete;
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -452,16 +452,44 @@ xpcAccessible::GetBounds(int32_t* aX, in
     rect = IntlGeneric().AsProxy()->Bounds();
   }
 
   rect.GetRect(aX, aY, aWidth, aHeight);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+xpcAccessible::GetBoundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                  int32_t* aWidth, int32_t* aHeight)
+{
+  NS_ENSURE_ARG_POINTER(aX);
+  *aX = 0;
+  NS_ENSURE_ARG_POINTER(aY);
+  *aY = 0;
+  NS_ENSURE_ARG_POINTER(aWidth);
+  *aWidth = 0;
+  NS_ENSURE_ARG_POINTER(aHeight);
+  *aHeight = 0;
+
+  if (IntlGeneric().IsNull()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsIntRect rect;
+  if (Accessible* acc = IntlGeneric().AsAccessible()) {
+    rect = acc->BoundsInCSSPixels();
+  } else {
+    rect = IntlGeneric().AsProxy()->BoundsInCSSPixels();
+  }
+
+  rect.GetRect(aX, aY, aWidth, aHeight);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 xpcAccessible::GroupPosition(int32_t* aGroupLevel,
                              int32_t* aSimilarItemsInGroup,
                              int32_t* aPositionInGroup)
 {
   NS_ENSURE_ARG_POINTER(aGroupLevel);
   *aGroupLevel = 0;
 
   NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
--- a/accessible/xpcom/xpcAccessible.h
+++ b/accessible/xpcom/xpcAccessible.h
@@ -55,16 +55,18 @@ public:
 
   NS_IMETHOD GetAccessKey(nsAString& aAccessKey) final;
   NS_IMETHOD GetKeyboardShortcut(nsAString& aKeyBinding) final;
 
   NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes)
     final;
   NS_IMETHOD GetBounds(int32_t* aX, int32_t* aY,
                        int32_t* aWidth, int32_t* aHeight) final;
+  NS_IMETHOD GetBoundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                  int32_t* aWidth, int32_t* aHeight) final;
   NS_IMETHOD GroupPosition(int32_t* aGroupLevel, int32_t* aSimilarItemsInGroup,
                            int32_t* aPositionInGroup) final;
   NS_IMETHOD GetRelationByType(uint32_t aType,
                                nsIAccessibleRelation** aRelation)
     final;
   NS_IMETHOD GetRelations(nsIArray** aRelations) final;
 
   NS_IMETHOD GetFocusedChild(nsIAccessible** aChild) final;
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -717,47 +717,56 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHER
 
 Accessible*
 XULTreeItemAccessibleBase::FocusedChild()
 {
   return FocusMgr()->FocusedAccessible() == this ? this : nullptr;
 }
 
 nsIntRect
-XULTreeItemAccessibleBase::Bounds() const
+XULTreeItemAccessibleBase::BoundsInCSSPixels() const
 {
   // Get x coordinate and width from treechildren element, get y coordinate and
   // height from tree cell.
 
   nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
-  if (!boxObj)
+  if (!boxObj) {
     return nsIntRect();
+  }
 
   nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
 
   int32_t x = 0, y = 0, width = 0, height = 0;
   nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(),
                                             &x, &y, &width, &height);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return nsIntRect();
+  }
 
   boxObj->GetWidth(&width);
 
   int32_t tcX = 0, tcY = 0;
   boxObj->GetScreenX(&tcX);
   boxObj->GetScreenY(&tcY);
 
   x = tcX;
   y += tcY;
 
+  return nsIntRect(x, y, width, height);
+}
+
+nsRect
+XULTreeItemAccessibleBase::BoundsInAppUnits() const
+{
+  nsIntRect bounds = BoundsInCSSPixels();
   nsPresContext* presContext = mDoc->PresContext();
-  return nsIntRect(presContext->CSSPixelsToDevPixels(x),
-                   presContext->CSSPixelsToDevPixels(y),
-                   presContext->CSSPixelsToDevPixels(width),
-                   presContext->CSSPixelsToDevPixels(height));
+  return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+                presContext->CSSPixelsToAppUnits(bounds.Y()),
+                presContext->CSSPixelsToAppUnits(bounds.Width()),
+                presContext->CSSPixelsToAppUnits(bounds.Height()));
 }
 
 void
 XULTreeItemAccessibleBase::SetSelected(bool aSelect)
 {
   nsCOMPtr<nsITreeSelection> selection;
   mTreeView->GetSelection(getter_AddRefs(selection));
   if (selection) {
--- a/accessible/xul/XULTreeAccessible.h
+++ b/accessible/xul/XULTreeAccessible.h
@@ -141,17 +141,18 @@ public:
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessibleBase,
                                            AccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
+  virtual nsIntRect BoundsInCSSPixels() const override;
   virtual GroupPos GroupPosition() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual int32_t IndexInParent() const override;
   virtual Relation RelationByType(RelationType aType) override;
   virtual Accessible* FocusedChild() override;
   virtual void SetSelected(bool aSelect) override;
   virtual void TakeFocus() override;
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -490,42 +490,51 @@ XULTreeGridCellAccessible::Name(nsString
   // GetCellValue for that cell would return "starred" or "flagged" for example.
   if (aName.IsEmpty())
     mTreeView->GetCellValue(mRow, mColumn, aName);
 
   return eNameOK;
 }
 
 nsIntRect
-XULTreeGridCellAccessible::Bounds() const
+XULTreeGridCellAccessible::BoundsInCSSPixels() const
 {
   // Get bounds for tree cell and add x and y of treechildren element to
   // x and y of the cell.
   nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
-  if (!boxObj)
+  if (!boxObj) {
     return nsIntRect();
+  }
 
   int32_t x = 0, y = 0, width = 0, height = 0;
   nsresult rv = mTree->GetCoordsForCellItem(mRow, mColumn,
                                             NS_LITERAL_STRING("cell"),
                                             &x, &y, &width, &height);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return nsIntRect();
+  }
 
   int32_t tcX = 0, tcY = 0;
   boxObj->GetScreenX(&tcX);
   boxObj->GetScreenY(&tcY);
   x += tcX;
   y += tcY;
 
+  return nsIntRect(x, y, width, height);
+}
+
+nsRect
+XULTreeGridCellAccessible::BoundsInAppUnits() const
+{
+  nsIntRect bounds = BoundsInCSSPixels();
   nsPresContext* presContext = mDoc->PresContext();
-  return nsIntRect(presContext->CSSPixelsToDevPixels(x),
-                   presContext->CSSPixelsToDevPixels(y),
-                   presContext->CSSPixelsToDevPixels(width),
-                   presContext->CSSPixelsToDevPixels(height));
+  return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+                presContext->CSSPixelsToAppUnits(bounds.Y()),
+                presContext->CSSPixelsToAppUnits(bounds.Width()),
+                presContext->CSSPixelsToAppUnits(bounds.Height()));
 }
 
 uint8_t
 XULTreeGridCellAccessible::ActionCount()
 {
   bool isCycler = false;
   mColumn->GetCycler(&isCycler);
   if (isCycler)
--- a/accessible/xul/XULTreeGridAccessible.h
+++ b/accessible/xul/XULTreeGridAccessible.h
@@ -121,17 +121,18 @@ public:
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridCellAccessible,
                                            LeafAccessible)
 
   // Accessible
   virtual void Shutdown() override;
   virtual TableCellAccessible* AsTableCell() override { return this; }
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
+  virtual nsIntRect BoundsInCSSPixels() const override;
   virtual ENameValueFlag Name(nsString& aName) override;
   virtual Accessible* FocusedChild() override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual int32_t IndexInParent() const override;
   virtual Relation RelationByType(RelationType aType) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -1,13 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
+ChromeUtils.defineModuleGetter(this, "Blocklist",
+                               "resource://gre/modules/Blocklist.jsm");
+
 var gPluginHandler = {
   PREF_SESSION_PERSIST_MINUTES: "plugin.sessionPermissionNow.intervalInMinutes",
   PREF_PERSISTENT_DAYS: "plugin.persistentPermissionAlways.intervalInDays",
   PREF_SHOW_INFOBAR: "plugins.show_infobar",
   PREF_INFOBAR_DISMISSAL_PERMANENT: "plugins.remember_infobar_dismissal",
 
   MESSAGES: [
     "PluginContent:ShowClickToPlayNotification",
@@ -89,19 +92,19 @@ var gPluginHandler = {
   // Callback for user clicking on a disabled plugin
   managePlugins() {
     BrowserOpenAddonsMgr("addons://list/plugin");
   },
 
   // Callback for user clicking on the link in a click-to-play plugin
   // (where the plugin has an update)
   openPluginUpdatePage(pluginTag) {
-    let url = Services.blocklist.getPluginInfoURL(pluginTag);
+    let url = Blocklist.getPluginInfoURL(pluginTag);
     if (!url) {
-      url = Services.blocklist.getPluginBlocklistURL(pluginTag);
+      url = Blocklist.getPluginBlocklistURL(pluginTag);
     }
     openTrustedLinkIn(url, "tab");
   },
 
   submitReport: function submitReport(runID, keyVals, submitURLOptIn) {
     if (!AppConstants.MOZ_CRASHREPORTER) {
       return;
     }
@@ -266,21 +269,21 @@ var gPluginHandler = {
 
     for (let pluginInfo of plugins) {
       if (pluginData.has(pluginInfo.permissionString)) {
         continue;
       }
 
       // If a block contains an infoURL, we should always prefer that to the default
       // URL that we construct in-product, even for other blocklist types.
-      let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
+      let url = Blocklist.getPluginInfoURL(pluginInfo.pluginTag);
 
       if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
         if (!url) {
-          url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
+          url = Blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
         }
       } else {
         url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
       }
       pluginInfo.detailsLink = url;
 
       pluginData.set(pluginInfo.permissionString, pluginInfo);
     }
@@ -300,21 +303,21 @@ var gPluginHandler = {
       }
       return;
     }
 
     if (plugins.length == 1) {
       let pluginInfo = plugins[0];
       // If a block contains an infoURL, we should always prefer that to the default
       // URL that we construct in-product, even for other blocklist types.
-      let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
+      let url = Blocklist.getPluginInfoURL(pluginInfo.pluginTag);
 
       if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
         if (!url) {
-          url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
+          url = Blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
         }
       } else {
         url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
       }
       pluginInfo.detailsLink = url;
 
       let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
       let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);
--- a/browser/base/content/test/general/browser_bug521216.js
+++ b/browser/base/content/test/general/browser_bug521216.js
@@ -14,16 +14,17 @@ function test() {
 function record(aName) {
   info("got " + aName);
   if (!actual.includes(aName))
     actual.push(aName);
   if (actual.length == expected.length) {
     is(actual.toString(), expected.toString(),
        "got events and progress notifications in expected order");
 
+    // eslint-disable-next-line no-shadow
     executeSoon(function(tab) {
       gBrowser.removeTab(tab);
       gBrowser.removeTabsProgressListener(progressListener);
       gBrowser.tabContainer.removeEventListener("TabOpen", TabOpen);
       finish();
     }.bind(null, tab));
   }
 }
--- a/browser/base/content/test/sanitize/browser_sanitize-offlineData.js
+++ b/browser/base/content/test/sanitize/browser_sanitize-offlineData.js
@@ -34,18 +34,16 @@ function hasIndexedDB(origin) {
 
 function waitForUnregister(host) {
   return new Promise(resolve => {
     let listener = {
       onUnregister: registration => {
         if (registration.principal.URI.host != host) {
           return;
         }
-        let swm = Cc["@mozilla.org/serviceworkers/manager;1"]
-                    .getService(Ci.nsIServiceWorkerManager);
         swm.removeListener(listener);
         resolve(registration);
       }
     };
     swm.addListener(listener);
   });
 }
 
--- a/browser/base/content/test/webextensions/head.js
+++ b/browser/base/content/test/webextensions/head.js
@@ -1,14 +1,15 @@
 
 const BASE = getRootDirectory(gTestPath)
   .replace("chrome://mochitests/content/", "https://example.com/");
 
 ChromeUtils.import("resource:///modules/ExtensionsUI.jsm");
 XPCOMUtils.defineLazyGetter(this, "Management", () => {
+  // eslint-disable-next-line no-shadow
   const {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
   return Management;
 });
 
 ChromeUtils.import("resource://testing-common/CustomizableUITestUtils.jsm", this);
 let gCUITestUtils = new CustomizableUITestUtils(window);
 
 /**
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -9,34 +9,28 @@ var EXPORTED_SYMBOLS = ["CustomizableWid
 ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
   PanelView: "resource:///modules/PanelMultiView.jsm",
-  PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
-  PlacesUIUtils: "resource:///modules/PlacesUIUtils.jsm",
   RecentlyClosedTabsAndWindowsMenuUtils: "resource:///modules/sessionstore/RecentlyClosedTabsAndWindowsMenuUtils.jsm",
   ShortcutUtils: "resource://gre/modules/ShortcutUtils.jsm",
   CharsetMenu: "resource://gre/modules/CharsetMenu.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   Sanitizer: "resource:///modules/Sanitizer.jsm",
   SyncedTabs: "resource://services-sync/SyncedTabs.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "CharsetBundle", function() {
   const kCharsetBundle = "chrome://global/locale/charsetMenu.properties";
   return Services.strings.createBundle(kCharsetBundle);
 });
-XPCOMUtils.defineLazyGetter(this, "BrandBundle", function() {
-  const kBrandBundle = "chrome://branding/locale/brand.properties";
-  return Services.strings.createBundle(kBrandBundle);
-});
 
 const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 const kPrefCustomizationDebug = "browser.uiCustomization.debug";
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let scope = {};
   ChromeUtils.import("resource://gre/modules/Console.jsm", scope);
   let debug = Services.prefs.getBoolPref(kPrefCustomizationDebug, false);
--- a/browser/components/customizableui/PanelMultiView.jsm
+++ b/browser/components/customizableui/PanelMultiView.jsm
@@ -101,18 +101,16 @@
 
 var EXPORTED_SYMBOLS = [
   "PanelMultiView",
   "PanelView",
 ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-ChromeUtils.defineModuleGetter(this, "AppConstants",
-  "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gBundle", function() {
   return Services.strings.createBundle(
     "chrome://browser/locale/browser.properties");
 });
 
--- a/browser/components/downloads/DownloadsCommon.jsm
+++ b/browser/components/downloads/DownloadsCommon.jsm
@@ -39,18 +39,16 @@ XPCOMUtils.defineLazyModuleGetters(this,
   NetUtil: "resource://gre/modules/NetUtil.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   DownloadHistory: "resource://gre/modules/DownloadHistory.jsm",
   Downloads: "resource://gre/modules/Downloads.jsm",
   DownloadUIHelper: "resource://gre/modules/DownloadUIHelper.jsm",
   DownloadUtils: "resource://gre/modules/DownloadUtils.jsm",
-  FileUtils: "resource://gre/modules/FileUtils.jsm",
-  OS: "resource://gre/modules/osfile.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
 });
 
 XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   let consoleOptions = {
     maxLogLevelPref: "browser.download.loglevel",
     prefix: "Downloads"
--- a/browser/components/extensions/parent/ext-browsingData.js
+++ b/browser/components/extensions/parent/ext-browsingData.js
@@ -11,19 +11,16 @@ ChromeUtils.defineModuleGetter(this, "Sa
                                "resource:///modules/Sanitizer.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "setTimeout",
                                "resource://gre/modules/Timer.jsm");
 ChromeUtils.defineModuleGetter(this, "ServiceWorkerCleanUp",
                                "resource://gre/modules/ServiceWorkerCleanUp.jsm");
 
-XPCOMUtils.defineLazyServiceGetter(this, "serviceWorkerManager",
-                                   "@mozilla.org/serviceworkers/manager;1",
-                                   "nsIServiceWorkerManager");
 XPCOMUtils.defineLazyServiceGetter(this, "quotaManagerService",
                                    "@mozilla.org/dom/quota-manager-service;1",
                                    "nsIQuotaManagerService");
 
 /**
 * A number of iterations after which to yield time back
 * to the system.
 */
--- a/browser/components/extensions/parent/ext-chrome-settings-overrides.js
+++ b/browser/components/extensions/parent/ext-chrome-settings-overrides.js
@@ -1,18 +1,14 @@
 /* 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";
 
-ChromeUtils.defineModuleGetter(this, "AddonManager",
-                               "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "BrowserUtils",
-                               "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionPreferencesManager",
                                "resource://gre/modules/ExtensionPreferencesManager.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 
 const DEFAULT_SEARCH_STORE_TYPE = "default_search";
--- a/browser/components/extensions/parent/ext-url-overrides.js
+++ b/browser/components/extensions/parent/ext-url-overrides.js
@@ -1,18 +1,14 @@
 /* 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";
 
-ChromeUtils.defineModuleGetter(this, "AddonManager",
-                               "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "CustomizableUI",
-                               "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionControlledPopup",
                                "resource:///modules/ExtensionControlledPopup.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                "resource://gre/modules/ExtensionSettingsStore.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -120,16 +120,19 @@ skip-if = debug && (os == 'linux' || os 
 [browser_ext_pageAction_title.js]
 [browser_ext_popup_api_injection.js]
 [browser_ext_popup_background.js]
 [browser_ext_popup_corners.js]
 [browser_ext_popup_focus.js]
 disabled = bug 1438663
 [browser_ext_popup_sendMessage.js]
 [browser_ext_popup_shutdown.js]
+[browser_ext_port_disconnect_on_crash.js]
+skip-if = !e10s || !crashreporter # the tab's process is killed during the test. Without e10s the parent process would die too.
+[browser_ext_port_disconnect_on_window_close.js]
 [browser_ext_runtime_openOptionsPage.js]
 [browser_ext_runtime_openOptionsPage_uninstall.js]
 [browser_ext_runtime_setUninstallURL.js]
 [browser_ext_sessions_forgetClosedTab.js]
 [browser_ext_sessions_forgetClosedWindow.js]
 [browser_ext_sessions_getRecentlyClosed.js]
 [browser_ext_sessions_getRecentlyClosed_private.js]
 [browser_ext_sessions_getRecentlyClosed_tabs.js]
--- a/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
+++ b/browser/components/extensions/test/browser/browser_ext_devtools_panels_elements_sidebar.js
@@ -1,18 +1,16 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "gDevTools",
                                "resource://devtools/client/framework/gDevTools.jsm");
 ChromeUtils.defineModuleGetter(this, "devtools",
                                "resource://devtools/shared/Loader.jsm");
-ChromeUtils.defineModuleGetter(this, "ContentTaskUtils",
-                               "resource://testing-common/ContentTaskUtils.jsm");
 
 /* globals getExtensionSidebarActors, expectNoSuchActorIDs, testSetExpressionSidebarPanel */
 
 // Import the shared test helpers from the related devtools tests.
 Services.scriptloader.loadSubScript(
   new URL("head_devtools_inspector_sidebar.js", gTestPath).href,
   this);
 
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_port_disconnect_on_crash.js
@@ -0,0 +1,91 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+add_task(async function connect_from_tab_to_bg_and_crash_tab() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [{
+        "js": ["contentscript.js"],
+        "matches": ["http://example.com/?crashme"],
+      }],
+    },
+
+    background() {
+      browser.runtime.onConnect.addListener((port) => {
+        browser.test.assertEq("tab_to_bg", port.name, "expected port");
+        port.onDisconnect.addListener(() => {
+          browser.test.assertEq(null, port.error, "port should be disconnected without errors");
+          browser.test.sendMessage("port_disconnected");
+        });
+        browser.test.sendMessage("bg_runtime_onConnect");
+      });
+    },
+
+    files: {
+      "contentscript.js": function() {
+        let port = browser.runtime.connect({name: "tab_to_bg"});
+        port.onDisconnect.addListener(() => {
+          browser.test.fail("Unexpected onDisconnect event in content script");
+        });
+      },
+    },
+  });
+
+  await extension.startup();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/?crashme");
+  await extension.awaitMessage("bg_runtime_onConnect");
+  // Force the message manager to disconnect without giving the content a
+  // chance to send an "Extension:Port:Disconnect" message.
+  await BrowserTestUtils.crashBrowser(tab.linkedBrowser);
+  await extension.awaitMessage("port_disconnected");
+  BrowserTestUtils.removeTab(tab);
+  await extension.unload();
+});
+
+add_task(async function connect_from_bg_to_tab_and_crash_tab() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "content_scripts": [{
+        "js": ["contentscript.js"],
+        "matches": ["http://example.com/?crashme"],
+      }],
+    },
+
+    background() {
+      browser.runtime.onMessage.addListener((msg, sender) => {
+        browser.test.assertEq("contentscript_ready", msg, "expected message");
+        let port = browser.tabs.connect(sender.tab.id, {name: "bg_to_tab"});
+        port.onDisconnect.addListener(() => {
+          browser.test.assertEq(null, port.error, "port should be disconnected without errors");
+          browser.test.sendMessage("port_disconnected");
+        });
+      });
+    },
+
+    files: {
+      "contentscript.js": function() {
+        browser.runtime.onConnect.addListener((port) => {
+          browser.test.assertEq("bg_to_tab", port.name, "expected port");
+          port.onDisconnect.addListener(() => {
+            browser.test.fail("Unexpected onDisconnect event in content script");
+          });
+          browser.test.sendMessage("tab_runtime_onConnect");
+        });
+        browser.runtime.sendMessage("contentscript_ready");
+      },
+    },
+  });
+
+  await extension.startup();
+
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com/?crashme");
+  await extension.awaitMessage("tab_runtime_onConnect");
+  // Force the message manager to disconnect without giving the content a
+  // chance to send an "Extension:Port:Disconnect" message.
+  await BrowserTestUtils.crashBrowser(tab.linkedBrowser);
+  await extension.awaitMessage("port_disconnected");
+  BrowserTestUtils.removeTab(tab);
+  await extension.unload();
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_port_disconnect_on_window_close.js
@@ -0,0 +1,36 @@
+/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set sts=2 sw=2 et tw=80: */
+"use strict";
+
+// Regression test for https://bugzil.la/1392067 .
+add_task(async function connect_from_window_and_close() {
+  let extension = ExtensionTestUtils.loadExtension({
+    background() {
+      browser.runtime.onConnect.addListener((port) => {
+        browser.test.assertEq("page_to_bg", port.name, "expected port");
+        port.onDisconnect.addListener(() => {
+          browser.test.assertEq(null, port.error, "port should be disconnected without errors");
+          browser.test.sendMessage("port_disconnected");
+        });
+        browser.windows.remove(port.sender.tab.windowId);
+      });
+
+      browser.windows.create({url: "page.html"});
+    },
+
+    files: {
+      "page.html":
+        `<!DOCTYPE html><meta charset="utf-8"><script src="page.js"></script>`,
+      "page.js": function() {
+        let port = browser.runtime.connect({name: "page_to_bg"});
+        port.onDisconnect.addListener(() => {
+          browser.test.fail("Unexpected onDisconnect event in page");
+        });
+      },
+    },
+  });
+
+  await extension.startup();
+  await extension.awaitMessage("port_disconnected");
+  await extension.unload();
+});
--- a/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js
+++ b/browser/components/extensions/test/browser/browser_ext_webNavigation_urlbar_transitions.js
@@ -1,16 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
-                               "resource://testing-common/PlacesTestUtils.jsm");
 
 const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
 const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";
 
 async function promiseAutocompleteResultPopup(inputText) {
   gURLBar.focus();
   gURLBar.value = inputText;
   gURLBar.controller.startSearch(inputText);
--- a/browser/components/extensions/test/xpcshell/head.js
+++ b/browser/components/extensions/test/xpcshell/head.js
@@ -1,33 +1,27 @@
 "use strict";
 
 /* exported createHttpServer, promiseConsoleOutput  */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "AppConstants",
-                               "resource://gre/modules/AppConstants.jsm");
-ChromeUtils.defineModuleGetter(this, "Extension",
-                               "resource://gre/modules/Extension.jsm");
-ChromeUtils.defineModuleGetter(this, "ExtensionData",
-                               "resource://gre/modules/Extension.jsm");
-ChromeUtils.defineModuleGetter(this, "ExtensionTestUtils",
-                               "resource://testing-common/ExtensionXPCShellUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "FileUtils",
-                               "resource://gre/modules/FileUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "HttpServer",
-                               "resource://testing-common/httpd.js");
-ChromeUtils.defineModuleGetter(this, "NetUtil",
-                               "resource://gre/modules/NetUtil.jsm");
-ChromeUtils.defineModuleGetter(this, "Schemas",
-                               "resource://gre/modules/Schemas.jsm");
-ChromeUtils.defineModuleGetter(this, "TestUtils",
-                               "resource://testing-common/TestUtils.jsm");
+// eslint-disable-next-line no-unused-vars
+XPCOMUtils.defineLazyModuleGetters(this, {
+  AppConstants: "resource://gre/modules/AppConstants.jsm",
+  Extension: "resource://gre/modules/Extension.jsm",
+  ExtensionData: "resource://gre/modules/Extension.jsm",
+  ExtensionTestUtils: "resource://testing-common/ExtensionXPCShellUtils.jsm",
+  FileUtils: "resource://gre/modules/FileUtils.jsm",
+  HttpServer: "resource://testing-common/httpd.js",
+  NetUtil: "resource://gre/modules/NetUtil.jsm",
+  Schemas: "resource://gre/modules/Schemas.jsm",
+  TestUtils: "resource://testing-common/TestUtils.jsm",
+});
 
 Services.prefs.setBoolPref("extensions.webextensions.remote", false);
 
 ExtensionTestUtils.init(this);
 
 
 /**
  * Creates a new HttpServer for testing, and begins listening on the
--- a/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab.js
@@ -1,21 +1,20 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 XPCOMUtils.defineLazyGetter(this, "Management", () => {
+  // eslint-disable-next-line no-shadow
   const {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
   return Management;
 });
 
 ChromeUtils.defineModuleGetter(this, "AddonManager",
                                "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "Preferences",
-                               "resource://gre/modules/Preferences.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 
 const {
--- a/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab_update.js
+++ b/browser/components/extensions/test/xpcshell/test_ext_url_overrides_newtab_update.js
@@ -1,22 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-XPCOMUtils.defineLazyGetter(this, "Management", () => {
-  const {Management} = ChromeUtils.import("resource://gre/modules/Extension.jsm", {});
-  return Management;
-});
-
-ChromeUtils.defineModuleGetter(this, "AddonManager",
-                               "resource://gre/modules/AddonManager.jsm");
-ChromeUtils.defineModuleGetter(this, "Preferences",
-                               "resource://gre/modules/Preferences.jsm");
-
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 ChromeUtils.import("resource://testing-common/AddonTestUtils.jsm");
 
 const {
   createAppInfo,
--- a/browser/components/migration/FirefoxProfileMigrator.js
+++ b/browser/components/migration/FirefoxProfileMigrator.js
@@ -22,18 +22,16 @@ ChromeUtils.defineModuleGetter(this, "Pl
 ChromeUtils.defineModuleGetter(this, "SessionMigration",
                                "resource:///modules/sessionstore/SessionMigration.jsm");
 ChromeUtils.defineModuleGetter(this, "OS",
                                "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "ProfileAge",
                                "resource://gre/modules/ProfileAge.jsm");
-ChromeUtils.defineModuleGetter(this, "AppConstants",
-                               "resource://gre/modules/AppConstants.jsm");
 
 
 function FirefoxProfileMigrator() {
   this.wrappedJSObject = this; // for testing...
 }
 
 FirefoxProfileMigrator.prototype = Object.create(MigratorPrototype);
 
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -15,18 +15,16 @@ ChromeUtils.import("resource:///modules/
 
 
 ChromeUtils.defineModuleGetter(this, "ctypes",
                                "resource://gre/modules/ctypes.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "OSCrypto",
                                "resource://gre/modules/OSCrypto.jsm");
-ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
-                               "resource://gre/modules/WindowsRegistry.jsm");
 
 Cu.importGlobalProperties(["URL"]);
 
 // Resources
 
 function History() {
 }
 
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -6,18 +6,16 @@
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Downloads",
-                               "resource://gre/modules/Downloads.jsm");
 ChromeUtils.defineModuleGetter(this, "PropertyListUtils",
                                "resource://gre/modules/PropertyListUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "FormHistory",
                                "resource://gre/modules/FormHistory.jsm");
 
 Cu.importGlobalProperties(["URL"]);
--- a/browser/components/migration/tests/unit/head_migration.js
+++ b/browser/components/migration/tests/unit/head_migration.js
@@ -10,20 +10,23 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 ChromeUtils.import("resource://testing-common/PlacesTestUtils.jsm");
 
+// eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
+// eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "Sqlite",
                                "resource://gre/modules/Sqlite.jsm");
+
 // Initialize profile.
 var gProfD = do_get_profile();
 
 ChromeUtils.import("resource://testing-common/AppInfo.jsm");
 updateAppInfo();
 
 /**
  * Migrates the requested resource and waits for the migration to be complete.
--- a/browser/components/migration/tests/unit/test_IE7_passwords.js
+++ b/browser/components/migration/tests/unit/test_IE7_passwords.js
@@ -1,15 +1,13 @@
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
-                               "resource://gre/modules/WindowsRegistry.jsm");
 ChromeUtils.defineModuleGetter(this, "OSCrypto",
                                "resource://gre/modules/OSCrypto.jsm");
 
 const IE7_FORM_PASSWORDS_MIGRATOR_NAME = "IE7FormPasswords";
 const LOGINS_KEY = "Software\\Microsoft\\Internet Explorer\\IntelliForms\\Storage2";
 const EXTENSION = "-backup";
 const TESTED_WEBSITES = {
   twitter: {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -86,16 +86,17 @@ XPCOMUtils.defineLazyGetter(this, "Weave
 // lazy module getters
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
   AsyncPrefs: "resource://gre/modules/AsyncPrefs.jsm",
   AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
   AutoCompletePopup: "resource://gre/modules/AutoCompletePopup.jsm",
+  Blocklist: "resource://gre/modules/Blocklist.jsm",
   BookmarkHTMLUtils: "resource://gre/modules/BookmarkHTMLUtils.jsm",
   BookmarkJSONUtils: "resource://gre/modules/BookmarkJSONUtils.jsm",
   BrowserErrorReporter: "resource:///modules/BrowserErrorReporter.jsm",
   BrowserUITelemetry: "resource:///modules/BrowserUITelemetry.jsm",
   BrowserUsageTelemetry: "resource:///modules/BrowserUsageTelemetry.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   ContentClick: "resource:///modules/ContentClick.jsm",
   ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
@@ -1218,17 +1219,17 @@ BrowserGlue.prototype = {
       });
     }
 
     Services.tm.idleDispatchToMainThread(() => {
       LanguagePrompt.init();
     });
 
     Services.tm.idleDispatchToMainThread(() => {
-      Services.blocklist.loadBlocklistAsync();
+      Blocklist.loadBlocklistAsync();
     });
   },
 
   /**
    * Use this function as an entry point to schedule tasks that need
    * to run once per session, at any arbitrary point in time.
    * This function will be called from an idle observer. Check the value of
    * LATE_TASKS_IDLE_TIME_SEC to see the current value for this idle
--- a/browser/components/preferences/connection.js
+++ b/browser/components/preferences/connection.js
@@ -269,13 +269,13 @@ var gConnectionsDialog = {
       }
     }
 
     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)
+      handleControllingExtension(PREF_SETTING_TYPE, PROXY_KEY, "extensionControlled.proxyConfig")
         .then(setInputsDisabledState);
     }
   }
 };
--- a/browser/components/preferences/in-content/extensionControlled.js
+++ b/browser/components/preferences/in-content/extensionControlled.js
@@ -16,17 +16,17 @@ ChromeUtils.defineModuleGetter(this, "De
                                   "resource://gre/modules/DeferredTask.jsm");
 ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
                                   "resource://gre/modules/ExtensionSettingsStore.jsm");
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "trackingprotectionUiEnabled",
                                       "privacy.trackingprotection.ui.enabled");
 
 const PREF_SETTING_TYPE = "prefs";
-const PROXY_KEY = "proxyConfig";
+const PROXY_KEY = "proxy.settings";
 const API_PROXY_PREFS = [
   "network.proxy.type",
   "network.proxy.http",
   "network.proxy.http_port",
   "network.proxy.share_proxy_settings",
   "network.proxy.ftp",
   "network.proxy.ftp_port",
   "network.proxy.ssl",
@@ -40,17 +40,17 @@ const API_PROXY_PREFS = [
   "signon.autologin.proxy",
 ];
 
 let extensionControlledContentIds = {
   "privacy.containers": "browserContainersExtensionContent",
   "homepage_override": "browserHomePageExtensionContent",
   "newTabURL": "browserNewTabExtensionContent",
   "defaultSearch": "browserDefaultSearchExtensionContent",
-  "proxyConfig": "proxyExtensionContent",
+  "proxy.settings": "proxyExtensionContent",
   get "websites.trackingProtectionMode"() {
     return {
       button: "trackingProtectionExtensionContentButton",
       section:
         trackingprotectionUiEnabled ?
           "trackingProtectionExtensionContentLabel" :
           "trackingProtectionPBMExtensionContentLabel",
     };
@@ -58,17 +58,17 @@ let extensionControlledContentIds = {
 };
 
 const extensionControlledL10nKeys = {
   "homepage_override": "homepage-override",
   "newTabURL": "new-tab-url",
   "defaultSearch": "default-search",
   "privacy.containers": "privacy-containers",
   "websites.trackingProtectionMode": "websites-tracking-protection-mode",
-  "proxyConfig": "proxy-config",
+  "proxy.settings": "proxy-config",
 };
 
 let extensionControlledIds = {};
 
 /**
   * Check if a pref is being managed by an extension.
   */
 async function getControllingExtensionInfo(type, settingName) {
--- a/browser/components/preferences/in-content/tests/browser_extension_controlled.js
+++ b/browser/components/preferences/in-content/tests/browser_extension_controlled.js
@@ -655,17 +655,17 @@ add_task(async function testExtensionCon
   const CONTROLLED_SECTION_ID = "proxyExtensionContent";
   const CONTROLLED_BUTTON_ID = "disableProxyExtension";
   const CONNECTION_SETTINGS_DESC_ID = "connectionSettingsDescription";
   const PANEL_URL = "chrome://browser/content/preferences/connection.xul";
 
   await SpecialPowers.pushPrefEnv({"set": [[PROXY_PREF, PROXY_DEFAULT]]});
 
   function background() {
-    browser.browserSettings.proxyConfig.set({value: {proxyType: "none"}});
+    browser.proxy.settings.set({value: {proxyType: "none"}});
   }
 
   function expectedConnectionSettingsMessage(doc, isControlled) {
     return isControlled ?
       "extension-controlled-proxy-config" :
       "network-proxy-connection-description";
   }
 
@@ -793,17 +793,17 @@ add_task(async function testExtensionCon
   verifyState(mainDoc, false);
 
   // Install an extension that controls proxy settings.
   let extension = ExtensionTestUtils.loadExtension({
     useAddonManager: "permanent",
     manifest: {
       name: "set_proxy",
       applications: {gecko: {id: EXTENSION_ID}},
-      permissions: ["browserSettings"],
+      permissions: ["proxy"],
     },
     background,
   });
 
   let messageChanged = connectionSettingsMessagePromise(mainDoc, true);
   await extension.startup();
   await messageChanged;
   let addon = await AddonManager.getAddonByID(EXTENSION_ID);
--- a/browser/components/sessionstore/SessionCookies.jsm
+++ b/browser/components/sessionstore/SessionCookies.jsm
@@ -4,18 +4,16 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["SessionCookies"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", this);
 
-ChromeUtils.defineModuleGetter(this, "Utils",
-  "resource://gre/modules/sessionstore/Utils.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivacyLevel",
   "resource://gre/modules/sessionstore/PrivacyLevel.jsm");
 
 const MAX_EXPIRY = Number.MAX_SAFE_INTEGER;
 
 /**
  * The external API implemented by the SessionCookies module.
  */
--- a/browser/components/sessionstore/SessionFile.jsm
+++ b/browser/components/sessionstore/SessionFile.jsm
@@ -27,18 +27,16 @@ var EXPORTED_SYMBOLS = ["SessionFile"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 ChromeUtils.import("resource://gre/modules/AsyncShutdown.jsm");
 
 ChromeUtils.defineModuleGetter(this, "RunState",
   "resource:///modules/sessionstore/RunState.jsm");
-ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
-  "resource://gre/modules/TelemetryStopwatch.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
   "@mozilla.org/base/telemetry;1", "nsITelemetry");
 XPCOMUtils.defineLazyServiceGetter(this, "sessionStartup",
   "@mozilla.org/browser/sessionstartup;1", "nsISessionStartup");
 ChromeUtils.defineModuleGetter(this, "SessionWorker",
   "resource:///modules/sessionstore/SessionWorker.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
   "resource:///modules/sessionstore/SessionStore.jsm");
--- a/browser/extensions/formautofill/test/unit/head.js
+++ b/browser/extensions/formautofill/test/unit/head.js
@@ -12,18 +12,20 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://gre/modules/ObjectUtils.jsm");
 ChromeUtils.import("resource://gre/modules/FormLikeFactory.jsm");
 ChromeUtils.import("resource://testing-common/FileTestUtils.jsm");
 ChromeUtils.import("resource://testing-common/MockDocument.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 
+// eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "DownloadPaths",
                                "resource://gre/modules/DownloadPaths.jsm");
+// eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "resProto",
                                    "@mozilla.org/network/protocol;1?name=resource",
                                    "nsISubstitutingProtocolHandler");
 
 do_get_profile();
--- a/browser/extensions/onboarding/OnboardingTelemetry.jsm
+++ b/browser/extensions/onboarding/OnboardingTelemetry.jsm
@@ -4,17 +4,16 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["OnboardingTelemetry"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetters(this, {
   PingCentre: "resource:///modules/PingCentre.jsm",
-  Services: "resource://gre/modules/Services.jsm",
 });
 XPCOMUtils.defineLazyServiceGetter(this, "gUUIDGenerator",
   "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");
 
 // Validate the content has non-empty string
 function hasString(str) {
   return typeof str == "string" && str.length > 0;
 }
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -299,16 +299,17 @@ var PdfJs = {
     if (Services.appinfo.processType !==
         Services.appinfo.PROCESS_TYPE_DEFAULT) {
       throw new Error("Only the parent process should be observing PDF " +
                       "handler changes.");
     }
 
     this.updateRegistration();
     let jsm = "resource://pdf.js/PdfjsChromeUtils.jsm";
+    // eslint-disable-next-line no-shadow
     let PdfjsChromeUtils = ChromeUtils.import(jsm, {}).PdfjsChromeUtils;
     PdfjsChromeUtils.notifyChildOfSettingsChange(this.enabled);
   },
 
   /**
    * pdf.js is only enabled if it is both selected as the pdf viewer and if the
    * global switch enabling it is true.
    * @return {boolean} Whether or not it's enabled.
--- a/browser/extensions/pocket/content/AboutPocket.jsm
+++ b/browser/extensions/pocket/content/AboutPocket.jsm
@@ -6,16 +6,17 @@
 const Cm = Components.manager;
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 // See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
 const PREF_LOG_LEVEL = "loop.debug.loglevel";
 
+// eslint-disable-next-line no-unused-vars
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
   let consoleOptions = {
     maxLogLevelPref: PREF_LOG_LEVEL,
     prefix: "Loop"
   };
   return new ConsoleAPI(consoleOptions);
 });
--- a/browser/extensions/pocket/content/Pocket.jsm
+++ b/browser/extensions/pocket/content/Pocket.jsm
@@ -6,18 +6,16 @@
 
 var EXPORTED_SYMBOLS = ["Pocket"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
   "resource://gre/modules/BrowserUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "ReaderMode",
-  "resource://gre/modules/ReaderMode.jsm");
 
 var Pocket = {
   get site() { return Services.prefs.getCharPref("extensions.pocket.site"); },
   get listURL() { return "https://" + Pocket.site + "/?src=ff_ext"; },
 
   openList(event) {
     let win = event.view;
     let where = win.whereToOpenLink(event);
--- a/browser/extensions/webcompat/content/lib/ua_overrider.jsm
+++ b/browser/extensions/webcompat/content/lib/ua_overrider.jsm
@@ -1,15 +1,14 @@
 /* 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/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "UserAgentOverrides", "resource://gre/modules/UserAgentOverrides.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "eTLDService", "@mozilla.org/network/effective-tld-service;1", "nsIEffectiveTLDService");
 
 class UAOverrider {
   constructor(overrides) {
     this._overrides = {};
 
     this.initOverrides(overrides);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -225,17 +225,16 @@
 @RESPATH@/components/NetworkGeolocationProvider.js
 @RESPATH@/components/extensions.manifest
 @RESPATH@/components/EditorUtils.manifest
 @RESPATH@/components/EditorUtils.js
 @RESPATH@/components/addonManager.js
 @RESPATH@/components/amContentHandler.js
 @RESPATH@/components/amInstallTrigger.js
 @RESPATH@/components/amWebAPI.js
-@RESPATH@/components/nsBlocklistService.js
 #ifdef MOZ_UPDATER
 @RESPATH@/components/nsUpdateService.manifest
 @RESPATH@/components/nsUpdateService.js
 @RESPATH@/components/nsUpdateServiceStub.js
 #endif
 @RESPATH@/components/nsUpdateTimerManager.manifest
 @RESPATH@/components/nsUpdateTimerManager.js
 @RESPATH@/components/utils.manifest
--- a/browser/modules/AboutHome.jsm
+++ b/browser/modules/AboutHome.jsm
@@ -8,20 +8,16 @@ var EXPORTED_SYMBOLS = [ "AboutHomeUtils
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "AutoMigrate",
   "resource:///modules/AutoMigrate.jsm");
-ChromeUtils.defineModuleGetter(this, "fxAccounts",
-  "resource://gre/modules/FxAccounts.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
-  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "SessionStore",
   "resource:///modules/sessionstore/SessionStore.jsm");
 
 // Url to fetch snippets, in the urlFormatter service format.
 const SNIPPETS_URL_PREF = "browser.aboutHomeSnippets.updateUrl";
 
 // Should be bumped up if the snippets content format changes.
 const STARTPAGE_VERSION = 4;
--- a/browser/modules/BrowserUITelemetry.jsm
+++ b/browser/modules/BrowserUITelemetry.jsm
@@ -5,20 +5,18 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["BrowserUITelemetry"];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
-  AppConstants: "resource://gre/modules/AppConstants.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   CustomizableUI: "resource:///modules/CustomizableUI.jsm",
-  UITour: "resource:///modules/UITour.jsm",
 });
 XPCOMUtils.defineLazyGetter(this, "Timer", function() {
   let timer = {};
   ChromeUtils.import("resource://gre/modules/Timer.jsm", timer);
   return timer;
 });
 
 const MS_SECOND = 1000;
--- a/browser/modules/ContentLinkHandler.jsm
+++ b/browser/modules/ContentLinkHandler.jsm
@@ -6,18 +6,16 @@
 
 var EXPORTED_SYMBOLS = [ "ContentLinkHandler" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Feeds",
   "resource:///modules/Feeds.jsm");
-ChromeUtils.defineModuleGetter(this, "BrowserUtils",
-  "resource://gre/modules/BrowserUtils.jsm");
 
 const SIZES_TELEMETRY_ENUM = {
   NO_SIZES: 0,
   ANY: 1,
   DIMENSION: 2,
   INVALID: 3,
 };
 
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -7,17 +7,16 @@ var EXPORTED_SYMBOLS = ["ExtensionsUI"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/EventEmitter.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AddonManager: "resource://gre/modules/AddonManager.jsm",
   AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
   AppMenuNotifications: "resource://gre/modules/AppMenuNotifications.jsm",
-  BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   ExtensionData: "resource://gre/modules/Extension.jsm",
   Services: "resource://gre/modules/Services.jsm"
 });
 
 XPCOMUtils.defineLazyPreferenceGetter(this, "WEBEXT_PERMISSION_PROMPTS",
                                       "extensions.webextPermissionPrompts", false);
 
 const DEFAULT_EXTENSION_ICON = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -63,21 +63,16 @@ ChromeUtils.import("resource://gre/modul
 
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "SitePermissions",
   "resource:///modules/SitePermissions.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
-  return Services.strings
-                 .createBundle("chrome://branding/locale/brand.properties");
-});
-
 XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function() {
   return Services.strings
                  .createBundle("chrome://browser/locale/browser.properties");
 });
 
 var PermissionUI = {};
 
 /**
--- a/browser/modules/ReaderParent.jsm
+++ b/browser/modules/ReaderParent.jsm
@@ -7,17 +7,16 @@
 
 var EXPORTED_SYMBOLS = [ "ReaderParent" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "ReaderMode", "resource://gre/modules/ReaderMode.jsm");
-ChromeUtils.defineModuleGetter(this, "UITour", "resource:///modules/UITour.jsm");
 
 const gStringBundle = Services.strings.createBundle("chrome://global/locale/aboutReader.properties");
 
 var ReaderParent = {
   // Listeners are added in nsBrowserGlue.js
   receiveMessage(message) {
     switch (message.name) {
       case "Reader:FaviconRequest": {
--- a/browser/modules/RemotePrompt.jsm
+++ b/browser/modules/RemotePrompt.jsm
@@ -4,20 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = [ "RemotePrompt" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-ChromeUtils.defineModuleGetter(this, "PlacesUIUtils",
-                               "resource:///modules/PlacesUIUtils.jsm");
-ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
-                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PromptUtils",
                                "resource://gre/modules/SharedPromptUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 
 var RemotePrompt = {
   // Listeners are added in nsBrowserGlue.js
   receiveMessage(message) {
--- a/browser/modules/Sanitizer.jsm
+++ b/browser/modules/Sanitizer.jsm
@@ -8,17 +8,16 @@ var EXPORTED_SYMBOLS = ["Sanitizer"];
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   FormHistory: "resource://gre/modules/FormHistory.jsm",
   Downloads: "resource://gre/modules/Downloads.jsm",
-  DownloadsCommon: "resource:///modules/DownloadsCommon.jsm",
   TelemetryStopwatch: "resource://gre/modules/TelemetryStopwatch.jsm",
   setTimeout: "resource://gre/modules/Timer.jsm",
   ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm",
   OfflineAppCacheHelper: "resource://gre/modules/offlineAppCache.jsm",
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "sas",
                                    "@mozilla.org/storage/activity-service;1",
--- a/caps/tests/mochitest/mochitest.ini
+++ b/caps/tests/mochitest/mochitest.ini
@@ -8,9 +8,8 @@ support-files =
   !/js/xpconnect/tests/mochitest/file_empty.html
 
 [test_bug246699.html]
 [test_bug292789.html]
 [test_bug423375.html]
 [test_bug470804.html]
 [test_bug1367586.html]
 [test_disallowInheritPrincipal.html]
-[test_extensionURL.html]
deleted file mode 100644
--- a/caps/tests/mochitest/test_extensionURL.html
+++ /dev/null
@@ -1,290 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
--->
-<head>
-  <meta charset="utf-8">
-  <title>Test for Bug 1161831</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="application/javascript">
-
-  /** Test for Bug 1161831 **/
-  SimpleTest.waitForExplicitFinish();
-
-  let module = SpecialPowers.Cu.getGlobalForObject(SpecialPowers.Cu.import("resource://gre/modules/Services.jsm", {}));
-  var {MatchGlob, MatchPatternSet, WebExtensionPolicy} = module;
-
-  var policy1, policy2;
-
-  var XPCOMUtils = SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
-  var resourceHandler = SpecialPowers.Services.io.getProtocolHandler("resource")
-                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
-  var extensionHandler = SpecialPowers.Services.io.getProtocolHandler("moz-extension")
-                                     .QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
-  var fileHandler = SpecialPowers.Cc["@mozilla.org/network/protocol;1?name=file"]
-                                  .getService(SpecialPowers.Ci.nsIFileProtocolHandler);
-
-  // Chrome script that adds handles for inserting substitutions and
-  // resolving symlinked paths in the parent process.
-  var script = SpecialPowers.loadChromeScript(() => {
-    ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-    // Sets up a substitution in the parent process
-    this.addMessageListener("SetSubstitution", ({from, to}) => {
-      // Convert the passed |to| string to a URI.
-      // A null |to| value clears the substitution
-      if (to != null) {
-        var uri = Services.io.newURI(to);
-      }
-      Services.io.getProtocolHandler("moz-extension")
-                  .QueryInterface(Ci.nsISubstitutingProtocolHandler)
-                  .setSubstitution(from, uri);
-    });
-
-    // Gets a normalized (de-symlinked) path in the parent process
-    this.addMessageListener("ResolvePath", (path) => {
-      let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
-      file.initWithPath(path);
-      file.normalize();
-      return file.path;
-    });
-  });
-
-  // An array of objects each containing a callback and a host
-  // for a substitution that has been set in either the parent
-  // or local child process and for which we are waiting for the
-  // observer notification in the child process.
-  var pendingSubstitutions = [];
-
-  // Adds a new callback to |pendingSubstitutions|.
-  function pushSubstitutionCallback(callback, host) {
-    let entry = {callback, host};
-    pendingSubstitutions.push(entry);
-  }
-
-  // Invoke the first callback found in |pendingSubstitutions|
-  // with a matching host.
-  function popSubstitutionCallback(host) {
-    for (let i = 0; i < pendingSubstitutions.length; i++) {
-      let entry = pendingSubstitutions[i];
-      if (host === entry.host) {
-        entry.callback();
-        pendingSubstitutions.splice(i, 1);
-        return;
-      }
-    }
-    // This indicates we installed a mapping in either the
-    // parent or the local child process, but never received an
-    // observer notification in the child for a mapping with
-    // a matching host.
-    ok(false, `popSubstitutionCallback(${host}) no match found`);
-  }
-
-  // Define an implementation of nsISubstitutionObserver and add it
-  // to this process' ExtensionProtocolHandler to observe substitutions
-  // as they are propagated to the child.
-  function SubstitutionObserver() {}
-  SubstitutionObserver.prototype = {
-    onSetSubstitution: SpecialPowers.wrapCallback(function(root, uri) {
-      popSubstitutionCallback(root);
-    }),
-    QueryInterface:
-      XPCOMUtils.generateQI([SpecialPowers.Ci.nsISupports,
-          SpecialPowers.Ci.nsISubstitutionObserver]),
-  };
-  var observer = new SubstitutionObserver();
-  var wrappedObserver = SpecialPowers.wrap(observer);
-  extensionHandler.addObserver(wrappedObserver);
-
-  // Set a substitution in the parent. The parent
-  // propagates all substitutions to child processes.
-  function globalSetSubstitution(chromeScript, from, to) {
-    var p = new Promise(function(resolve, reject) {
-      pushSubstitutionCallback(resolve, from);
-      chromeScript.sendSyncMessage("SetSubstitution", {from, to});
-    });
-    return p;
-  }
-
-  SimpleTest.registerCleanupFunction(function() {
-      extensionHandler.removeObserver(wrappedObserver);
-      policy1.active = false;
-      policy2.active = false;
-      script.sendSyncMessage("SetSubstitution", {from: "cherise", to: null});
-      script.sendSyncMessage("SetSubstitution", {from: "liebchen", to: null});
-  });
-
-  addLoadEvent(function() {
-
-    // First, get a file:// URI to something - open to suggestions on how to do
-    //  this more easily.
-    var resURI = SpecialPowers.Services.io.newURI("resource://testing-common/resource_test_file.html");
-    var filePath = resourceHandler.resolveURI(resURI);
-    ok(filePath.startsWith("file://"), "resource:// URI resolves where we expect: " + filePath);
-    var resFile = fileHandler.getFileFromURLSpec(filePath);
-
-    // Get normalized path to the test file. We already have a file object
-    // |resFile|, but its underlying path may contain a symlink we can't
-    // resolve in the child process.
-    let resolvedPath = script.sendSyncMessage("ResolvePath", resFile.path);
-    let file = SpecialPowers.Cc["@mozilla.org/file/local;1"]
-                            .createInstance(SpecialPowers.Ci.nsIFile);
-    info(`resolved test file path: ${resolvedPath}`);
-    file.initWithPath(resolvedPath);
-
-    // Setup the base directory URI string and a URI string to refer to
-    // the test file within that directory.
-    let cheriseURIStr = "moz-extension://cherise/" + file.leafName;
-    let liebchenURIStr = "moz-extension://liebchen/" + file.leafName;
-    let cheriseBaseDirURIStr = "file://" + file.parent.path + "/";
-    info(`cheriseURIStr: ${cheriseURIStr}`);
-    info(`liebchenURIStr: ${liebchenURIStr}`);
-    info(`cheriseBaseDirURIStr: ${cheriseBaseDirURIStr}`);
-
-    function StubPolicy(id, accessible) {
-      let policy = new WebExtensionPolicy(SpecialPowers.Cu.cloneInto({
-        id: `imaginaryaddon-${id[0]}`,
-        mozExtensionHostname: id,
-        baseURL: cheriseBaseDirURIStr,
-
-        allowedOrigins: SpecialPowers.unwrap(new MatchPatternSet([])),
-        webAccessibleResources: accessible ? [SpecialPowers.unwrap(new MatchGlob("*"))] : [],
-        localizeCallback(string) {},
-      }, module, {cloneFunctions: true, wrapReflectors: true}));
-
-      // Activating the policy results in a substitution being added,
-      // which triggers SubstitutionObserver.onSetSubstitution().
-      // All observer notifications must be accounted for (in this
-      // test to validate they are working correctly) so ignore this
-      // substitution when the observer gets notified.
-      pushSubstitutionCallback(() => {}, id);
-      policy.active = true;
-      return policy;
-    }
-
-    // Register a moz-extension:// URI locally.
-    policy1 = StubPolicy("cherise", false);
-    policy2 = StubPolicy("liebchen", false);
-
-    //
-    // Make sure that non-file:// URIs don't work.
-    //
-
-    // resource://
-    try {
-      extensionHandler.setSubstitution("interdit", resURI);
-      ok(false, "Should have thrown for mapping moz-extension to resource");
-    } catch (e) {
-      ok(true, "Threw correctly: " + e);
-    }
-
-    // chrome://
-    try {
-      var chromeURI = SpecialPowers.Services.io.newURI("chrome://global/content/mozilla.xhtml");
-      extensionHandler.setSubstitution("verboten", chromeURI);
-      ok(false, "Should have thrown for mapping moz-extension to chrome");
-    } catch (e) {
-      ok(true, "Threw correctly: " + e);
-    }
-
-    function navigateWithLocation(ifr, url) { ifr.contentWindow.location = url; }
-    function navigateWithSrc(ifr, url) { ifr.setAttribute("src", url); }
-    function navigateFromChromeWithLocation(ifr, url) { SpecialPowers.wrap(ifr).contentWindow.location = url; }
-    function navigateFromChromeWithWebNav(ifr, url) {
-      SpecialPowers.wrap(ifr).contentWindow
-                   .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                   .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-                   .loadURI(url, 0, null, null, null);
-    }
-
-
-    function setWhitelistCallback(paths) {
-      pushSubstitutionCallback(() => {}, policy1.mozExtensionHostname);
-      policy1.active = false;
-
-      pushSubstitutionCallback(() => {}, policy2.mozExtensionHostname);
-      policy2.active = false;
-
-      policy1 = StubPolicy("cherise", paths.includes("cherise"));
-      policy2 = StubPolicy("liebchen", paths.includes("liebchen"));
-    }
-
-    function testLoad(url, navigate, shouldThrow) {
-      var ifr = document.createElement("iframe");
-      var p = new Promise(function(resolve, reject) {
-        ifr.onload = function() {
-          ok(true, "Loaded " + url);
-          var prin = SpecialPowers.wrap(ifr.contentWindow).document.nodePrincipal;
-          function stripTrailingSlash(s) { return s.replace(/\/$/, ""); }
-          is(stripTrailingSlash(prin.URI.spec), url, "Principal uri is correct: " + url);
-          function stripPath(s) { return s.replace(/(.*\/\/.+)\/.*/, "$1"); }
-          is(prin.originNoSuffix, stripPath(url), "Principal origin is correct: " + prin.originNoSuffix);
-          is(prin.addonId, "imaginaryaddon-" + url[url.indexOf("/") + 2], "addonId is correct");
-          is(SpecialPowers.wrap(ifr.contentWindow).document.title, "resource test file",
-             "document looks right");
-          ifr.remove();
-          resolve();
-        };
-        document.body.appendChild(ifr);
-
-        var threw = false;
-        try {
-          navigate(ifr, url);
-        } catch (e) {
-          ifr.remove();
-          threw = true;
-          ok(/denied|insecure/.test(e), "exception correct: " + e);
-        }
-        is(threw, !!shouldThrow, "Correct throwing behavior for: " + url);
-        !threw || resolve();
-      });
-
-      return p;
-    }
-
-    function testXHR(url, shouldError) {
-      return new Promise(function(resolve, reject) {
-        var xhr = new XMLHttpRequest();
-        xhr.addEventListener("load", () => { ok(!shouldError, `XHR to ${url} should succeed`); resolve(); });
-        xhr.addEventListener("error", () => { ok(shouldError, `XHR to ${url} should fail`); resolve(); });
-        xhr.open("GET", url, true);
-        xhr.send();
-      });
-    }
-
-    //
-    // Perform some loads and make sure they work correctly.
-    //
-    globalSetSubstitution(script, "cherise", cheriseBaseDirURIStr)
-    .then(testLoad.bind(null, cheriseURIStr, navigateFromChromeWithLocation))
-    .then(testLoad.bind(null, cheriseURIStr, navigateFromChromeWithWebNav))
-    .then(testLoad.bind(null, cheriseURIStr, navigateWithLocation, /* shouldThrow = */ true))
-    .then(testXHR.bind(null, cheriseURIStr, /* shouldError = */ true))
-    .then(setWhitelistCallback.bind(null, ["cherise"]))
-    .then(testLoad.bind(null, cheriseURIStr, navigateWithLocation))
-    .then(testXHR.bind(null, cheriseURIStr))
-    .then(globalSetSubstitution(script, "liebchen", "moz-extension://cherise"))
-    .then(testLoad.bind(null, liebchenURIStr, navigateWithLocation, /* shouldThrow = */ true))
-    .then(testXHR.bind(null, liebchenURIStr, /* shouldError = */ true))
-    .then(setWhitelistCallback.bind(null, ["cherise", "liebchen"]))
-    .then(testLoad.bind(null, liebchenURIStr, navigateWithLocation))
-    .then(testLoad.bind(null, liebchenURIStr, navigateWithSrc))
-    .then(testLoad.bind(null, cheriseURIStr, navigateWithSrc))
-    .then(SimpleTest.finish.bind(SimpleTest),
-          function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish(); }
-    );
-  });
-
-  </script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1161831">Mozilla Bug 1161831</a>
-<p id="display"></p>
-<div id="content" style="display: none">
-</div>
-<pre id="test">
-</pre>
-</body>
-</html>
--- a/devtools/client/aboutdebugging/components/addons/Target.js
+++ b/devtools/client/aboutdebugging/components/addons/Target.js
@@ -14,19 +14,16 @@ const {
   debugRemoteAddon,
   isLegacyTemporaryExtension,
   isTemporaryID,
   parseFileUri,
   uninstallAddon
 } = require("../../modules/addon");
 const Services = require("Services");
 
-loader.lazyImporter(this, "BrowserToolboxProcess",
-  "resource://devtools/client/framework/ToolboxProcess.jsm");
-
 loader.lazyRequireGetter(this, "DebuggerClient",
   "devtools/shared/client/debugger-client", true);
 
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 
 const TEMP_ID_URL = "https://developer.mozilla.org/Add-ons" +
                     "/WebExtensions/WebExtensions_and_the_Add-on_ID";
--- a/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
+++ b/devtools/client/aboutdebugging/components/workers/MultiE10sWarning.js
@@ -1,29 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* eslint-env browser */
 
 "use strict";
 
-loader.lazyImporter(this, "PrivateBrowsingUtils",
-  "resource://gre/modules/PrivateBrowsingUtils.jsm");
 const { Component } = require("devtools/client/shared/vendor/react");
 const dom = require("devtools/client/shared/vendor/react-dom-factories");
 const Services = require("Services");
 const { Ci } = require("chrome");
 
-loader.lazyImporter(this, "PrivateBrowsingUtils",
-  "resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-loader.lazyRequireGetter(this, "DebuggerClient",
-  "devtools/shared/client/debugger-client", true);
-
 const Strings = Services.strings.createBundle("chrome://devtools/locale/aboutdebugging.properties");
 const MULTI_OPT_OUT_PREF = "dom.ipc.multiOptOut";
 
 class multiE10SWarning extends Component {
   constructor(props) {
     super(props);
     this.onUpdatePreferenceClick = this.onUpdatePreferenceClick.bind(this);
   }
--- a/devtools/client/accessibility/accessibility-view.js
+++ b/devtools/client/accessibility/accessibility-view.js
@@ -1,14 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-/* global EVENTS */
+/* global EVENTS, gToolbox */
+
+const nodeConstants = require("devtools/shared/dom-node-constants");
 
 // React & Redux
 const { createFactory, createElement } = require("devtools/client/shared/vendor/react");
 const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 const { Provider } = require("devtools/client/shared/vendor/react-redux");
 const { combineReducers } = require("devtools/client/shared/vendor/redux");
 
 // Accessibility Panel
@@ -68,17 +70,32 @@ AccessibilityView.prototype = {
   },
 
   async highlightAccessible(walker, accessible) {
     await this.store.dispatch(highlight(walker, accessible));
     window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_HIGHLIGHTED);
   },
 
   async selectNodeAccessible(walker, node) {
-    const accessible = await walker.getAccessibleFor(node);
+    let accessible = await walker.getAccessibleFor(node);
+    // If node does not have an accessible object, try to find node's child text node and
+    // try to retrieve an accessible object for that child instead. This is the best
+    // effort approach until there's accessibility API to retrieve accessible object at
+    // point.
+    if (!accessible || accessible.indexInParent < 0) {
+      const { nodes: children } = await gToolbox.walker.children(node);
+      for (let child of children) {
+        if (child.nodeType === nodeConstants.TEXT_NODE) {
+          accessible = await walker.getAccessibleFor(child);
+          if (accessible && accessible.indexInParent >= 0) {
+            break;
+          }
+        }
+      }
+    }
 
     await this.store.dispatch(select(walker, accessible));
     window.emit(EVENTS.NEW_ACCESSIBLE_FRONT_HIGHLIGHTED);
   },
 
   /**
    * Process message from accessibility panel.
    *
--- a/devtools/client/accessibility/accessibility.css
+++ b/devtools/client/accessibility/accessibility.css
@@ -99,16 +99,17 @@ body {
   display: flex;
   align-items: center;
   margin-bottom: 1.7em;
 }
 
 .description img {
   margin-right: 12px;
   flex-basis: 42px;
+  flex-shrink: 0;
   -moz-context-properties: fill;
   fill: var(--grey-40);
 }
 
 .description .devtools-button {
   display: flex;
   align-items: center;
   margin: auto;
--- a/devtools/client/accessibility/test/browser_accessibility_context_menu_inspector.js
+++ b/devtools/client/accessibility/test/browser_accessibility_context_menu_inspector.js
@@ -1,43 +1,83 @@
 /* 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";
 
-const TEST_URI = "<h1 id=\"h1\">header</h1><p id=\"p\">paragraph</p>";
+const TEST_URI = `
+  <h1 id="h1">header</h1>
+  <p id="p">paragraph</p>
+  <span id="span-1">text</span>
+  <span id="span-2">
+    IamaverylongtextwhichdoesntfitinInlineTextChildReallyIdontIamtoobig
+  </span>`;
+
+async function openContextMenuForNode({ toolbox }, selector) {
+  info("Selecting Inspector tab and opening a context menu");
+  const inspector = await toolbox.selectTool("inspector");
+
+  if (!selector) {
+    ok(inspector.selection.isBodyNode(), "Default selection is a body node.");
+  } else if (typeof selector === "string") {
+    await selectNode(selector, inspector, "test");
+  } else {
+    let updated = inspector.once("inspector-updated");
+    inspector.selection.setNodeFront(selector, { reason: "test" });
+    await updated;
+  }
+
+  let menuUpdated = inspector.once("node-menu-updated");
+  let allMenuItems = openContextMenuAndGetAllItems(inspector);
+  await menuUpdated;
+  return allMenuItems;
+}
+
+function checkShowA11YPropertiesNode(allMenuItems, disabled) {
+  let showA11YPropertiesNode = allMenuItems.find(item =>
+    item.id === "node-menu-showaccessibilityproperties");
+  ok(showA11YPropertiesNode,
+    "the popup menu now has a show accessibility properties item");
+  is(showA11YPropertiesNode.disabled, disabled,
+    "Show accessibility properties item has correct state");
+  return showA11YPropertiesNode;
+}
+
+async function checkAccessibleObjectSelection({ toolbox, panel }, menuItem, isText) {
+  const inspector = await toolbox.getPanel("inspector");
+  info("Triggering 'Show Accessibility Properties' and waiting for " +
+       "accessibility panel to open");
+  let panelSelected = toolbox.once("accessibility-selected");
+  let objectSelected = panel.once("new-accessible-front-selected");
+  menuItem.click();
+  await panelSelected;
+  let selected = await objectSelected;
+
+  let expectedNode = isText ?
+    inspector.selection.nodeFront.inlineTextChild : inspector.selection.nodeFront;
+  let expectedSelected = await panel.walker.getAccessibleFor(expectedNode);
+  is(selected, expectedSelected, "Accessible front selected correctly");
+}
 
 addA11YPanelTask("Test show accessibility properties context menu.", TEST_URI,
-  async function testShowAccessibilityPropertiesContextMenu({ panel, toolbox }) {
-    let inspector = await toolbox.selectTool("inspector");
+  async function testShowAccessibilityPropertiesContextMenu(env) {
+    let allMenuItems = await openContextMenuForNode(env);
+    let showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, true);
 
-    ok(inspector.selection.isBodyNode(), "Default selection is a body node.");
-    let menuUpdated = inspector.once("node-menu-updated");
-    let allMenuItems = openContextMenuAndGetAllItems(inspector);
-    let showA11YPropertiesNode = allMenuItems.find(item =>
-      item.id === "node-menu-showaccessibilityproperties");
-    ok(showA11YPropertiesNode,
-      "the popup menu now has a show accessibility properties item");
-    await menuUpdated;
-    ok(showA11YPropertiesNode.disabled, "Body node does not have accessible");
+    allMenuItems = await openContextMenuForNode(env, "#h1");
+    showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, false);
+    await checkAccessibleObjectSelection(env, showA11YPropertiesNode);
+
+    allMenuItems = await openContextMenuForNode(env, "#span-1");
+    showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, false);
+    await checkAccessibleObjectSelection(env, showA11YPropertiesNode, true);
 
-    await selectNode("#h1", inspector, "test");
-    menuUpdated = inspector.once("node-menu-updated");
-    allMenuItems = openContextMenuAndGetAllItems(inspector);
-    showA11YPropertiesNode = allMenuItems.find(item =>
-      item.id === "node-menu-showaccessibilityproperties");
-    ok(showA11YPropertiesNode,
-      "the popup menu now has a show accessibility properties item");
-    await menuUpdated;
-    ok(!showA11YPropertiesNode.disabled, "Body node has an accessible");
+    allMenuItems = await openContextMenuForNode(env, "#span-2");
+    showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, true);
 
-    info("Triggering 'Show Accessibility Properties' and waiting for " +
-         "accessibility panel to open");
-    let panelSelected = toolbox.once("accessibility-selected");
-    let objectSelected = panel.once("new-accessible-front-selected");
-    showA11YPropertiesNode.click();
-    await panelSelected;
-    let selected = await objectSelected;
-
-    let expectedSelected = await panel.walker.getAccessibleFor(
-      inspector.selection.nodeFront);
-    is(selected, expectedSelected, "Accessible front selected correctly");
+    const inspector = env.toolbox.getPanel("inspector");
+    let span2 = await getNodeFront("#span-2", inspector);
+    await inspector.markup.expandNode(span2);
+    let { nodes } = await inspector.walker.children(span2);
+    allMenuItems = await openContextMenuForNode(env, nodes[0]);
+    showA11YPropertiesNode = checkShowA11YPropertiesNode(allMenuItems, false);
+    await checkAccessibleObjectSelection(env, showA11YPropertiesNode, false);
   });
--- a/devtools/client/debugger/new/README.mozilla
+++ b/devtools/client/debugger/new/README.mozilla
@@ -1,13 +1,13 @@
 This is the debugger.html project output.
 See https://github.com/devtools-html/debugger.html
 
-Version 43.0
+Version 44.0
 
-Comparison: https://github.com/devtools-html/debugger.html/compare/release-42...release-43
+Comparison: https://github.com/devtools-html/debugger.html/compare/release-43...release-44
 
 Packages:
 - babel-plugin-transform-es2015-modules-commonjs @6.26.0
 - babel-preset-react @6.24.1
 - react @16.2.0
 - react-dom @16.2.0
 - webpack @3.11.0
--- a/devtools/client/debugger/new/debugger.css
+++ b/devtools/client/debugger/new/debugger.css
@@ -3087,41 +3087,45 @@ html[dir="rtl"] .breakpoints-list .break
     transform: translateX(-10px);
   }
   40%,
   80% {
     transform: translateX(10px);
   }
 }
 
-.input-expression.error {
-  border: 1px solid red;
-  animation: 150ms cubic-bezier(0.07, 0.95, 0, 1) shake;
-}
-
 .input-expression::placeholder {
   font-style: italic;
   color: var(--theme-comment);
 }
 
 .input-expression:focus {
   outline: none;
   cursor: text;
 }
 
 .expressions-list {
   /* TODO: add normalize */
   margin: 0;
   padding: 0;
 }
+
 .expression-input-container {
   display: flex;
+  border: 1px solid transparent;
+}
+
+.expression-input-container.focused {
   border: 1px solid var(--theme-highlight-blue);
 }
 
+.expression-input-container.error {
+  border: 1px solid red;
+}
+
 .expression-container {
   border: 1px;
   padding: 0.25em 1em 0.25em 0.5em;
   width: 100%;
   color: var(--theme-body-color);
   background-color: var(--theme-body-background);
   display: block;
   position: relative;
--- a/devtools/client/debugger/new/debugger.js
+++ b/devtools/client/debugger/new/debugger.js
@@ -5280,19 +5280,22 @@ function clearProjectDirectoryRoot() {
     url: ""
   };
 }
 
 function setProjectDirectoryRoot(newRoot) {
   return ({ dispatch, getState }) => {
     const curRoot = (0, _ui.getProjectDirectoryRoot)(getState());
     if (newRoot && curRoot) {
-      const temp = newRoot.split("/");
-      temp.splice(0, 2);
-      newRoot = `${curRoot}/${temp.join("/")}`;
+      const newRootArr = newRoot.replace(/\/+/g, "/").split("/");
+      const curRootArr = curRoot.replace(/^\//, "").replace(/\/+/g, "/").split("/");
+      if (newRootArr[0] !== curRootArr[0]) {
+        newRootArr.splice(0, 2);
+        newRoot = `${curRoot}/${newRootArr.join("/")}`;
+      }
     }
 
     dispatch({
       type: "SET_PROJECT_DIRECTORY_ROOT",
       url: newRoot
     });
   };
 }
@@ -17017,19 +17020,20 @@ class SourcesTree extends _react.Compone
     let roots = () => sourceTree.contents;
 
     let clearProjectRootButton = null;
 
     // The "sourceTree.contents[0]" check ensures that there are contents
     // A custom root with no existing sources will be ignored
     if (isCustomRoot) {
       let rootLabel = projectRoot.split("/").pop();
-      if (sourceTree.contents[0]) {
-        rootLabel = sourceTree.contents[0].name;
-        roots = () => sourceTree.contents[0].contents;
+      const sourceContents = sourceTree.contents[0];
+      if (sourceContents) {
+        rootLabel = sourceContents.contents[0].name;
+        roots = () => sourceContents.contents[0].contents;
       }
 
       clearProjectRootButton = _react2.default.createElement(
         "button",
         {
           className: "sources-clear-root",
           onClick: () => this.props.clearProjectDirectoryRoot(),
           title: L10N.getStr("removeDirectoryRoot.label")
@@ -21517,17 +21521,17 @@ class Breakpoint extends _react.Componen
       }
 
       // NOTE: we need to wait for the breakpoint to be loaded
       // to get the generated location
       if (!selectedSource || breakpoint.loading) {
         return;
       }
 
-      const sourceId = selectedSource.get("id");
+      const sourceId = selectedSource.id;
       const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);
 
       editor.codeMirror.setGutterMarker(line, "breakpoints", makeMarker(breakpoint.disabled));
 
       editor.codeMirror.addLineClass(line, "line", "new-breakpoint");
       if (breakpoint.condition) {
         editor.codeMirror.addLineClass(line, "line", "has-condition");
       } else {
@@ -21555,17 +21559,17 @@ class Breakpoint extends _react.Componen
     if (!selectedSource) {
       return;
     }
 
     if (breakpoint.loading) {
       return;
     }
 
-    const sourceId = selectedSource.get("id");
+    const sourceId = selectedSource.id;
     const doc = (0, _editor.getDocument)(sourceId);
     if (!doc) {
       return;
     }
 
     const line = (0, _editor.toEditorLine)(sourceId, breakpoint.location.line);
 
     // NOTE: when we upgrade codemirror we can use `doc.setGutterMarker`
@@ -23315,19 +23319,22 @@ class Breakpoints extends _react.Compone
   }
 
   removeBreakpoint(event, breakpoint) {
     event.stopPropagation();
     this.props.removeBreakpoint(breakpoint.location);
   }
 
   renderBreakpoint(breakpoint) {
+    const { selectedSource } = this.props;
+
     return _react2.default.createElement(_Breakpoint2.default, {
       key: breakpoint.locationId,
       breakpoint: breakpoint,
+      selectedSource: selectedSource,
       onClick: () => this.selectBreakpoint(breakpoint),
       onContextMenu: e => (0, _BreakpointsContextMenu2.default)(_extends({}, this.props, { breakpoint, contextMenuEvent: e })),
       onChange: () => this.handleBreakpointCheckbox(breakpoint),
       onCloseClick: ev => this.removeBreakpoint(ev, breakpoint)
     });
   }
 
   renderExceptionsOptions() {
@@ -23389,17 +23396,20 @@ function updateLocation(sources, frame, 
   const locationId = (0, _breakpoint.makeLocationId)(bp.location);
   const localBP = _extends({}, bp, { locationId, isCurrentlyPaused, source });
 
   return localBP;
 }
 
 const _getBreakpoints = (0, _reselect.createSelector)(_selectors.getBreakpoints, _selectors.getSources, _selectors.getTopFrame, _selectors.getPauseReason, (breakpoints, sources, frame, why) => breakpoints.map(bp => updateLocation(sources, frame, why, bp)).filter(bp => bp.source && !bp.source.isBlackBoxed));
 
-exports.default = (0, _reactRedux.connect)((state, props) => ({ breakpoints: _getBreakpoints(state) }), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
+exports.default = (0, _reactRedux.connect)((state, props) => ({
+  breakpoints: _getBreakpoints(state),
+  selectedSource: (0, _selectors.getSelectedSource)(state)
+}), dispatch => (0, _redux.bindActionCreators)(_actions2.default, dispatch))(Breakpoints);
 
 /***/ }),
 
 /***/ 1601:
 /***/ (function(module, exports, __webpack_require__) {
 
 "use strict";
 
@@ -23460,19 +23470,24 @@ class Expressions extends _react.Compone
 
     this.handleKeyDown = e => {
       if (e.key === "Escape") {
         this.clear();
       }
     };
 
     this.hideInput = () => {
+      this.setState({ focused: false });
       this.props.onExpressionAdded();
     };
 
+    this.onFocus = () => {
+      this.setState({ focused: true });
+    };
+
     this.handleExistingSubmit = async (e, expression) => {
       e.preventDefault();
       e.stopPropagation();
 
       this.props.updateExpression(this.state.inputValue, expression);
       this.hideInput();
     };
 
@@ -23542,17 +23557,18 @@ class Expressions extends _react.Compone
           )
         )
       );
     };
 
     this.state = {
       editing: false,
       editIndex: -1,
-      inputValue: ""
+      inputValue: "",
+      focused: false
     };
   }
 
   componentDidMount() {
     const { expressions, evaluateExpressions } = this.props;
     if (expressions.size > 0) {
       evaluateExpressions();
     }
@@ -23560,20 +23576,20 @@ class Expressions extends _react.Compone
 
   componentWillReceiveProps(nextProps) {
     if (this.state.editing && !nextProps.expressionError) {
       this.clear();
     }
   }
 
   shouldComponentUpdate(nextProps, nextState) {
-    const { editing, inputValue } = this.state;
+    const { editing, inputValue, focused } = this.state;
     const { expressions, expressionError, showInput } = this.props;
 
-    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue || nextProps.showInput !== showInput;
+    return expressions !== nextProps.expressions || expressionError !== nextProps.expressionError || editing !== nextState.editing || inputValue !== nextState.inputValue || nextProps.showInput !== showInput || focused !== nextState.focused;
   }
 
   componentDidUpdate(prevProps, prevState) {
     if (this._input && !prevState.editing) {
       const input = this._input;
       input.setSelectionRange(0, input.value.length);
       input.focus();
     }
@@ -23594,60 +23610,70 @@ class Expressions extends _react.Compone
   }
 
   onBlur() {
     this.clear();
     this.hideInput();
   }
 
   renderNewExpressionInput() {
-    const { expressionError } = this.props;
-    const { editing, inputValue } = this.state;
+    const { expressionError, expressions } = this.props;
+    const { editing, inputValue, focused } = this.state;
     const error = editing === false && expressionError === true;
     const placeholder = error ? L10N.getStr("expressions.errorMsg") : L10N.getStr("expressions.placeholder");
+    const autoFocus = expressions.size > 0;
+
     return _react2.default.createElement(
       "li",
-      { className: "expression-input-container" },
+      {
+        className: (0, _classnames2.default)("expression-input-container", { focused, error })
+      },
       _react2.default.createElement(
         "form",
         { className: "expression-input-form", onSubmit: this.handleNewSubmit },
         _react2.default.createElement("input", {
-          className: (0, _classnames2.default)("input-expression", { error }),
+          className: "input-expression",
           type: "text",
           placeholder: placeholder,
           onChange: this.handleChange,
           onBlur: this.hideInput,
           onKeyDown: this.handleKeyDown,
-          autoFocus: "true",
+          onFocus: this.onFocus,
+          autoFocus: autoFocus,
           value: !editing ? inputValue : ""
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
   renderExpressionEditInput(expression) {
     const { expressionError } = this.props;
-    const { inputValue, editing } = this.state;
+    const { inputValue, editing, focused } = this.state;
     const error = editing === true && expressionError === true;
+
     return _react2.default.createElement(
       "span",
-      { className: "expression-input-container", key: expression.input },
+      {
+        className: (0, _classnames2.default)("expression-input-container", { focused, error }),
+        key: expression.input
+      },
       _react2.default.createElement(
         "form",
         {
           className: "expression-input-form",
           onSubmit: e => this.handleExistingSubmit(e, expression)
         },
         _react2.default.createElement("input", {
           className: (0, _classnames2.default)("input-expression", { error }),
           type: "text",
           onChange: this.handleChange,
           onBlur: this.clear,
           onKeyDown: this.handleKeyDown,
+          onFocus: this.onFocus,
           value: editing ? inputValue : expression.input,
           ref: c => this._input = c
         }),
         _react2.default.createElement("input", { type: "submit", style: { display: "none" } })
       )
     );
   }
 
@@ -31458,32 +31484,36 @@ function createOriginalSource(originalUr
     isPrettyPrinted: false,
     isWasm: false,
     isBlackBoxed: false,
     loadedState: "unloaded"
   };
 }
 
 function loadSourceMaps(sources) {
-  return async function ({ dispatch, getState, sourceMaps }) {
+  return async function ({ dispatch, sourceMaps }) {
+    if (!sourceMaps) {
+      return;
+    }
+
     const originalSources = await Promise.all(sources.map(source => dispatch(loadSourceMap(source.id))));
 
     await dispatch(newSources((0, _lodash.flatten)(originalSources)));
   };
 }
 
 /**
  * @memberof actions/sources
  * @static
  */
 function loadSourceMap(sourceId) {
   return async function ({ dispatch, getState, sourceMaps }) {
     const source = (0, _selectors.getSource)(getState(), sourceId).toJS();
 
-    if (!(0, _devtoolsSourceMap.isGeneratedId)(sourceId) || !source.sourceMapURL) {
+    if (!sourceMaps || !(0, _devtoolsSourceMap.isGeneratedId)(sourceId) || !source.sourceMapURL) {
       return;
     }
 
     let urls = null;
     try {
       urls = await sourceMaps.getOriginalURLs(source);
     } catch (e) {
       console.error(e);
@@ -39094,72 +39124,91 @@ var _react2 = _interopRequireDefault(_re
 var _reactDom = __webpack_require__(4);
 
 var _reactDom2 = _interopRequireDefault(_reactDom);
 
 var _classnames = __webpack_require__(175);
 
 var _classnames2 = _interopRequireDefault(_classnames);
 
+var _devtoolsSourceMap = __webpack_require__(1360);
+
 var _Close = __webpack_require__(1374);
 
 var _Close2 = _interopRequireDefault(_Close);
 
 var _breakpoint = __webpack_require__(1364);
 
 var _prefs = __webpack_require__(226);
 
 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
 
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
-
 function getBreakpointLocation(source, line, column) {
   const isWasm = source && source.isWasm;
   const columnVal = _prefs.features.columnBreakpoints && column ? `:${column}` : "";
   const bpLocation = isWasm ? `0x${line.toString(16).toUpperCase()}` : `${line}${columnVal}`;
 
   return bpLocation;
+} /* This Source Code Form is subject to the terms of the Mozilla Public
+   * License, v. 2.0. If a copy of the MPL was not distributed with this
+   * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+function getBreakpointText(selectedSource, breakpoint) {
+  const { condition, text, originalText } = breakpoint;
+
+  if (condition) {
+    return condition;
+  }
+
+  if (!selectedSource || (0, _devtoolsSourceMap.isGeneratedId)(selectedSource.id) || originalText.length == 0) {
+    return text;
+  }
+
+  return originalText;
 }
 
 class Breakpoint extends _react.Component {
 
   componentDidMount() {
     this.setupEditor();
   }
 
-  componentDidUpdate() {
+  componentDidUpdate(prevProps) {
+    if (getBreakpointText(this.props.selectedSource, this.props.breakpoint) != getBreakpointText(prevProps.selectedSource, prevProps.breakpoint)) {
+      this.destroyEditor();
+    }
     this.setupEditor();
   }
 
   componentWillUnmount() {
-    if (this.editor) {
-      this.editor.destroy();
-    }
+    this.destroyEditor();
   }
 
   shouldComponentUpdate(nextProps) {
     const prevBreakpoint = this.props.breakpoint;
     const nextBreakpoint = nextProps.breakpoint;
 
-    return !prevBreakpoint || prevBreakpoint.text != nextBreakpoint.text || prevBreakpoint.disabled != nextBreakpoint.disabled || prevBreakpoint.condition != nextBreakpoint.condition || prevBreakpoint.hidden != nextBreakpoint.hidden || prevBreakpoint.isCurrentlyPaused != nextBreakpoint.isCurrentlyPaused;
-  }
-
-  getBreakpointText() {
-    const { breakpoint } = this.props;
-    return breakpoint.condition || breakpoint.text;
+    return !prevBreakpoint || this.props.selectedSource != nextProps.selectedSource || prevBreakpoint.text != nextBreakpoint.text || prevBreakpoint.disabled != nextBreakpoint.disabled || prevBreakpoint.condition != nextBreakpoint.condition || prevBreakpoint.hidden != nextBreakpoint.hidden || prevBreakpoint.isCurrentlyPaused != nextBreakpoint.isCurrentlyPaused;
+  }
+
+  destroyEditor() {
+    if (this.editor) {
+      this.editor.destroy();
+      this.editor = null;
+    }
   }
 
   setupEditor() {
     if (this.editor) {
       return;
     }
 
-    this.editor = (0, _breakpoint.createEditor)(this.getBreakpointText());
+    const { selectedSource, breakpoint } = this.props;
+
+    this.editor = (0, _breakpoint.createEditor)(getBreakpointText(selectedSource, breakpoint));
 
     // disables the default search shortcuts
     // $FlowIgnore
     this.editor._initShortcuts = () => {};
 
     const node = _reactDom2.default.findDOMNode(this);
     if (node instanceof HTMLElement) {
       const mountNode = node.querySelector(".breakpoint-label");
@@ -39181,17 +39230,18 @@ class Breakpoint extends _react.Componen
       className: "breakpoint-checkbox",
       checked: !disabled,
       onChange: onChange,
       onClick: ev => ev.stopPropagation()
     });
   }
 
   renderText() {
-    const text = this.getBreakpointText();
+    const { selectedSource, breakpoint } = this.props;
+    const text = getBreakpointText(selectedSource, breakpoint);
 
     return _react2.default.createElement(
       "label",
       { className: "breakpoint-label", title: text },
       text
     );
   }
 
--- a/devtools/client/debugger/new/parser-worker.js
+++ b/devtools/client/debugger/new/parser-worker.js
@@ -21390,25 +21390,33 @@ function mapOriginalExpression(expressio
   const ast = (0, _ast.parseScript)(expression);
   t.traverse(ast, (node, ancestors) => {
     const parent = ancestors[ancestors.length - 1];
     if (!parent) {
       return;
     }
 
     const parentNode = parent.node;
+
+    let name = null;
     if (t.isIdentifier(node) && t.isReferenced(node, parentNode)) {
-      if (mappings.hasOwnProperty(node.name)) {
-        const mapping = mappings[node.name];
-        if (mapping && mapping !== node.name) {
-          const mappingNode = getFirstExpression((0, _ast.parseScript)(mapping));
-          replaceNode(ancestors, mappingNode);
-
-          didReplace = true;
-        }
+      name = node.name;
+    } else if (t.isThisExpression(node)) {
+      name = "this";
+    } else {
+      return;
+    }
+
+    if (mappings.hasOwnProperty(name)) {
+      const mapping = mappings[name];
+      if (mapping && mapping !== name) {
+        const mappingNode = getFirstExpression((0, _ast.parseScript)(mapping));
+        replaceNode(ancestors, mappingNode);
+
+        didReplace = true;
       }
     }
   });
 
   if (!didReplace) {
     // Avoid the extra code generation work and also avoid potentially
     // reformatting the user's code unnecessarily.
     return expression;
--- a/devtools/client/framework/devtools-browser.js
+++ b/devtools/client/framework/devtools-browser.js
@@ -25,18 +25,16 @@ loader.lazyRequireGetter(this, "Debugger
 loader.lazyRequireGetter(this, "BrowserMenus", "devtools/client/framework/browser-menus");
 loader.lazyRequireGetter(this, "appendStyleSheet", "devtools/client/shared/stylesheet-utils", true);
 loader.lazyRequireGetter(this, "DeveloperToolbar", "devtools/client/shared/developer-toolbar", true);
 loader.lazyRequireGetter(this, "ResponsiveUIManager", "devtools/client/responsive.html/manager", true);
 loader.lazyImporter(this, "BrowserToolboxProcess", "resource://devtools/client/framework/ToolboxProcess.jsm");
 loader.lazyImporter(this, "ScratchpadManager", "resource://devtools/client/scratchpad/scratchpad-manager.jsm");
 
 loader.lazyImporter(this, "CustomizableUI", "resource:///modules/CustomizableUI.jsm");
-loader.lazyImporter(this, "CustomizableWidgets", "resource:///modules/CustomizableWidgets.jsm");
-loader.lazyImporter(this, "AppConstants", "resource://gre/modules/AppConstants.jsm");
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 const BROWSER_STYLESHEET_URL = "chrome://devtools/skin/devtools-browser.css";
 
 /**
  * gDevToolsBrowser exposes functions to connect the gDevTools instance with a
--- a/devtools/client/inspector/animation-old/test/browser_animation_timeline_pause_button_02.js
+++ b/devtools/client/inspector/animation-old/test/browser_animation_timeline_pause_button_02.js
@@ -1,18 +1,16 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
  http://creativecommons.org/publicdomain/zero/1.0/ */
 /* eslint-disable mozilla/no-arbitrary-setTimeout */
 
 "use strict";
 
 requestLongerTimeout(2);
-ChromeUtils.defineModuleGetter(this, "Preferences",
-  "resource://gre/modules/Preferences.jsm");
 
 // Checks that the play/pause button goes to the right state when the scrubber has reached
 // the end of the timeline but there are infinite animations playing.
 
 add_task(async function() {
   // TODO see if this is needed?
   // let timerPrecision = Preferences.get("privacy.reduceTimerPrecision");
   // Preferences.set("privacy.reduceTimerPrecision", false);
--- a/devtools/client/inspector/extensions/test/head_devtools_inspector_sidebar.js
+++ b/devtools/client/inspector/extensions/test/head_devtools_inspector_sidebar.js
@@ -5,16 +5,19 @@
 
 /* exported getExtensionSidebarActors, expectNoSuchActorIDs,
             waitForObjectInspector, testSetExpressionSidebarPanel, assertTreeView,
             assertObjectInspector, moveMouseOnObjectInspectorDOMNode,
             moveMouseOnPanelCenter, clickOpenInspectorIcon */
 
 "use strict";
 
+ChromeUtils.defineModuleGetter(this, "ContentTaskUtils",
+                               "resource://testing-common/ContentTaskUtils.jsm");
+
 // Retrieve the array of all the objectValueGrip actors from the
 // inspector extension sidebars state
 // (used in browser_ext_devtools_panels_elements_sidebar.js).
 function getExtensionSidebarActors(inspector) {
   const state = inspector.store.getState();
 
   const actors = [];
 
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -1588,17 +1588,17 @@ Inspector.prototype = {
       menu.append(menuitem);
     }
 
     menu.popup(screenX, screenY, this._toolbox);
     return menu;
   },
 
   buildA11YMenuItem: function(menu) {
-    if (!this.selection.isElementNode() ||
+    if (!(this.selection.isElementNode() || this.selection.isTextNode()) ||
         !Services.prefs.getBoolPref("devtools.accessibility.enabled")) {
       return;
     }
 
     const showA11YPropsItem = new MenuItem({
       id: "node-menu-showaccessibilityproperties",
       label: INSPECTOR_L10N.getStr("inspectorShowAccessibilityProperties.label"),
       click: () => this.showAccessibilityProperties(),
--- a/devtools/client/jsonview/converter-observer.js
+++ b/devtools/client/jsonview/converter-observer.js
@@ -8,22 +8,24 @@
 
 const Cm = Components.manager;
 
 const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 
 // Load devtools module lazily.
 XPCOMUtils.defineLazyGetter(this, "devtools", function() {
+  // eslint-disable-next-line no-shadow
   const {devtools} = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
   return devtools;
 });
 
 // Load JsonView services lazily.
 XPCOMUtils.defineLazyGetter(this, "JsonViewService", function() {
+  // eslint-disable-next-line no-shadow
   const {JsonViewService} = devtools.require("devtools/client/jsonview/converter-child");
   return JsonViewService;
 });
 
 // Constants
 const JSON_VIEW_PREF = "devtools.jsonview.enabled";
 const JSON_VIEW_MIME_TYPE = "application/vnd.mozilla.json.view";
 const JSON_VIEW_CONTRACT_ID = "@mozilla.org/streamconv;1?from=" +
--- a/devtools/client/netmonitor/src/har/har-utils.js
+++ b/devtools/client/netmonitor/src/har/har-utils.js
@@ -4,30 +4,21 @@
 
 /* eslint-disable mozilla/reject-some-requires */
 
 "use strict";
 
 const Services = require("Services");
 const { Ci, Cc, CC } = require("chrome");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
-const { gDevTools } = require("devtools/client/framework/devtools");
 
 XPCOMUtils.defineLazyGetter(this, "ZipWriter", function() {
   return CC("@mozilla.org/zipwriter;1", "nsIZipWriter");
 });
 
-XPCOMUtils.defineLazyGetter(this, "LocalFile", function() {
-  return new CC("@mozilla.org/file/local;1", "nsIFile", "initWithPath");
-});
-
-XPCOMUtils.defineLazyGetter(this, "getMostRecentBrowserWindow", function() {
-  return Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
-});
-
 const OPEN_FLAGS = {
   RDONLY: parseInt("0x01", 16),
   WRONLY: parseInt("0x02", 16),
   CREATE_FILE: parseInt("0x08", 16),
   APPEND: parseInt("0x10", 16),
   TRUNCATE: parseInt("0x20", 16),
   EXCL: parseInt("0x80", 16)
 };
--- a/devtools/client/shared/AppCacheUtils.jsm
+++ b/devtools/client/shared/AppCacheUtils.jsm
@@ -607,16 +607,11 @@ ManifestParser.prototype = {
       }
     }
   },
 };
 
 XPCOMUtils.defineLazyGetter(this, "l10n", () => Services.strings
   .createBundle("chrome://devtools/locale/appcacheutils.properties"));
 
-XPCOMUtils.defineLazyGetter(this, "appcacheservice", function() {
-  return Cc["@mozilla.org/network/application-cache-service;1"]
-           .getService(Ci.nsIApplicationCacheService);
-});
-
 XPCOMUtils.defineLazyGetter(this, "_DOMParser", function() {
   return globals.DOMParser();
 });
--- a/devtools/client/shared/developer-toolbar.js
+++ b/devtools/client/shared/developer-toolbar.js
@@ -1,33 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { Ci } = require("chrome");
 const promise = require("promise");
 const Services = require("Services");
 const { TargetFactory } = require("devtools/client/framework/target");
 const Telemetry = require("devtools/client/shared/telemetry");
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/client/locales/toolbox.properties");
 
 const NS_XHTML = "http://www.w3.org/1999/xhtml";
 
 const { PluralForm } = require("devtools/shared/plural-form");
 
-loader.lazyGetter(this, "prefBranch", function() {
-  return Services.prefs.getBranch(null)
-                    .QueryInterface(Ci.nsIPrefBranch);
-});
-
 loader.lazyRequireGetter(this, "gcliInit", "devtools/shared/gcli/commands/index");
-loader.lazyRequireGetter(this, "util", "gcli/util/util");
 loader.lazyRequireGetter(this, "ConsoleServiceListener", "devtools/server/actors/webconsole/listeners", true);
 loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
 loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
 loader.lazyRequireGetter(this, "nodeConstants", "devtools/shared/dom-node-constants");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 /**
  * A collection of utilities to help working with commands
--- a/devtools/client/shared/widgets/VariablesView.jsm
+++ b/devtools/client/shared/widgets/VariablesView.jsm
@@ -37,24 +37,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 Object.defineProperty(this, "WebConsoleUtils", {
   get: function() {
     return require("devtools/client/webconsole/utils").Utils;
   },
   configurable: true,
   enumerable: true
 });
 
-Object.defineProperty(this, "NetworkHelper", {
-  get: function() {
-    return require("devtools/shared/webconsole/network-helper");
-  },
-  configurable: true,
-  enumerable: true
-});
-
 this.EXPORTED_SYMBOLS = ["VariablesView", "escapeHTML"];
 
 /**
  * A tree view for inspecting scopes, objects and properties.
  * Iterable via "for (let [id, scope] of instance) { }".
  * Requires the devtools common.css and debugger.css skin stylesheets.
  *
  * To allow replacing variable or property values in this view, provide an
--- a/devtools/client/storage/ui.js
+++ b/devtools/client/storage/ui.js
@@ -17,18 +17,16 @@ const { getUnicodeHostname } = require("
 // devtools/client/storage/test/head.js and
 // devtools/server/tests/browser/head.js
 const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
 
 loader.lazyRequireGetter(this, "TreeWidget",
                          "devtools/client/shared/widgets/TreeWidget", true);
 loader.lazyRequireGetter(this, "TableWidget",
                          "devtools/client/shared/widgets/TableWidget", true);
-loader.lazyRequireGetter(this, "ViewHelpers",
-                         "devtools/client/shared/widgets/view-helpers");
 loader.lazyImporter(this, "VariablesView",
   "resource://devtools/client/shared/widgets/VariablesView.jsm");
 
 /**
  * Localization convenience methods.
  */
 const STORAGE_STRINGS = "devtools/client/locales/storage.properties";
 const L10N = new LocalizationHelper(STORAGE_STRINGS);
--- a/devtools/client/webconsole/test/mochitest/browser_webconsole_split.js
+++ b/devtools/client/webconsole/test/mochitest/browser_webconsole_split.js
@@ -24,16 +24,24 @@ add_task(async function() {
 
   info("Testing host types");
   checkHostType(Toolbox.HostType.BOTTOM);
   checkToolboxUI();
   await toolbox.switchHost(Toolbox.HostType.SIDE);
   checkHostType(Toolbox.HostType.SIDE);
   checkToolboxUI();
   await toolbox.switchHost(Toolbox.HostType.WINDOW);
+
+  // checkHostType, below,  will open the meatball menu to read the "Split
+  // console" menu item label. However, if we've just opened a new window then
+  // on some platforms when we switch focus to the new window we might end up
+  // triggering the auto-close behavior on the menu popup. To avoid that, wait
+  // a moment before querying the menu.
+  await new Promise(resolve => requestIdleCallback(resolve));
+
   checkHostType(Toolbox.HostType.WINDOW);
   checkToolboxUI();
   await toolbox.switchHost(Toolbox.HostType.BOTTOM);
 
   async function testConsoleLoadOnDifferentPanel() {
     info("About to check console loads even when non-webconsole panel is open");
 
     await openPanel("inspector");
--- a/devtools/server/actors/accessibility.js
+++ b/devtools/server/actors/accessibility.js
@@ -320,17 +320,17 @@ const AccessibleActor = ActorClassWithSp
 
   get bounds() {
     if (this.isDefunct) {
       return null;
     }
 
     let x = {}, y = {}, w = {}, h = {};
     try {
-      this.rawAccessible.getBounds(x, y, w, h);
+      this.rawAccessible.getBoundsInCSSPixels(x, y, w, h);
       x = x.value;
       y = y.value;
       w = w.value;
       h = h.value;
     } catch (e) {
       return null;
     }
 
--- a/devtools/server/actors/csscoverage.js
+++ b/devtools/server/actors/csscoverage.js
@@ -8,17 +8,16 @@ const { Ci } = require("chrome");
 
 const InspectorUtils = require("InspectorUtils");
 const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 const protocol = require("devtools/shared/protocol");
 const { cssUsageSpec } = require("devtools/shared/specs/csscoverage");
 
-loader.lazyRequireGetter(this, "stylesheets", "devtools/server/actors/stylesheets");
 loader.lazyRequireGetter(this, "prettifyCSS", "devtools/shared/inspector/css-logic", true);
 
 const MAX_UNUSED_RULES = 10000;
 
 /**
  * Allow: let foo = l10n.lookup("csscoverageFoo");
  */
 const l10n = exports.l10n = {
--- a/devtools/server/actors/highlighters/utils/accessibility.js
+++ b/devtools/server/actors/highlighters/utils/accessibility.js
@@ -21,29 +21,29 @@ const { getCurrentZoom } = require("devt
  *         - {Number} w
  *           width of the the accessible object
  *         - {Number} h
  *           height of the the accessible object
  * @return {Object|null} Returns, if available, positioning and bounds information for
  *                 the accessible object.
  */
 function getBounds(win, { x, y, w, h }) {
-  let { devicePixelRatio, mozInnerScreenX, mozInnerScreenY, scrollX, scrollY } = win;
+  let { mozInnerScreenX, mozInnerScreenY, scrollX, scrollY } = win;
   let zoom = getCurrentZoom(win);
   let left = x, right = x + w, top = y, bottom = y + h;
 
-  left -= (mozInnerScreenX - scrollX) * devicePixelRatio;
-  right -= (mozInnerScreenX - scrollX) * devicePixelRatio;
-  top -= (mozInnerScreenY - scrollY) * devicePixelRatio;
-  bottom -= (mozInnerScreenY - scrollY) * devicePixelRatio;
+  left -= mozInnerScreenX - scrollX;
+  right -= mozInnerScreenX - scrollX;
+  top -= mozInnerScreenY - scrollY;
+  bottom -= mozInnerScreenY - scrollY;
 
-  left *= zoom / devicePixelRatio;
-  right *= zoom / devicePixelRatio;
-  top *= zoom / devicePixelRatio;
-  bottom *= zoom / devicePixelRatio;
+  left *= zoom;
+  right *= zoom;
+  top *= zoom;
+  bottom *= zoom;
 
   let width = right - left;
   let height = bottom - top;
 
   return { left, right, top, bottom, width, height };
 }
 
 exports.getBounds = getBounds;
--- a/devtools/server/actors/inspector/walker.js
+++ b/devtools/server/actors/inspector/walker.js
@@ -2018,14 +2018,23 @@ var WalkerActor = protocol.ActorClassWit
    */
   hasAccessibilityProperties: async function(node) {
     if (isNodeDead(node) || !Services.appinfo.accessibilityEnabled) {
       return false;
     }
 
     const accService = Cc["@mozilla.org/accessibilityService;1"].getService(
       Ci.nsIAccessibilityService);
-    const acc = accService.getAccessibleFor(node.rawNode);
+    let acc = accService.getAccessibleFor(node.rawNode);
+    // If node does not have an accessible object, but has an inline text child,
+    // try to retrieve an accessible object for the child instead.
+    if (!acc || acc.indexInParent < 0) {
+      const inlineTextChild = this.inlineTextChild(node);
+      if (inlineTextChild) {
+        acc = accService.getAccessibleFor(inlineTextChild.rawNode);
+      }
+    }
+
     return acc && acc.indexInParent > -1;
   },
 });
 
 exports.WalkerActor = WalkerActor;
--- a/devtools/server/actors/thread.js
+++ b/devtools/server/actors/thread.js
@@ -13,21 +13,16 @@ const { createValueGrip } = require("dev
 const { longStringGrip } = require("devtools/server/actors/object/long-string");
 const { ActorClassWithSpec } = require("devtools/shared/protocol");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const flags = require("devtools/shared/flags");
 const { assert, dumpn } = DevToolsUtils;
 const { DevToolsWorker } = require("devtools/shared/worker/worker");
 const { threadSpec } = require("devtools/shared/specs/script");
 
-loader.lazyGetter(this, "Debugger", () => {
-  let Debugger = require("Debugger");
-  hackDebugger(Debugger);
-  return Debugger;
-});
 loader.lazyRequireGetter(this, "findCssSelector", "devtools/shared/inspector/css-logic", true);
 loader.lazyRequireGetter(this, "BreakpointActor", "devtools/server/actors/breakpoint", true);
 loader.lazyRequireGetter(this, "setBreakpointAtEntryPoints", "devtools/server/actors/breakpoint", true);
 loader.lazyRequireGetter(this, "EnvironmentActor", "devtools/server/actors/environment", true);
 loader.lazyRequireGetter(this, "SourceActorStore", "devtools/server/actors/utils/source-actor-store", true);
 loader.lazyRequireGetter(this, "BreakpointActorMap", "devtools/server/actors/utils/breakpoint-actor-map", true);
 loader.lazyRequireGetter(this, "PauseScopedObjectActor", "devtools/server/actors/pause-scoped", true);
 loader.lazyRequireGetter(this, "EventLoopStack", "devtools/server/actors/utils/event-loop", true);
@@ -1777,69 +1772,16 @@ exports.ThreadActor = ThreadActor;
 function PauseActor(pool) {
   this.pool = pool;
 }
 
 PauseActor.prototype = {
   actorPrefix: "pause"
 };
 
-function hackDebugger(Debugger) {
-  // TODO: Improve native code instead of hacking on top of it
-
-  /**
-   * Override the toString method in order to get more meaningful script output
-   * for debugging the debugger.
-   */
-  Debugger.Script.prototype.toString = function() {
-    if (this.type == "wasm") {
-      return "[wasm]";
-    }
-
-    let output = "";
-    if (this.url) {
-      output += this.url;
-    }
-
-    if (typeof this.staticLevel != "undefined") {
-      output += ":L" + this.staticLevel;
-    }
-    if (typeof this.startLine != "undefined") {
-      output += ":" + this.startLine;
-      if (this.lineCount && this.lineCount > 1) {
-        output += "-" + (this.startLine + this.lineCount - 1);
-      }
-    }
-    if (typeof this.startLine != "undefined") {
-      output += ":" + this.startLine;
-      if (this.lineCount && this.lineCount > 1) {
-        output += "-" + (this.startLine + this.lineCount - 1);
-      }
-    }
-    if (this.strictMode) {
-      output += ":strict";
-    }
-    return output;
-  };
-
-  /**
-   * Helper property for quickly getting to the line number a stack frame is
-   * currently paused at.
-   */
-  Object.defineProperty(Debugger.Frame.prototype, "line", {
-    configurable: true,
-    get: function() {
-      if (this.script) {
-        return this.script.getOffsetLocation(this.offset).lineNumber;
-      }
-      return null;
-    }
-  });
-}
-
 /**
  * Creates an actor for handling chrome debugging. ChromeDebuggerActor is a
  * thin wrapper over ThreadActor, slightly changing some of its behavior.
  *
  * @param connection object
  *        The DebuggerServerConnection with which this ChromeDebuggerActor
  *        is associated. (Currently unused, but required to make this
  *        constructor usable with addGlobalActor.)
--- a/devtools/server/actors/utils/TabSources.js
+++ b/devtools/server/actors/utils/TabSources.js
@@ -8,17 +8,16 @@ const DevToolsUtils = require("devtools/
 const { assert, fetch } = DevToolsUtils;
 const EventEmitter = require("devtools/shared/event-emitter");
 const { OriginalLocation, GeneratedLocation } = require("devtools/server/actors/common");
 const { joinURI } = require("devtools/shared/path");
 
 loader.lazyRequireGetter(this, "SourceActor", "devtools/server/actors/source", true);
 loader.lazyRequireGetter(this, "isEvalSource", "devtools/server/actors/source", true);
 loader.lazyRequireGetter(this, "SourceMapConsumer", "source-map", true);
-loader.lazyRequireGetter(this, "SourceMapGenerator", "source-map", true);
 loader.lazyRequireGetter(this, "WasmRemap", "devtools/shared/wasm-source-map", true);
 
 /**
  * Manages the sources for a thread. Handles source maps, locations in the
  * sources, etc for ThreadActors.
  */
 function TabSources(threadActor, allowSourceFn = () => true) {
   EventEmitter.decorate(this);
--- a/devtools/server/actors/webconsole/listeners.js
+++ b/devtools/server/actors/webconsole/listeners.js
@@ -5,20 +5,16 @@
 "use strict";
 
 const {Cc, Ci, components} = require("chrome");
 const {isWindowIncluded} = require("devtools/shared/layout/utils");
 const Services = require("Services");
 const {XPCOMUtils} = require("resource://gre/modules/XPCOMUtils.jsm");
 const {CONSOLE_WORKER_IDS, WebConsoleUtils} = require("devtools/server/actors/webconsole/utils");
 
-XPCOMUtils.defineLazyServiceGetter(this,
-                                   "swm",
-                                   "@mozilla.org/serviceworkers/manager;1",
-                                   "nsIServiceWorkerManager");
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 
 // Process script used to forward console calls from content processes to parent process
 const CONTENT_PROCESS_SCRIPT = "resource://devtools/server/actors/webconsole/content-process-forward.js";
 
 // The page errors listener
 
 /**
--- a/devtools/server/actors/webextension.js
+++ b/devtools/server/actors/webextension.js
@@ -5,17 +5,16 @@
 "use strict";
 
 const { Ci, Cu, Cc } = require("chrome");
 const Services = require("Services");
 
 const { ChromeActor } = require("./chrome");
 const makeDebugger = require("./utils/make-debugger");
 
-loader.lazyRequireGetter(this, "mapURIToAddonID", "devtools/server/actors/utils/map-uri-to-addon-id");
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/thread", true);
 loader.lazyRequireGetter(this, "ChromeUtils");
 
 const FALLBACK_DOC_MESSAGE = "Your addon does not have any document opened yet.";
 
 /**
  * Creates a TabActor for debugging all the contexts associated to a target WebExtensions
  * add-on running in a child extension process.
--- a/devtools/server/main.js
+++ b/devtools/server/main.js
@@ -13,30 +13,33 @@ var Services = require("Services");
 var { ActorPool, OriginalLocation, RegisteredActorFactory,
       ObservedActorFactory } = require("devtools/server/actors/common");
 var { LocalDebuggerTransport, ChildDebuggerTransport, WorkerDebuggerTransport } =
   require("devtools/shared/transport/transport");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 var { dumpn } = DevToolsUtils;
 
 DevToolsUtils.defineLazyGetter(this, "DebuggerSocket", () => {
+  // eslint-disable-next-line no-shadow
   let { DebuggerSocket } = require("devtools/shared/security/socket");
   return DebuggerSocket;
 });
 DevToolsUtils.defineLazyGetter(this, "Authentication", () => {
   return require("devtools/shared/security/auth");
 });
 DevToolsUtils.defineLazyGetter(this, "generateUUID", () => {
+  // eslint-disable-next-line no-shadow
   let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
                            .getService(Ci.nsIUUIDGenerator);
   return generateUUID;
 });
 
 // Overload `Components` to prevent DevTools loader exception on Components
 // object usage
+// eslint-disable-next-line no-unused-vars
 Object.defineProperty(this, "Components", {
   get() {
     return require("chrome").components;
   }
 });
 
 const CONTENT_PROCESS_SERVER_STARTUP_SCRIPT =
   "resource://devtools/server/startup/content-process.js";
--- a/devtools/server/performance/memory.js
+++ b/devtools/server/performance/memory.js
@@ -9,18 +9,16 @@ const { reportException } = require("dev
 const { expectState } = require("devtools/server/actors/common");
 
 loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
 loader.lazyRequireGetter(this, "DeferredTask",
   "resource://gre/modules/DeferredTask.jsm", true);
 loader.lazyRequireGetter(this, "StackFrameCache",
   "devtools/server/actors/utils/stack", true);
 loader.lazyRequireGetter(this, "ChromeUtils");
-loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
-  "devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
 loader.lazyRequireGetter(this, "ChromeActor", "devtools/server/actors/chrome",
                          true);
 loader.lazyRequireGetter(this, "ChildProcessActor",
                          "devtools/server/actors/child-process", true);
 
 /**
  * A class that returns memory data for a parent actor's window.
  * Using a tab-scoped actor with this instance will measure the memory footprint of its
--- a/devtools/server/performance/recorder.js
+++ b/devtools/server/performance/recorder.js
@@ -11,22 +11,18 @@ loader.lazyRequireGetter(this, "EventEmi
 loader.lazyRequireGetter(this, "Memory",
   "devtools/server/performance/memory", true);
 loader.lazyRequireGetter(this, "Timeline",
   "devtools/server/performance/timeline", true);
 loader.lazyRequireGetter(this, "Profiler",
   "devtools/server/performance/profiler", true);
 loader.lazyRequireGetter(this, "PerformanceRecordingActor",
   "devtools/server/actors/performance-recording", true);
-loader.lazyRequireGetter(this, "PerformanceRecordingFront",
-  "devtools/server/actors/performance-recording", true);
 loader.lazyRequireGetter(this, "mapRecordingOptions",
   "devtools/shared/performance/recording-utils", true);
-loader.lazyRequireGetter(this, "DevToolsUtils",
-  "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "getSystemInfo",
   "devtools/shared/system", true);
 
 const PROFILER_EVENTS = [
   "console-api-profiler",
   "profiler-started",
   "profiler-stopped",
   "profiler-status"
--- a/devtools/shared/gcli/commands/cmd.js
+++ b/devtools/shared/gcli/commands/cmd.js
@@ -7,18 +7,16 @@
 const { Cc, Ci, Cu } = require("chrome");
 const Services = require("Services");
 const { OS } = require("resource://gre/modules/osfile.jsm");
 const { Task } = require("devtools/shared/task");
 
 const gcli = require("gcli/index");
 const l10n = require("gcli/l10n");
 
-loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
-
 const PREF_DIR = "devtools.commands.dir";
 
 /**
  * Load all the .mozcmd files in the directory pointed to by PREF_DIR
  * @return A promise of an array of items suitable for gcli.addItems or
  * using in gcli.addItemsByModule
  */
 function loadItemsFromMozDir() {
--- a/devtools/shared/gcli/commands/listen.js
+++ b/devtools/shared/gcli/commands/listen.js
@@ -17,16 +17,17 @@ XPCOMUtils.defineLazyGetter(this, "debug
   // Create a separate loader instance, so that we can be sure to receive
   // a separate instance of the DebuggingServer from the rest of the
   // devtools.  This allows us to safely use the tools against even the
   // actors and DebuggingServer itself, especially since we can mark
   // serverLoader as invisible to the debugger (unlike the usual loader
   // settings).
   let serverLoader = new DevToolsLoader();
   serverLoader.invisibleToDebugger = true;
+  // eslint-disable-next-line no-shadow
   let { DebuggerServer: debuggerServer } = serverLoader.require("devtools/server/main");
   debuggerServer.init();
   debuggerServer.registerAllActors();
   debuggerServer.allowChromeProcess = !l10n.hiddenByChromePref();
   return debuggerServer;
 });
 
 exports.items = [
--- a/devtools/shared/layout/utils.js
+++ b/devtools/shared/layout/utils.js
@@ -8,16 +8,17 @@ const { Ci, Cc } = require("chrome");
 const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
 
 const SHEET_TYPE = {
   "agent": "AGENT_SHEET",
   "user": "USER_SHEET",
   "author": "AUTHOR_SHEET"
 };
 
+// eslint-disable-next-line no-unused-vars
 loader.lazyRequireGetter(this, "setIgnoreLayoutChanges", "devtools/server/actors/reflow", true);
 exports.setIgnoreLayoutChanges = (...args) =>
   this.setIgnoreLayoutChanges(...args);
 
 /**
  * Returns the `DOMWindowUtils` for the window given.
  *
  * @param {DOMWindow} win
--- a/devtools/shared/security/auth.js
+++ b/devtools/shared/security/auth.js
@@ -289,16 +289,17 @@ OOBCert.Client.prototype = {
    *        Whether the server requires encryption.  Defaults to false.
    * @param cert object (optional)
    *        The server's cert details.
    * @param socket nsISocketTransport
    *        Underlying socket transport, in case more details are needed.
    * @return boolean
    *         Whether the connection is valid.
    */
+  // eslint-disable-next-line no-shadow
   validateConnection({ cert, socket }) {
     // Step B.7
     // Client verifies that Server's cert matches hash(ServerCert) from the
     // advertisement
     dumpv("Validate server cert hash");
     let serverCert = socket.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                            .SSLStatus.serverCert;
     let advertisedCert = cert;
@@ -322,16 +323,17 @@ OOBCert.Client.prototype = {
    * @param encryption boolean (optional)
    *        Whether the server requires encryption.  Defaults to false.
    * @param cert object (optional)
    *        The server's cert details.
    * @param transport DebuggerTransport
    *        A transport that can be used to communicate with the server.
    * @return A promise can be used if there is async behavior.
    */
+  // eslint-disable-next-line no-shadow
   authenticate({ host, port, cert, transport }) {
     let deferred = defer();
     let oobData;
 
     let activeSendDialog;
     let closeDialog = () => {
       // Close any prompts the client may have been showing from previous
       // authentication steps
--- a/devtools/shared/security/prompt.js
+++ b/devtools/shared/security/prompt.js
@@ -4,18 +4,16 @@
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 var { Ci } = require("chrome");
 var Services = require("Services");
 var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-loader.lazyRequireGetter(this, "DebuggerSocket",
-  "devtools/shared/security/socket", true);
 loader.lazyRequireGetter(this, "AuthenticationResult",
   "devtools/shared/security/auth", true);
 
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const L10N = new LocalizationHelper("devtools/shared/locales/debugger.properties");
 
 var Client = exports.Client = {};
 var Server = exports.Server = {};
--- a/devtools/shared/security/socket.js
+++ b/devtools/shared/security/socket.js
@@ -74,16 +74,17 @@ var DebuggerSocket = {};
  *         Resolved to a DebuggerTransport instance.
  */
 DebuggerSocket.connect = async function(settings) {
   // Default to PROMPT |Authenticator| instance if not supplied
   if (!settings.authenticator) {
     settings.authenticator = new (Authenticators.get().Client)();
   }
   _validateSettings(settings);
+  // eslint-disable-next-line no-shadow
   let { host, port, encryption, authenticator, cert } = settings;
   let transport = await _getTransport(settings);
   await authenticator.authenticate({
     host,
     port,
     encryption,
     cert,
     transport
@@ -346,16 +347,17 @@ function _isInputAlive(input) {
 }
 
 /**
  * To allow the connection to proceed with self-signed cert, we store a cert
  * override.  This implies that we take on the burden of authentication for
  * these connections.
  */
 function _storeCertOverride(s, host, port) {
+  // eslint-disable-next-line no-shadow
   let cert = s.securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
               .SSLStatus.serverCert;
   let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                      Ci.nsICertOverrideService.ERROR_MISMATCH;
   certOverrideService.rememberValidityOverride(host, port, cert, overrideBits,
                                                true /* temporary */);
 }
 
--- a/devtools/shared/system.js
+++ b/devtools/shared/system.js
@@ -9,20 +9,16 @@ loader.lazyRequireGetter(this, "Services
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "defer", "devtools/shared/defer");
 loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
 loader.lazyRequireGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm", true);
 loader.lazyGetter(this, "screenManager", () => {
   return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
 });
-loader.lazyGetter(this, "oscpu", () => {
-  return Cc["@mozilla.org/network/protocol;1?name=http"]
-           .getService(Ci.nsIHttpProtocolHandler).oscpu;
-});
 loader.lazyGetter(this, "hostname", () => {
   try {
     // On some platforms (Linux according to try), this service does not exist and fails.
     return Cc["@mozilla.org/network/dns-service;1"]
               .getService(Ci.nsIDNSService).myHostName;
   } catch (e) {
     return "";
   }
--- a/devtools/shared/transport/packets.js
+++ b/devtools/shared/transport/packets.js
@@ -27,16 +27,17 @@
 const { Cc, Ci } = require("chrome");
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const { dumpn, dumpv } = DevToolsUtils;
 const flags = require("devtools/shared/flags");
 const StreamUtils = require("devtools/shared/transport/stream-utils");
 const defer = require("devtools/shared/defer");
 
 DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
+  // eslint-disable-next-line no-shadow
   const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                            .createInstance(Ci.nsIScriptableUnicodeConverter);
   unicodeConverter.charset = "UTF-8";
   return unicodeConverter;
 });
 
 // The transport's previous check ensured the header length did not exceed 20
 // characters.  Here, we opt for the somewhat smaller, but still large limit of
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -11,18 +11,16 @@ const Services = require("Services");
 const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "NetworkHelper",
                          "devtools/shared/webconsole/network-helper");
 loader.lazyRequireGetter(this, "DevToolsUtils",
                          "devtools/shared/DevToolsUtils");
 loader.lazyRequireGetter(this, "flags",
                          "devtools/shared/flags");
-loader.lazyRequireGetter(this, "DebuggerServer",
-                         "devtools/server/main", true);
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
 loader.lazyServiceGetter(this, "gActivityDistributor",
                          "@mozilla.org/network/http-activity-distributor;1",
                          "nsIHttpActivityDistributor");
 const {NetworkThrottleManager} = require("devtools/shared/webconsole/throttle");
 
 // Network logging
 
@@ -128,16 +126,17 @@ ChannelEventSink.prototype = {
   unregisterCollector(collector) {
     this.collectors.delete(collector);
 
     if (this.collectors.size == 0) {
       ChannelEventSinkFactory.unregister();
     }
   },
 
+  // eslint-disable-next-line no-shadow
   asyncOnChannelRedirect(oldChannel, newChannel, flags, callback) {
     for (let collector of this.collectors) {
       try {
         collector.onChannelRedirect(oldChannel, newChannel, flags);
       } catch (ex) {
         console.error("StackTraceCollector.onChannelRedirect threw an exception", ex);
       }
     }
@@ -220,16 +219,17 @@ StackTraceCollector.prototype = {
         });
         frame = frame.caller || frame.asyncCaller;
       }
     }
 
     this._saveStackTrace(channel, stacktrace);
   },
 
+  // eslint-disable-next-line no-shadow
   onChannelRedirect(oldChannel, newChannel, flags) {
     // We can be called with any nsIChannel, but are interested only in HTTP channels
     try {
       oldChannel.QueryInterface(Ci.nsIHttpChannel);
       newChannel.QueryInterface(Ci.nsIHttpChannel);
     } catch (ex) {
       return;
     }
--- a/devtools/startup/DevToolsShim.jsm
+++ b/devtools/startup/DevToolsShim.jsm
@@ -12,16 +12,17 @@ XPCOMUtils.defineLazyGetter(this, "Devto
             .getService(Ci.nsICommandLineHandler)
             .wrappedJSObject;
 });
 
 // We don't want to spend time initializing the full loader here so we create
 // our own lazy require.
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+  // eslint-disable-next-line no-shadow
   const Telemetry = require("devtools/client/shared/telemetry");
 
   return Telemetry;
 });
 
 const DEVTOOLS_ENABLED_PREF = "devtools.enabled";
 const DEVTOOLS_POLICY_DISABLED_PREF = "devtools.policy.disabled";
 
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -44,16 +44,17 @@ ChromeUtils.defineModuleGetter(this, "Cu
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableWidgets",
                                "resource:///modules/CustomizableWidgets.jsm");
 
 // We don't want to spend time initializing the full loader here so we create
 // our own lazy require.
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
+  // eslint-disable-next-line no-shadow
   const Telemetry = require("devtools/client/shared/telemetry");
 
   return Telemetry;
 });
 
 XPCOMUtils.defineLazyGetter(this, "StartupBundle", function() {
   const url = "chrome://devtools-startup/locale/startup.properties";
   return Services.strings.createBundle(url);
--- a/dom/animation/KeyframeUtils.cpp
+++ b/dom/animation/KeyframeUtils.cpp
@@ -19,17 +19,16 @@
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/dom/KeyframeEffectReadOnly.h" // For PropertyValuesPair etc.
 #include "mozilla/dom/Nullable.h"
 #include "jsapi.h" // For ForOfIterator etc.
 #include "nsClassHashtable.h"
 #include "nsContentUtils.h" // For GetContextForContent, and
                             // AnimationsAPICoreEnabled
-#include "nsCSSParser.h"
 #include "nsCSSPropertyIDSet.h"
 #include "nsCSSProps.h"
 #include "nsCSSPseudoElements.h" // For CSSPseudoElementType
 #include "nsDocument.h" // For nsDocument::IsWebAnimationsEnabled
 #include "nsIScriptError.h"
 #include "nsTArray.h"
 #include <algorithm> // For std::stable_sort, std::min
 
@@ -173,17 +172,17 @@ AppendStringOrStringSequenceToArray(JSCo
 
 static bool
 AppendValueAsString(JSContext* aCx,
                     nsTArray<nsString>& aValues,
                     JS::Handle<JS::Value> aValue);
 
 static Maybe<PropertyValuePair>
 MakePropertyValuePair(nsCSSPropertyID aProperty, const nsAString& aStringValue,
-                      nsCSSParser& aParser, nsIDocument* aDocument);
+                      nsIDocument* aDocument);
 
 static bool
 HasValidOffsets(const nsTArray<Keyframe>& aKeyframes);
 
 #ifdef DEBUG
 static void
 MarkAsComputeValuesFailureKey(PropertyValuePair& aPair);
 
@@ -413,17 +412,16 @@ GetKeyframeListFromKeyframeSequence(JSCo
  */
 static bool
 ConvertKeyframeSequence(JSContext* aCx,
                         nsIDocument* aDocument,
                         JS::ForOfIterator& aIterator,
                         nsTArray<Keyframe>& aResult)
 {
   JS::Rooted<JS::Value> value(aCx);
-  nsCSSParser parser(aDocument->CSSLoader());
   ErrorResult parseEasingResult;
 
   for (;;) {
     bool done;
     if (!aIterator.next(&value, &done)) {
       return false;
     }
     if (done) {
@@ -479,18 +477,17 @@ ConvertKeyframeSequence(JSContext* aCx,
       // loop since (since it is never likely to be handled by WebIDL unlike the
       // rest of this loop).
     }
 
     for (PropertyValuesPair& pair : propertyValuePairs) {
       MOZ_ASSERT(pair.mValues.Length() == 1);
 
       Maybe<PropertyValuePair> valuePair =
-        MakePropertyValuePair(pair.mProperty, pair.mValues[0], parser,
-                              aDocument);
+        MakePropertyValuePair(pair.mProperty, pair.mValues[0], aDocument);
       if (!valuePair) {
         continue;
       }
       keyframe->mPropertyValues.AppendElement(Move(valuePair.ref()));
 
 #ifdef DEBUG
       // When we go to convert keyframes into arrays of property values we
       // call StyleAnimation::ComputeValues. This should normally return true
@@ -657,24 +654,23 @@ ReportInvalidPropertyValueToConsole(nsCS
 }
 
 /**
  * Construct a PropertyValuePair parsing the given string into a suitable
  * nsCSSValue object.
  *
  * @param aProperty The CSS property.
  * @param aStringValue The property value to parse.
- * @param aParser The CSS parser object to use.
  * @param aDocument The document to use when parsing.
  * @return The constructed PropertyValuePair, or Nothing() if |aStringValue| is
  *   an invalid property value.
  */
 static Maybe<PropertyValuePair>
 MakePropertyValuePair(nsCSSPropertyID aProperty, const nsAString& aStringValue,
-                      nsCSSParser& aParser, nsIDocument* aDocument)
+                      nsIDocument* aDocument)
 {
   MOZ_ASSERT(aDocument);
   Maybe<PropertyValuePair> result;
 
   ServoCSSParser::ParsingEnvironment env =
     ServoCSSParser::GetParsingEnvironment(aDocument);
   RefPtr<RawServoDeclarationBlock> servoDeclarationBlock =
     ServoCSSParser::ParseProperty(aProperty, aStringValue, env);
@@ -1045,17 +1041,16 @@ GetKeyframeListFromPropertyIndexedKeyfra
   nsTArray<PropertyValuesPair> propertyValuesPairs;
   if (!GetPropertyValuesPairs(aCx, object, ListAllowance::eAllow,
                               propertyValuesPairs)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   // Create a set of keyframes for each property.
-  nsCSSParser parser(aDocument->CSSLoader());
   nsClassHashtable<nsFloatHashKey, Keyframe> processedKeyframes;
   for (const PropertyValuesPair& pair : propertyValuesPairs) {
     size_t count = pair.mValues.Length();
     if (count == 0) {
       // No animation values for this property.
       continue;
     }
 
@@ -1075,17 +1070,17 @@ GetKeyframeListFromPropertyIndexedKeyfra
       // keyframe with offset 1.
       double offset = n ? i++ / double(n) : 1;
       Keyframe* keyframe = processedKeyframes.LookupOrAdd(offset);
       if (keyframe->mPropertyValues.IsEmpty()) {
         keyframe->mComputedOffset = offset;
       }
 
       Maybe<PropertyValuePair> valuePair =
-        MakePropertyValuePair(pair.mProperty, stringValue, parser, aDocument);
+        MakePropertyValuePair(pair.mProperty, stringValue, aDocument);
       if (!valuePair) {
         continue;
       }
       keyframe->mPropertyValues.AppendElement(Move(valuePair.ref()));
     }
   }
 
   aResult.SetCapacity(processedKeyframes.Count());
--- a/dom/animation/TimingParams.cpp
+++ b/dom/animation/TimingParams.cpp
@@ -6,17 +6,16 @@
 
 #include "mozilla/TimingParams.h"
 
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/dom/KeyframeAnimationOptionsBinding.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 #include "mozilla/ServoCSSParser.h"
-#include "nsCSSParser.h" // For nsCSSParser
 #include "nsIDocument.h"
 
 namespace mozilla {
 
 template <class OptionsType>
 static const dom::AnimationEffectTimingProperties&
 GetTimingProperties(const OptionsType& aOptions);
 
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -18,16 +18,17 @@ namespace devtools {
 class HeapSnapshot;
 } // namespace devtools
 
 namespace dom {
 
 class ArrayBufferViewOrArrayBuffer;
 class IdleRequestCallback;
 struct IdleRequestOptions;
+class MozQueryInterface;
 class PrecompiledScript;
 class Promise;
 
 class ChromeUtils
 {
 private:
   // Implemented in devtools/shared/heapsnapshot/HeapSnapshot.cpp
   static void SaveHeapSnapshotShared(GlobalObject& global,
@@ -118,16 +119,20 @@ public:
 
   // Implemented in js/xpconnect/loader/ChromeScriptLoader.cpp
   static already_AddRefed<Promise>
   CompileScript(GlobalObject& aGlobal,
                 const nsAString& aUrl,
                 const dom::CompileScriptOptionsDictionary& aOptions,
                 ErrorResult& aRv);
 
+  static MozQueryInterface*
+  GenerateQI(const GlobalObject& global, const Sequence<OwningStringOrIID>& interfaces,
+             ErrorResult& aRv);
+
   static void WaiveXrays(GlobalObject& aGlobal,
                          JS::HandleValue aVal,
                          JS::MutableHandleValue aRetval,
                          ErrorResult& aRv);
 
   static void UnwaiveXrays(GlobalObject& aGlobal,
                            JS::HandleValue aVal,
                            JS::MutableHandleValue aRetval,
--- a/dom/base/DOMIntersectionObserver.cpp
+++ b/dom/base/DOMIntersectionObserver.cpp
@@ -1,16 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DOMIntersectionObserver.h"
-#include "nsCSSParser.h"
 #include "nsCSSPropertyID.h"
 #include "nsIFrame.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/ServoBindings.h"
 
 namespace mozilla {
 namespace dom {
--- a/dom/base/DOMMatrix.cpp
+++ b/dom/base/DOMMatrix.cpp
@@ -8,17 +8,16 @@
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/DOMMatrixBinding.h"
 #include "mozilla/dom/DOMPoint.h"
 #include "mozilla/dom/DOMPointBinding.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/ServoCSSParser.h"
-#include "nsCSSParser.h"
 #include "nsGlobalWindowInner.h"
 #include "nsStyleTransformMatrix.h"
 #include "nsGlobalWindowInner.h"
 
 #include <math.h>
 
 namespace mozilla {
 namespace dom {
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -114,17 +114,16 @@
 #include "nsIScrollableFrame.h"
 #include "nsTextNode.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsCCUncollectableMarker.h"
 
 #include "mozAutoDocUpdate.h"
 
-#include "nsCSSParser.h"
 #include "nsDOMMutationObserver.h"
 #include "nsWrapperCacheInlines.h"
 #include "xpcpublic.h"
 #include "nsIScriptError.h"
 #include "mozilla/Telemetry.h"
 
 #include "mozilla/CORSMode.h"
 #include "mozilla/dom/ShadowRoot.h"
new file mode 100644
--- /dev/null
+++ b/dom/base/MozQueryInterface.cpp
@@ -0,0 +1,141 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ChromeUtils.h"
+#include "MozQueryInterface.h"
+
+#include <string.h>
+
+#include "jsapi.h"
+
+#include "xpcpublic.h"
+#include "xpcjsid.h"
+
+namespace mozilla {
+namespace dom {
+
+constexpr size_t IID_SIZE = sizeof(nsIID);
+
+static_assert(IID_SIZE == 16,
+              "Size of nsID struct changed. Please ensure this code is still valid.");
+
+static int
+CompareIIDs(const nsIID& aA, const nsIID &aB)
+{
+  return memcmp((void*)&aA.m0, (void*)&aB.m0, IID_SIZE);
+}
+
+struct IIDComparator
+{
+  bool
+  LessThan(const nsIID& aA, const nsIID &aB) const
+  {
+    return CompareIIDs(aA, aB) < 0;
+  }
+
+  bool
+  Equals(const nsIID& aA, const nsIID &aB) const
+  {
+    return aA.Equals(aB);
+  }
+};
+
+/* static */
+MozQueryInterface*
+ChromeUtils::GenerateQI(const GlobalObject& aGlobal, const Sequence<OwningStringOrIID>& aInterfaces, ErrorResult& aRv)
+{
+  JSContext* cx = aGlobal.Context();
+  JS::RootedObject xpcIfaces(cx);
+
+  nsTArray<nsIID> ifaces;
+
+  JS::RootedValue val(cx);
+  for (auto& iface : aInterfaces) {
+    if (iface.IsIID()) {
+      ifaces.AppendElement(*iface.GetAsIID()->GetID());
+      continue;
+    }
+
+    // If we have a string value, we need to look up the interface name. The
+    // simplest and most efficient way to do this is to just grab the "Ci"
+    // object from the global scope.
+    if (!xpcIfaces) {
+      JS::RootedObject global(cx, aGlobal.Get());
+      if (!JS_GetProperty(cx, global, "Ci", &val)) {
+        aRv.NoteJSContextException(cx);
+        return nullptr;
+      }
+      if (!val.isObject()) {
+        aRv.Throw(NS_ERROR_UNEXPECTED);
+        return nullptr;
+      }
+      xpcIfaces = &val.toObject();
+    }
+
+    auto& name = iface.GetAsString();
+    if (!JS_GetUCProperty(cx, xpcIfaces, name.get(), name.Length(), &val)) {
+      aRv.NoteJSContextException(cx);
+      return nullptr;
+    }
+
+    if (val.isNullOrUndefined()) {
+      continue;
+    }
+    if (!val.isObject()) {
+      aRv.Throw(NS_ERROR_INVALID_ARG);
+      return nullptr;
+    }
+
+    nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(&val.toObject());
+    nsCOMPtr<nsIJSID> iid = do_QueryInterface(base);
+    if (!iid) {
+      aRv.Throw(NS_ERROR_INVALID_ARG);
+      return nullptr;
+    }
+    ifaces.AppendElement(*iid->GetID());
+  }
+
+  MOZ_ASSERT(!ifaces.Contains(NS_GET_IID(nsISupports), IIDComparator()));
+  ifaces.AppendElement(NS_GET_IID(nsISupports));
+
+  ifaces.Sort(IIDComparator());
+
+  return new MozQueryInterface(Move(ifaces));
+}
+
+bool
+MozQueryInterface::QueriesTo(const nsIID& aIID) const
+{
+  // We use BinarySearchIf here because nsTArray::ContainsSorted requires
+  // twice as many comparisons.
+  size_t result;
+  return BinarySearchIf(mInterfaces, 0, mInterfaces.Length(),
+                        [&] (const nsIID& aOther) { return CompareIIDs(aIID, aOther); },
+                        &result);
+}
+
+void
+MozQueryInterface::LegacyCall(JSContext* cx, JS::Handle<JS::Value> thisv,
+                              nsIJSID* aIID,
+                              JS::MutableHandle<JS::Value> aResult,
+                              ErrorResult& aRv) const
+{
+  if (!QueriesTo(*aIID->GetID())) {
+    aRv.Throw(NS_ERROR_NO_INTERFACE);
+  } else {
+    aResult.set(thisv);
+  }
+}
+
+bool
+MozQueryInterface::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector)
+{
+  return MozQueryInterfaceBinding::Wrap(aCx, this, aGivenProto, aReflector);
+}
+
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/base/MozQueryInterface.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MozQueryInterface
+#define mozilla_dom_MozQueryInterface
+
+/**
+ * This class implements an optimized QueryInterface method for
+ * XPConnect-wrapped JS objects.
+ *
+ * For JavaScript callers, it behaves as an ordinary QueryInterface method,
+ * returning its `this` object or throwing depending on the interface it was
+ * passed.
+ *
+ * For native XPConnect callers, we bypass JSAPI entirely, and directly check
+ * whether the queried interface is in the interfaces list.
+ */
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ChromeUtilsBinding.h"
+#include "mozilla/ErrorResult.h"
+
+namespace mozilla {
+namespace dom {
+
+class MozQueryInterface final : public NonRefcountedDOMObject
+{
+public:
+  explicit MozQueryInterface(nsTArray<nsIID>&& aInterfaces)
+    : mInterfaces(Move(aInterfaces))
+  {}
+
+  bool QueriesTo(const nsIID& aIID) const;
+
+  void LegacyCall(JSContext* cx, JS::Handle<JS::Value> thisv, nsIJSID* aIID, JS::MutableHandle<JS::Value> aResult, ErrorResult& aRv) const;
+
+  nsISupports* GetParentObject() const { return nullptr; }
+
+  bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
+
+private:
+  nsTArray<nsIID> mInterfaces;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MozQueryInterface
--- a/dom/base/ResponsiveImageSelector.cpp
+++ b/dom/base/ResponsiveImageSelector.cpp
@@ -7,17 +7,16 @@
 #include "mozilla/dom/ResponsiveImageSelector.h"
 #include "mozilla/ServoStyleSetInlines.h"
 #include "mozilla/TextUtils.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsPresContext.h"
 
-#include "nsCSSParser.h"
 #include "nsCSSProps.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -190,16 +190,17 @@ EXPORTS.mozilla.dom += [
     'ImageEncoder.h',
     'ImageTracker.h',
     'IntlUtils.h',
     'Link.h',
     'Location.h',
     'MessageListenerManager.h',
     'MessageManagerGlobal.h',
     'MessageSender.h',
+    'MozQueryInterface.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'Pose.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
@@ -270,16 +271,17 @@ UNIFIED_SOURCES += [
     'ImageEncoder.cpp',
     'ImageTracker.cpp',
     'IntlUtils.cpp',
     'Link.cpp',
     'Location.cpp',
     'MessageListenerManager.cpp',
     'MessageManagerGlobal.cpp',
     'MessageSender.cpp',
+    'MozQueryInterface.cpp',
     'Navigator.cpp',
     'NodeInfo.cpp',
     'NodeIterator.cpp',
     'nsAttrAndChildArray.cpp',
     'nsAttrValue.cpp',
     'nsAttrValueOrString.cpp',
     'nsCCUncollectableMarker.cpp',
     'nsContentAreaDragDrop.cpp',
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -19,17 +19,16 @@
 #include "mozilla/CORSMode.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ServoBindingTypes.h"
 #include "mozilla/ServoUtils.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "nsContentUtils.h"
 #include "nsReadableUtils.h"
 #include "nsHTMLCSSStyleSheet.h"
-#include "nsCSSParser.h"
 #include "nsStyledElement.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 #define MISC_STR_PTR(_cont) \
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -88,17 +88,16 @@
 #include "nsStyleConsts.h"
 #include "nsSVGUtils.h"
 #include "nsTextNode.h"
 #include "nsUnicharUtils.h"
 #include "nsXBLBinding.h"
 #include "nsXBLPrototypeBinding.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
-#include "nsCSSParser.h"
 #include "HTMLLegendElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "WrapperFactory.h"
 #include <algorithm>
 #include "nsGlobalWindow.h"
 #include "nsDOMMutationObserver.h"
 #include "GeometryUtils.h"
 #include "nsIAnimationObserver.h"
--- a/dom/base/nsStyledElement.cpp
+++ b/dom/base/nsStyledElement.cpp
@@ -12,17 +12,16 @@
 #include "mozilla/dom/ElementInlines.h"
 #include "mozilla/dom/MutationEventBinding.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "nsDOMCSSDeclaration.h"
 #include "nsDOMCSSAttrDeclaration.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIDocument.h"
 #include "mozilla/DeclarationBlockInlines.h"
-#include "nsCSSParser.h"
 #include "mozilla/css/Loader.h"
 #include "nsXULElement.h"
 #include "nsContentUtils.h"
 #include "nsStyleUtil.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
--- a/dom/base/nsTreeSanitizer.cpp
+++ b/dom/base/nsTreeSanitizer.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/DeclarationBlock.h"
 #include "mozilla/DeclarationBlockInlines.h"
 #include "mozilla/ServoDeclarationBlock.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/css/Rule.h"
 #include "mozilla/dom/CSSRuleList.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/SRIMetadata.h"
-#include "nsCSSParser.h"
 #include "nsCSSPropertyID.h"
 #include "nsUnicharInputStream.h"
 #include "nsAttrName.h"
 #include "nsIScriptError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 #include "nsComponentManagerUtils.h"
 #include "NullPrincipal.h"
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -187,31 +187,31 @@ struct TErrorResult<CleanupPolicy>::Mess
 template<typename CleanupPolicy>
 nsTArray<nsString>&
 TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(const dom::ErrNum errorNumber,
                                                       nsresult errorType)
 {
   AssertInOwningThread();
   mResult = errorType;
 
-  mMessage = new Message();
-  mMessage->mErrorNumber = errorNumber;
-  return mMessage->mArgs;
+  Message* message = InitMessage(new Message());
+  message->mErrorNumber = errorNumber;
+  return message->mArgs;
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SerializeMessage(IPC::Message* aMsg) const
 {
   using namespace IPC;
   AssertInOwningThread();
   MOZ_ASSERT(mUnionState == HasMessage);
-  MOZ_ASSERT(mMessage);
-  WriteParam(aMsg, mMessage->mArgs);
-  WriteParam(aMsg, mMessage->mErrorNumber);
+  MOZ_ASSERT(mExtra.mMessage);
+  WriteParam(aMsg, mExtra.mMessage->mArgs);
+  WriteParam(aMsg, mExtra.mMessage->mErrorNumber);
 }
 
 template<typename CleanupPolicy>
 bool
 TErrorResult<CleanupPolicy>::DeserializeMessage(const IPC::Message* aMsg,
                                                 PickleIterator* aIter)
 {
   using namespace IPC;
@@ -221,32 +221,33 @@ TErrorResult<CleanupPolicy>::Deserialize
       !ReadParam(aMsg, aIter, &readMessage->mErrorNumber)) {
     return false;
   }
   if (!readMessage->HasCorrectNumberOfArguments()) {
     return false;
   }
 
   MOZ_ASSERT(mUnionState == HasNothing);
-  mMessage = readMessage.forget();
+  InitMessage(readMessage.forget());
 #ifdef DEBUG
   mUnionState = HasMessage;
 #endif // DEBUG
   return true;
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(JSContext* aCx)
 {
   AssertInOwningThread();
-  MOZ_ASSERT(mMessage, "SetPendingExceptionWithMessage() can be called only once");
   MOZ_ASSERT(mUnionState == HasMessage);
-
-  Message* message = mMessage;
+  MOZ_ASSERT(mExtra.mMessage,
+             "SetPendingExceptionWithMessage() can be called only once");
+
+  Message* message = mExtra.mMessage;
   MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
   const uint32_t argCount = message->mArgs.Length();
   const char16_t* args[JS::MaxNumErrorArguments + 1];
   for (uint32_t i = 0; i < argCount; ++i) {
     args[i] = message->mArgs.ElementAt(i).get();
   }
   args[argCount] = nullptr;
 
@@ -259,67 +260,68 @@ TErrorResult<CleanupPolicy>::SetPendingE
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::ClearMessage()
 {
   AssertInOwningThread();
   MOZ_ASSERT(IsErrorWithMessage());
-  delete mMessage;
-  mMessage = nullptr;
+  MOZ_ASSERT(mUnionState == HasMessage);
+  delete mExtra.mMessage;
+  mExtra.mMessage = nullptr;
 #ifdef DEBUG
   mUnionState = HasNothing;
 #endif // DEBUG
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx, JS::Handle<JS::Value> exn)
 {
   AssertInOwningThread();
   MOZ_ASSERT(mMightHaveUnreportedJSException,
              "Why didn't you tell us you planned to throw a JS exception?");
 
   ClearUnionData();
 
-  // Make sure mJSException is initialized _before_ we try to root it.  But
-  // don't set it to exn yet, because we don't want to do that until after we
-  // root.
-  mJSException.asValueRef().setUndefined();
-  if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
+  // Make sure mExtra.mJSException is initialized _before_ we try to root it.
+  // But don't set it to exn yet, because we don't want to do that until after
+  // we root.
+  JS::Value& exc = InitJSException();
+  if (!js::AddRawValueRoot(cx, &exc, "TErrorResult::mExtra::mJSException")) {
     // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that
-    // indicates we have in fact rooted mJSException.
+    // indicates we have in fact rooted mExtra.mJSException.
     mResult = NS_ERROR_OUT_OF_MEMORY;
   } else {
-    mJSException = exn;
+    exc = exn;
     mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION;
 #ifdef DEBUG
     mUnionState = HasJSException;
 #endif // DEBUG
   }
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx)
 {
   AssertInOwningThread();
   MOZ_ASSERT(!mMightHaveUnreportedJSException,
              "Why didn't you tell us you planned to handle JS exceptions?");
   MOZ_ASSERT(mUnionState == HasJSException);
 
-  JS::Rooted<JS::Value> exception(cx, mJSException);
+  JS::Rooted<JS::Value> exception(cx, mExtra.mJSException);
   if (JS_WrapValue(cx, &exception)) {
     JS_SetPendingException(cx, exception);
   }
-  mJSException = exception;
+  mExtra.mJSException = exception;
   // If JS_WrapValue failed, not much we can do about it...  No matter
-  // what, go ahead and unroot mJSException.
-  js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
+  // what, go ahead and unroot mExtra.mJSException.
+  js::RemoveRawValueRoot(cx, &mExtra.mJSException);
 
   mResult = NS_OK;
 #ifdef DEBUG
   mUnionState = HasNothing;
 #endif // DEBUG
 }
 
 template<typename CleanupPolicy>
@@ -334,20 +336,20 @@ struct TErrorResult<CleanupPolicy>::DOME
 };
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(IPC::Message* aMsg) const
 {
   using namespace IPC;
   AssertInOwningThread();
-  MOZ_ASSERT(mDOMExceptionInfo);
   MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
-  WriteParam(aMsg, mDOMExceptionInfo->mMessage);
-  WriteParam(aMsg, mDOMExceptionInfo->mRv);
+  MOZ_ASSERT(mExtra.mDOMExceptionInfo);
+  WriteParam(aMsg, mExtra.mDOMExceptionInfo->mMessage);
+  WriteParam(aMsg, mExtra.mDOMExceptionInfo->mRv);
 }
 
 template<typename CleanupPolicy>
 bool
 TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(const IPC::Message* aMsg,
                                                          PickleIterator* aIter)
 {
   using namespace IPC;
@@ -356,77 +358,78 @@ TErrorResult<CleanupPolicy>::Deserialize
   nsresult rv;
   if (!ReadParam(aMsg, aIter, &message) ||
       !ReadParam(aMsg, aIter, &rv)) {
     return false;
   }
 
   MOZ_ASSERT(mUnionState == HasNothing);
   MOZ_ASSERT(IsDOMException());
-  mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
+  InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
 #ifdef DEBUG
   mUnionState = HasDOMExceptionInfo;
 #endif // DEBUG
   return true;
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv,
                                                const nsACString& message)
 {
   AssertInOwningThread();
   ClearUnionData();
 
   mResult = NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION;
-  mDOMExceptionInfo = new DOMExceptionInfo(rv, message);
+  InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
 #ifdef DEBUG
   mUnionState = HasDOMExceptionInfo;
 #endif
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx)
 {
   AssertInOwningThread();
-  MOZ_ASSERT(mDOMExceptionInfo,
+  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
+  MOZ_ASSERT(mExtra.mDOMExceptionInfo,
              "SetPendingDOMException() can be called only once");
-  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
-
-  dom::Throw(cx, mDOMExceptionInfo->mRv, mDOMExceptionInfo->mMessage);
+
+  dom::Throw(cx, mExtra.mDOMExceptionInfo->mRv,
+             mExtra.mDOMExceptionInfo->mMessage);
 
   ClearDOMExceptionInfo();
   mResult = NS_OK;
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo()
 {
   AssertInOwningThread();
   MOZ_ASSERT(IsDOMException());
-  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo || !mDOMExceptionInfo);
-  delete mDOMExceptionInfo;
-  mDOMExceptionInfo = nullptr;
+  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
+  delete mExtra.mDOMExceptionInfo;
+  mExtra.mDOMExceptionInfo = nullptr;
 #ifdef DEBUG
   mUnionState = HasNothing;
 #endif // DEBUG
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::ClearUnionData()
 {
   AssertInOwningThread();
   if (IsJSException()) {
     JSContext* cx = dom::danger::GetJSContext();
     MOZ_ASSERT(cx);
-    mJSException.asValueRef().setUndefined();
-    js::RemoveRawValueRoot(cx, &mJSException.asValueRef());
+    mExtra.mJSException.setUndefined();
+    js::RemoveRawValueRoot(cx, &mExtra.mJSException);
 #ifdef DEBUG
     mUnionState = HasNothing;
 #endif // DEBUG
   } else if (IsErrorWithMessage()) {
     ClearMessage();
   } else if (IsDOMException()) {
     ClearDOMExceptionInfo();
   }
@@ -454,34 +457,36 @@ TErrorResult<CleanupPolicy>::operator=(T
   // start writing to it.
   ClearUnionData();
 
 #ifdef DEBUG
   mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
   aRHS.mMightHaveUnreportedJSException = false;
 #endif
   if (aRHS.IsErrorWithMessage()) {
-    mMessage = aRHS.mMessage;
-    aRHS.mMessage = nullptr;
+    InitMessage(aRHS.mExtra.mMessage);
+    aRHS.mExtra.mMessage = nullptr;
   } else if (aRHS.IsJSException()) {
     JSContext* cx = dom::danger::GetJSContext();
     MOZ_ASSERT(cx);
-    mJSException.asValueRef().setUndefined();
-    if (!js::AddRawValueRoot(cx, &mJSException.asValueRef(), "TErrorResult::mJSException")) {
-      MOZ_CRASH("Could not root mJSException, we're about to OOM");
+    JS::Value& exn = InitJSException();
+    if (!js::AddRawValueRoot(cx, &exn,
+                             "TErrorResult::mExtra::mJSException")) {
+      MOZ_CRASH("Could not root mExtra.mJSException, we're about to OOM");
     }
-    mJSException = aRHS.mJSException;
-    aRHS.mJSException.asValueRef().setUndefined();
-    js::RemoveRawValueRoot(cx, &aRHS.mJSException.asValueRef());
+    mExtra.mJSException = aRHS.mExtra.mJSException;
+    aRHS.mExtra.mJSException.setUndefined();
+    js::RemoveRawValueRoot(cx, &aRHS.mExtra.mJSException);
   } else if (aRHS.IsDOMException()) {
-    mDOMExceptionInfo = aRHS.mDOMExceptionInfo;
-    aRHS.mDOMExceptionInfo = nullptr;
+    InitDOMExceptionInfo(aRHS.mExtra.mDOMExceptionInfo);
+    aRHS.mExtra.mDOMExceptionInfo = nullptr;
   } else {
-    // Null out the union on both sides for hygiene purposes.
-    mMessage = aRHS.mMessage = nullptr;
+    // Null out the union on both sides for hygiene purposes.  This is purely
+    // precautionary, so InitMessage/placement-new is unnecessary.
+    mExtra.mMessage = aRHS.mExtra.mMessage = nullptr;
   }
 
 #ifdef DEBUG
   mUnionState = aRHS.mUnionState;
   aRHS.mUnionState = HasNothing;
 #endif // DEBUG
 
   // Note: It's important to do this last, since this affects the condition
@@ -503,31 +508,32 @@ TErrorResult<CleanupPolicy>::CloneTo(TEr
 #ifdef DEBUG
   aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException;
 #endif
 
   if (IsErrorWithMessage()) {
 #ifdef DEBUG
     aRv.mUnionState = HasMessage;
 #endif
-    aRv.mMessage = new Message();
-    aRv.mMessage->mArgs = mMessage->mArgs;
-    aRv.mMessage->mErrorNumber = mMessage->mErrorNumber;
+    Message* message = aRv.InitMessage(new Message());
+    message->mArgs = mExtra.mMessage->mArgs;
+    message->mErrorNumber = mExtra.mMessage->mErrorNumber;
   } else if (IsDOMException()) {
 #ifdef DEBUG
     aRv.mUnionState = HasDOMExceptionInfo;
 #endif
-    aRv.mDOMExceptionInfo = new DOMExceptionInfo(mDOMExceptionInfo->mRv,
-                                                 mDOMExceptionInfo->mMessage);
+    auto* exnInfo = new DOMExceptionInfo(mExtra.mDOMExceptionInfo->mRv,
+                                         mExtra.mDOMExceptionInfo->mMessage);
+    aRv.InitDOMExceptionInfo(exnInfo);
   } else if (IsJSException()) {
 #ifdef DEBUG
     aRv.mUnionState = HasJSException;
 #endif
     JSContext* cx = dom::danger::GetJSContext();
-    JS::Rooted<JS::Value> exception(cx, mJSException.asValueRef());
+    JS::Rooted<JS::Value> exception(cx, mExtra.mJSException);
     aRv.ThrowJSException(cx, exception);
   }
 }
 
 template<typename CleanupPolicy>
 void
 TErrorResult<CleanupPolicy>::SuppressException()
 {
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -603,16 +603,20 @@ DOMInterfaces = {
     'nativeType': 'mozilla::storage::StatementParams',
 },
 
 'MozStorageStatementRow': {
     'headerFile': 'mozilla/storage/mozStorageStatementRow.h',
     'nativeType': 'mozilla::storage::StatementRow',
 },
 
+'MozQueryInterface': {
+    'wrapperCache': False,
+},
+
 'MutationObserver': {
     'nativeType': 'nsDOMMutationObserver',
 },
 
 'MutationRecord': {
     'nativeType': 'nsDOMMutationRecord',
     'headerFile': 'nsDOMMutationObserver.h',
 },
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -20,16 +20,17 @@
  *    with) via dom::ToJSValue.
  *
  * An IgnoredErrorResult will automatically do the first of those four things.
  */
 
 #ifndef mozilla_ErrorResult_h
 #define mozilla_ErrorResult_h
 
+#include <new>
 #include <stdarg.h>
 
 #include "js/GCAnnotations.h"
 #include "js/Value.h"
 #include "nscore.h"
 #include "nsString.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
@@ -458,22 +459,22 @@ private:
     MOZ_ASSERT(aRv != NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT,
                "Use NoteJSContextException");
     mResult = aRv;
   }
 
   void ClearMessage();
   void ClearDOMExceptionInfo();
 
-  // ClearUnionData will try to clear the data in our
-  // mMessage/mJSException/mDOMExceptionInfo union.  After this the union may be
-  // in an uninitialized state (e.g. mMessage or mDOMExceptionInfo may be
-  // pointing to deleted memory) and the caller must either reinitialize it or
-  // change mResult to something that will not involve us touching the union
-  // anymore.
+  // ClearUnionData will try to clear the data in our mExtra union.  After this
+  // the union may be in an uninitialized state (e.g. mMessage or
+  // mDOMExceptionInfo may point to deleted memory, or mJSException may be a
+  // JS::Value containing an invalid gcthing) and the caller must either
+  // reinitialize it or change mResult to something that will not involve us
+  // touching the union anymore.
   void ClearUnionData();
 
   // Implementation of MaybeSetPendingException for the case when we're a
   // failure result.
   void SetPendingException(JSContext* cx);
 
   // Methods for setting various specific kinds of pending exceptions.
   void SetPendingExceptionWithMessage(JSContext* cx);
@@ -495,27 +496,56 @@ private:
   //                                               on us.
   // NS_ERROR_UNCATCHABLE_EXCEPTION -- ThrowUncatchableException called on us.
   // NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION -- ThrowDOMException() called
   //                                               on us.
   nsresult mResult;
 
   struct Message;
   struct DOMExceptionInfo;
-  // mMessage is set by ThrowErrorWithMessage and reported (and deallocated) by
-  // SetPendingExceptionWithMessage.
-  // mJSException is set (and rooted) by ThrowJSException and reported
-  // (and unrooted) by SetPendingJSException.
-  // mDOMExceptionInfo is set by ThrowDOMException and reported
-  // (and deallocated) by SetPendingDOMException.
-  union {
+  union Extra {
+    // mMessage is set by ThrowErrorWithMessage and reported (and deallocated)
+    // by SetPendingExceptionWithMessage.
     Message* mMessage; // valid when IsErrorWithMessage()
-    JS::UninitializedValue mJSException; // valid when IsJSException()
+
+    // mJSException is set (and rooted) by ThrowJSException and reported (and
+    // unrooted) by SetPendingJSException.
+    JS::Value mJSException; // valid when IsJSException()
+
+    // mDOMExceptionInfo is set by ThrowDOMException and reported (and
+    // deallocated) by SetPendingDOMException.
     DOMExceptionInfo* mDOMExceptionInfo; // valid when IsDOMException()
-  };
+
+    // |mJSException| has a non-trivial constructor and therefore MUST be
+    // placement-new'd into existence.
+    MOZ_PUSH_DISABLE_NONTRIVIAL_UNION_WARNINGS
+    Extra() {}
+    MOZ_POP_DISABLE_NONTRIVIAL_UNION_WARNINGS
+  } mExtra;
+
+  Message* InitMessage(Message* aMessage) {
+    // The |new| here switches the active arm of |mExtra|, from the compiler's
+    // point of view.  Mere assignment *won't* necessarily do the right thing!
+    new (&mExtra.mMessage) Message*(aMessage);
+    return mExtra.mMessage;
+  }
+
+  JS::Value& InitJSException() {
+    // The |new| here switches the active arm of |mExtra|, from the compiler's
+    // point of view.  Mere assignment *won't* necessarily do the right thing!
+    new (&mExtra.mJSException) JS::Value(); // sets to undefined
+    return mExtra.mJSException;
+  }
+
+  DOMExceptionInfo* InitDOMExceptionInfo(DOMExceptionInfo* aDOMExceptionInfo) {
+    // The |new| here switches the active arm of |mExtra|, from the compiler's
+    // point of view.  Mere assignment *won't* necessarily do the right thing!
+    new (&mExtra.mDOMExceptionInfo) DOMExceptionInfo*(aDOMExceptionInfo);
+    return mExtra.mDOMExceptionInfo;
+  }
 
 #ifdef DEBUG
   // Used to keep track of codepaths that might throw JS exceptions,
   // for assertion purposes.
   bool mMightHaveUnreportedJSException;
 
   // Used to keep track of what's stored in our union right now.  Note
   // that this may be set to HasNothing even if our mResult suggests
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -20,17 +20,16 @@
 #include "SVGObserverUtils.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
 #include "nsError.h"
 
-#include "nsCSSParser.h"
 #include "nsCSSPseudoElements.h"
 #include "nsComputedDOMStyle.h"
 
 #include "nsPrintfCString.h"
 
 #include "nsReadableUtils.h"
 
 #include "nsColor.h"
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -1,15 +1,31 @@
 /* -*- 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/.
  */
 
 /**
+ * An optimized QueryInterface method, generated by generateQI.
+ *
+ * For JS callers, this behaves like a normal QueryInterface function. When
+ * called with a supported interface, it returns its `this` object. When
+ * called with an unsupported interface, it throws NS_ERROR_NO_INTERFACE.
+ *
+ * C++ callers use a fast path, and never call the JSAPI or WebIDL methods of
+ * this object.
+ */
+[ChromeOnly, Exposed=(Window,System)]
+interface MozQueryInterface {
+  [Throws]
+  legacycaller any (IID aIID);
+};
+
+/**
  * A collection of static utility methods that are only exposed to system code.
  * This is exposed in all the system globals where we can expose stuff by
  * default, so should only include methods that are **thread-safe**.
  */
 [ChromeOnly, Exposed=(Window,System,Worker)]
 namespace ChromeUtils {
   /**
    * Serialize a snapshot of the heap graph, as seen by |JS::ubi::Node| and
@@ -190,16 +206,33 @@ partial namespace ChromeUtils {
    * which may be used to execute it repeatedly, in different globals, without
    * re-parsing.
    */
   [NewObject]
   Promise<PrecompiledScript>
   compileScript(DOMString url, optional CompileScriptOptionsDictionary options);
 
   /**
+   * Returns an optimized QueryInterface method which, when called from
+   * JavaScript, acts as an ordinary QueryInterface function call, and when
+   * called from XPConnect, circumvents JSAPI entirely.
+   *
+   * The list of interfaces may include a mix of nsIJSID objects and interface
+   * name strings. Strings for nonexistent interface names are silently
+   * ignored, as long as they don't refer to any non-IID property of the Ci
+   * global. Any non-IID value is implicitly coerced to a string, and treated
+   * as an interface name.
+   *
+   * nsISupports is implicitly supported, and must not be included in the
+   * interface list.
+   */
+  [Affects=Nothing, NewObject, Throws]
+  MozQueryInterface generateQI(sequence<(DOMString or IID)> interfaces);
+
+  /**
    * Waive Xray on a given value. Identity op for primitives.
    */
   [Throws]
   any waiveXrays(any val);
 
   /**
    * Strip off Xray waivers on a given value. Identity op for primitives.
    */
--- a/dom/html/HTMLFontElement.cpp
+++ b/dom/html/HTMLFontElement.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "HTMLFontElement.h"
 #include "mozilla/dom/HTMLFontElementBinding.h"
 #include "mozilla/GenericSpecifiedValuesInlines.h"
 #include "nsAttrValueInlines.h"
 #include "nsMappedAttributes.h"
 #include "nsContentUtils.h"
-#include "nsCSSParser.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Font)
 
 namespace mozilla {
 namespace dom {
 
 HTMLFontElement::~HTMLFontElement()
 {
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -6065,16 +6065,19 @@ class MOZ_STACK_CLASS DatabaseOperationB
 public:
   AutoSetProgressHandler();
 
   ~AutoSetProgressHandler();
 
   nsresult
   Register(mozIStorageConnection* aConnection,
            DatabaseOperationBase* aDatabaseOp);
+
+  void
+  Unregister();
 };
 
 class TransactionDatabaseOperationBase
   : public DatabaseOperationBase
 {
   enum class InternalState
   {
     Initial,
@@ -20466,20 +20469,17 @@ AutoSetProgressHandler::AutoSetProgressH
 }
 
 DatabaseOperationBase::
 AutoSetProgressHandler::~AutoSetProgressHandler()
 {
   MOZ_ASSERT(!IsOnBackgroundThread());
 
   if (mConnection) {
-    nsCOMPtr<mozIStorageProgressHandler> oldHandler;
-    MOZ_ALWAYS_SUCCEEDS(
-      mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)));
-    MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
+    Unregister();
   }
 }
 
 nsresult
 DatabaseOperationBase::
 AutoSetProgressHandler::Register(mozIStorageConnection* aConnection,
                                  DatabaseOperationBase* aDatabaseOp)
 {
@@ -20503,16 +20503,31 @@ AutoSetProgressHandler::Register(mozISto
   mConnection = aConnection;
 #ifdef DEBUG
   mDEBUGDatabaseOp = aDatabaseOp;
 #endif
 
   return NS_OK;
 }
 
+void
+DatabaseOperationBase::
+AutoSetProgressHandler::Unregister()
+{
+  MOZ_ASSERT(!IsOnBackgroundThread());
+  MOZ_ASSERT(mConnection);
+
+  nsCOMPtr<mozIStorageProgressHandler> oldHandler;
+  MOZ_ALWAYS_SUCCEEDS(
+    mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)));
+  MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
+
+  mConnection = nullptr;
+}
+
 MutableFile::MutableFile(nsIFile* aFile,
                          Database* aDatabase,
                          FileInfo* aFileInfo)
   : BackgroundMutableFileParentBase(FILE_HANDLE_STORAGE_IDB,
                                     aDatabase->Id(),
                                     IntString(aFileInfo->Id()),
                                     aFile)
   , mDatabase(aDatabase)
@@ -21649,16 +21664,22 @@ OpenDatabaseOp::DoDatabaseWork()
       return rv;
     }
 
     mgr->AddFileManager(fileManager);
   }
 
   mFileManager = fileManager.forget();
 
+  // Must close connection before dispatching otherwise we might race with the
+  // connection thread which needs to open the same database.
+  asph.Unregister();
+
+  MOZ_ALWAYS_SUCCEEDS(connection->Close());
+
   // Must set mState before dispatching otherwise we will race with the owning
   // thread.
   mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion) ?
            State::SendingResults :
            State::BeginVersionChange;
 
   rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -112,19 +112,19 @@ namespace mozilla {
 namespace dom {
 
 // Used on Android/B2G to pass the list of fonts on the device
 // to the child process
 struct FontListEntry {
     nsString  familyName;
     nsString  faceName;
     nsCString filepath;
-    float     weight;
-    int16_t   stretch;
-    uint8_t   italic;
+    uint32_t  weightRange;
+    uint32_t  stretchRange;
+    uint32_t  styleRange;
     uint8_t   index;
 };
 
 // Used on Mac OS X to pass the list of font families (not faces)
 // from chrome to content processes.
 // The entryType field distinguishes several types of font family
 // record; see gfxMacPlatformFontList.h for values and meaning.
 struct FontFamilyListEntry {
--- a/dom/locales/en-US/chrome/security/security.properties
+++ b/dom/locales/en-US/chrome/security/security.properties
@@ -1,16 +1,19 @@
 # Mixed Content Blocker
 # LOCALIZATION NOTE: "%1$S" is the URI of the blocked mixed content resource
 BlockMixedDisplayContent = Blocked loading mixed display content “%1$S”
 BlockMixedActiveContent = Blocked loading mixed active content “%1$S”
 
 # CORS
 # LOCALIZATION NOTE: Do not translate "Access-Control-Allow-Origin", Access-Control-Allow-Credentials, Access-Control-Allow-Methods, Access-Control-Allow-Headers
 CORSDisabled=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS disabled).
+CORSDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request did not succeed).
+CORSOriginHeaderNotAdded=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header ‘Origin’ cannot be added).
+CORSExternalRedirectNotAllowed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request external redirect not allowed).
 CORSRequestNotHttp=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS request not http).
 CORSMissingAllowOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing).
 CORSAllowOriginNotMatchingOrigin=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS header ‘Access-Control-Allow-Origin’ does not match ‘%2$S’).
 CORSNotSupportingCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at ‘%1$S’. (Reason: Credential is not supported if the CORS header ‘Access-Control-Allow-Origin’ is ‘*’).
 CORSMethodNotFound=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: Did not find method in CORS header ‘Access-Control-Allow-Methods’).
 CORSMissingAllowCredentials=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: expected ‘true’ in CORS header ‘Access-Control-Allow-Credentials’).
 CORSPreflightDidNotSucceed=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: CORS preflight channel did not succeed).
 CORSInvalidAllowMethod=Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at %1$S. (Reason: invalid token ‘%2$S’ in CORS header ‘Access-Control-Allow-Methods’).
--- a/dom/mathml/nsMathMLElement.cpp
+++ b/dom/mathml/nsMathMLElement.cpp
@@ -9,17 +9,16 @@
 #include "base/compiler_specific.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/TextUtils.h"
 #include "nsGkAtoms.h"
 #include "nsITableCellLayout.h" // for MAX_COLSPAN / MAX_ROWSPAN
 #include "nsLayoutStylesheetCache.h"
 #include "nsCSSValue.h"
-#include "nsCSSParser.h"
 #include "nsMappedAttributes.h"
 #include "nsStyleConsts.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "mozAutoDocUpdate.h"
 #include "nsIScriptError.h"
 #include "nsContentUtils.h"
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -30,26 +30,26 @@ support-files =
   ting-48k-1ch.wav
   ting-48k-2ch.wav
   sine-440-10s.opus
   webaudio.js
   ../../tests/mochitest/mediaStreamPlayback.js
   ../../tests/mochitest/head.js
 
 [test_analyserNode.html]
-skip-if = !asan || toolkit != android
+skip-if = !asan && toolkit != android # These are tested in web-platform-tests, except on ASan and Android which don't run WPT.
 [test_analyserScale.html]
-skip-if = !asan || toolkit != android
+skip-if = !asan && toolkit != android # These are tested in web-platform-tests, except on ASan and Android which don't run WPT.
 [test_analyserNodeOutput.html]
-skip-if = !asan || toolkit != android
+skip-if = !asan && toolkit != android # These are tested in web-platform-tests, except on ASan and Android which don't run WPT.
 [test_analyserNodePassThrough.html]
 [test_analyserNodeWithGain.html]
-skip-if = !asan || toolkit != android
+skip-if = !asan && toolkit != android # These are tested in web-platform-tests, except on ASan and Android which don't run WPT.
 [test_analyserNodeMinimum.html]
-skip-if = !asan || toolkit != android
+skip-if = !asan && toolkit != android # These are tested in web-platform-tests, except on ASan and Android which don't run WPT.
 [test_AudioBuffer.html]
 [test_audioBufferSourceNode.html]
 [test_audioBufferSourceNodeEnded.html]
 [test_audioBufferSourceNodeLazyLoopParam.html]
 [test_audioBufferSourceNodeLoop.html]
 [test_audioBufferSourceNodeLoopStartEnd.html]
 [test_audioBufferSourceNodeLoopStartEndSame.html]
 [test_audioBufferSourceNodeDetached.html]
--- a/dom/plugins/base/nsIPluginHost.idl
+++ b/dom/plugins/base/nsIPluginHost.idl
@@ -153,16 +153,24 @@ interface nsIPluginHost : nsISupports
    * FakePluginTagInit.webidl for what it should look like.  Will throw
    * NS_ERROR_UNEXPECTED if there is already a fake plugin registered with the
    * given handler URI.
    */
   [implicit_jscontext]
   nsIFakePluginTag registerFakePlugin(in jsval initDictionary);
 
   /**
+   * Create a fake plugin tag without registering it.
+   *
+   * Only for use in tests.
+   */
+  [implicit_jscontext]
+  nsIFakePluginTag createFakePlugin(in jsval initDictionary);
+
+  /**
    * Get a reference to an existing fake plugin tag for the given MIME type, if
    * any.  Can return null.
    */
   nsIFakePluginTag getFakePlugin(in AUTF8String mimeType);
 
   /**
    * Unregister a fake plugin.  The argument can be the .handlerURI.spec of an
    * existing nsIFakePluginTag, or just a known handler URI string that was
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -2,17 +2,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/. */
 
 #include "nsISupports.idl"
 
 interface nsIURI;
 
-[scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)]
+[builtinclass, scriptable, uuid(5daa99d5-265a-4397-b429-c943803e2619)]
 interface nsIPluginTag : nsISupports
 {
   // enabledState is stored as one of the following as an integer in prefs,
   // so if new states are added, they must not renumber the existing states.
   const unsigned long STATE_DISABLED = 0;
   const unsigned long STATE_CLICKTOPLAY = 1;
   const unsigned long STATE_ENABLED = 2;
 
@@ -25,31 +25,38 @@ interface nsIPluginTag : nsISupports
   // The 'nice' name of this plugin, e.g. 'flash' 'java'
   readonly attribute AUTF8String niceName;
 
   /**
    * true only if this plugin is "hardblocked" and cannot be enabled.
    */
   // FIXME-jsplugins QI to fakePluginTag possible
   // FIXME-jsplugins implement missing + tests (whatever that means)
+  [infallible]
   readonly attribute boolean blocklisted;
 
   /**
    * true if the state is non-default and locked, false otherwise.
    */
+  [infallible]
   readonly attribute boolean isEnabledStateLocked;
 
   // If this plugin is capable of being used (not disabled, blocklisted, etc)
+  [infallible]
   readonly attribute boolean active;
 
   // Get a specific nsIBlocklistService::STATE_*
+  [infallible]
   readonly attribute unsigned long blocklistState;
 
+  [infallible]
   readonly attribute boolean disabled;
+  [infallible]
   readonly attribute boolean clicktoplay;
+  [infallible]
   readonly attribute boolean loaded;
   // See the STATE_* values above.
            attribute unsigned long enabledState;
 
   readonly attribute PRTime lastModifiedTime;
 
   void getMimeTypes([optional] out unsigned long aCount,
                     [retval, array, size_is(aCount)] out wstring aResults);
@@ -61,17 +68,17 @@ interface nsIPluginTag : nsISupports
                      out wstring aResults);
 };
 
 /**
  * An interface representing a "fake" plugin: one implemented in JavaScript, not
  * as a NPAPI plug-in.  See nsIPluginHost.registerFakePlugin and the
  * documentation for the FakePluginTagInit dictionary.
  */
-[scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
+[builtinclass, scriptable, uuid(6d22c968-226d-4156-b230-da6ad6bbf6e8)]
 interface nsIFakePluginTag : nsIPluginTag
 {
   /**
    * The URI that should be loaded into the tag (as a frame) to handle the
    * plugin. Note that the original data/src value for the plugin is not loaded
    * and will need to be requested by the handler via XHR or similar if desired.
    */
   readonly attribute nsIURI handlerURI;
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -300,18 +300,17 @@ class BlocklistPromiseHandler final : pu
         MOZ_ASSERT(false, "Blocklist should always return int32");
         return;
       }
       int32_t newState = aValue.toInt32();
       MOZ_ASSERT(newState >= 0 && newState < nsIBlocklistService::STATE_MAX,
         "Shouldn't get an out of bounds blocklist state");
 
       // Check the old and new state and see if there was a change:
-      uint32_t oldState = nsIBlocklistService::STATE_NOT_BLOCKED;
-      MOZ_ALWAYS_SUCCEEDS(mTag->GetBlocklistState(&oldState));
+      uint32_t oldState = mTag->GetBlocklistState();
       bool changed = oldState != (uint32_t)newState;
       mTag->SetBlocklistState(newState);
 
       if (newState == nsIBlocklistService::STATE_SOFTBLOCKED && mShouldDisableWhenSoftblocked) {
         mTag->SetEnabledState(nsIPluginTag::STATE_DISABLED);
         changed = true;
       }
       sPluginBlocklistStatesChangedSinceLastWrite |= changed;
@@ -1609,16 +1608,34 @@ nsPluginHost::RegisterFakePlugin(JS::Han
     }
   }
 
   newTag.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsPluginHost::CreateFakePlugin(JS::Handle<JS::Value> aInitDictionary,
+                               JSContext* aCx,
+                               nsIFakePluginTag **aResult)
+{
+  FakePluginTagInit initDictionary;
+  if (!initDictionary.Init(aCx, aInitDictionary)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  RefPtr<nsFakePluginTag> newTag;
+  nsresult rv = nsFakePluginTag::Create(initDictionary, getter_AddRefs(newTag));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  newTag.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsPluginHost::UnregisterFakePlugin(const nsACString& aHandlerURI)
 {
   nsCOMPtr<nsIURI> handlerURI;
   nsresult rv = NS_NewURI(getter_AddRefs(handlerURI), aHandlerURI);
   NS_ENSURE_SUCCESS(rv, rv);
 
   for (uint32_t i = 0; i < mFakePlugins.Length(); ++i) {
     if (mFakePlugins[i]->HandlerURIMatches(handlerURI)) {
@@ -2120,19 +2137,21 @@ nsresult nsPluginHost::ScanPluginsDirect
     const bool fromExtension = GetPluginIsFromExtension(localfile, extensionDirs);
 
     // Look for it in our cache
     NS_ConvertUTF16toUTF8 filePath(utf16FilePath);
     RefPtr<nsPluginTag> pluginTag;
     RemoveCachedPluginsInfo(filePath.get(), getter_AddRefs(pluginTag));
 
     bool seenBefore = false;
+    uint32_t blocklistState = nsIBlocklistService::STATE_NOT_BLOCKED;
 
     if (pluginTag) {
       seenBefore = true;
+      blocklistState = pluginTag->GetBlocklistState();
       // If plugin changed, delete cachedPluginTag and don't use cache
       if (fileModTime != pluginTag->mLastModifiedTime) {
         // Plugins has changed. Don't use cached plugin info.
         pluginTag = nullptr;
 
         // plugin file changed, flag this fact
         *aPluginsChanged = true;
       }
@@ -2194,18 +2213,17 @@ nsresult nsPluginHost::ScanPluginsDirect
         }
         mInvalidPlugins = invalidTag;
 
         // Mark aPluginsChanged so pluginreg is rewritten
         *aPluginsChanged = true;
         continue;
       }
 
-      uint32_t state = nsIBlocklistService::STATE_NOT_BLOCKED;
-      pluginTag = new nsPluginTag(&info, fileModTime, fromExtension, state);
+      pluginTag = new nsPluginTag(&info, fileModTime, fromExtension, blocklistState);
       pluginTag->mLibrary = library;
       pluginFile.FreePluginInfo(info);
       // Pass whether we've seen this plugin before. If the plugin is
       // softblocked and new (not seen before), it will be disabled.
       UpdatePluginBlocklistState(pluginTag, !seenBefore);
 
       // Plugin unloading is tag-based. If we created a new tag and loaded
       // the library in the process then we want to attempt to unload it here.
--- a/dom/svg/SVGFragmentIdentifier.cpp
+++ b/dom/svg/SVGFragmentIdentifier.cpp
@@ -105,17 +105,16 @@ public:
       }
       nsAtom* valAtom = NS_GetStaticAtom(aParams);
       if (!valAtom ||
           NS_FAILED(mSVGView->mZoomAndPan.SetBaseValueAtom(
                       valAtom, mRoot))) {
         return false;
       }
     } else {
-      // We don't support viewTarget currently
       return false;
     }
     return true;
   }
 
   void SetValid() {
     mValid = true;
   }
--- a/dom/svg/SVGViewElement.cpp
+++ b/dom/svg/SVGViewElement.cpp
@@ -1,36 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/SVGViewElement.h"
 #include "mozilla/dom/SVGViewElementBinding.h"
-#include "DOMSVGStringList.h"
 
 NS_IMPL_NS_NEW_NAMESPACED_SVG_ELEMENT(View)
 
 namespace mozilla {
 namespace dom {
 
 using namespace SVGViewElementBinding;
 
 JSObject*
 SVGViewElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return SVGViewElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
-nsSVGElement::StringListInfo SVGViewElement::sStringListInfo[1] =
-{
-  { &nsGkAtoms::viewTarget }
-};
-
 nsSVGEnumMapping SVGViewElement::sZoomAndPanMap[] = {
   {&nsGkAtoms::disable, SVG_ZOOMANDPAN_DISABLE},
   {&nsGkAtoms::magnify, SVG_ZOOMANDPAN_MAGNIFY},
   {nullptr, 0}
 };
 
 nsSVGElement::EnumInfo SVGViewElement::sEnumInfo[1] =
 {
@@ -75,25 +69,16 @@ SVGViewElement::ViewBox()
 
 already_AddRefed<DOMSVGAnimatedPreserveAspectRatio>
 SVGViewElement::PreserveAspectRatio()
 {
   return mPreserveAspectRatio.ToDOMAnimatedPreserveAspectRatio(this);
 }
 
 //----------------------------------------------------------------------
-
-already_AddRefed<DOMSVGStringList>
-SVGViewElement::ViewTarget()
-{
-  return DOMSVGStringList::GetDOMWrapper(
-           &mStringListAttributes[VIEW_TARGET], this, false, VIEW_TARGET);
-}
-
-//----------------------------------------------------------------------
 // nsSVGElement methods
 
 nsSVGElement::EnumAttributesInfo
 SVGViewElement::GetEnumInfo()
 {
   return EnumAttributesInfo(mEnumAttributes, sEnumInfo,
                             ArrayLength(sEnumInfo));
 }
@@ -105,17 +90,10 @@ SVGViewElement::GetViewBox()
 }
 
 SVGAnimatedPreserveAspectRatio *
 SVGViewElement::GetPreserveAspectRatio()
 {
   return &mPreserveAspectRatio;
 }
 
-nsSVGElement::StringListAttributesInfo
-SVGViewElement::GetStringListInfo()
-{
-  return StringListAttributesInfo(mStringListAttributes, sStringListInfo,
-                                  ArrayLength(sStringListInfo));
-}
-
 } // namespace dom
 } // namespace mozilla
--- a/dom/svg/SVGViewElement.h
+++ b/dom/svg/SVGViewElement.h
@@ -42,17 +42,16 @@ public:
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   // WebIDL
   uint16_t ZoomAndPan() { return mEnumAttributes[ZOOMANDPAN].GetAnimValue(); }
   void SetZoomAndPan(uint16_t aZoomAndPan, ErrorResult& rv);
   already_AddRefed<SVGAnimatedRect> ViewBox();
   already_AddRefed<DOMSVGAnimatedPreserveAspectRatio> PreserveAspectRatio();
-  already_AddRefed<DOMSVGStringList> ViewTarget();
 
 private:
 
   // nsSVGElement overrides
 
   virtual EnumAttributesInfo GetEnumInfo() override;
 
   enum { ZOOMANDPAN };
@@ -60,20 +59,14 @@ private:
   static nsSVGEnumMapping sZoomAndPanMap[];
   static EnumInfo sEnumInfo[1];
 
   virtual nsSVGViewBox *GetViewBox() override;
   virtual SVGAnimatedPreserveAspectRatio *GetPreserveAspectRatio() override;
 
   nsSVGViewBox                   mViewBox;
   SVGAnimatedPreserveAspectRatio mPreserveAspectRatio;
-
-  virtual StringListAttributesInfo GetStringListInfo() override;
-
-  enum { VIEW_TARGET };
-  SVGStringList mStringListAttributes[1];
-  static StringListInfo sStringListInfo[1];
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_SVGViewElement_h
--- a/dom/svg/nsSVGElement.cpp
+++ b/dom/svg/nsSVGElement.cpp
@@ -19,17 +19,16 @@
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozAutoDocUpdate.h"
 #include "nsError.h"
 #include "nsIPresShell.h"
 #include "nsGkAtoms.h"
 #include "nsCSSProps.h"
-#include "nsCSSParser.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsLayoutUtils.h"
 #include "nsSVGAnimatedTransformList.h"
 #include "nsSVGLength2.h"
 #include "nsSVGNumber2.h"
 #include "nsSVGNumberPair.h"
 #include "nsSVGInteger.h"
 #include "nsSVGIntegerPair.h"
--- a/dom/tests/mochitest/bugs/test_bug61098.html
+++ b/dom/tests/mochitest/bugs/test_bug61098.html
@@ -32,17 +32,22 @@ function registerMockPromptService()
   var XPCOMUtils = SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
   var Ci = SpecialPowers.Ci;
 
   function MockPrompt(aDOMWindow) {
     this.domWindow = SpecialPowers.unwrap(aDOMWindow);
   }
 
   MockPrompt.prototype = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+    QueryInterface(iid) {
+      if (iid.equals(Ci.nsIPrompt)) {
+        return this;
+      }
+      throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+    },
 
     domWindow : null,
 
     _toggleModalState: function()
     {
       // The real prompt service puts the window into a modal state
       // immediately before showing a modal prompt, and leaves the modal state
       // when the prompt is dismissed by the user. This mock prompt doesn't
@@ -149,17 +154,22 @@ function registerMockPromptService()
   // Override the prompt service with our own so that we can test
   // modal dialogs
 
   function MockPromptService()
   {
   }
 
   MockPromptService.prototype = {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptFactory, Ci.nsIPromptService]),
+    QueryInterface(iid) {
+      if (iid.equals(Ci.nsIPromptFactory) || iid.equals(Ci.nsIPromptService)) {
+        return this;
+      }
+      throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+    },
 
     getPrompt: function(aDOMWindow, aIID)
     {
         return new MockPrompt(aDOMWindow);
     },
 
     alert: function(aParent, aDialogTitle, aText)
     {
--- a/dom/webidl/SVGViewElement.webidl
+++ b/dom/webidl/SVGViewElement.webidl
@@ -6,14 +6,13 @@
  * The origin of this IDL file is
  * http://www.w3.org/TR/SVG2/
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface SVGViewElement : SVGElement {
-  readonly attribute SVGStringList viewTarget;
 };
 
 SVGViewElement implements SVGFitToViewBox;
 SVGViewElement implements SVGZoomAndPanValues;
 
--- a/dom/xslt/xslt/txFormatNumberFunctionCall.cpp
+++ b/dom/xslt/xslt/txFormatNumberFunctionCall.cpp
@@ -266,16 +266,22 @@ txFormatNumberFunctionCall::evaluate(txI
     value = fabs(value) * multiplier;
 
     // Make sure the multiplier didn't push value to infinity.
     if (value == mozilla::PositiveInfinity<double>()) {
         return aContext->recycler()->getStringResult(format->mInfinity,
                                                      aResult);
     }
 
+    // Make sure the multiplier didn't push value to infinity.
+    if (value == mozilla::PositiveInfinity<double>()) {
+        return aContext->recycler()->getStringResult(format->mInfinity,
+                                                     aResult);
+    }
+
     // Prefix
     nsAutoString res(prefix);
 
     int bufsize;
     if (value > 1)
         bufsize = (int)log10(value) + 30;
     else
         bufsize = 1 + 30;
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -41,17 +41,16 @@
 #include "mozilla/dom/Event.h"
 #include "nsStyleConsts.h"
 #include "nsString.h"
 #include "nsXULControllers.h"
 #include "nsIBoxObject.h"
 #include "nsPIBoxObject.h"
 #include "XULDocument.h"
 #include "nsXULPopupListener.h"
-#include "nsCSSParser.h"
 #include "ListBoxObject.h"
 #include "nsContentUtils.h"
 #include "nsContentList.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/MouseEvents.h"
 #include "nsPIDOMWindow.h"
 #include "nsJSPrincipals.h"
 #include "nsDOMAttributeMap.h"
--- a/gfx/cairo/cairo/src/cairo-win32-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-surface.c
@@ -122,17 +122,17 @@ cairo_status_t
 			 FORMAT_MESSAGE_FROM_SYSTEM,
 			 NULL,
 			 last_error,
 			 MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
 			 (LPWSTR) &lpMsgBuf,
 			 0, NULL)) {
 	fprintf (stderr, "%s: Unknown GDI error", context);
     } else {
-	fwprintf (stderr, L"%s: %S", context, (wchar_t *)lpMsgBuf);
+	fprintf (stderr, "%s: %S", context, (wchar_t *)lpMsgBuf);
 
 	LocalFree (lpMsgBuf);
     }
     fflush(stderr);
 
     /* We should switch off of last_status, but we'd either return
      * CAIRO_STATUS_NO_MEMORY or CAIRO_STATUS_UNKNOWN_ERROR and there
      * is no CAIRO_STATUS_UNKNOWN_ERROR.
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -313,21 +313,16 @@ CompositorBridgeParent::Shutdown()
 
 void
 CompositorBridgeParent::FinishShutdown()
 {
   // TODO: this should be empty by now...
   sIndirectLayerTrees.clear();
 }
 
-static void SetThreadPriority()
-{
-  hal::SetCurrentThreadPriority(hal::THREAD_PRIORITY_COMPOSITOR);
-}
-
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
 static int32_t
 CalculateCompositionFrameRate()
 {
   // Used when layout.frame_rate is -1. Needs to be kept in sync with
   // DEFAULT_FRAME_RATE in nsRefreshDriver.cpp.
   // TODO: This should actually return the vsync rate.
   const int32_t defaultFrameRate = 60;
@@ -420,19 +415,16 @@ CompositorBridgeParent::Initialize()
   // FIXME: This holds on the the fact that right now the only thing that
   // can destroy this instance is initialized on the compositor thread after
   // this task has been processed.
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(NewRunnableFunction("AddCompositorRunnable",
                                                  &AddCompositor,
                                                  this, &mCompositorBridgeID));
 
-  CompositorLoop()->PostTask(NewRunnableFunction("SetThreadPriorityRunnable",
-                                                 SetThreadPriority));
-
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
   }
 
   LayerScope::SetPixelScale(mScale.scale);
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -491,21 +491,16 @@ ImageBridgeChild::EndTransaction()
   }
 
   if (!SendUpdate(cset, mTxn->mDestroyedActors, GetFwdTransactionId())) {
     NS_WARNING("could not send async texture transaction");
     return;
   }
 }
 
-void
-ImageBridgeChild::SendImageBridgeThreadId()
-{
-}
-
 bool
 ImageBridgeChild::InitForContent(Endpoint<PImageBridgeChild>&& aEndpoint, uint32_t aNamespace)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   gfxPlatform::GetPlatform();
 
   if (!sImageBridgeChildThread) {
@@ -552,31 +547,29 @@ ImageBridgeChild::Bind(Endpoint<PImageBr
   if (!aEndpoint.Bind(this)) {
     return;
   }
 
   // This reference is dropped in DeallocPImageBridgeChild.
   this->AddRef();
 
   mCanSend = true;
-  SendImageBridgeThreadId();
 }
 
 void
 ImageBridgeChild::BindSameProcess(RefPtr<ImageBridgeParent> aParent)
 {
   MessageLoop *parentMsgLoop = aParent->GetMessageLoop();
   ipc::MessageChannel *parentChannel = aParent->GetIPCChannel();
   Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
 
   // This reference is dropped in DeallocPImageBridgeChild.
   this->AddRef();
 
   mCanSend = true;
-  SendImageBridgeThreadId();
 }
 
 /* static */ void
 ImageBridgeChild::ShutDown()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   ShutdownSingleton();
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -59,17 +59,16 @@ ImageBridgeParent::Setup()
     sImageBridgesLock = new Monitor("ImageBridges");
     mozilla::ClearOnShutdown(&sImageBridgesLock);
   }
 }
 
 ImageBridgeParent::ImageBridgeParent(MessageLoop* aLoop,
                                      ProcessId aChildProcessId)
   : mMessageLoop(aLoop)
-  , mSetChildThreadPriority(false)
   , mClosed(false)
   , mCompositorThreadHolder(CompositorThreadHolder::GetSingleton())
 {
   MOZ_ASSERT(NS_IsMainThread());
   SetOtherProcessId(aChildProcessId);
 }
 
 ImageBridgeParent::~ImageBridgeParent()
@@ -160,27 +159,16 @@ ImageBridgeParent::ActorDestroy(ActorDes
   // It is very important that this method gets called at shutdown (be it a clean
   // or an abnormal shutdown), because DeferredDestroy is what clears mSelfRef.
   // If mSelfRef is not null and ActorDestroy is not called, the ImageBridgeParent
   // is leaked which causes the CompositorThreadHolder to be leaked and
   // CompsoitorParent's shutdown ends up spinning the event loop forever, waiting
   // for the compositor thread to terminate.
 }
 
-mozilla::ipc::IPCResult
-ImageBridgeParent::RecvImageBridgeThreadId(const PlatformThreadId& aThreadId)
-{
-  MOZ_ASSERT(!mSetChildThreadPriority);
-  if (mSetChildThreadPriority) {
-    return IPC_FAIL_NO_REASON(this);
-  }
-  mSetChildThreadPriority = true;
-  return IPC_OK();
-}
-
 class MOZ_STACK_CLASS AutoImageBridgeParentAsyncMessageSender
 {
 public:
   explicit AutoImageBridgeParentAsyncMessageSender(ImageBridgeParent* aImageBridge,
                                                    InfallibleTArray<OpDestroy>* aToDestroy = nullptr)
     : mImageBridge(aImageBridge)
     , mToDestroy(aToDestroy)
   {
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -71,17 +71,16 @@ public:
   virtual void NotifyNotUsed(PTextureParent* aTexture, uint64_t aTransactionId) override;
 
   virtual base::ProcessId GetChildProcessId() override
   {
     return OtherPid();
   }
 
   // PImageBridge
-  virtual mozilla::ipc::IPCResult RecvImageBridgeThreadId(const PlatformThreadId& aThreadId) override;
   virtual mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits, OpDestroyArray&& aToDestroy,
                                           const uint64_t& aFwdTransactionId) override;
 
   virtual PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
                                               const ReadLockDescriptor& aReadLock,
                                               const LayersBackend& aLayersBackend,
                                               const TextureFlags& aFlags,
                                               const uint64_t& aSerial,
@@ -130,17 +129,16 @@ private:
   static void ShutdownInternal();
 
   void DeferredDestroy();
   MessageLoop* mMessageLoop;
   // This keeps us alive until ActorDestroy(), at which point we do a
   // deferred destruction of ourselves.
   RefPtr<ImageBridgeParent> mSelfRef;
 
-  bool mSetChildThreadPriority;
   bool mClosed;
 
   /**
    * Map of all living ImageBridgeParent instances
    */
   typedef std::map<base::ProcessId, ImageBridgeParent*> ImageBridgeMap;
   static ImageBridgeMap sImageBridges;
 
--- a/gfx/layers/ipc/PImageBridge.ipdl
+++ b/gfx/layers/ipc/PImageBridge.ipdl
@@ -13,18 +13,16 @@ include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 include "mozilla/layers/WebRenderMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::MaybeExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 
-using PlatformThreadId from "base/platform_thread.h";
-
 namespace mozilla {
 namespace layers {
 
 /**
  * The PImageBridge protocol is used to allow isolated threads or processes to push
  * frames directly to the compositor thread/process without relying on the main thread
  * which might be too busy dealing with content script.
  */
@@ -34,18 +32,16 @@ sync protocol PImageBridge
   manages PMediaSystemResourceManager;
 
 child:
   async ParentAsyncMessages(AsyncParentMessageData[] aMessages);
 
   async DidComposite(ImageCompositeNotification[] aNotifications);
 
 parent:
-  async ImageBridgeThreadId(PlatformThreadId aTreahdId);
-
   async Update(CompositableOperation[] ops, OpDestroy[] toDestroy, uint64_t fwdTransactionId);
 
   // First step of the destruction sequence. This puts ImageBridge
   // in a state in which it can't send asynchronous messages
   // so as to not race with the channel getting closed.
   // In the child side, the Closing the channel does not happen right after WillClose,
   // it is scheduled in the ImageBridgeChild's message queue in order to ensure
   // that all of the messages from the parent side have been received and processed
--- a/gfx/src/FontPropertyTypes.h
+++ b/gfx/src/FontPropertyTypes.h
@@ -3,20 +3,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* font specific types shared by both thebes and layout */
 
 #ifndef GFX_FONT_PROPERTY_TYPES_H
 #define GFX_FONT_PROPERTY_TYPES_H
 
+#include <algorithm>
 #include <cstdint>
 #include <cmath>
+#include <utility>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "mozilla/Assertions.h"
-#include "nsStringFwd.h"
+#include "nsString.h"
 
 /*
  * This file is separate from gfxFont.h so that layout can include it
  * without bringing in gfxFont.h and everything it includes.
  */
 
 namespace mozilla {
 
@@ -30,17 +37,17 @@ namespace mozilla {
  *         ambiguity between constructors from /int/ and /T/, which mean
  *         different things.
  *   FractionBits - number of bits to use for the fractional part
  *   Min, Max - [inclusive] limits to the range of values that may be stored
  * Values are constructed from and exposed as floating-point, but stored
  * internally as fixed point, so there will be a quantization effect on
  * fractional values, depending on the number of fractional bits used.
  * Using (16-bit) fixed-point types rather than floats for these style
- * attributes reduces the memory footprint of gfxFontEntry and gfxFontSlantStyle;
+ * attributes reduces the memory footprint of gfxFontEntry and gfxFontStyle;
  * it will also tend to reduce the number of distinct font instances that
  * get created, particularly when styles are animated or set to arbitrary
  * values (e.g. by sliders in the UI), which should reduce pressure on
  * graphics resources and improve cache hit rates.
  */
 template<class InternalType, unsigned FractionBits, int Min, int Max>
 class FontPropertyValue
 {
@@ -50,17 +57,17 @@ public:
   // constructors to be "trivial" (i.e. the compiler implemented defaults that
   // do no initialization).
   // Annoyingly we can't make the default implementations constexpr (at least
   // in clang). That would be nice to do in order to allow the methods of
   // subclasses that always return the same value (e.g., FontWeight::Thin())
   // to also be constexpr. :/
   FontPropertyValue() = default;
   explicit FontPropertyValue(const FontPropertyValue& aOther) = default;
-  FontPropertyValue& operator= (const FontPropertyValue& aOther) = default;
+  FontPropertyValue& operator=(const FontPropertyValue& aOther) = default;
 
   bool operator==(const FontPropertyValue& aOther) const
   {
     return mValue == aOther.mValue;
   }
   bool operator!=(const FontPropertyValue& aOther) const
   {
     return mValue != aOther.mValue;
@@ -85,19 +92,20 @@ public:
   // The difference between two values, returned as a raw floating-point number
   // (which might not be a valid property value in its own right).
   float operator-(const FontPropertyValue& aOther) const
   {
     return (mValue - aOther.mValue) * kInverseScale;
   }
 
   /// Return the raw internal representation, for purposes of hashing.
-  InternalType ForHash() const
+  /// (Do not try to interpret the numeric value of this.)
+  uint16_t ForHash() const
   {
-    return mValue;
+    return uint16_t(mValue);
   }
 
   static constexpr const float kMin = float(Min);
   static constexpr const float kMax = float(Max);
 
 protected:
   // Construct from a floating-point or integer value, checking that it is
   // within the allowed range and converting to fixed-point representation.
@@ -181,18 +189,20 @@ public:
   }
 
   bool IsNormal() const { return mValue == kNormal; }
   bool IsBold() const { return mValue >= kBoldThreshold; }
 
   float ToFloat() const { return FontPropertyValue::ToFloat(); }
   int ToIntRounded() const { return FontPropertyValue::ToIntRounded(); }
 
+  typedef uint16_t InternalType;
+
 private:
-  typedef uint16_t InternalType;
+  friend class WeightRange;
 
   explicit FontWeight(InternalType aValue)
     : FontPropertyValue(aValue)
   {
   }
 
   static const InternalType kNormal        = 400u << kFractionBits;
   static const InternalType kBold          = 700u << kFractionBits;
@@ -262,18 +272,20 @@ public:
   static FontStretch UltraExpanded()
   {
     return FontStretch(kUltraExpanded);
   }
 
   bool IsNormal() const { return mValue == kNormal; }
   float Percentage() const { return ToFloat(); }
 
+  typedef uint16_t InternalType;
+
 private:
-  typedef uint16_t InternalType;
+  friend class StretchRange;
 
   explicit FontStretch(InternalType aValue)
     : FontPropertyValue(aValue)
   {
   }
 
   static const InternalType kUltraCondensed =  50u << kFractionBits;
   static const InternalType kExtraCondensed = (62u << kFractionBits) + kPointFive;
@@ -293,17 +305,16 @@ private:
  *   (representable range -128.0 - 127.99609375)
  * - Define min value (-128.0) as meaning 'normal'
  * - Define max value (127.99609375) as 'italic'
  * - Other values represent 'oblique <angle>'
  * - Note that 'oblique 0deg' is distinct from 'normal' (should it be?)
  */
 class FontSlantStyle final : public FontPropertyValue<int16_t,8,-90,90>
 {
-  typedef int16_t InternalType;
 public:
   const static constexpr float kDefaultAngle = 14.0;
 
   // See comment in FontPropertyValue regarding requirement for a trivial
   // default constructor.
   FontSlantStyle() = default;
 
   static FontSlantStyle Normal()
@@ -316,39 +327,270 @@ public:
     return FontSlantStyle(kItalic);
   }
 
   static FontSlantStyle Oblique(float aAngle = kDefaultAngle)
   {
     return FontSlantStyle(aAngle);
   }
 
+  // Create from a string as generated by ToString. This is for internal use
+  // when serializing/deserializing entries for the startupcache, and is not
+  // intended to parse arbitrary (untrusted) strings.
+  static FontSlantStyle FromString(const char* aString)
+  {
+    if (strcmp(aString, "normal") == 0) {
+      return Normal();
+    } else if (strcmp(aString, "italic") == 0) {
+      return Italic();
+    } else {
+      if (isdigit(aString[0]) && strstr(aString, "deg")) {
+        float angle = strtof(aString, nullptr);
+        return Oblique(angle);
+      }
+      // Not recognized as an oblique angle; maybe it's from a startup-cache
+      // created by an older version. Just treat it as 'normal'.      
+      return Normal();
+    }
+  }
+
   bool IsNormal() const { return mValue == kNormal; }
   bool IsItalic() const { return mValue == kItalic; }
   bool IsOblique() const { return mValue != kItalic && mValue != kNormal; }
 
   float ObliqueAngle() const
   {
     // It's not meaningful to get the oblique angle from a style that is
     // actually 'normal' or 'italic'.
     MOZ_ASSERT(IsOblique());
     return ToFloat();
   }
 
+  /**
+   * Write a string representation of the value to aOutString.
+   *
+   * NOTE that this APPENDS to the output string, it does not replace
+   * any existing contents.
+   */
+  void ToString(nsACString& aOutString) const
+  {
+    if (IsNormal()) {
+      aOutString.Append("normal");
+    } else if (IsItalic()) {
+      aOutString.Append("italic");
+    } else {
+      aOutString.AppendPrintf("%gdeg", ObliqueAngle());
+    }
+  }
+
+  typedef int16_t InternalType;
+
 private:
+  friend class SlantStyleRange;
+
   explicit FontSlantStyle(InternalType aConstant)
     : FontPropertyValue(aConstant)
   {
   }
 
   explicit FontSlantStyle(float aAngle)
     : FontPropertyValue(aAngle)
   {
   }
 
   static const InternalType kNormal = INT16_MIN;
   static const InternalType kItalic = INT16_MAX;
 };
 
+
+/**
+ * Convenience type to hold a <min, max> pair representing a range of values.
+ *
+ * The min and max are both inclusive, so when min == max the range represents
+ * a single value (not an empty range).
+ */
+template<class T>
+class FontPropertyRange
+{
+  // This implementation assumes the underlying property type is a 16-bit value
+  // (see FromScalar and AsScalar below).
+  static_assert(sizeof(T) == 2, "FontPropertyValue should be a 16-bit type!");
+
+public:
+  /**
+   * Construct a range from given minimum and maximum values (inclusive).
+   */
+  FontPropertyRange(T aMin, T aMax)
+    : mValues(aMin, aMax)
+  {
+    MOZ_ASSERT(aMin <= aMax);
+  }
+
+  /**
+   * Construct a range representing a single value (min==max).
+   */
+  explicit FontPropertyRange(T aValue)
+    : mValues(aValue, aValue)
+  {
+  }
+
+  explicit FontPropertyRange(const FontPropertyRange& aOther) = default;
+  FontPropertyRange& operator=(const FontPropertyRange& aOther) = default;
+
+  T Min() const { return mValues.first; }
+  T Max() const { return mValues.second; }
+
+  /**
+   * Clamp the given value to this range.
+   *
+   * (We can't use mozilla::Clamp here because it only accepts integral types.)
+   */
+  T Clamp(T aValue) const
+  {
+    return aValue <= Min() ? Min() : (aValue >= Max() ? Max() : aValue);
+  }
+
+  /**
+   * Return whether the range consists of a single unique value.
+   */
+  bool IsSingle() const
+  {
+    return Min() == Max();
+  }
+
+  bool operator==(const FontPropertyRange& aOther) const
+  {
+    return mValues == aOther.mValues;
+  }
+  bool operator!=(const FontPropertyRange& aOther) const
+  {
+    return mValues != aOther.mValues;
+  }
+
+  /**
+   * Conversion of the property range to/from a single 32-bit scalar value,
+   * suitable for IPC serialization, hashing, caching.
+   *
+   * No assumptions should be made about the numeric value of the scalar.
+   *
+   * This depends on the underlying property type being a 16-bit value!
+   */
+  typedef uint32_t ScalarType;
+
+  ScalarType AsScalar() const
+  {
+    return (mValues.first.ForHash() << 16) | mValues.second.ForHash();
+  }
+
+  /*
+   * FIXME:
+   * FromScalar is defined in each individual subclass, because I can't
+   * persuade the compiler to accept a definition here in the template. :\
+   *
+  static FontPropertyRange FromScalar(ScalarType aScalar)
+  {
+    return FontPropertyRange(T(typename T::InternalType(aScalar >> 16)),
+                             T(typename T::InternalType(aScalar & 0xffff)));
+  }
+   */
+
+protected:
+  std::pair<T,T> mValues;
+};
+
+class WeightRange : public FontPropertyRange<FontWeight>
+{
+public:
+  WeightRange(FontWeight aMin, FontWeight aMax)
+    : FontPropertyRange(aMin, aMax)
+  {
+  }
+
+  explicit WeightRange(FontWeight aWeight)
+    : FontPropertyRange(aWeight)
+  {
+  }
+
+  WeightRange(const WeightRange& aOther) = default;
+
+  void ToString(nsACString& aOutString, const char* aDelim = "..") const
+  {
+    aOutString.AppendFloat(Min().ToFloat());
+    if (!IsSingle()) {
+      aOutString.Append(aDelim);
+      aOutString.AppendFloat(Max().ToFloat());
+    }
+  }
+
+  static WeightRange FromScalar(ScalarType aScalar)
+  {
+    return WeightRange(FontWeight(FontWeight::InternalType(aScalar >> 16)),
+                       FontWeight(FontWeight::InternalType(aScalar & 0xffff)));
+  }
+};
+
+class StretchRange : public FontPropertyRange<FontStretch>
+{
+public:
+  StretchRange(FontStretch aMin, FontStretch aMax)
+    : FontPropertyRange(aMin, aMax)
+  {
+  }
+
+  explicit StretchRange(FontStretch aStretch)
+    : FontPropertyRange(aStretch)
+  {
+  }
+
+  StretchRange(const StretchRange& aOther) = default;
+
+  void ToString(nsACString& aOutString, const char* aDelim = "..") const
+  {
+    aOutString.AppendFloat(Min().Percentage());
+    if (!IsSingle()) {
+      aOutString.Append(aDelim);
+      aOutString.AppendFloat(Max().Percentage());
+    }
+  }
+
+  static StretchRange FromScalar(ScalarType aScalar)
+  {
+    return StretchRange(
+      FontStretch(FontStretch::InternalType(aScalar >> 16)),
+      FontStretch(FontStretch::InternalType(aScalar & 0xffff)));
+  }
+};
+
+class SlantStyleRange : public FontPropertyRange<FontSlantStyle>
+{
+public:
+  SlantStyleRange(FontSlantStyle aMin, FontSlantStyle aMax)
+    : FontPropertyRange(aMin, aMax)
+  {
+  }
+
+  explicit SlantStyleRange(FontSlantStyle aStyle)
+    : FontPropertyRange(aStyle)
+  {
+  }
+
+  SlantStyleRange(const SlantStyleRange& aOther) = default;
+
+  void ToString(nsACString& aOutString, const char* aDelim = "..") const
+  {
+    Min().ToString(aOutString);
+    if (!IsSingle()) {
+      aOutString.Append(aDelim);
+      Max().ToString(aOutString);
+    }
+  }
+
+  static SlantStyleRange FromScalar(ScalarType aScalar)
+  {
+    return SlantStyleRange(
+      FontSlantStyle(FontSlantStyle::InternalType(aScalar >> 16)),
+      FontSlantStyle(FontSlantStyle::InternalType(aScalar & 0xffff)));
+  }
+};
+
 } // namespace mozilla
 
 #endif // GFX_FONT_PROPERTY_TYPES_H
-
--- a/gfx/src/nsFont.cpp
+++ b/gfx/src/nsFont.cpp
@@ -287,19 +287,16 @@ void nsFont::AddFontFeaturesToStyle(gfxF
     aStyle->useGrayscaleAntialiasing = true;
   }
 
   aStyle->fontSmoothingBackgroundColor = fontSmoothingBackgroundColor;
 }
 
 void nsFont::AddFontVariationsToStyle(gfxFontStyle *aStyle) const
 {
-  // TODO: add variation settings from specific CSS properties
-  // such as weight, width, stretch
-
   // If auto optical sizing is enabled, and if there's no 'opsz' axis in
   // fontVariationSettings, then set the automatic value on the style.
   class VariationTagComparator {
   public:
     bool Equals(const gfxFontVariation& aVariation, uint32_t aTag) const {
       return aVariation.mTag == aTag;
     }
   };
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -44,85 +44,88 @@ const uint8_t kGenericFont_cursive      
 const uint8_t kGenericFont_fantasy      = 0x20;
 
 // Font structure.
 struct nsFont {
   typedef mozilla::FontStretch FontStretch;
   typedef mozilla::FontSlantStyle FontSlantStyle;
   typedef mozilla::FontWeight FontWeight;
 
-  // list of font families, either named or generic
+  // List of font families, either named or generic.
+  // This contains a RefPtr and a uint32_t field.
   mozilla::FontFamilyList fontlist;
 
-  // Force this font to not be considered a 'generic' font, even if
-  // the name is the same as a CSS generic font family.
-  bool systemFont = false;
+  // Font features from CSS font-feature-settings
+  nsTArray<gfxFontFeature> fontFeatureSettings;
+
+  // Font variations from CSS font-variation-settings
+  nsTArray<gfxFontVariation> fontVariationSettings;
+
+  // -- list of value tags for font-specific alternate features
+  nsTArray<gfxAlternateValue> alternateValues;
+
+  // -- object used to look these up once the font is matched
+  RefPtr<gfxFontFeatureValueSet> featureValueLookup;
+
+  // The logical size of the font, in nscoord units
+  nscoord size = 0;
 
-  // Variant subproperties
-  uint8_t variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
-  uint8_t variantNumeric = NS_FONT_VARIANT_NUMERIC_NORMAL;
-  uint8_t variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
-  uint8_t variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
+  // The aspect-value (ie., the ratio actualsize:actualxheight) that any
+  // actual physical font created from this font structure must have when
+  // rendering or measuring a string. A value of -1.0 means no adjustment
+  // needs to be done; otherwise the value must be nonnegative.
+  float sizeAdjust = -1.0f;
+
+  // The estimated background color behind the text. Enables a special
+  // rendering mode when NS_GET_A(.) > 0. Only used for text in the chrome.
+  nscolor fontSmoothingBackgroundColor = NS_RGBA(0,0,0,0);
 
-  uint16_t variantLigatures = NS_FONT_VARIANT_LIGATURES_NORMAL;
-  uint16_t variantEastAsian = NS_FONT_VARIANT_EAST_ASIAN_NORMAL;
+  // Language system tag, to override document language;
+  // this is an OpenType "language system" tag represented as a 32-bit integer
+  // (see http://www.microsoft.com/typography/otspec/languagetags.htm).
+  uint32_t languageOverride = 0;
+
+  // Font-selection/rendering properties corresponding to CSS font-style,
+  // font-weight, font-stretch. These are all 16-bit types.
+  FontSlantStyle style = FontSlantStyle::Normal();
+  FontWeight weight = FontWeight::Normal();
+  FontStretch stretch = FontStretch::Normal();
 
   // Some font-variant-alternates property values require
   // font-specific settings defined via @font-feature-values rules.
   // These are resolved *after* font matching occurs.
 
   // -- bitmask for both enumerated and functional propvals
   uint16_t variantAlternates = NS_FONT_VARIANT_ALTERNATES_NORMAL;
 
+  // Variant subproperties
+  uint16_t variantLigatures = NS_FONT_VARIANT_LIGATURES_NORMAL;
+  uint16_t variantEastAsian = NS_FONT_VARIANT_EAST_ASIAN_NORMAL;
+
+  uint8_t variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
+  uint8_t variantNumeric = NS_FONT_VARIANT_NUMERIC_NORMAL;
+  uint8_t variantPosition = NS_FONT_VARIANT_POSITION_NORMAL;
+  uint8_t variantWidth = NS_FONT_VARIANT_WIDTH_NORMAL;
+
   // Smoothing - controls subpixel-antialiasing (currently OSX only)
   uint8_t smoothing = NS_FONT_SMOOTHING_AUTO;
 
-  // The estimated background color behind the text. Enables a special
-  // rendering mode when NS_GET_A(.) > 0. Only used for text in the chrome.
-  nscolor fontSmoothingBackgroundColor = NS_RGBA(0,0,0,0);
-
-  FontSlantStyle style = FontSlantStyle::Normal();
-  FontWeight weight = FontWeight::Normal();
-  FontStretch stretch = FontStretch::Normal();
-
   // Kerning
   uint8_t kerning = NS_FONT_KERNING_AUTO;
 
   // Whether automatic optical sizing should be applied to variation fonts
   // that include an 'opsz' axis
   uint8_t opticalSizing = NS_FONT_OPTICAL_SIZING_AUTO;
 
   // Synthesis setting, controls use of fake bolding/italics
   uint8_t synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE;
 
-  // The logical size of the font, in nscoord units
-  nscoord size = 0;
-
-  // The aspect-value (ie., the ratio actualsize:actualxheight) that any
-  // actual physical font created from this font structure must have when
-  // rendering or measuring a string. A value of -1.0 means no adjustment
-  // needs to be done; otherwise the value must be nonnegative.
-  float sizeAdjust = -1.0f;
-
-  // -- list of value tags for font-specific alternate features
-  nsTArray<gfxAlternateValue> alternateValues;
-
-  // -- object used to look these up once the font is matched
-  RefPtr<gfxFontFeatureValueSet> featureValueLookup;
-
-  // Font features from CSS font-feature-settings
-  nsTArray<gfxFontFeature> fontFeatureSettings;
-
-  // Font variations from CSS font-variation-settings
-  nsTArray<gfxFontVariation> fontVariationSettings;
-
-  // Language system tag, to override document language;
-  // this is an OpenType "language system" tag represented as a 32-bit integer
-  // (see http://www.microsoft.com/typography/otspec/languagetags.htm).
-  uint32_t languageOverride = 0;
+  // Force this font to not be considered a 'generic' font, even if
+  // the name is the same as a CSS generic font family.
+  bool systemFont = false;
 
   // initialize the font with a fontlist
   nsFont(const mozilla::FontFamilyList& aFontlist, nscoord aSize);
 
   // initialize the font with a single generic
   nsFont(mozilla::FontFamilyType aGenericType, nscoord aSize);
 
   // Make a copy of the given font
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -185,16 +185,19 @@ gfxDWriteFontFamily::FindStyleVariations
         // <em> and <i> should be rendered as italic in the default style.
         if (fullID.EqualsLiteral("Meiryo Italic") ||
             fullID.EqualsLiteral("Meiryo Bold Italic")) {
             continue;
         }
 
         gfxDWriteFontEntry *fe = new gfxDWriteFontEntry(fullID, font, mIsSystemFontFamily);
         fe->SetForceGDIClassic(mForceGDIClassic);
+
+        fe->SetupVariationRanges();
+
         AddFontEntry(fe);
 
         // postscript/fullname if needed
         nsAutoString psname, fullname;
         if (fontInfoShouldHaveFaceNames) {
             aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
             if (!fullname.IsEmpty()) {
                 fp->AddFullname(fe, fullname);
@@ -214,23 +217,26 @@ gfxDWriteFontFamily::FindStyleVariations
             if (FAILED(hr)) {
                 skipFaceNames = true;
             } else if (fullname.Length() > 0) {
                 fp->AddFullname(fe, fullname);
             }
         }
 
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fe->Weight().ToString(weightString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %d psname: %s fullname: %s",
+                 " with style: %s weight: %s stretch: %d psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  (fe->IsItalic()) ?
                   "italic" : (fe->IsOblique() ? "oblique" : "normal"),
-                 fe->Weight().ToFloat(), fe->Stretch(),
+                 weightString.get(),
+                 fe->Stretch(),
                  NS_ConvertUTF16toUTF8(psname).get(),
                  NS_ConvertUTF16toUTF8(fullname).get()));
         }
     }
 
     // assume that if no error, all postscript/fullnames were initialized
     if (!skipFaceNames) {
         mFaceNamesInitialized = true;
@@ -365,17 +371,21 @@ gfxDWriteFontFamily::AddSizeOfIncludingT
 
 ////////////////////////////////////////////////////////////////////////////////
 // gfxDWriteFontEntry
 
 gfxFontEntry*
 gfxDWriteFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
-    return new gfxDWriteFontEntry(Name(), mFont);
+    gfxDWriteFontEntry* fe = new gfxDWriteFontEntry(Name(), mFont);
+    fe->mWeightRange = mWeightRange;
+    fe->mStretchRange = mStretchRange;
+    fe->mStyleRange = mStyleRange;
+    return fe;
 }
 
 gfxDWriteFontEntry::~gfxDWriteFontEntry()
 {
 }
 
 static bool
 UsingArabicOrHebrewScriptSystemLocale()
@@ -664,46 +674,46 @@ gfxDWriteFontEntry::CreateFontInstance(c
     DWRITE_FONT_SIMULATIONS sims =
         aNeedsBold ? DWRITE_FONT_SIMULATIONS_BOLD : DWRITE_FONT_SIMULATIONS_NONE;
     if (HasVariations() && !aFontStyle->variationSettings.IsEmpty()) {
         // If we need to apply variations, we can't use the cached mUnscaledFont
         // or mUnscaledFontBold here.
         // XXX todo: consider caching a small number of variation instances?
         RefPtr<IDWriteFontFace> fontFace;
         nsresult rv = CreateFontFace(getter_AddRefs(fontFace),
-                                     &aFontStyle->variationSettings,
+                                     aFontStyle,
                                      sims);
         if (NS_FAILED(rv)) {
             return nullptr;
         }
         RefPtr<UnscaledFontDWrite> unscaledFont =
             new UnscaledFontDWrite(fontFace, mIsSystemFont ? mFont : nullptr, sims);
         return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
     }
 
     ThreadSafeWeakPtr<UnscaledFontDWrite>& unscaledFontPtr =
         aNeedsBold ? mUnscaledFontBold : mUnscaledFont;
     RefPtr<UnscaledFontDWrite> unscaledFont(unscaledFontPtr);
     if (!unscaledFont) {
         RefPtr<IDWriteFontFace> fontFace;
-        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), nullptr, sims);
+        nsresult rv = CreateFontFace(getter_AddRefs(fontFace), aFontStyle, sims);
         if (NS_FAILED(rv)) {
             return nullptr;
         }
         unscaledFont =
             new UnscaledFontDWrite(fontFace,
                                    mIsSystemFont ? mFont : nullptr, sims);
         unscaledFontPtr = unscaledFont;
     }
     return new gfxDWriteFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 nsresult
 gfxDWriteFontEntry::CreateFontFace(IDWriteFontFace **aFontFace,
-                                   const nsTArray<gfxFontVariation>* aVariations,
+                                   const gfxFontStyle* aFontStyle,
                                    DWRITE_FONT_SIMULATIONS aSimulations)
 {
     // Convert an OpenType font tag from our uint32_t representation
     // (as constructed by TRUETYPE_TAG(...)) to the order DWrite wants.
     auto makeDWriteAxisTag = [](uint32_t aTag) {
         return DWRITE_MAKE_FONT_AXIS_TAG((aTag >> 24) & 0xff,
                                          (aTag >> 16) & 0xff,
                                          (aTag >> 8) & 0xff,
@@ -761,46 +771,38 @@ gfxDWriteFontEntry::CreateFontFace(IDWri
 
     // Do we need to modify DWrite simulations from what mFontFace has?
     bool needSimulations =
         (aSimulations & DWRITE_FONT_SIMULATIONS_BOLD) &&
         !(mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD);
 
     // If the IDWriteFontFace5 interface is available, we can go via
     // IDWriteFontResource to create a new modified face.
-    if (mFontFace5 && (aVariations && !aVariations->IsEmpty() ||
-                       needSimulations)) {
+    if (mFontFace5 && (HasVariations() || needSimulations)) {
         RefPtr<IDWriteFontResource> resource;
         HRESULT hr = mFontFace5->GetFontResource(getter_AddRefs(resource));
         MOZ_ASSERT(SUCCEEDED(hr));
         AutoTArray<DWRITE_FONT_AXIS_VALUE, 4> fontAxisValues;
-        if (aVariations) {
-            // Merge mVariationSettings and *aVariations if both present
-            const nsTArray<gfxFontVariation>* vars;
-            AutoTArray<gfxFontVariation,4> mergedSettings;
-            if (!aVariations) {
-                vars = &mVariationSettings;
-            } else  {
-                if (mVariationSettings.IsEmpty()) {
-                    vars = aVariations;
-                } else {
-                    gfxFontUtils::MergeVariations(mVariationSettings,
-                                                  *aVariations,
-                                                  &mergedSettings);
-                    vars = &mergedSettings;
-                }
-            }
-            for (const auto& v : *vars) {
+
+        // Get the variation settings needed to instantiate the fontEntry
+        // for a particular fontStyle.
+        AutoTArray<gfxFontVariation,4> vars;
+        GetVariationsForStyle(vars, *aFontStyle);
+
+        // Copy variation settings to DWrite's type.
+        if (!vars.IsEmpty()) {
+            for (const auto& v : vars) {
                 DWRITE_FONT_AXIS_VALUE axisValue = {
                     makeDWriteAxisTag(v.mTag),
                     v.mValue
                 };
                 fontAxisValues.AppendElement(axisValue);
             }
         }
+
         IDWriteFontFace5* ff5;
         resource->CreateFontFace(aSimulations,
                                  fontAxisValues.Elements(),
                                  fontAxisValues.Length(),
                                  &ff5);
         if (ff5) {
             *aFontFace = ff5;
         }
@@ -952,43 +954,43 @@ gfxDWriteFontList::GetDefaultFontForPlat
         }
     }
 
     return nullptr;
 }
 
 gfxFontEntry *
 gfxDWriteFontList::LookupLocalFont(const nsAString& aFontName,
-                                   FontWeight aWeight,
-                                   FontStretch aStretch,
-                                   FontSlantStyle aStyle)
+                                   WeightRange aWeightForEntry,
+                                   StretchRange aStretchForEntry,
+                                   SlantStyleRange aStyleForEntry)
 {
     gfxFontEntry *lookup;
 
     lookup = LookupInFaceNameLists(aFontName);
     if (!lookup) {
         return nullptr;
     }
 
     gfxDWriteFontEntry* dwriteLookup = static_cast<gfxDWriteFontEntry*>(lookup);
     gfxDWriteFontEntry *fe =
         new gfxDWriteFontEntry(lookup->Name(),
                                dwriteLookup->mFont,
-                               aWeight,
-                               aStretch,
-                               aStyle);
+                               aWeightForEntry,
+                               aStretchForEntry,
+                               aStyleForEntry);
     fe->SetForceGDIClassic(dwriteLookup->GetForceGDIClassic());
     return fe;
 }
 
 gfxFontEntry *
 gfxDWriteFontList::MakePlatformFont(const nsAString& aFontName,
-                                    FontWeight aWeight,
-                                    FontStretch aStretch,
-                                    FontSlantStyle aStyle,
+                                    WeightRange aWeightForEntry,
+                                    StretchRange aStretchForEntry,
+                                    SlantStyleRange aStyleForEntry,
                                     const uint8_t* aFontData,
                                     uint32_t aLength)
 {
     RefPtr<IDWriteFontFileStream> fontFileStream;
     RefPtr<IDWriteFontFile> fontFile;
     HRESULT hr =
       gfxDWriteFontFileLoader::CreateCustomFontFile(aFontData, aLength,
                                                     getter_AddRefs(fontFile),
@@ -1008,19 +1010,19 @@ gfxDWriteFontList::MakePlatformFont(cons
     BOOL isSupported;
     DWRITE_FONT_FILE_TYPE fileType;
     UINT32 numFaces;
 
     gfxDWriteFontEntry *entry = 
         new gfxDWriteFontEntry(uniqueName,
                                fontFile,
                                fontFileStream,
-                               aWeight,
-                               aStretch,
-                               aStyle);
+                               aWeightForEntry,
+                               aStretchForEntry,
+                               aStyleForEntry);
 
     fontFile->Analyze(&isSupported, &fileType, &entry->mFaceType, &numFaces);
     if (!isSupported || numFaces > 1) {
         // We don't know how to deal with 0 faces either.
         delete entry;
         return nullptr;
     }
 
@@ -1146,23 +1148,26 @@ gfxDWriteFontList::InitFontListForPlatfo
             // add faces to Gill Sans MT
             for (i = 0; i < faces.Length(); i++) {
                 // change the entry's family name to match its adoptive family
                 faces[i]->mFamilyName = gillSansMTFamily->Name();
                 gillSansMTFamily->AddFontEntry(faces[i]);
 
                 if (LOG_FONTLIST_ENABLED()) {
                     gfxFontEntry *fe = faces[i];
+                    nsAutoCString weightString;
+                    fe->Weight().ToString(weightString);
                     LOG_FONTLIST(("(fontlist) moved (%s) to family (%s)"
-                         " with style: %s weight: %g stretch: %d",
+                         " with style: %s weight: %s stretch: %d",
                          NS_ConvertUTF16toUTF8(fe->Name()).get(),
                          NS_ConvertUTF16toUTF8(gillSansMTFamily->Name()).get(),
                          (fe->IsItalic()) ?
                           "italic" : (fe->IsOblique() ? "oblique" : "normal"),
-                         fe->Weight().ToFloat(), fe->Stretch()));
+                         weightString.get(),
+                         fe->Stretch()));
                 }
             }
 
             // remove Gills Sans
             mFontFamilies.Remove(nameGillSans);
         }
     }
 
--- a/gfx/thebes/gfxDWriteFontList.h
+++ b/gfx/thebes/gfxDWriteFontList.h
@@ -110,25 +110,30 @@ public:
     gfxDWriteFontEntry(const nsAString& aFaceName,
                        IDWriteFont *aFont,
                        bool aIsSystemFont = false)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
         mIsSystemFont(aIsSystemFont), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
         DWRITE_FONT_STYLE dwriteStyle = aFont->GetStyle();
-        mStyle = (dwriteStyle == DWRITE_FONT_STYLE_ITALIC ?
-                  FontSlantStyle::Italic() :
-                  (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE ?
-                   FontSlantStyle::Oblique() : FontSlantStyle::Normal()));
-        mStretch = FontStretchFromDWriteStretch(aFont->GetStretch());
+        FontSlantStyle style =
+            (dwriteStyle == DWRITE_FONT_STYLE_ITALIC
+                ? FontSlantStyle::Italic()
+                : (dwriteStyle == DWRITE_FONT_STYLE_OBLIQUE
+                    ? FontSlantStyle::Oblique()
+                    : FontSlantStyle::Normal()));
+        mStyleRange = SlantStyleRange(style);
+
+        mStretchRange =
+            StretchRange(FontStretchFromDWriteStretch(aFont->GetStretch()));
+
         int weight = NS_ROUNDUP(aFont->GetWeight() - 50, 100);
-
         weight = mozilla::Clamp(weight, 100, 900);
-        mWeight = FontWeight(weight);
+        mWeightRange = WeightRange(FontWeight(weight));
 
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     /**
      * Constructs a font entry using a font. But with custom font values.
      * This is used for creating correct font entries for @font-face with local
      * font source.
@@ -136,26 +141,26 @@ public:
      * \param aFaceName The name of the corresponding font face.
      * \param aFont DirectWrite font object
      * \param aWeight Weight of the font
      * \param aStretch Stretch of the font
      * \param aStyle italic or oblique of font
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
                        IDWriteFont *aFont,
-                       FontWeight aWeight,
-                       FontStretch aStretch,
-                       FontSlantStyle aStyle)
+                       WeightRange aWeight,
+                       StretchRange aStretch,
+                       SlantStyleRange aStyle)
       : gfxFontEntry(aFaceName), mFont(aFont), mFontFile(nullptr),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
-        mWeight = aWeight;
-        mStretch = aStretch;
-        mStyle = aStyle;
+        mWeightRange = aWeight;
+        mStretchRange = aStretch;
+        mStyleRange = aStyle;
         mIsLocalUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     /**
      * Constructs a font entry using a font file.
      *
      * \param aFaceName The name of the corresponding font face.
@@ -163,27 +168,27 @@ public:
      * \param aFontFileStream DirectWrite fontfile stream object
      * \param aWeight Weight of the font
      * \param aStretch Stretch of the font
      * \param aStyle italic or oblique of font
      */
     gfxDWriteFontEntry(const nsAString& aFaceName,
                               IDWriteFontFile *aFontFile,
                               IDWriteFontFileStream *aFontFileStream,
-                              FontWeight aWeight,
-                              FontStretch aStretch,
-                              FontSlantStyle aStyle)
+                              WeightRange aWeight,
+                              StretchRange aStretch,
+                              SlantStyleRange aStyle)
       : gfxFontEntry(aFaceName), mFont(nullptr),
         mFontFile(aFontFile), mFontFileStream(aFontFileStream),
         mIsSystemFont(false), mForceGDIClassic(false),
         mHasVariations(false), mHasVariationsInitialized(false)
     {
-        mWeight = aWeight;
-        mStretch = aStretch;
-        mStyle = aStyle;
+        mWeightRange = aWeight;
+        mStretchRange = aStretch;
+        mStyleRange = aStyle;
         mIsDataUserFont = true;
         mIsCJK = UNINITIALIZED_VALUE;
     }
 
     gfxFontEntry* Clone() const override;
 
     virtual ~gfxDWriteFontEntry();
 
@@ -212,17 +217,17 @@ protected:
     virtual nsresult CopyFontTable(uint32_t aTableTag,
                                    nsTArray<uint8_t>& aBuffer) override;
 
     virtual gfxFont *CreateFontInstance(const gfxFontStyle *aFontStyle,
                                         bool aNeedsBold);
     
     nsresult CreateFontFace(
         IDWriteFontFace **aFontFace,
-        const nsTArray<gfxFontVariation>* aVariations = nullptr,
+        const gfxFontStyle* aFontStyle = nullptr,
         DWRITE_FONT_SIMULATIONS aSimulations = DWRITE_FONT_SIMULATIONS_NONE);
 
     static bool InitLogFont(IDWriteFont *aFont, LOGFONTW *aLogFont);
 
     /**
      * A fontentry only needs to have either of these. If it has both only
      * the IDWriteFont will be used.
      */
@@ -400,24 +405,24 @@ public:
     }
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          FontWeight aWeight,
-                                          FontStretch aStretch,
-                                          FontSlantStyle aStyle);
+                                          WeightRange aWeightForEntry,
+                                          StretchRange aStretchForEntry,
+                                          SlantStyleRange aStyleForEntry);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           FontWeight aWeight,
-                                           FontStretch aStretch,
-                                           FontSlantStyle aStyle,
+                                           WeightRange aWeightForEntry,
+                                           StretchRange aStretchForEntry,
+                                           SlantStyleRange aStyleForEntry,
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
     
     bool GetStandardFamilyName(const nsAString& aFontName,
                                  nsAString& aFamilyName);
 
     IDWriteGdiInterop *GetGDIInterop() { return mGDIInterop; }
     bool UseGDIFontTableAccess() { return mGDIFontTableAccess; }
--- a/gfx/thebes/gfxDWriteFonts.cpp
+++ b/gfx/thebes/gfxDWriteFonts.cpp
@@ -152,17 +152,18 @@ gfxDWriteFont::GetFakeMetricsForArialBla
 
     return true;
 }
 
 void
 gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
 {
     DWRITE_FONT_METRICS fontMetrics;
-    if (!(mFontEntry->Weight() == FontWeight(900) &&
+    if (!(mFontEntry->Weight().Min() == FontWeight(900) &&
+          mFontEntry->Weight().Max() == FontWeight(900) &&
           !mFontEntry->IsUserFont() &&
           mFontEntry->Name().EqualsLiteral("Arial Black") &&
           GetFakeMetricsForArialBlack(&fontMetrics)))
     {
         mFontFace->GetMetrics(&fontMetrics);
     }
 
     if (mStyle.sizeAdjust >= 0.0) {
--- a/gfx/thebes/gfxFT2FontBase.cpp
+++ b/gfx/thebes/gfxFT2FontBase.cpp
@@ -224,33 +224,21 @@ gfxFT2FontBase::InitMetrics()
         mMetrics.underlineOffset = -underlineSize;
         mMetrics.strikeoutOffset = 0.25 * emHeight;
         mMetrics.strikeoutSize = underlineSize;
 
         SanitizeMetrics(&mMetrics, false);
         return;
     }
 
-    if ((!mFontEntry->mVariationSettings.IsEmpty() ||
-         !mStyle.variationSettings.IsEmpty()) &&
-         (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
+    if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
         // Resolve variations from entry (descriptor) and style (property)
-        const nsTArray<gfxFontVariation>* settings;
-        AutoTArray<gfxFontVariation,8> mergedSettings;
-        if (mFontEntry->mVariationSettings.IsEmpty()) {
-            settings = &mStyle.variationSettings;
-        } else if (mStyle.variationSettings.IsEmpty()) {
-            settings = &mFontEntry->mVariationSettings;
-        } else {
-            gfxFontUtils::MergeVariations(mFontEntry->mVariationSettings,
-                                          mStyle.variationSettings,
-                                          &mergedSettings);
-            settings = &mergedSettings;
-        }
-        SetupVarCoords(face, *settings, &mCoords);
+        AutoTArray<gfxFontVariation,8> settings;
+        mFontEntry->GetVariationsForStyle(settings, mStyle);
+        SetupVarCoords(face, settings, &mCoords);
         if (!mCoords.IsEmpty()) {
 #if MOZ_TREE_FREETYPE
             FT_Set_Var_Design_Coordinates(face, mCoords.Length(), mCoords.Elements());
 #else
             typedef FT_Error (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
             static SetCoordsFunc setCoords;
             static bool firstTime = true;
             if (firstTime) {
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -219,19 +219,19 @@ FT2FontEntry::~FT2FontEntry()
 
 gfxFontEntry*
 FT2FontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
     FT2FontEntry* fe = new FT2FontEntry(Name());
     fe->mFilename = mFilename;
     fe->mFTFontIndex = mFTFontIndex;
-    fe->mWeight = mWeight;
-    fe->mStretch = mStretch;
-    fe->mStyle = mStyle;
+    fe->mWeightRange = mWeightRange;
+    fe->mStretchRange = mStretchRange;
+    fe->mStyleRange = mStyleRange;
     return fe;
 }
 
 gfxFont*
 FT2FontEntry::CreateFontInstance(const gfxFontStyle *aFontStyle, bool aNeedsBold)
 {
     cairo_scaled_font_t *scaledFont = CreateScaledFont(aFontStyle);
     if (!scaledFont) {
@@ -252,19 +252,19 @@ FT2FontEntry::CreateFontInstance(const g
                                    aFontStyle, aNeedsBold);
     cairo_scaled_font_destroy(scaledFont);
     return font;
 }
 
 /* static */
 FT2FontEntry*
 FT2FontEntry::CreateFontEntry(const nsAString& aFontName,
-                              FontWeight aWeight,
-                              FontStretch aStretch,
-                              FontSlantStyle aStyle,
+                              WeightRange aWeight,
+                              StretchRange aStretch,
+                              SlantStyleRange aStyle,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     // Ownership of aFontData is passed in here; the fontEntry must
     // retain it as long as the FT_Face needs it, and ensure it is
     // eventually deleted.
     FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
     if (!face) {
@@ -277,19 +277,19 @@ FT2FontEntry::CreateFontEntry(const nsAS
         return nullptr;
     }
     // Create our FT2FontEntry, which inherits the name of the userfont entry
     // as it's not guaranteed that the face has valid names (bug 737315)
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(face, nullptr, 0, aFontName,
                                       aFontData, aLength);
     if (fe) {
-        fe->mStyle = aStyle;
-        fe->mWeight = aWeight;
-        fe->mStretch = aStretch;
+        fe->mStyleRange = aStyle;
+        fe->mWeightRange = aWeight;
+        fe->mStretchRange = aStretch;
         fe->mIsDataUserFont = true;
     }
     return fe;
 }
 
 class FTUserFontData {
 public:
     FTUserFontData(FT_Face aFace, const uint8_t* aData, uint32_t aLength)
@@ -323,22 +323,19 @@ FTFontDestroyFunc(void *data)
 
 /* static */
 FT2FontEntry*
 FT2FontEntry::CreateFontEntry(const FontListEntry& aFLE)
 {
     FT2FontEntry *fe = new FT2FontEntry(aFLE.faceName());
     fe->mFilename = aFLE.filepath();
     fe->mFTFontIndex = aFLE.index();
-    // The weight transported across IPC is a float, so we need to explicitly
-    // convert it back to a FontWeight.
-    fe->mWeight = FontWeight(aFLE.weight());
-    fe->mStretch = FontStretch(float(aFLE.stretch()));
-    fe->mStyle = aFLE.italic()
-      ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
+    fe->mWeightRange = WeightRange::FromScalar(aFLE.weightRange());
+    fe->mStretchRange = StretchRange::FromScalar(aFLE.stretchRange());
+    fe->mStyleRange = SlantStyleRange::FromScalar(aFLE.styleRange());
     return fe;
 }
 
 // Helpers to extract font entry properties from an FT_Face
 static bool
 FTFaceIsItalic(FT_Face aFace)
 {
     return !!(aFace->style_flags & FT_STYLE_FLAG_ITALIC);
@@ -386,19 +383,20 @@ FTFaceGetWeight(FT_Face aFace)
 FT2FontEntry*
 FT2FontEntry::CreateFontEntry(FT_Face aFace,
                               const char* aFilename, uint8_t aIndex,
                               const nsAString& aName,
                               const uint8_t* aFontData,
                               uint32_t aLength)
 {
     FT2FontEntry *fe = new FT2FontEntry(aName);
-    fe->mStyle = (FTFaceIsItalic(aFace) ?
-                  FontSlantStyle::Italic() : FontSlantStyle::Normal());
-    fe->mWeight = FTFaceGetWeight(aFace);
+    fe->mStyleRange = SlantStyleRange(FTFaceIsItalic(aFace)
+                                      ? FontSlantStyle::Italic()
+                                      : FontSlantStyle::Normal());
+    fe->mWeightRange = WeightRange(FTFaceGetWeight(aFace));
     fe->mFilename = aFilename;
     fe->mFTFontIndex = aIndex;
 
     if (aFontData) {
         fe->mFTFace = aFace;
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
@@ -461,32 +459,20 @@ FT2FontEntry::CairoFontFace(const gfxFon
     // have custom variation coordinates applied.
     if ((!mVariationSettings.IsEmpty() ||
         (aStyle && !aStyle->variationSettings.IsEmpty())) &&
         (mFTFace->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
         int flags = gfxPlatform::GetPlatform()->FontHintingEnabled() ?
                     FT_LOAD_DEFAULT :
                     (FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING);
         // Resolve variations from entry (descriptor) and style (property)
-        const nsTArray<gfxFontVariation>* settings;
-        AutoTArray<gfxFontVariation,8> mergedSettings;
-        if (aStyle) {
-            if (mVariationSettings.IsEmpty()) {
-                settings = &aStyle->variationSettings;
-            } else {
-                gfxFontUtils::MergeVariations(mVariationSettings,
-                                              aStyle->variationSettings,
-                                              &mergedSettings);
-                settings = &mergedSettings;
-            }
-        } else {
-            settings = &mVariationSettings;
-        }
+        AutoTArray<gfxFontVariation,8> settings;
+        GetVariationsForStyle(settings, aStyle ? *aStyle : gfxFontStyle());
         AutoTArray<FT_Fixed,8> coords;
-        gfxFT2FontBase::SetupVarCoords(mFTFace, *settings, &coords);
+        gfxFT2FontBase::SetupVarCoords(mFTFace, settings, &coords);
         // Create a separate FT_Face because we need to apply custom
         // variation settings to it.
         FT_Face ftFace;
         if (!mFilename.IsEmpty()) {
             ftFace = Factory::NewFTFace(nullptr, mFilename.get(), mFTFontIndex);
         } else {
             auto ufd = reinterpret_cast<FTUserFontData*>(
                 cairo_font_face_get_user_data(mFontFace, &sFTUserFontDataKey));
@@ -660,26 +646,22 @@ FT2FontFamily::AddFacesToFontList(Infall
 {
     for (int i = 0, n = mAvailableFonts.Length(); i < n; ++i) {
         const FT2FontEntry *fe =
             static_cast<const FT2FontEntry*>(mAvailableFonts[i].get());
         if (!fe) {
             continue;
         }
 
-        // We convert the weight to a float purely for transport across IPC.
-        // Ideally we'd avoid doing that.
         aFontList->AppendElement(FontListEntry(Name(),
                                                fe->Name(),
                                                fe->mFilename,
-                                               fe->Weight().ToFloat(),
-                                               fe->Stretch().Percentage(),
-                                               fe->mStyle.IsItalic()
-                                                ? NS_FONT_STYLE_ITALIC
-                                                : NS_FONT_STYLE_NORMAL,
+                                               fe->Weight().AsScalar(),
+                                               fe->Stretch().AsScalar(),
+                                               fe->SlantStyle().AsScalar(),
                                                fe->mFTFontIndex));
     }
 }
 
 /*
  * Startup cache support for the font list:
  * We store the list of families and faces, with their style attributes and the
  * corresponding font files, in the startup cache.
@@ -943,44 +925,75 @@ gfxFT2FontList::AppendFacesFromCachedFac
     const nsCString& aFaceList,
     StandardFile aStdFile)
 {
     const char *beginning = aFaceList.get();
     const char *end = strchr(beginning, ',');
     while (end) {
         NS_ConvertUTF8toUTF16 familyName(beginning, end - beginning);
         ToLowerCase(familyName);
+
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
         NS_ConvertUTF8toUTF16 faceName(beginning, end - beginning);
+
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
         uint32_t index = strtoul(beginning, nullptr, 10);
+
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
-        bool italic = (*beginning != '0');
+        nsAutoCString minStyle(beginning, end - beginning);
+        nsAutoCString maxStyle(minStyle);
+        int32_t colon = minStyle.FindChar(':');
+        if (colon > 0) {
+            maxStyle.Assign(minStyle.BeginReading() + colon + 1);
+            minStyle.Truncate(colon - 1);
+        }
+
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
-        uint32_t weight = strtoul(beginning, nullptr, 10);
+        char* limit;
+        float minWeight = strtof(beginning, &limit);
+        float maxWeight;
+        if (*limit == ':' && limit + 1 < end) {
+            maxWeight = strtof(limit + 1, nullptr);
+        } else {
+            maxWeight = minWeight;
+        }
+
         beginning = end + 1;
         if (!(end = strchr(beginning, ','))) {
             break;
         }
-        uint32_t stretch = strtoul(beginning, nullptr, 10);
+        float minStretch = strtof(beginning, &limit);
+        float maxStretch;
+        if (*limit == ':' && limit + 1 < end) {
+            maxStretch = strtof(limit + 1, nullptr);
+        } else {
+            maxStretch = minStretch;
+        }
 
-        FontListEntry fle(familyName, faceName, aFileName,
-                          weight, stretch, italic, index);
+        FontListEntry fle(
+            familyName, faceName, aFileName,
+            WeightRange(FontWeight(minWeight),
+                        FontWeight(maxWeight)).AsScalar(),
+            StretchRange(FontStretch(minStretch),
+                         FontStretch(maxStretch)).AsScalar(),
+            SlantStyleRange(FontSlantStyle::FromString(minStyle.get()),
+                            FontSlantStyle::FromString(maxStyle.get())).AsScalar(),
+            index);
         AppendFaceFromFontListEntry(fle, aStdFile);
 
         beginning = end + 1;
         end = strchr(beginning, ',');
     }
 }
 
 static void
@@ -990,20 +1003,23 @@ AppendToFaceList(nsCString& aFaceList,
     aFaceList.Append(NS_ConvertUTF16toUTF8(aFamilyName));
     aFaceList.Append(',');
     aFaceList.Append(NS_ConvertUTF16toUTF8(aFontEntry->Name()));
     aFaceList.Append(',');
     aFaceList.AppendInt(aFontEntry->mFTFontIndex);
     aFaceList.Append(',');
     aFaceList.Append(aFontEntry->IsItalic() ? '1' : '0');
     aFaceList.Append(',');
-    aFaceList.AppendFloat(aFontEntry->Weight().ToFloat());
+    aFaceList.AppendFloat(aFontEntry->Weight().Min().ToFloat());
+    aFaceList.Append(':');
+    aFaceList.AppendFloat(aFontEntry->Weight().Max().ToFloat());
     aFaceList.Append(',');
-    // FIXME(emilio): Probably the stretch should be converted to float.
-    aFaceList.AppendInt(int32_t(aFontEntry->Stretch().Percentage()));
+    aFaceList.AppendFloat(aFontEntry->Stretch().Min().Percentage());
+    aFaceList.Append(':');
+    aFaceList.AppendFloat(aFontEntry->Stretch().Max().Percentage());
     aFaceList.Append(',');
 }
 
 void
 FT2FontEntry::CheckForBrokenFont(gfxFontFamily *aFamily)
 {
     // note if the family is in the "bad underline" blacklist
     if (aFamily->IsBadUnderlineFamily()) {
@@ -1149,22 +1165,27 @@ gfxFT2FontList::AddFaceToList(const nsCS
         }
         fe->mStandardFace = (aStdFile == kStandard);
         family->AddFontEntry(fe);
 
         fe->CheckForBrokenFont(family);
 
         AppendToFaceList(aFaceList, name, fe);
         if (LOG_ENABLED()) {
+            nsAutoCString weightString;
+            fe->Weight().ToString(weightString);
+            nsAutoCString stretchString;
+            fe->Stretch().ToString(stretchString);
             LOG(("(fontinit) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %g%%",
+                 " with style: %s weight: %s stretch: %s",
                  NS_ConvertUTF16toUTF8(fe->Name()).get(),
                  NS_ConvertUTF16toUTF8(family->Name()).get(),
                  fe->IsItalic() ? "italic" : "normal",
-                 fe->Weight().ToFloat(), fe->Stretch().Percentage()));
+                 weightString.get(),
+                 stretchString.get()));
         }
     }
 }
 
 void
 gfxFT2FontList::AppendFacesFromOmnijarEntry(nsZipArchive* aArchive,
                                             const nsCString& aEntryName,
                                             FontNameCache *aCache,
@@ -1464,19 +1485,19 @@ gfxFT2FontList::InitFontListForPlatform(
     return NS_OK;
 }
 
 // called for each family name, based on the assumption that the
 // first part of the full name is the family name
 
 gfxFontEntry*
 gfxFT2FontList::LookupLocalFont(const nsAString& aFontName,
-                                FontWeight aWeight,
-                                FontStretch aStretch,
-                                FontSlantStyle aStyle)
+                                WeightRange aWeightForEntry,
+                                StretchRange aStretchForEntry,
+                                SlantStyleRange aStyleForEntry)
 {
     // walk over list of names
     FT2FontEntry* fontEntry = nullptr;
     nsString fullName(aFontName);
 
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         // Check family name, based on the assumption that the
         // first part of the full name is the family name
@@ -1521,19 +1542,19 @@ searchDone:
     }
 
     FT2FontEntry* fe =
         FT2FontEntry::CreateFontEntry(fontEntry->mFTFace,
                                       fontEntry->mFilename.get(),
                                       fontEntry->mFTFontIndex,
                                       fontEntry->Name(), nullptr);
     if (fe) {
-        fe->mStyle = aStyle;
-        fe->mWeight = aWeight;
-        fe->mStretch = aStretch;
+        fe->mStyleRange = aStyleForEntry;
+        fe->mWeightRange = aWeightForEntry;
+        fe->mStretchRange = aStretchForEntry;
         fe->mIsLocalUserFont = true;
     }
 
     return fe;
 }
 
 gfxFontFamily*
 gfxFT2FontList::GetDefaultFontForPlatform(const gfxFontStyle* aStyle)
@@ -1546,27 +1567,30 @@ gfxFT2FontList::GetDefaultFontForPlatfor
     }
 #endif
     /* TODO: what about Qt or other platforms that may use this? */
     return ff;
 }
 
 gfxFontEntry*
 gfxFT2FontList::MakePlatformFont(const nsAString& aFontName,
-                                 FontWeight aWeight,
-                                 FontStretch aStretch,
-                                 FontSlantStyle aStyle,
+                                 WeightRange aWeightForEntry,
+                                 StretchRange aStretchForEntry,
+                                 SlantStyleRange aStyleForEntry,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
     // The FT2 font needs the font data to persist, so we do NOT free it here
     // but instead pass ownership to the font entry.
     // Deallocation will happen later, when the font face is destroyed.
-    return FT2FontEntry::CreateFontEntry(aFontName, aWeight, aStretch,
-                                         aStyle, aFontData, aLength);
+    return FT2FontEntry::CreateFontEntry(aFontName,
+                                         aWeightForEntry,
+                                         aStretchForEntry,
+                                         aStyleForEntry,
+                                         aFontData, aLength);
 }
 
 void
 gfxFT2FontList::GetFontFamilyList(nsTArray<RefPtr<gfxFontFamily> >& aFamilyArray)
 {
     for (auto iter = mFontFamilies.Iter(); !iter.Done(); iter.Next()) {
         RefPtr<gfxFontFamily>& family = iter.Data();
         aFamilyArray.AppendElement(family);
--- a/gfx/thebes/gfxFT2FontList.h
+++ b/gfx/thebes/gfxFT2FontList.h
@@ -38,19 +38,19 @@ public:
 
     const nsString& GetName() const {
         return Name();
     }
 
     // create a font entry for a downloaded font
     static FT2FontEntry* 
     CreateFontEntry(const nsAString& aFontName,
-                    FontWeight aWeight,
-                    FontStretch aStretch,
-                    FontSlantStyle aStyle,
+                    WeightRange aWeight,
+                    StretchRange aStretch,
+                    SlantStyleRange aStyle,
                     const uint8_t* aFontData,
                     uint32_t aLength);
 
     // create a font entry representing an installed font, identified by
     // a FontListEntry; the freetype and cairo faces will not be instantiated
     // until actually needed
     static FT2FontEntry*
     CreateFontEntry(const FontListEntry& aFLE);
@@ -117,24 +117,24 @@ public:
 
 class gfxFT2FontList : public gfxPlatformFontList
 {
 public:
     gfxFT2FontList();
     virtual ~gfxFT2FontList();
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          FontWeight aWeight,
-                                          FontStretch aStretch,
-                                          FontSlantStyle aStyle) override;
+                                          WeightRange aWeightForEntry,
+                                          StretchRange aStretchForEntry,
+                                          SlantStyleRange aStyleForEntry) override;
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           FontWeight aWeight,
-                                           FontStretch aStretch,
-                                           FontSlantStyle aStyle,
+                                           WeightRange aWeightForEntry,
+                                           StretchRange aStretchForEntry,
+                                           SlantStyleRange aStyleForEntry,
                                            const uint8_t* aFontData,
                                            uint32_t aLength) override;
 
     void GetSystemFontList(InfallibleTArray<FontListEntry>* retValue);
 
     static gfxFT2FontList* PlatformFontList() {
         return static_cast<gfxFT2FontList*>(gfxPlatformFontList::PlatformFontList());
     }
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -53,16 +53,19 @@ using namespace mozilla::gfx;
 using namespace mozilla::unicode;
 
 using mozilla::dom::SystemFontListEntry;
 using mozilla::dom::FontPatternListEntry;
 
 #ifndef FC_POSTSCRIPT_NAME
 #define FC_POSTSCRIPT_NAME  "postscriptname"      /* String */
 #endif
+#ifndef FC_VARIABLE
+#define FC_VARIABLE         "variable"            /* Bool */
+#endif
 
 #define PRINTING_FC_PROPERTY "gfx.printing"
 
 #define LOG_FONTLIST(args) MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontlist), \
                                LogLevel::Debug, args)
 #define LOG_FONTLIST_ENABLED() MOZ_LOG_TEST( \
                                    gfxPlatform::GetLog(eGfxLog_fontlist), \
                                    LogLevel::Debug)
@@ -228,42 +231,43 @@ MapFcWidth(int aFcWidth)
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
                                                FcPattern* aFontPattern,
                                                bool aIgnoreFcCharmap)
         : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
           mFTFace(nullptr), mFTFaceInitialized(false),
           mIgnoreFcCharmap(aIgnoreFcCharmap),
+          mHasVariationsInitialized(false),
           mAspect(0.0), mFontData(nullptr), mLength(0)
 {
     // italic
     int slant;
     if (FcPatternGetInteger(aFontPattern, FC_SLANT, 0, &slant) != FcResultMatch) {
         slant = FC_SLANT_ROMAN;
     }
     if (slant == FC_SLANT_OBLIQUE) {
-        mStyle = FontSlantStyle::Oblique();
+        mStyleRange = SlantStyleRange(FontSlantStyle::Oblique());
     } else if (slant > 0) {
-        mStyle = FontSlantStyle::Italic();
+        mStyleRange = SlantStyleRange(FontSlantStyle::Italic());
     }
 
     // weight
     int weight;
     if (FcPatternGetInteger(aFontPattern, FC_WEIGHT, 0, &weight) != FcResultMatch) {
         weight = FC_WEIGHT_REGULAR;
     }
-    mWeight = MapFcWeight(weight);
+    mWeightRange = WeightRange(MapFcWeight(weight));
 
     // width
     int width;
     if (FcPatternGetInteger(aFontPattern, FC_WIDTH, 0, &width) != FcResultMatch) {
         width = FC_WIDTH_NORMAL;
     }
-    mStretch = MapFcWidth(width);
+    mStretchRange = StretchRange(MapFcWidth(width));
 }
 
 gfxFontEntry*
 gfxFontconfigFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
     return new gfxFontconfigFontEntry(Name(), mFontPattern, mIgnoreFcCharmap);
 }
@@ -307,49 +311,51 @@ CreateFaceForPattern(FcPattern* aPattern
     int index;
     if (FcPatternGetInteger(aPattern, FC_INDEX, 0, &index) != FcResultMatch) {
         index = 0; // default to 0 if not found in pattern
     }
     return Factory::NewFTFace(nullptr, ToCharPtr(filename), index);
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
-                                               FontWeight aWeight,
-                                               FontStretch aStretch,
-                                               FontSlantStyle aStyle,
+                                               WeightRange aWeight,
+                                               StretchRange aStretch,
+                                               SlantStyleRange aStyle,
                                                const uint8_t *aData,
                                                uint32_t aLength,
                                                FT_Face aFace)
     : gfxFontEntry(aFaceName),
       mFTFace(aFace), mFTFaceInitialized(true),
       mIgnoreFcCharmap(true),
+      mHasVariationsInitialized(false),
       mAspect(0.0), mFontData(aData), mLength(aLength)
 {
-    mWeight = aWeight;
-    mStyle = aStyle;
-    mStretch = aStretch;
+    mWeightRange = aWeight;
+    mStyleRange = aStyle;
+    mStretchRange = aStretch;
     mIsDataUserFont = true;
 
     mFontPattern = CreatePatternForFace(mFTFace);
 
     mUserFontData = new FTUserFontData(mFTFace, mFontData);
 }
 
 gfxFontconfigFontEntry::gfxFontconfigFontEntry(const nsAString& aFaceName,
                                                FcPattern* aFontPattern,
-                                               FontWeight aWeight,
-                                               FontStretch aStretch,
-                                               FontSlantStyle aStyle)
+                                               WeightRange aWeight,
+                                               StretchRange aStretch,
+                                               SlantStyleRange aStyle)
         : gfxFontEntry(aFaceName), mFontPattern(aFontPattern),
           mFTFace(nullptr), mFTFaceInitialized(false),
+          mHasVariationsInitialized(false),
           mAspect(0.0), mFontData(nullptr), mLength(0)
 {
-    mWeight = aWeight;
-    mStyle = aStyle;
-    mStretch = aStretch;
+    mWeightRange = aWeight;
+    mStyleRange = aStyle;
+    mStretchRange = aStretch;
     mIsLocalUserFont = true;
 
     // The proper setting of mIgnoreFcCharmap is tricky for fonts loaded
     // via src:local()...
     // If the local font happens to come from the application fontset,
     // we want to set it to true so that color/svg fonts will work even
     // if the default glyphs are blank; but if the local font is a non-
     // sfnt face (e.g. legacy type 1) then we need to set it to false
@@ -760,32 +766,22 @@ gfxFontconfigFontEntry::CreateScaledFont
 
     if (needsOblique) {
         // disable embedded bitmaps (mimics behavior in 90-synthetic.conf)
         FcPatternDel(aRenderPattern, FC_EMBEDDED_BITMAP);
         FcPatternAddBool(aRenderPattern, FC_EMBEDDED_BITMAP, FcFalse);
     }
 
     AutoTArray<FT_Fixed,8> coords;
-    if (!aStyle->variationSettings.IsEmpty() || !mVariationSettings.IsEmpty()) {
+    if (HasVariations()) {
         FT_Face ftFace = GetFTFace();
         if (ftFace) {
-            const nsTArray<gfxFontVariation>* settings;
-            AutoTArray<gfxFontVariation,8> mergedSettings;
-            if (mVariationSettings.IsEmpty()) {
-                settings = &aStyle->variationSettings;
-            } else if (aStyle->variationSettings.IsEmpty()) {
-                settings = &mVariationSettings;
-            } else {
-                gfxFontUtils::MergeVariations(mVariationSettings,
-                                              aStyle->variationSettings,
-                                              &mergedSettings);
-                settings = &mergedSettings;
-            }
-            gfxFT2FontBase::SetupVarCoords(ftFace, *settings, &coords);
+            AutoTArray<gfxFontVariation,8> settings;
+            GetVariationsForStyle(settings, *aStyle);
+            gfxFT2FontBase::SetupVarCoords(ftFace, settings, &coords);
         }
     }
 
     cairo_font_face_t *face =
         cairo_ft_font_face_create_for_pattern(aRenderPattern,
                                               coords.Elements(),
                                               coords.Length());
 
@@ -1059,21 +1055,42 @@ gfxFontconfigFontEntry::GetFTFace()
         mFTFace = CreateFaceForPattern(mFontPattern);
     }
     return mFTFace;
 }
 
 bool
 gfxFontconfigFontEntry::HasVariations()
 {
-    FT_Face face = GetFTFace();
-    if (face) {
-        return face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
+    if (mHasVariationsInitialized) {
+        return mHasVariations;
+    }
+    mHasVariationsInitialized = true;
+    mHasVariations = false;
+
+    if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
+        return mHasVariations;
     }
-    return false;
+
+    // For installed fonts, query the fontconfig pattern rather than paying
+    // the cost of loading a FT_Face that we otherwise might never need.
+    if (!IsUserFont() || IsLocalUserFont()) {
+        FcBool variable;
+        if ((FcPatternGetBool(mFontPattern, FC_VARIABLE, 0,
+                              &variable) == FcResultMatch) && variable) {
+            mHasVariations = true;
+        }
+    } else {
+        FT_Face face = GetFTFace();
+        if (face) {
+            mHasVariations = face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS;
+        }
+    }
+
+    return mHasVariations;
 }
 
 FT_MM_Var*
 gfxFontconfigFontEntry::GetMMVar()
 {
     if (mMMVarInitialized) {
         return mMMVar;
     }
@@ -1190,32 +1207,42 @@ gfxFontconfigFontFamily::FindStyleVariat
 
         // figure out the psname/fullname and choose which to use as the facename
         nsAutoString psname, fullname;
         GetFaceNames(face, mName, psname, fullname);
         const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
 
         gfxFontconfigFontEntry *fontEntry =
             new gfxFontconfigFontEntry(faceName, face, mContainsAppFonts);
+
+        if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
+            fontEntry->SetupVariationRanges();
+        }
+
         AddFontEntry(fontEntry);
 
         if (fontEntry->IsNormalStyle()) {
             numRegularFaces++;
         }
 
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fontEntry->Weight().ToString(weightString);
+            nsAutoCString stretchString;
+            fontEntry->Stretch().ToString(stretchString);
+            nsAutoCString styleString;
+            fontEntry->SlantStyle().ToString(styleString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %g stretch: %g%%"
+                 " with style: %s weight: %s stretch: %s"
                  " psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
-                 (fontEntry->IsItalic()) ?
-                  "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
-                 fontEntry->Weight().ToFloat(),
-                 fontEntry->Stretch().Percentage(),
+                 styleString.get(),
+                 weightString.get(),
+                 stretchString.get(),
                  NS_ConvertUTF16toUTF8(psname).get(),
                  NS_ConvertUTF16toUTF8(fullname).get()));
         }
     }
 
     // somewhat arbitrary, but define a family with two or more regular
     // faces as a family for which intra-family fallback should be used
     if (numRegularFaces > 1) {
@@ -1311,17 +1338,17 @@ gfxFontconfigFontFamily::FindAllFontsFor
         double dist = SizeDistance(entry, aFontStyle,
                                    mForceScalable || aIgnoreSizeTolerance);
         // If the entry is scalable or has a style that does not match
         // the group of unscalable fonts, then start a new group.
         if (dist < 0.0 ||
             !bestEntry ||
             bestEntry->Stretch() != entry->Stretch() ||
             bestEntry->Weight() != entry->Weight() ||
-            bestEntry->mStyle != entry->mStyle) {
+            bestEntry->SlantStyle() != entry->SlantStyle()) {
             // If the best entry in this group is still outside the tolerance,
             // then skip the entire group.
             if (bestDist >= kRejectDistance) {
                 skipped++;
             }
             // Remove any compacted entries from the previous group.
             if (skipped) {
                 i -= skipped;
@@ -1854,54 +1881,59 @@ gfxFcPlatformFontList::GetDefaultFontFor
     if (prefFonts && !prefFonts->IsEmpty()) {
         return (*prefFonts)[0];
     }
     return nullptr;
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::LookupLocalFont(const nsAString& aFontName,
-                                       FontWeight aWeight,
-                                       FontStretch aStretch,
-                                       FontSlantStyle aStyle)
+                                       WeightRange aWeightForEntry,
+                                       StretchRange aStretchForEntry,
+                                       SlantStyleRange aStyleForEntry)
 {
     nsAutoString keyName(aFontName);
     ToLowerCase(keyName);
 
     // if name is not in the global list, done
     FcPattern* fontPattern = mLocalNames.Get(keyName);
     if (!fontPattern) {
         return nullptr;
     }
 
     return new gfxFontconfigFontEntry(aFontName, fontPattern,
-                                      aWeight, aStretch, aStyle);
+                                      aWeightForEntry,
+                                      aStretchForEntry,
+                                      aStyleForEntry);
 }
 
 gfxFontEntry*
 gfxFcPlatformFontList::MakePlatformFont(const nsAString& aFontName,
-                                        FontWeight aWeight,
-                                        FontStretch aStretch,
-                                        FontSlantStyle aStyle,
+                                        WeightRange aWeightForEntry,
+                                        StretchRange aStretchForEntry,
+                                        SlantStyleRange aStyleForEntry,
                                         const uint8_t* aFontData,
                                         uint32_t aLength)
 {
     FT_Face face = Factory::NewFTFaceFromData(nullptr, aFontData, aLength, 0);
     if (!face) {
         free((void*)aFontData);
         return nullptr;
     }
     if (FT_Err_Ok != FT_Select_Charmap(face, FT_ENCODING_UNICODE)) {
         Factory::ReleaseFTFace(face);
         free((void*)aFontData);
         return nullptr;
     }
 
-    return new gfxFontconfigFontEntry(aFontName, aWeight, aStretch,
-                                      aStyle, aFontData, aLength, face);
+    return new gfxFontconfigFontEntry(aFontName,
+                                      aWeightForEntry,
+                                      aStretchForEntry,
+                                      aStyleForEntry,
+                                      aFontData, aLength, face);
 }
 
 bool
 gfxFcPlatformFontList::FindAndAddFamilies(const nsAString& aFamily,
                                           nsTArray<gfxFontFamily*>* aOutput,
                                           FindFamiliesFlags aFlags,
                                           gfxFontStyle* aStyle,
                                           gfxFloat aDevToCssSize)
@@ -2175,37 +2207,36 @@ gfxFcPlatformFontList::GetFTLibrary()
         // has been called on each FT_Face, at least until this bug is fixed:
         // https://bugs.freedesktop.org/show_bug.cgi?id=18857
         //
         // Cairo keeps it's own FT_Library object for creating FT_Face
         // instances, so use that. There's no simple API for accessing this
         // so use the hacky method below of making a font and extracting
         // the library pointer from that.
 
-        bool needsBold;
-        gfxFontStyle style;
-        gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
-        gfxFontFamily* family = pfl->GetDefaultFont(&style);
-        NS_ASSERTION(family, "couldn't find a default font family");
-        gfxFontEntry* fe = family->FindFontForStyle(style, needsBold, true);
-        if (!fe) {
-            return nullptr;
-        }
-        RefPtr<gfxFont> font = fe->FindOrMakeFont(&style, false);
-        if (!font) {
-            return nullptr;
-        }
+        FcPattern* pat =
+            FcPatternBuild(0, FC_FAMILY, FcTypeString, "serif", (char*)0);
+        cairo_font_face_t* face =
+            cairo_ft_font_face_create_for_pattern(pat, nullptr, 0);
+        FcPatternDestroy(pat);
 
-        gfxFT2FontBase* ft2Font = reinterpret_cast<gfxFT2FontBase*>(font.get());
-        gfxFT2LockedFace face(ft2Font);
-        if (!face.get()) {
-            return nullptr;
-        }
+        cairo_matrix_t identity;
+        cairo_matrix_init_identity(&identity);
+        cairo_font_options_t* options = cairo_font_options_create();
+        cairo_scaled_font_t* sf =
+            cairo_scaled_font_create(face, &identity, &identity, options);
+        cairo_font_options_destroy(options);
+        cairo_font_face_destroy(face);
 
-        sCairoFTLibrary = face.get()->glyph->library;
+        FT_Face ft = cairo_ft_scaled_font_lock_face(sf);
+
+        sCairoFTLibrary = ft->glyph->library;
+
+        cairo_ft_scaled_font_unlock_face(sf);
+        cairo_scaled_font_destroy(sf);
     }
 
     return sCairoFTLibrary;
 }
 
 gfxPlatformFontList::PrefFontList*
 gfxFcPlatformFontList::FindGenericFamilies(const nsAString& aGeneric,
                                            nsAtom* aLanguage)
--- a/gfx/thebes/gfxFcPlatformFontList.h
+++ b/gfx/thebes/gfxFcPlatformFontList.h
@@ -89,29 +89,29 @@ public:
     // used for system fonts with explicit patterns
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
                                     FcPattern* aFontPattern,
                                     bool aIgnoreFcCharmap);
 
     // used for data fonts where the fontentry takes ownership
     // of the font data and the FT_Face
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
-                                    FontWeight aWeight,
-                                    FontStretch aStretch,
-                                    FontSlantStyle aStyle,
+                                    WeightRange aWeight,
+                                    StretchRange aStretch,
+                                    SlantStyleRange aStyle,
                                     const uint8_t *aData,
                                     uint32_t aLength,
                                     FT_Face aFace);
 
     // used for @font-face local system fonts with explicit patterns
     explicit gfxFontconfigFontEntry(const nsAString& aFaceName,
                                     FcPattern* aFontPattern,
-                                    FontWeight aWeight,
-                                    FontStretch aStretch,
-                                    FontSlantStyle aStyle);
+                                    WeightRange aWeight,
+                                    StretchRange aStretch,
+                                    SlantStyleRange aStyle);
 
     gfxFontEntry* Clone() const override;
 
     FcPattern* GetPattern() { return mFontPattern; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
     bool TestCharacterMap(uint32_t aCh) override;
 
@@ -163,16 +163,23 @@ protected:
 
     // Whether TestCharacterMap should check the actual cmap rather than asking
     // fontconfig about character coverage.
     // We do this for app-bundled (rather than system) fonts, as they may
     // include color glyphs that fontconfig would overlook, and for fonts
     // loaded via @font-face.
     bool      mIgnoreFcCharmap;
 
+    // Whether the face supports variations. For system-installed fonts, we
+    // query fontconfig for this (so they will only work if fontconfig is
+    // recent enough to include support); for downloaded user-fonts we query
+    // the FreeType face.
+    bool      mHasVariations;
+    bool      mHasVariationsInitialized;
+
     double    mAspect;
 
     // data font
     const uint8_t* mFontData;
     uint32_t       mLength;
 
     class UnscaledFontCache
     {
@@ -284,25 +291,25 @@ public:
                      const nsACString& aGenericFamily,
                      nsTArray<nsString>& aListOfFonts) override;
 
     void ReadSystemFontList(
         InfallibleTArray<mozilla::dom::SystemFontListEntry>* retValue);
 
     gfxFontEntry*
     LookupLocalFont(const nsAString& aFontName,
-                    FontWeight aWeight,
-                    FontStretch aStretch,
-                    FontSlantStyle aStyle) override;
+                    WeightRange aWeightForEntry,
+                    StretchRange aStretchForEntry,
+                    SlantStyleRange aStyleForEntry) override;
 
     gfxFontEntry*
     MakePlatformFont(const nsAString& aFontName,
-                     FontWeight aWeight,
-                     FontStretch aStretch,
-                     FontSlantStyle aStyle,
+                     WeightRange aWeightForEntry,
+                     StretchRange aStretchForEntry,
+                     SlantStyleRange aStyleForEntry,
                      const uint8_t* aFontData,
                      uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -765,24 +765,25 @@ gfxFont::RunMetrics::CombineWith(const R
     }
     mAdvanceWidth += aOther.mAdvanceWidth;
 }
 
 gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
                  gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
                  AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
     mScaledFont(aScaledFont),
-    mFontEntry(aFontEntry), mIsValid(true),
-    mApplySyntheticBold(false),
-    mMathInitialized(false),
+    mFontEntry(aFontEntry),
+    mUnscaledFont(aUnscaledFont),
     mStyle(*aFontStyle),
     mAdjustedSize(0.0),
     mFUnitsConvFactor(-1.0f), // negative to indicate "not yet initialized"
     mAntialiasOption(anAAOption),
-    mUnscaledFont(aUnscaledFont)
+    mIsValid(true),
+    mApplySyntheticBold(false),
+    mMathInitialized(false)
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     ++gFontCount;
 #endif
     mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
 }
 
 gfxFont::~gfxFont()
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -50,18 +50,16 @@ class gfxFont;
 class gfxGlyphExtents;
 class gfxShapedText;
 class gfxShapedWord;
 class gfxSkipChars;
 class gfxMathTable;
 
 #define FONT_MAX_SIZE                  2000.0
 
-#define NO_FONT_LANGUAGE_OVERRIDE      0
-
 #define SMALL_CAPS_SCALE_FACTOR        0.8
 
 // The skew factor used for synthetic-italic [oblique] fonts;
 // we use a platform-dependent value to harmonize with the platform's own APIs.
 #ifdef XP_WIN
 #define OBLIQUE_SKEW_FACTOR  0.3f
 #elif defined(MOZ_WIDGET_GTK)
 #define OBLIQUE_SKEW_FACTOR  0.2f
@@ -136,30 +134,36 @@ struct gfxFontStyle {
     // use font-language-override to request the Serbian option in the font
     // in order to get correct glyph shapes.)
     uint32_t languageOverride;
 
     // The estimated background color behind the text. Enables a special
     // rendering mode when NS_GET_A(.) > 0. Only used for text in the chrome.
     nscolor fontSmoothingBackgroundColor;
 
+    // The Font{Weight,Stretch,SlantStyle} fields are each a 16-bit type.
+
     // The weight of the font: 100, 200, ... 900.
     FontWeight weight;
 
     // The stretch of the font
     FontStretch stretch;
 
     // The style of font
     FontSlantStyle style;
 
+    // We pack these two small-integer fields into a single byte to avoid
+    // overflowing an 8-byte boundary [in a 64-bit build] and ending up with
+    // 7 bytes of padding at the end of the struct.
+
     // caps variant (small-caps, petite-caps, etc.)
-    uint8_t variantCaps;
+    uint8_t variantCaps : 4; // uses range 0..6
 
     // sub/superscript variant
-    uint8_t variantSubSuper;
+    uint8_t variantSubSuper : 4; // uses range 0..2
 
     // Say that this font is a system font and therefore does not
     // require certain fixup that we do for fonts from untrusted
     // sources.
     bool systemFont : 1;
 
     // Say that this font is used for print or print preview.
     bool printerFont : 1;
@@ -1472,17 +1476,17 @@ public:
             return 0;
         }
         return mRefCnt;
     }
 
     int32_t GetRefCount() { return mRefCnt; }
 
     // options to specify the kind of AA to be used when creating a font
-    typedef enum {
+    typedef enum : uint8_t {
         kAntialiasDefault,
         kAntialiasNone,
         kAntialiasGrayscale,
         kAntialiasSubpixel
     } AntialiasOption;
 
 protected:
     nsAutoRefCnt mRefCnt;
@@ -2236,43 +2240,20 @@ protected:
 
         mozilla::UniquePtr<gfxShapedWord> mShapedWord;
     };
 
     mozilla::UniquePtr<nsTHashtable<CacheHashEntry> > mWordCache;
 
     static const uint32_t  kShapedWordCacheMaxAge = 3;
 
-    bool                       mIsValid;
-
-    // use synthetic bolding for environments where this is not supported
-    // by the platform
-    bool                       mApplySyntheticBold;
-
-    bool                       mKerningSet;     // kerning explicitly set?
-    bool                       mKerningEnabled; // if set, on or off?
-
-    bool                       mMathInitialized; // TryGetMathTable() called?
-
-    nsExpirationState          mExpirationState;
-    gfxFontStyle               mStyle;
     nsTArray<mozilla::UniquePtr<gfxGlyphExtents>> mGlyphExtentsArray;
     mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<GlyphChangeObserver>>>
                                mGlyphChangeObservers;
 
-    gfxFloat                   mAdjustedSize;
-
-    // Conversion factor from font units to dev units; note that this may be
-    // zero (in the degenerate case where mAdjustedSize has become zero).
-    // This is OK because we only multiply by this factor, never divide.
-    float                      mFUnitsConvFactor;
-
-    // the AA setting requested for this font - may affect glyph bounds
-    AntialiasOption            mAntialiasOption;
-
     // a copy of the font without antialiasing, if needed for separate
     // measurement by mathml code
     mozilla::UniquePtr<gfxFont>         mNonAAFont;
 
     // we create either or both of these shapers when needed, depending
     // whether the font has graphite tables, and whether graphite shaping
     // is actually enabled
     mozilla::UniquePtr<gfxFontShaper>   mHarfBuzzShaper;
@@ -2286,16 +2267,40 @@ protected:
     RefPtr<mozilla::gfx::ScaledFont> mAzureScaledFont;
 
     // For vertical metrics, created on demand.
     mozilla::UniquePtr<const Metrics> mVerticalMetrics;
 
     // Table used for MathML layout.
     mozilla::UniquePtr<gfxMathTable> mMathTable;
 
+    gfxFontStyle               mStyle;
+    gfxFloat                   mAdjustedSize;
+
+    // Conversion factor from font units to dev units; note that this may be
+    // zero (in the degenerate case where mAdjustedSize has become zero).
+    // This is OK because we only multiply by this factor, never divide.
+    float                      mFUnitsConvFactor;
+
+    nsExpirationState          mExpirationState;
+
+    // the AA setting requested for this font - may affect glyph bounds
+    AntialiasOption            mAntialiasOption;
+
+    bool                       mIsValid;
+
+    // use synthetic bolding for environments where this is not supported
+    // by the platform
+    bool                       mApplySyntheticBold;
+
+    bool                       mKerningSet;     // kerning explicitly set?
+    bool                       mKerningEnabled; // if set, on or off?
+
+    bool                       mMathInitialized; // TryGetMathTable() called?
+
     // Helper for subclasses that want to initialize standard metrics from the
     // tables of sfnt (TrueType/OpenType) fonts.
     // This will use mFUnitsConvFactor if it is already set, else compute it
     // from mAdjustedSize and the unitsPerEm in the font's 'head' table.
     // Returns TRUE and sets mIsValid=TRUE if successful;
     // Returns TRUE but leaves mIsValid=FALSE if the font seems to be broken.
     // Returns FALSE if the font does not appear to be an sfnt at all,
     // and should be handled (if possible) using other APIs.
--- a/gfx/thebes/gfxFontEntry.cpp
+++ b/gfx/thebes/gfxFontEntry.cpp
@@ -74,68 +74,46 @@ gfxFontEntry::gfxFontEntry() :
     mSkipDefaultFeatureSpaceCheck(false),
     mGraphiteSpaceContextualsInitialized(false),
     mHasGraphiteSpaceContextuals(false),
     mSpaceGlyphIsInvisible(false),
     mSpaceGlyphIsInvisibleInitialized(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
-    mCheckedForColorGlyph(false),
-    mWeight(500),
-    mStretch(FontStretch::Normal()),
-    mStyle(FontSlantStyle::Normal()),
-    mUVSOffset(0), mUVSData(nullptr),
-    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
-    mCOLR(nullptr),
-    mCPAL(nullptr),
-    mUnitsPerEm(0),
-    mHBFace(nullptr),
-    mGrFace(nullptr),
-    mGrFaceRefCnt(0),
-    mComputedSizeOfUserFont(0)
+    mCheckedForColorGlyph(false)
 {
     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
 }
 
 gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
-    mName(aName), mFixedPitch(false),
+    mName(aName),
+    mFixedPitch(false),
     mIsBadUnderlineFont(false),
     mIsUserFontContainer(false),
     mIsDataUserFont(false),
-    mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
+    mIsLocalUserFont(false),
+    mStandardFace(aIsStandardFace),
     mIgnoreGDEF(false),
     mIgnoreGSUB(false),
     mSVGInitialized(false),
     mHasSpaceFeaturesInitialized(false),
     mHasSpaceFeatures(false),
     mHasSpaceFeaturesKerning(false),
     mHasSpaceFeaturesNonKerning(false),
     mSkipDefaultFeatureSpaceCheck(false),
     mGraphiteSpaceContextualsInitialized(false),
     mHasGraphiteSpaceContextuals(false),
     mSpaceGlyphIsInvisible(false),
     mSpaceGlyphIsInvisibleInitialized(false),
     mCheckedForGraphiteTables(false),
     mHasCmapTable(false),
     mGrFaceInitialized(false),
-    mCheckedForColorGlyph(false),
-    mWeight(500),
-    mStretch(FontStretch::Normal()),
-    mStyle(FontSlantStyle::Normal()),
-    mUVSOffset(0), mUVSData(nullptr),
-    mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
-    mCOLR(nullptr),
-    mCPAL(nullptr),
-    mUnitsPerEm(0),
-    mHBFace(nullptr),
-    mGrFace(nullptr),
-    mGrFaceRefCnt(0),
-    mComputedSizeOfUserFont(0)
+    mCheckedForColorGlyph(false)
 {
     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
 }
 
 gfxFontEntry::~gfxFontEntry()
 {
     // Should not be dropped by stylo
@@ -1027,16 +1005,138 @@ gfxFontEntry::GetColorLayersInfo(uint32_
     return gfxFontUtils::GetColorGlyphLayers(mCOLR,
                                              mCPAL,
                                              aGlyphId,
                                              aDefaultColor,
                                              aLayerGlyphs,
                                              aLayerColors);
 }
 
+void
+gfxFontEntry::SetupVariationRanges()
+{
+    if (!gfxPlatform::GetPlatform()->HasVariationFontSupport() ||
+        !HasVariations() || IsUserFont()) {
+        return;
+    }
+    AutoTArray<gfxFontVariationAxis,4> axes;
+    GetVariationAxes(axes);
+    for (const auto& axis : axes) {
+        switch (axis.mTag) {
+        case HB_TAG('w','g','h','t'):
+            // If the axis range looks like it doesn't fit the CSS font-weight
+            // scale, we don't hook up the high-level property. Setting 'wght'
+            // with font-variation-settings will still work.
+            // Strictly speaking, the min value should be checked against 1.0,
+            // not 0.0, but we'll allow font makers that amount of leeway, as
+            // in practice a number of fonts seem to use 0..1000.
+            if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
+                // If axis.mMaxValue is less than the default weight we already
+                // set up, assume the axis has a non-standard range (like Skia)
+                // and don't try to map it.
+                Weight().Min() <= FontWeight(axis.mMaxValue)) {
+                if (FontWeight(axis.mDefaultValue) != Weight().Min()) {
+                    mStandardFace = false;
+                }
+                mWeightRange =
+                    WeightRange(FontWeight(std::max(1.0f, axis.mMinValue)),
+                                FontWeight(axis.mMaxValue));
+            }
+            break;
+
+        case HB_TAG('w','d','t','h'):
+            if (axis.mMinValue >= 0.0f && axis.mMaxValue <= 1000.0f &&
+                Stretch().Min() <= FontStretch(axis.mMaxValue)) {
+                if (FontStretch(axis.mDefaultValue) != Stretch().Min()) {
+                    mStandardFace = false;
+                }
+                mStretchRange =
+                    StretchRange(FontStretch(axis.mMinValue),
+                                 FontStretch(axis.mMaxValue));
+            }
+            break;
+
+        case HB_TAG('s','l','n','t'):
+            if (axis.mMinValue >= -90.0f && axis.mMaxValue <= 90.0f) {
+                if (FontSlantStyle::Oblique(axis.mDefaultValue) != SlantStyle().Min()) {
+                    mStandardFace = false;
+                }
+                mStyleRange =
+                    SlantStyleRange(FontSlantStyle::Oblique(axis.mMinValue),
+                                    FontSlantStyle::Oblique(axis.mMaxValue));
+            }
+            break;
+
+        // case HB_TAG('i','t','a','l'): // XXX how to handle?
+        default:
+            continue;
+        }
+    }
+}
+
+void
+gfxFontEntry::GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
+                                    const gfxFontStyle& aStyle)
+{
+    if (!gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
+        return;
+    }
+
+    // Resolve high-level CSS properties from the requested style
+    // (font-{style,weight,stretch}) to the appropriate variations.
+    float clampedWeight = Weight().Clamp(aStyle.weight).ToFloat();
+    aResult.AppendElement(gfxFontVariation{HB_TAG('w','g','h','t'),
+                                           clampedWeight});
+
+    float clampedStretch = Stretch().Clamp(aStyle.stretch).Percentage();
+    aResult.AppendElement(gfxFontVariation{HB_TAG('w','d','t','h'),
+                                           clampedStretch});
+
+    if (SlantStyle().Min().IsOblique()) {
+        float clampedSlant =
+          aStyle.style.IsOblique()
+          ? SlantStyle().Clamp(aStyle.style).ObliqueAngle()
+          : aStyle.style.IsItalic()
+            ? SlantStyle().Clamp(FontSlantStyle::Oblique()).ObliqueAngle()
+            : SlantStyle().Clamp(FontSlantStyle::Oblique(0.0f)).ObliqueAngle();
+        aResult.AppendElement(gfxFontVariation{HB_TAG('s','l','n','t'),
+                                               clampedSlant});
+    }
+
+    // Although there is a registered tag 'ital', it is normally considered
+    // a binary toggle rather than a variable axis, 
+
+    auto replaceOrAppend = [&aResult](const gfxFontVariation& aSetting) {
+        struct TagEquals {
+            bool Equals(const gfxFontVariation& aIter, uint32_t aTag) const {
+                return aIter.mTag == aTag;
+            }
+        };
+        auto index = aResult.IndexOf(aSetting.mTag, 0, TagEquals());
+        if (index == aResult.NoIndex) {
+            aResult.AppendElement(aSetting);
+        } else {
+            aResult[index].mValue = aSetting.mValue;
+        }
+    };
+
+    // The low-level font-variation-settings descriptor from @font-face,
+    // if present, takes precedence over automatic variation settings
+    // from high-level properties.
+    for (const auto& v : mVariationSettings) {
+        replaceOrAppend(v);
+    }
+
+    // And the low-level font-variation-settings property takes precedence
+    // over the descriptor.
+    for (const auto& v : aStyle.variationSettings) {
+        replaceOrAppend(v);
+    }
+}
+
 size_t
 gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t n = 0;
     if (mBlob) {
         n += aMallocSizeOf(mBlob);
     }
     if (mSharedBlobData) {
@@ -1173,135 +1273,179 @@ gfxFontFamily::FindFontForStyle(const gf
     FindAllFontsForStyle(aFontStyle, matched, aNeedsSyntheticBold,
                          aIgnoreSizeTolerance);
     if (!matched.IsEmpty()) {
         return matched[0];
     }
     return nullptr;
 }
 
-#define STYLE_SHIFT 2 // number of bits to contain style distance
-
-// style distance ==> [0,2]
-static inline uint32_t
-StyleDistance(FontSlantStyle aFontStyle, FontSlantStyle aTargetStyle)
+// style distance ==> [0,500]
+static inline double
+StyleDistance(const gfxFontEntry* aFontEntry, FontSlantStyle aTargetStyle)
 {
-    if (aFontStyle == aTargetStyle) {
-        return 0; // styles match exactly ==> 0
+    FontSlantStyle minStyle = aFontEntry->SlantStyle().Min();
+    if (aTargetStyle == minStyle) {
+        return 0.0; // styles match exactly ==> 0
+    }
+
+    // Compare oblique angles to see how closely they match.
+    // The range of angles is [-90.0 .. 90.0], although in practice values
+    // are unlikely to get anywhere near the extremes.
+    // The style 'italic' is treated as having the same angle as the default
+    // for 'oblique', but with a constant added to the distance to reflect
+    // distinction.
+
+    double extraDistance = 0.0;
+    const double kReverseDistance = 100.0;
+
+    double target;
+    if (aTargetStyle.IsNormal()) {
+        target = 0.0;
+        extraDistance = 300.0;
+    } else if (aTargetStyle.IsOblique()) {
+        target = aTargetStyle.ObliqueAngle();
+    } else {
+        target = FontSlantStyle::Oblique().ObliqueAngle();
+        extraDistance = 200.0;
     }
-    if (aFontStyle == FontSlantStyle::Normal() ||
-        aTargetStyle == FontSlantStyle::Normal()) {
-        return 2; // one is normal (but not the other) ==> 2
+
+    FontSlantStyle maxStyle = aFontEntry->SlantStyle().Max();
+
+    double minAngle, maxAngle;
+    // There can only be a range of styles if it's oblique
+    if (minStyle.IsNormal()) {
+        minAngle = maxAngle = 0.0;
+        extraDistance = 300.0;
+    } else if (minStyle.IsOblique()) {
+        MOZ_ASSERT(maxStyle.IsOblique());
+        minAngle = minStyle.ObliqueAngle();
+        maxAngle = maxStyle.ObliqueAngle();
+    } else {
+        minAngle = maxAngle = FontSlantStyle::Oblique().ObliqueAngle();
+        extraDistance = 200.0;
     }
-    return 1; // neither is normal; must be italic vs oblique ==> 1
+
+    double distance = 0.0;
+    if (target < minAngle || target > maxAngle) {
+        if (target > 0.0) {
+            distance = minAngle - target;
+        } else {
+            distance = target - maxAngle;
+        }
+    }
+    if (distance < 0.0) {
+        distance = kReverseDistance - distance;
+    }
+
+    return distance + extraDistance;
 }
 
-#define REVERSE_STRETCH_DISTANCE 200.0f
+// stretch distance ==> [0,2000]
+static inline double
+StretchDistance(const gfxFontEntry* aFontEntry, FontStretch aTargetStretch)
+{
+    const double kReverseDistance = 1000.0;
+    double distance = 0.0;
 
-// stretch distance ==> [0,350]
-static inline uint32_t
-StretchDistance(FontStretch aFontStretch, FontStretch aTargetStretch)
-{
-    float distance = 0.0f;
-    if (aTargetStretch != aFontStretch) {
-        // stretch values are in the range 50 .. 200
+    FontStretch minStretch = aFontEntry->Stretch().Min();
+    FontStretch maxStretch = aFontEntry->Stretch().Max();
+
+    if (aTargetStretch < minStretch || aTargetStretch > maxStretch) {
+        // stretch values are in the range 0 .. 1000
         // if aTargetStretch is >100, we prefer larger values;
         // if <=100, prefer smaller
         if (aTargetStretch > FontStretch::Normal()) {
-            distance = (aFontStretch - aTargetStretch);
+            distance = (minStretch - aTargetStretch);
         } else {
-            distance = (aTargetStretch - aFontStretch);
+            distance = (aTargetStretch - maxStretch);
         }
         // if the computed "distance" here is negative, it means that
         // aFontEntry lies in the "non-preferred" direction from aTargetStretch,
         // so we treat that as larger than any preferred-direction distance
-        // (max possible is 150) by adding an extra 200 to the absolute value
+        // (max possible is 1000) by adding an extra 1000 to the absolute value
         if (distance < 0.0f) {
-            distance = -distance + REVERSE_STRETCH_DISTANCE;
+            distance = kReverseDistance - distance;
         }
     }
-    return uint32_t(distance);
+    return distance;
 }
 
-// CSS currently limits font weights to multiples of 100 but the weight
-// matching code below does not assume this.
-//
 // Calculate weight distance with values in the range (0..1000). In general,
 // heavier weights match towards even heavier weights while lighter weights
 // match towards even lighter weights. Target weight values in the range
 // [400..500] are special, since they will first match up to 500, then down
 // towards 0, then up again towards 999.
 //
 // Example: with target 600 and font weight 800, distance will be 200. With
 // target 300 and font weight 600, distance will be 900, since heavier
 // weights are farther away than lighter weights. If the target is 5 and the
 // font weight 995, the distance would be 1590 for the same reason.
 
-#define REVERSE_WEIGHT_DISTANCE 600
-#define WEIGHT_SHIFT             11 // number of bits to contain weight distance
-
-// weight distance ==> [0,1598]
-static inline uint32_t
-WeightDistance(FontWeight aFontWeight, FontWeight aTargetWeight)
+// weight distance ==> [0,1600]
+static inline double
+WeightDistance(const gfxFontEntry* aFontEntry, FontWeight aTargetWeight)
 {
-    // Compute a measure of the "distance" between the requested
-    // weight and the given fontEntry
+    const double kReverseDistance = 600.0;
 
-    float distance = 0.0f, addedDistance = 0.0f;
-    if (aTargetWeight != aFontWeight) {
+    double distance = 0.0, addedDistance = 0.0;
+    FontWeight minWeight = aFontEntry->Weight().Min();
+    FontWeight maxWeight = aFontEntry->Weight().Max();
+    if (aTargetWeight < minWeight || aTargetWeight > maxWeight) {
         if (aTargetWeight > FontWeight(500)) {
-            distance = aFontWeight - aTargetWeight;
+            distance = minWeight - aTargetWeight;
         } else if (aTargetWeight < FontWeight(400)) {
-            distance = aTargetWeight - aFontWeight;
+            distance = aTargetWeight - maxWeight;
         } else {
             // special case - target is between 400 and 500
 
             // font weights between 400 and 500 are close
-            if (aFontWeight >= FontWeight(400) &&
-                aFontWeight <= FontWeight(500)) {
-                if (aFontWeight < aTargetWeight) {
-                    distance = FontWeight(500) - aFontWeight;
+            if (maxWeight >= FontWeight(400) &&
+                minWeight <= FontWeight(500)) {
+                if (maxWeight < aTargetWeight) {
+                    distance = FontWeight(500) - maxWeight;
                 } else {
-                    distance = aFontWeight - aTargetWeight;
+                    distance = minWeight - aTargetWeight;
                 }
             } else {
                 // font weights outside use rule for target weights < 400 with
                 // added distance to separate from font weights in
                 // the [400..500] range
-                distance = aTargetWeight - aFontWeight;
-                addedDistance = 100;
+                distance = aTargetWeight - maxWeight;
+                addedDistance = 100.0;
             }
         }
-        if (distance < 0.0f) {
-            distance = -distance + REVERSE_WEIGHT_DISTANCE;
+        if (distance < 0.0) {
+            distance = kReverseDistance - distance;
         }
         distance += addedDistance;
     }
-    return uint32_t(distance);
+    return distance;
 }
 
-#define MAX_DISTANCE 0xffffffff
+#define MAX_DISTANCE 1.0e20 // >> than any WeightStyleStretchDistance result
 
-static inline uint32_t
+static inline double
 WeightStyleStretchDistance(gfxFontEntry* aFontEntry,
                            const gfxFontStyle& aTargetStyle)
 {
-    // weight/style/stretch priority: stretch >> style >> weight
-    uint32_t stretchDist =
-        StretchDistance(aFontEntry->mStretch, aTargetStyle.stretch);
-    uint32_t styleDist = StyleDistance(aFontEntry->mStyle, aTargetStyle.style);
-    uint32_t weightDist =
-        WeightDistance(aFontEntry->Weight(), aTargetStyle.weight);
+    double stretchDist = StretchDistance(aFontEntry, aTargetStyle.stretch);
+    double styleDist = StyleDistance(aFontEntry, aTargetStyle.style);
+    double weightDist = WeightDistance(aFontEntry, aTargetStyle.weight);
 
-    NS_ASSERTION(weightDist < (1 << WEIGHT_SHIFT), "weight value out of bounds");
-    NS_ASSERTION(styleDist < (1 << STYLE_SHIFT), "slope value out of bounds");
+    // Sanity-check that the distances are within the expected range
+    // (update if implementation of the distance functions is changed).
+    MOZ_ASSERT(stretchDist >= 0.0 && stretchDist <= 2000.0);
+    MOZ_ASSERT(styleDist >= 0.0 && styleDist <= 500.0);
+    MOZ_ASSERT(weightDist >= 0.0 && weightDist <= 1600.0);
 
-    return (stretchDist << (STYLE_SHIFT + WEIGHT_SHIFT)) |
-           (styleDist << WEIGHT_SHIFT) |
-           weightDist;
+    // weight/style/stretch priority: stretch >> style >> weight
+    // so we multiply the stretch and style values to make them dominate
+    // the result
+    return stretchDist * 1.0e8 + styleDist * 1.0e4 + weightDist;
 }
 
 void
 gfxFontFamily::FindAllFontsForStyle(const gfxFontStyle& aFontStyle,
                                     nsTArray<gfxFontEntry*>& aFontEntryList,
                                     bool& aNeedsSyntheticBold,
                                     bool aIgnoreSizeTolerance)
 {
@@ -1382,24 +1526,24 @@ gfxFontFamily::FindAllFontsForStyle(cons
     // given but the 99% use case is only a single font entry per
     // weight/style/stretch distance value. To optimize this, only add entries
     // to the matched font array when another entry already has the same
     // weight/style/stretch distance and add the last matched font entry. For
     // normal platform fonts with a single font entry for each
     // weight/style/stretch combination, only the last matched font entry will
     // be added.
 
-    uint32_t minDistance = MAX_DISTANCE;
+    double minDistance = MAX_DISTANCE;
     gfxFontEntry* matched = nullptr;
     // iterate in forward order so that faces like 'Bold' are matched before
     // matching style distance faces such as 'Bold Outline' (see bug 1185812)
     for (uint32_t i = 0; i < count; i++) {
         fe = mAvailableFonts[i];
         // weight/style/stretch priority: stretch >> style >> weight
-        uint32_t distance = WeightStyleStretchDistance(fe, aFontStyle);
+        double distance = WeightStyleStretchDistance(fe, aFontStyle);
         if (distance < minDistance) {
             matched = fe;
             if (!aFontEntryList.IsEmpty()) {
                 aFontEntryList.Clear();
             }
             minDistance = distance;
         } else if (distance == minDistance) {
             if (matched) {
@@ -1434,27 +1578,33 @@ gfxFontFamily::CheckForSimpleFamily()
                 // if none then the family is unusable anyway
     }
 
     if (count == 1) {
         mIsSimpleFamily = true;
         return;
     }
 
-    FontStretch firstStretch = mAvailableFonts[0]->Stretch();
+    StretchRange firstStretch = mAvailableFonts[0]->Stretch();
+    if (!firstStretch.IsSingle()) {
+        return; // family with variation fonts is not considered "simple"
+    }
 
     gfxFontEntry *faces[4] = { 0 };
     for (uint8_t i = 0; i < count; ++i) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (fe->Stretch() != firstStretch || fe->IsOblique()) {
             // simple families don't have varying font-stretch or oblique
             return;
         }
+        if (!fe->Weight().IsSingle() || !fe->SlantStyle().IsSingle()) {
+            return; // family with variation fonts is not considered "simple"
+        }
         uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
-                            (fe->Weight() >= FontWeight(600) ? kBoldMask : 0);
+                            (fe->IsBold() ? kBoldMask : 0);
         if (faces[faceIndex]) {
             return; // two faces resolve to the same slot; family isn't "simple"
         }
         faces[faceIndex] = fe;
     }
 
     // we have successfully slotted the available faces into the standard
     // 4-face framework
@@ -1491,43 +1641,51 @@ gfxFontFamily::ContainsFace(gfxFontEntry
 
 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
 {
     // just return the primary name; subclasses should override
     aLocalizedName = mName;
 }
 
 // metric for how close a given font matches a style
-static int32_t
+static float
 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
 {
-    int32_t rank = 0;
+    float rank = 0;
     if (aStyle) {
-         // italics
-         bool wantUpright = aStyle->style.IsNormal();
-         if (aFontEntry->IsUpright() == wantUpright) {
-             rank += 10;
-         }
+        // TODO: stretch
+
+        // italics
+        bool wantUpright = aStyle->style.IsNormal();
+        if (aFontEntry->IsUpright() == wantUpright) {
+            rank += 5000.0f;
+        }
 
         // measure of closeness of weight to the desired value
-        rank += 9 - Abs((aFontEntry->Weight() - aStyle->weight) / 100.0f);
+        if (aFontEntry->Weight().Min() > aStyle->weight) {
+            rank += aFontEntry->Weight().Min() - aStyle->weight;
+        } else if (aFontEntry->Weight().Max() < aStyle->weight) {
+            rank += aStyle->weight - aFontEntry->Weight().Max();
+        } else {
+            rank += 2000.0f; // the font supports the exact weight wanted
+        }
     } else {
         // if no font to match, prefer non-bold, non-italic fonts
         if (aFontEntry->IsUpright()) {
-            rank += 3;
+            rank += 2000.0f;
         }
         if (!aFontEntry->IsBold()) {
-            rank += 2;
+            rank += 1000.0f;
         }
     }
 
     return rank;
 }
 
-#define RANK_MATCHED_CMAP   20
+#define RANK_MATCHED_CMAP   10000.0f
 
 void
 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
 {
     if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
         // none of the faces in the family support the required char,
         // so bail out immediately
         return;
@@ -1535,17 +1693,17 @@ gfxFontFamily::FindFontForChar(GlobalFon
 
     bool needsBold;
     gfxFontEntry *fe =
         FindFontForStyle(aMatchData->mStyle ? *aMatchData->mStyle
                                             : gfxFontStyle(),
                          needsBold, true);
 
     if (fe && !fe->SkipDuringSystemFallback()) {
-        int32_t rank = 0;
+        float rank = 0;
 
         if (fe->HasCharacter(aMatchData->mCh)) {
             rank += RANK_MATCHED_CMAP;
             aMatchData->mCount++;
 
             LogModule* log = gfxPlatform::GetLog(eGfxLog_textrun);
 
             if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
@@ -1595,17 +1753,17 @@ gfxFontFamily::FindFontForChar(GlobalFon
 
 void
 gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
 {
     uint32_t i, numFonts = mAvailableFonts.Length();
     for (i = 0; i < numFonts; i++) {
         gfxFontEntry *fe = mAvailableFonts[i];
         if (fe && fe->HasCharacter(aMatchData->mCh)) {
-            int32_t rank = RANK_MATCHED_CMAP;
+            float rank = RANK_MATCHED_CMAP;
             rank += CalcStyleMatch(fe, aMatchData->mStyle);
             if (rank > aMatchData->mMatchRank
                 || (rank == aMatchData->mMatchRank &&
                     Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
             {
                 aMatchData->mBestMatch = fe;
                 aMatchData->mMatchedFamily = this;
                 aMatchData->mMatchRank = rank;
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -40,16 +40,18 @@ class gfxSVGGlyphs;
 class FontInfoData;
 struct FontListSizes;
 class nsAtom;
 
 namespace mozilla {
 class SVGContextPaint;
 };
 
+#define NO_FONT_LANGUAGE_OVERRIDE      0
+
 class gfxCharacterMap : public gfxSparseBitSet {
 public:
     nsrefcnt AddRef() {
         NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
         ++mRefCnt;
         NS_LOG_ADDREF(this, mRefCnt, "gfxCharacterMap", sizeof(*this));
         return mRefCnt;
     }
@@ -110,16 +112,19 @@ struct gfxFontFeatureInfo {
 
 class gfxFontEntry {
 public:
     typedef mozilla::gfx::DrawTarget DrawTarget;
     typedef mozilla::unicode::Script Script;
     typedef mozilla::FontWeight FontWeight;
     typedef mozilla::FontSlantStyle FontSlantStyle;
     typedef mozilla::FontStretch FontStretch;
+    typedef mozilla::WeightRange WeightRange;
+    typedef mozilla::SlantStyleRange SlantStyleRange;
+    typedef mozilla::StretchRange StretchRange;
 
     // Used by stylo
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(gfxFontEntry)
 
     explicit gfxFontEntry(const nsAString& aName, bool aIsStandardFace = false);
 
     // Create a new entry that refers to the same font as this, but without
     // additional state that may have been set up (such as family name).
@@ -138,40 +143,43 @@ public:
     // will (usually, except on Linux) load and parse the 'name' table;
     // they are intended only for the font-inspection API, not for
     // perf-critical layout/drawing work.
 
     // The "real" name of the face, if available from the font resource;
     // returns Name() if nothing better is available.
     virtual nsString RealFaceName();
 
-    FontWeight Weight() const { return mWeight; }
-    FontStretch Stretch() const { return mStretch; }
+    WeightRange Weight() const { return mWeightRange; }
+    StretchRange Stretch() const { return mStretchRange; }
+    SlantStyleRange SlantStyle() const { return mStyleRange; }
 
     bool IsUserFont() const { return mIsDataUserFont || mIsLocalUserFont; }
     bool IsLocalUserFont() const { return mIsLocalUserFont; }
     bool IsFixedPitch() const { return mFixedPitch; }
-    bool IsItalic() const { return mStyle.IsItalic(); }
-    bool IsOblique() const { return mStyle.IsOblique(); }
-    bool IsUpright() const { return mStyle.IsNormal(); }
-    bool IsBold() const { return mWeight.IsBold(); } // bold == weights 600 and above
+    bool IsItalic() const { return SlantStyle().Min().IsItalic(); }
+    bool IsOblique() const { return SlantStyle().Min().IsOblique(); }
+    bool IsUpright() const { return SlantStyle().Min().IsNormal(); }
+    bool IsBold() const { return Weight().Max().IsBold(); } // bold == weights 600 and above
     bool IgnoreGDEF() const { return mIgnoreGDEF; }
     bool IgnoreGSUB() const { return mIgnoreGSUB; }
 
     // Return whether the face corresponds to "normal" CSS style properties:
     //    font-style: normal;
     //    font-weight: normal;
     //    font-stretch: normal;
     // If this is false, we might want to fall back to a different face and
     // possibly apply synthetic styling.
     bool IsNormalStyle() const
     {
         return IsUpright() &&
-               Weight() == FontWeight::Normal() &&
-               Stretch().IsNormal();
+               Weight().Min() <= FontWeight::Normal() &&
+               Weight().Max() >= FontWeight::Normal() &&
+               Stretch().Min() <= FontStretch::Normal() &&
+               Stretch().Max() >= FontStretch::Normal();
     }
 
     // whether a feature is supported by the font (limited to a small set
     // of features for which some form of fallback needs to be implemented)
     virtual bool SupportsOpenTypeFeature(Script aScript, uint32_t aFeatureTag);
     bool SupportsGraphiteFeature(uint32_t aFeatureTag);
 
     // returns a set containing all input glyph ids for a given feature
@@ -365,22 +373,61 @@ public:
     }
     virtual void GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes)
     {
     }
     virtual void GetVariationInstances(nsTArray<gfxFontVariationInstance>& aInstances)
     {
     }
 
+    // Set up the entry's weight/stretch/style ranges according to axes found
+    // by GetVariationAxes (for installed fonts; do NOT call this for user
+    // fonts, where the ranges are provided by @font-face descriptors).
+    void SetupVariationRanges();
+
+    // Get variation axis settings that should be used to implement a particular
+    // font style using this resource.
+    void GetVariationsForStyle(nsTArray<gfxFontVariation>& aResult,
+                               const gfxFontStyle& aStyle);
+
     // Get the font's list of features (if any) for DevTools support.
     void GetFeatureInfo(nsTArray<gfxFontFeatureInfo>& aFeatureInfo);
 
     nsString         mName;
     nsString         mFamilyName;
 
+    RefPtr<gfxCharacterMap> mCharacterMap;
+
+    mozilla::UniquePtr<uint8_t[]> mUVSData;
+    mozilla::UniquePtr<gfxUserFontData> mUserFontData;
+    mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
+    // list of gfxFonts that are using SVG glyphs
+    nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
+    nsTArray<gfxFontFeature> mFeatureSettings;
+    nsTArray<gfxFontVariation> mVariationSettings;
+    mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
+    mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
+
+    // Color Layer font support
+    hb_blob_t*       mCOLR = nullptr;
+    hb_blob_t*       mCPAL = nullptr;
+
+    // bitvector of substitution space features per script, one each
+    // for default and non-default features
+    uint32_t         mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
+    uint32_t         mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
+
+    uint32_t         mUVSOffset = 0;
+
+    uint32_t         mLanguageOverride = NO_FONT_LANGUAGE_OVERRIDE;
+
+    WeightRange      mWeightRange = WeightRange(FontWeight(500));
+    StretchRange     mStretchRange = StretchRange(FontStretch::Normal());
+    SlantStyleRange  mStyleRange = SlantStyleRange(FontSlantStyle::Normal());
+
     bool             mFixedPitch  : 1;
     bool             mIsBadUnderlineFont : 1;
     bool             mIsUserFontContainer : 1; // userfont entry
     bool             mIsDataUserFont : 1;      // platform font entry (data)
     bool             mIsLocalUserFont : 1;     // platform font entry (local)
     bool             mStandardFace : 1;
     bool             mIgnoreGDEF  : 1;
     bool             mIgnoreGSUB  : 1;
@@ -395,42 +442,16 @@ public:
     bool             mSpaceGlyphIsInvisible : 1;
     bool             mSpaceGlyphIsInvisibleInitialized : 1;
     bool             mHasGraphiteTables : 1;
     bool             mCheckedForGraphiteTables : 1;
     bool             mHasCmapTable : 1;
     bool             mGrFaceInitialized : 1;
     bool             mCheckedForColorGlyph : 1;
 
-    // bitvector of substitution space features per script, one each
-    // for default and non-default features
-    uint32_t         mDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
-    uint32_t         mNonDefaultSubSpaceFeatures[(int(Script::NUM_SCRIPT_CODES) + 31) / 32];
-
-    FontWeight mWeight;
-    FontStretch mStretch;
-    FontSlantStyle mStyle;
-
-    RefPtr<gfxCharacterMap> mCharacterMap;
-    uint32_t         mUVSOffset;
-    mozilla::UniquePtr<uint8_t[]> mUVSData;
-    mozilla::UniquePtr<gfxUserFontData> mUserFontData;
-    mozilla::UniquePtr<gfxSVGGlyphs> mSVGGlyphs;
-    // list of gfxFonts that are using SVG glyphs
-    nsTArray<gfxFont*> mFontsUsingSVGGlyphs;
-    nsTArray<gfxFontFeature> mFeatureSettings;
-    nsTArray<gfxFontVariation> mVariationSettings;
-    mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,bool>> mSupportedFeatures;
-    mozilla::UniquePtr<nsDataHashtable<nsUint32HashKey,hb_set_t*>> mFeatureInputs;
-    uint32_t         mLanguageOverride;
-
-    // Color Layer font support
-    hb_blob_t*       mCOLR;
-    hb_blob_t*       mCPAL;
-
 protected:
     friend class gfxPlatformFontList;
     friend class gfxMacPlatformFontList;
     friend class gfxUserFcFontEntry;
     friend class gfxFontFamily;
     friend class gfxSingleFaceMacFontFamily;
     friend class gfxUserFontEntry;
 
@@ -455,61 +476,61 @@ protected:
     // lookup the cmap in cached font data
     virtual already_AddRefed<gfxCharacterMap>
     GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
                         uint32_t& aUVSOffset);
 
     // helper for HasCharacter(), which is what client code should call
     virtual bool TestCharacterMap(uint32_t aCh);
 
-    // Font's unitsPerEm from the 'head' table, if available (will be set to
-    // kInvalidUPEM for non-sfnt font formats)
-    uint16_t mUnitsPerEm;
-
     // Shaper-specific face objects, shared by all instantiations of the same
     // physical font, regardless of size.
     // Usually, only one of these will actually be created for any given font
     // entry, depending on the font tables that are present.
 
     // hb_face_t is refcounted internally, so each shaper that's using it will
     // bump the ref count when it acquires the face, and "destroy" (release) it
     // in its destructor. The font entry has only this non-owning reference to
     // the face; when the face is deleted, it will tell the font entry to forget
     // it, so that a new face will be created next time it is needed.
-    hb_face_t* mHBFace;
+    hb_face_t* mHBFace = nullptr;
 
     static hb_blob_t* HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData);
 
     // Callback that the hb_face will use to tell us when it is being deleted.
     static void HBFaceDeletedCallback(void *aUserData);
 
     // gr_face is -not- refcounted, so it will be owned directly by the font
     // entry, and we'll keep a count of how many references we've handed out;
     // each shaper is responsible to call ReleaseGrFace on its entry when
     // finished with it, so that we know when it can be deleted.
-    gr_face*   mGrFace;
+    gr_face*   mGrFace = nullptr;
 
     // hashtable to map raw table data ptr back to its owning blob, for use by
     // graphite table-release callback
-    nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap;
+    nsDataHashtable<nsPtrHashKey<const void>,void*>* mGrTableMap = nullptr;
 
     // number of current users of this entry's mGrFace
-    nsrefcnt mGrFaceRefCnt;
+    nsrefcnt mGrFaceRefCnt = 0;
 
     static const void* GrGetTable(const void *aAppFaceHandle,
                                   unsigned int aName,
                                   size_t *aLen);
     static void GrReleaseTable(const void *aAppFaceHandle,
                                const void *aTableBuffer);
 
     // For memory reporting: size of user-font data belonging to this entry.
     // We record this in the font entry because the actual data block may be
     // handed over to platform APIs, so that it would become difficult (and
     // platform-specific) to measure it directly at report-gathering time.
-    uint32_t mComputedSizeOfUserFont;
+    uint32_t mComputedSizeOfUserFont = 0;
+
+    // Font's unitsPerEm from the 'head' table, if available (will be set to
+    // kInvalidUPEM for non-sfnt font formats)
+    uint16_t mUnitsPerEm = 0;
 
 private:
     /**
      * Font table hashtable, to support GetFontTable for harfbuzz.
      *
      * The harfbuzz shaper (and potentially other clients) needs access to raw
      * font table data. This needs to be cached so that it can be used
      * repeatedly (each time we construct a text run; in some cases, for
@@ -607,24 +628,24 @@ private:
 };
 
 
 // used when iterating over all fonts looking for a match for a given character
 struct GlobalFontMatch {
     GlobalFontMatch(const uint32_t aCharacter,
                     const gfxFontStyle *aStyle) :
         mCh(aCharacter), mStyle(aStyle),
-        mMatchRank(0), mCount(0), mCmapsTested(0)
+        mMatchRank(0.0f), mCount(0), mCmapsTested(0)
         {
 
         }
 
     const uint32_t         mCh;          // codepoint to be matched
     const gfxFontStyle*    mStyle;       // style to match
-    int32_t                mMatchRank;   // metric indicating closest match
+    float                  mMatchRank;   // metric indicating closest match
     RefPtr<gfxFontEntry> mBestMatch;   // current best match
     RefPtr<gfxFontFamily> mMatchedFamily; // the family it belongs to
     uint32_t               mCount;       // number of fonts matched
     uint32_t               mCmapsTested; // number of cmaps tested
 };
 
 class gfxFontFamily {
 public:
--- a/gfx/thebes/gfxFontUtils.cpp
+++ b/gfx/thebes/gfxFontUtils.cpp
@@ -1939,34 +1939,16 @@ gfxFontUtils::GetVariationInstances(gfxF
             value.mAxis = axes[j].axisTag;
             value.mValue = int32_t(coords[j]) / 65536.0;
             instance.mValues.AppendElement(value);
         }
         aInstances.AppendElement(instance);
     }
 }
 
-void
-gfxFontUtils::MergeVariations(const nsTArray<gfxFontVariation>& aEntrySettings,
-                              const nsTArray<gfxFontVariation>& aStyleSettings,
-                              nsTArray<gfxFontVariation>* aMerged)
-{
-    MOZ_ASSERT(!aEntrySettings.IsEmpty() &&
-               !aStyleSettings.IsEmpty() &&
-               aMerged->IsEmpty());
-    // Settings from the CSS style will take precedence over those from the
-    // font entry (i.e. from the @font-face descriptor).
-    aMerged->AppendElements(aStyleSettings);
-    for (auto& setting : aEntrySettings) {
-        if (!aMerged->Contains(setting.mTag, VariationTagComparator())) {
-            aMerged->AppendElement(setting);
-        }
-    }
-}
-
 #ifdef XP_WIN
 
 /* static */
 bool
 gfxFontUtils::IsCffFont(const uint8_t* aFontData)
 {
     // this is only called after aFontData has passed basic validation,
     // so we know there is enough data present to allow us to read the version!
--- a/gfx/thebes/gfxFontUtils.h
+++ b/gfx/thebes/gfxFontUtils.h
@@ -1005,32 +1005,16 @@ public:
     // platforms where the native font APIs don't provide the info we want
     // in a convenient form.
     // (Not used on platforms -- currently, freetype -- where the font APIs
     // expose variation instance details directly.)
     static void
     GetVariationInstances(gfxFontEntry* aFontEntry,
                           nsTArray<gfxFontVariationInstance>& aInstances);
 
-    // Merge a list of font-variation-settings from a font entry and a list
-    // from a gfxFontStyle, to get a combined collection of settings that can
-    // be used to instantiate a font.
-    static void
-    MergeVariations(const nsTArray<gfxFontVariation>& aEntrySettings,
-                    const nsTArray<gfxFontVariation>& aStyleSettings,
-                    nsTArray<gfxFontVariation>* aMerged);
-
-    // Helper used by MergeVariations, and other code that wants to check
-    // whether an array of variation settings includes a particular tag.
-    struct VariationTagComparator {
-        bool Equals(const gfxFontVariation& aVariation, uint32_t aTag) const {
-            return aVariation.mTag == aTag;
-        }
-    };
-
 protected:
     friend struct MacCharsetMappingComparator;
 
     static nsresult
     ReadNames(const char *aNameData, uint32_t aDataLen, uint32_t aNameID,
               int32_t aLangID, int32_t aPlatformID, nsTArray<nsString>& aNames);
 
     // convert opentype name-table platform/encoding/language values to an
--- a/gfx/thebes/gfxGDIFont.cpp
+++ b/gfx/thebes/gfxGDIFont.cpp
@@ -455,17 +455,20 @@ gfxGDIFont::FillLogFont(LOGFONTW& aLogFo
             // choice of actual face used (bug 724231)
             weight = 0;
         } else {
             // avoid GDI synthetic bold which occurs when weight
             // specified is >= font data weight + 200
             weight = mNeedsBold ? 700 : 200;
         }
     } else {
-        weight = mNeedsBold ? 700 : fe->Weight().ToIntRounded();
+        // GDI doesn't support variation fonts, so for system fonts we know
+        // that the entry has only a single weight, not a range.
+        MOZ_ASSERT(fe->Weight().IsSingle());
+        weight = mNeedsBold ? 700 : fe->Weight().Min().ToIntRounded();
     }
 
     fe->FillLogFont(&aLogFont, weight, aSize);
 }
 
 uint32_t
 gfxGDIFont::GetGlyph(uint32_t aUnicode, uint32_t aVarSelector)
 {
--- a/gfx/thebes/gfxGDIFontList.cpp
+++ b/gfx/thebes/gfxGDIFontList.cpp
@@ -110,42 +110,42 @@ FontTypeToOutPrecision(uint8_t fontType)
 /***************************************************************
  *
  * GDIFontEntry
  *
  */
 
 GDIFontEntry::GDIFontEntry(const nsAString& aFaceName,
                            gfxWindowsFontType aFontType,
-                           FontSlantStyle aStyle,
-                           FontWeight aWeight,
-                           FontStretch aStretch,
+                           SlantStyleRange aStyle,
+                           WeightRange aWeight,
+                           StretchRange aStretch,
                            gfxUserFontData *aUserFontData)
     : gfxFontEntry(aFaceName),
       mFontType(aFontType),
       mForceGDI(false),
       mUnicodeRanges()
 {
     mUserFontData.reset(aUserFontData);
-    mStyle = aStyle;
-    mWeight = aWeight;
-    mStretch = aStretch;
+    mStyleRange = aStyle;
+    mWeightRange = aWeight;
+    mStretchRange = aStretch;
     if (IsType1())
         mForceGDI = true;
     mIsDataUserFont = aUserFontData != nullptr;
 
     InitLogFont(aFaceName, aFontType);
 }
 
 gfxFontEntry*
 GDIFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
-    return new GDIFontEntry(Name(), mFontType, mStyle, mWeight, mStretch,
-                            nullptr);
+    return new GDIFontEntry(Name(), mFontType, SlantStyle(), Weight(),
+                            Stretch(), nullptr);
 }
 
 nsresult
 GDIFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
 {
     AUTO_PROFILER_LABEL("GDIFontEntry::ReadCMAP", OTHER);
 
     // attempt this once, if errors occur leave a blank cmap
@@ -300,17 +300,17 @@ GDIFontEntry::TestCharacterMap(uint32_t 
         if (aCh > 0xFFFF)
             return false;
 
         // previous code was using the group style
         gfxFontStyle fakeStyle;
         if (!IsUpright()) {
             fakeStyle.style = FontSlantStyle::Italic();
         }
-        fakeStyle.weight = mWeight;
+        fakeStyle.weight = Weight().Min();
 
         RefPtr<gfxFont> tempFont = FindOrMakeFont(&fakeStyle, false);
         if (!tempFont || !tempFont->Valid())
             return false;
         gfxGDIFont *font = static_cast<gfxGDIFont*>(tempFont.get());
 
         HDC dc = GetDC((HWND)nullptr);
         SetGraphicsMode(dc, GM_ADVANCED);
@@ -379,29 +379,29 @@ GDIFontEntry::InitLogFont(const nsAStrin
     mLogFont.lfClipPrecision  = CLIP_TURNOFF_FONTASSOCIATION;
     mLogFont.lfQuality        = DEFAULT_QUALITY;
     mLogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
     // always force lfItalic if we want it.  Font selection code will
     // do its best to give us an italic font entry, but if no face exists
     // it may give us a regular one based on weight.  Windows should
     // do fake italic for us in that case.
     mLogFont.lfItalic         = !IsUpright();
-    mLogFont.lfWeight         = int(mWeight.ToFloat());
+    mLogFont.lfWeight         = Weight().Min().ToIntRounded();
 
     int len = std::min<int>(aName.Length(), LF_FACESIZE - 1);
     memcpy(&mLogFont.lfFaceName, aName.BeginReading(), len * sizeof(char16_t));
     mLogFont.lfFaceName[len] = '\0';
 }
 
 GDIFontEntry* 
 GDIFontEntry::CreateFontEntry(const nsAString& aName,
                               gfxWindowsFontType aFontType,
-                              FontSlantStyle aStyle,
-                              FontWeight aWeight,
-                              FontStretch aStretch,
+                              SlantStyleRange aStyle,
+                              WeightRange aWeight,
+                              StretchRange aStretch,
                               gfxUserFontData* aUserFontData)
 {
     // jtdfix - need to set charset, unicode ranges, pitch/family
 
     GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aStyle,
                                         aWeight, aStretch, aUserFontData);
 
     return fe;
@@ -462,35 +462,35 @@ GDIFontFamily::FamilyAddStylesProc(const
             // otherwise if the new type is worse, skip it
             return 1;
         }
     }
 
     for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) {
         fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get());
         // check if we already know about this face
-        if (fe->mWeight == FontWeight(int32_t(logFont.lfWeight)) &&
+        if (fe->Weight().Min() == FontWeight(int32_t(logFont.lfWeight)) &&
             fe->IsItalic() == (logFont.lfItalic == 0xFF)) {
             // update the charset bit here since this could be different
             // XXX Can we still do this now that we store mCharset
             // on the font family rather than the font entry?
             ff->mCharset.set(metrics.tmCharSet);
             return 1; 
         }
     }
 
     // We can't set the hasItalicFace flag correctly here,
     // because we might not have seen the family's italic face(s) yet.
     // So we'll set that flag for all members after loading all the faces.
     auto italicStyle = (logFont.lfItalic == 0xFF ?
                            FontSlantStyle::Italic() : FontSlantStyle::Normal());
     fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName),
-                                       feType, italicStyle,
-                                       FontWeight(int32_t(logFont.lfWeight)),
-                                       FontStretch::Normal(),
+                                       feType, SlantStyleRange(italicStyle),
+                                       WeightRange(FontWeight(int32_t(logFont.lfWeight))),
+                                       StretchRange(FontStretch::Normal()),
                                        nullptr);
     if (!fe)
         return 1;
 
     ff->AddFontEntry(fe);
 
     if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 &&
@@ -503,25 +503,22 @@ GDIFontFamily::FamilyAddStylesProc(const
             DWORD range = nmetrics->ntmFontSig.fsUsb[i];
             for (uint32_t k = 0; k < 32; ++k) {
                 fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0);
             }
         }
     }
 
     if (LOG_FONTLIST_ENABLED()) {
-        nsAutoCString stretchString;
         LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-             " with style: %s weight: %d stretch: %g%%",
+             " with style: %s weight: %d stretch: normal",
              NS_ConvertUTF16toUTF8(fe->Name()).get(),
              NS_ConvertUTF16toUTF8(ff->Name()).get(),
              (logFont.lfItalic == 0xff) ? "italic" : "normal",
-             logFont.lfWeight,
-             fe->Stretch().Percentage(),
-             stretchString.get()));
+             logFont.lfWeight));
     }
     return 1;
 }
 
 void
 GDIFontFamily::FindStyleVariations(FontInfoData *aFontInfoData)
 {
     if (mHasStyles)
@@ -709,45 +706,46 @@ gfxGDIFontList::EnumFontFamExProc(ENUMLO
         family->mCharset.set(metrics.tmCharSet);
     }
 
     return 1;
 }
 
 gfxFontEntry* 
 gfxGDIFontList::LookupLocalFont(const nsAString& aFontName,
-                                FontWeight aWeight,
-                                FontStretch aStretch,
-                                FontSlantStyle aStyle)
+                                WeightRange aWeightForEntry,
+                                StretchRange aStretchForEntry,
+                                SlantStyleRange aStyleForEntry)
 {
     gfxFontEntry *lookup;
 
     lookup = LookupInFaceNameLists(aFontName);
     if (!lookup) {
         return nullptr;
     }
 
     bool isCFF = false; // jtdfix -- need to determine this
     
     // use the face name from the lookup font entry, which will be the localized
     // face name which GDI mapping tables use (e.g. with the system locale set to
     // Dutch, a fullname of 'Arial Bold' will find a font entry with the face name
     // 'Arial Vet' which can be used as a key in GDI font lookups).
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(lookup->Name(), 
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/, 
-        lookup->mStyle, lookup->mWeight, aStretch, nullptr);
+        lookup->SlantStyle(), lookup->Weight(), aStretchForEntry, nullptr);
 
     if (!fe)
         return nullptr;
 
     fe->mIsLocalUserFont = true;
 
     // make the new font entry match the userfont entry style characteristics
-    fe->mWeight = aWeight;
-    fe->mStyle = aStyle;
+    fe->mWeightRange = aWeightForEntry;
+    fe->mStyleRange = aStyleForEntry;
+    fe->mStretchRange = aStretchForEntry;
 
     return fe;
 }
 
 // If aFontData contains only a MS/Symbol cmap subtable, not MS/Unicode,
 // we modify the subtable header to mark it as Unicode instead, because
 // otherwise GDI will refuse to load the font.
 // NOTE that this function does not bounds-check every access to the font data.
@@ -801,19 +799,19 @@ FixupSymbolEncodedFont(uint8_t* aFontDat
             return true;
         }
     }
     return false;
 }
 
 gfxFontEntry*
 gfxGDIFontList::MakePlatformFont(const nsAString& aFontName,
-                                 FontWeight aWeight,
-                                 FontStretch aStretch,
-                                 FontSlantStyle aStyle,
+                                 WeightRange aWeightForEntry,
+                                 StretchRange aStretchForEntry,
+                                 SlantStyleRange aStyleForEntry,
                                  const uint8_t* aFontData,
                                  uint32_t aLength)
 {
     // MakePlatformFont is responsible for deleting the font data with free
     // so we set up a stack object to ensure it is freed even if we take an
     // early exit
     struct FontDataDeleter {
         explicit FontDataDeleter(const uint8_t* aFontData)
@@ -868,17 +866,17 @@ gfxGDIFontList::MakePlatformFont(const n
         RemoveFontMemResourceEx(fontRef);
         return nullptr;
     }
 
     // make a new font entry using the unique name
     WinUserFontData *winUserFontData = new WinUserFontData(fontRef);
     GDIFontEntry *fe = GDIFontEntry::CreateFontEntry(uniqueName,
         gfxWindowsFontType(isCFF ? GFX_FONT_TYPE_PS_OPENTYPE : GFX_FONT_TYPE_TRUETYPE) /*type*/,
-        aStyle, aWeight, aStretch, winUserFontData);
+        aStyleForEntry, aWeightForEntry, aStretchForEntry, winUserFontData);
 
     if (fe) {
       fe->mIsDataUserFont = true;
     }
 
     return fe;
 }
 
--- a/gfx/thebes/gfxGDIFontList.h
+++ b/gfx/thebes/gfxGDIFontList.h
@@ -103,20 +103,16 @@ enum gfxWindowsFontType {
 };
 
 // A single member of a font family (i.e. a single face, such as Times Italic)
 // represented as a LOGFONT that will resolve to the correct face.
 // This replaces FontEntry from gfxWindowsFonts.h/cpp.
 class GDIFontEntry : public gfxFontEntry
 {
 public:
-    typedef mozilla::FontStretch FontStretch;
-    typedef mozilla::FontSlantStyle FontSlantStyle;
-    typedef mozilla::FontWeight FontWeight;
-
     LPLOGFONTW GetLogFont() { return &mLogFont; }
 
     nsresult ReadCMAP(FontInfoData *aFontInfoData = nullptr) override;
 
     void FillLogFont(LOGFONTW *aLogFont,
                      LONG aWeight,
                      gfxFloat aSize);
 
@@ -165,37 +161,31 @@ public:
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
     gfxFontEntry* Clone() const override;
 
     // create a font entry for a font with a given name
     static GDIFontEntry* CreateFontEntry(const nsAString& aName,
                                          gfxWindowsFontType aFontType,
-                                         FontSlantStyle aStyle,
-                                         FontWeight aWeight,
-                                         FontStretch aStretch,
+                                         SlantStyleRange aStyle,
+                                         WeightRange aWeight,
+                                         StretchRange aStretch,
                                          gfxUserFontData* aUserFontData);
 
-    // create a font entry for a font referenced by its fullname
-    static GDIFontEntry* LoadLocalFont(const nsAString& aFontName,
-                                       FontWeight aWeight,
-                                       FontStretch aStretch,
-                                       FontSlantStyle aStyle);
-
     gfxWindowsFontType mFontType;
     bool mForceGDI;
 
     gfxSparseBitSet mUnicodeRanges;
 
 protected:
     friend class gfxGDIFont;
 
     GDIFontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType,
-                 FontSlantStyle aStyle, FontWeight aWeight, FontStretch aStretch,
+                 SlantStyleRange aStyle, WeightRange aWeight, StretchRange aStretch,
                  gfxUserFontData *aUserFontData);
 
     void InitLogFont(const nsAString& aName, gfxWindowsFontType aFontType);
 
     virtual gfxFont* CreateFontInstance(const gfxFontStyle *aFontStyle,
                                         bool aNeedsBold) override;
 
     virtual nsresult CopyFontTable(uint32_t aTableTag,
@@ -317,44 +307,40 @@ protected:
 private:
     static int CALLBACK FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe,
                                             const NEWTEXTMETRICEXW *nmetrics,
                                             DWORD fontType, LPARAM data);
 };
 
 class gfxGDIFontList : public gfxPlatformFontList {
 public:
-    typedef mozilla::FontStretch FontStretch;
-    typedef mozilla::FontSlantStyle FontSlantStyle;
-    typedef mozilla::FontWeight FontWeight;
-
     static gfxGDIFontList* PlatformFontList() {
         return static_cast<gfxGDIFontList*>(sPlatformFontList);
     }
 
     // initialize font lists
     virtual nsresult InitFontListForPlatform() override;
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
 
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                          FontWeight aWeight,
-                                          FontStretch aStretch,
-                                          FontSlantStyle aStyle);
+                                          WeightRange aWeightForEntry,
+                                          StretchRange aStretchForEntry,
+                                          SlantStyleRange aStyleForEntry);
 
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                           FontWeight aWeight,
-                                           FontStretch aStretch,
-                                           FontSlantStyle aStyle,
+                                           WeightRange aWeightForEntry,
+                                           StretchRange aStretchForEntry,
+                                           SlantStyleRange aStyleForEntry,
                                            const uint8_t* aFontData,
                                            uint32_t aLength);
 
     virtual void AddSizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
     virtual void AddSizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
                                         FontListSizes* aSizes) const;
 
--- a/gfx/thebes/gfxMacFont.cpp
+++ b/gfx/thebes/gfxMacFont.cpp
@@ -31,44 +31,32 @@ gfxMacFont::gfxMacFont(const RefPtr<Unsc
       mCGFont(nullptr),
       mCTFont(nullptr),
       mFontFace(nullptr),
       mFontSmoothingBackgroundColor(aFontStyle->fontSmoothingBackgroundColor),
       mVariationFont(aFontEntry->HasVariations())
 {
     mApplySyntheticBold = aNeedsBold;
 
-    if (mVariationFont && (!aFontStyle->variationSettings.IsEmpty() ||
-                           !aFontEntry->mVariationSettings.IsEmpty())) {
+    if (mVariationFont) {
         CGFontRef baseFont = aUnscaledFont->GetFont();
         if (!baseFont) {
             mIsValid = false;
             return;
         }
 
-        // Probably one of the lists of variations, either from the @font-face
-        // descriptor or from the property, will be empty. So skip merging them
-        // unless really necessary.
-        const nsTArray<gfxFontVariation>* vars;
-        AutoTArray<gfxFontVariation,4> mergedSettings;
-        if (aFontStyle->variationSettings.IsEmpty()) {
-            vars = &aFontEntry->mVariationSettings;
-        } else if (aFontEntry->mVariationSettings.IsEmpty()) {
-            vars = &aFontStyle->variationSettings;
-        } else {
-            gfxFontUtils::MergeVariations(aFontEntry->mVariationSettings,
-                                          aFontStyle->variationSettings,
-                                          &mergedSettings);
-            vars = &mergedSettings;
-        }
+        // Get the variation settings needed to instantiate the fontEntry
+        // for a particular fontStyle.
+        AutoTArray<gfxFontVariation,4> vars;
+        aFontEntry->GetVariationsForStyle(vars, *aFontStyle);
 
         mCGFont =
             UnscaledFontMac::CreateCGFontWithVariations(baseFont,
-                                                        vars->Length(),
-                                                        vars->Elements());
+                                                        vars.Length(),
+                                                        vars.Elements());
         if (!mCGFont) {
           ::CFRetain(baseFont);
           mCGFont = baseFont;
         }
     } else {
         mCGFont = aUnscaledFont->GetFont();
         if (!mCGFont) {
             mIsValid = false;
--- a/gfx/thebes/gfxMacPlatformFontList.h
+++ b/gfx/thebes/gfxMacPlatformFontList.h
@@ -26,23 +26,23 @@
 class gfxMacPlatformFontList;
 
 // a single member of a font family (i.e. a single face, such as Times Italic)
 class MacOSFontEntry : public gfxFontEntry
 {
 public:
     friend class gfxMacPlatformFontList;
 
-    MacOSFontEntry(const nsAString& aPostscriptName, FontWeight aWeight,
+    MacOSFontEntry(const nsAString& aPostscriptName, WeightRange aWeight,
                    bool aIsStandardFace = false,
                    double aSizeHint = 0.0);
 
     // for use with data fonts
     MacOSFontEntry(const nsAString& aPostscriptName, CGFontRef aFontRef,
-                   FontWeight aWeight, FontStretch aStretch, FontSlantStyle aStyle,
+                   WeightRange aWeight, StretchRange aStretch, SlantStyleRange aStyle,
                    bool aIsDataUserFont, bool aIsLocal);
 
     virtual ~MacOSFontEntry() {
         if (mTrakTable) {
             hb_blob_destroy(mTrakTable);
         }
         ::CGFontRelease(mFontRef);
     }
@@ -126,24 +126,24 @@ public:
 
     gfxFontFamily* CreateFontFamily(const nsAString& aName) const override;
 
     static int32_t AppleWeightToCSSWeight(int32_t aAppleWeight);
 
     bool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) override;
 
     gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
-                                  FontWeight aWeight,
-                                  FontStretch aStretch,
-                                  FontSlantStyle aStyle) override;
+                                  WeightRange aWeightForEntry,
+                                  StretchRange aStretchForEntry,
+                                  SlantStyleRange aStyleForEntry) override;
 
     gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
-                                   FontWeight aWeight,
-                                   FontStretch aStretch,
-                                   FontSlantStyle aStyle,
+                                   WeightRange aWeightForEntry,
+                                   StretchRange aStretchForEntry,
+                                   SlantStyleRange aStyleForEntry,
                                    const uint8_t* aFontData,
                                    uint32_t aLength) override;
 
     bool FindAndAddFamilies(const nsAString& aFamily,
                             nsTArray<gfxFontFamily*>* aOutput,
                             FindFamiliesFlags aFlags,
                             gfxFontStyle* aStyle = nullptr,
                             gfxFloat aDevToCssSize = 1.0) override;
--- a/gfx/thebes/gfxMacPlatformFontList.mm
+++ b/gfx/thebes/gfxMacPlatformFontList.mm
@@ -283,17 +283,19 @@ MacOSFontEntry::CreateFontInstance(const
     return new gfxMacFont(unscaledFont, this, aFontStyle, aNeedsBold);
 }
 
 bool
 MacOSFontEntry::HasVariations()
 {
     if (!mHasVariationsInitialized) {
         mHasVariationsInitialized = true;
-        mHasVariations = HasFontTable(TRUETYPE_TAG('f','v','a','r'));
+        mHasVariations =
+            gfxPlatform::GetPlatform()->HasVariationFontSupport() &&
+            HasFontTable(TRUETYPE_TAG('f','v','a','r'));
     }
 
     return mHasVariations;
 }
 
 void
 MacOSFontEntry::GetVariationAxes(nsTArray<gfxFontVariationAxis>& aVariationAxes)
 {
@@ -359,17 +361,17 @@ MacOSFontEntry::IsCFF()
         mIsCFFInitialized = true;
         mIsCFF = HasFontTable(TRUETYPE_TAG('C','F','F',' '));
     }
 
     return mIsCFF;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
-                               FontWeight aWeight,
+                               WeightRange aWeight,
                                bool aIsStandardFace,
                                double aSizeHint)
     : gfxFontEntry(aPostscriptName, aIsStandardFace),
       mFontRef(NULL),
       mSizeHint(aSizeHint),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
@@ -378,24 +380,24 @@ MacOSFontEntry::MacOSFontEntry(const nsA
       mHasVariationsInitialized(false),
       mHasAATSmallCaps(false),
       mHasAATSmallCapsInitialized(false),
       mCheckedForTracking(false),
       mTrakTable(nullptr),
       mTrakValues(nullptr),
       mTrakSizeTable(nullptr)
 {
-    mWeight = aWeight;
+    mWeightRange = aWeight;
 }
 
 MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName,
                                CGFontRef aFontRef,
-                               FontWeight aWeight,
-                               FontStretch aStretch,
-                               FontSlantStyle aStyle,
+                               WeightRange aWeight,
+                               StretchRange aStretch,
+                               SlantStyleRange aStyle,
                                bool aIsDataUserFont,
                                bool aIsLocalUserFont)
     : gfxFontEntry(aPostscriptName, false),
       mFontRef(NULL),
       mSizeHint(0.0),
       mFontRefInitialized(false),
       mRequiresAAT(false),
       mIsCFF(false),
@@ -408,35 +410,35 @@ MacOSFontEntry::MacOSFontEntry(const nsA
       mTrakTable(nullptr),
       mTrakValues(nullptr),
       mTrakSizeTable(nullptr)
 {
     mFontRef = aFontRef;
     mFontRefInitialized = true;
     ::CFRetain(mFontRef);
 
-    mWeight = aWeight;
-    mStretch = aStretch;
+    mWeightRange = aWeight;
+    mStretchRange = aStretch;
     mFixedPitch = false; // xxx - do we need this for downloaded fonts?
-    mStyle = aStyle;
+    mStyleRange = aStyle;
 
     NS_ASSERTION(!(aIsDataUserFont && aIsLocalUserFont),
                  "userfont is either a data font or a local font");
     mIsDataUserFont = aIsDataUserFont;
     mIsLocalUserFont = aIsLocalUserFont;
 }
 
 gfxFontEntry*
 MacOSFontEntry::Clone() const
 {
     MOZ_ASSERT(!IsUserFont(), "we can only clone installed fonts!");
     MacOSFontEntry* fe =
-        new MacOSFontEntry(Name(), mWeight, mStandardFace, mSizeHint);
-    fe->mStyle = mStyle;
-    fe->mStretch = mStretch;
+        new MacOSFontEntry(Name(), Weight(), mStandardFace, mSizeHint);
+    fe->mStyleRange = mStyleRange;
+    fe->mStretchRange = mStretchRange;
     fe->mFixedPitch = mFixedPitch;
     return fe;
 }
 
 CGFontRef
 MacOSFontEntry::GetFontRef()
 {
     if (!mFontRefInitialized) {
@@ -899,48 +901,58 @@ gfxMacFontFamily::FindStyleVariations(Fo
             [facename isEqualToString:@"Bold Italic"] ||
             [facename isEqualToString:@"Bold Oblique"])
         {
             isStandardFace = true;
         }
 
         // create a font entry
         MacOSFontEntry *fontEntry =
-            new MacOSFontEntry(postscriptFontName, FontWeight(cssWeight),
+            new MacOSFontEntry(postscriptFontName,
+                               WeightRange(FontWeight(cssWeight)),
                                isStandardFace, mSizeHint);
         if (!fontEntry) {
             break;
         }
 
         // set additional properties based on the traits reported by Cocoa
         if (macTraits & (NSCondensedFontMask | NSNarrowFontMask | NSCompressedFontMask)) {
-            fontEntry->mStretch = FontStretch::Condensed();
+            fontEntry->mStretchRange = StretchRange(FontStretch::Condensed());
         } else if (macTraits & NSExpandedFontMask) {
-            fontEntry->mStretch = FontStretch::Expanded();
+            fontEntry->mStretchRange = StretchRange(FontStretch::Expanded());
         }
         // Cocoa fails to set the Italic traits bit for HelveticaLightItalic,
         // at least (see bug 611855), so check for style name endings as well
         if ((macTraits & NSItalicFontMask) ||
             [facename hasSuffix:@"Italic"] ||
             [facename hasSuffix:@"Oblique"])
         {
-            fontEntry->mStyle = FontSlantStyle::Italic();
+            fontEntry->mStyleRange = SlantStyleRange(FontSlantStyle::Italic());
         }
         if (macTraits & NSFixedPitchFontMask) {
             fontEntry->mFixedPitch = true;
         }
 
+        if (gfxPlatform::GetPlatform()->HasVariationFontSupport()) {
+            fontEntry->SetupVariationRanges();
+        }
+
         if (LOG_FONTLIST_ENABLED()) {
+            nsAutoCString weightString;
+            fontEntry->Weight().ToString(weightString);
+            nsAutoCString stretchString;
+            fontEntry->Stretch().ToString(stretchString);
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
-                 " with style: %s weight: %d stretch: %g%%"
+                 " with style: %s weight: %s stretch: %s"
                  " (apple-weight: %d macTraits: %8.8x)",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  fontEntry->IsItalic() ? "italic" : "normal",
-                 cssWeight, fontEntry->Stretch().Percentage(),
+                 weightString.get(),
+                 stretchString.get(),
                  appKitWeight, macTraits));
         }
 
         // insert into font entry array of family
         AddFontEntry(fontEntry);
     }
 
     SortAvailableFonts();
@@ -1277,17 +1289,17 @@ gfxMacPlatformFontList::InitSingleFaceLi
 
         // add only if doesn't exist already
         if (!mFontFamilies.GetWeak(key)) {
             RefPtr<gfxFontFamily> familyEntry =
                 new gfxSingleFaceMacFontFamily(familyName);
             // We need a separate font entry, because its family name will
             // differ from the one we found in the main list.
             MacOSFontEntry* fontEntry =
-                new MacOSFontEntry(fe->Name(), fe->mWeight, true,
+                new MacOSFontEntry(fe->Name(), fe->Weight(), true,
                                    static_cast<const MacOSFontEntry*>(fe)->
                                        mSizeHint);
             familyEntry->AddFontEntry(fontEntry);
             familyEntry->SetHasStyles(true);
             mFontFamilies.Put(key, familyEntry);
             LOG_FONTLIST(("(fontlist-singleface) added new family: %s, key: %s\n",
                           NS_ConvertUTF16toUTF8(familyName).get(),
                           NS_ConvertUTF16toUTF8(key).get()));
@@ -1528,60 +1540,55 @@ gfxMacPlatformFontList::AppleWeightToCSS
         aAppleWeight = 1;
     else if (aAppleWeight > kAppleMaxWeight)
         aAppleWeight = kAppleMaxWeight;
     return gAppleWeightToCSSWeight[aAppleWeight];
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::LookupLocalFont(const nsAString& aFontName,
-                                        FontWeight aWeight,
-                                        FontStretch aStretch,
-                                        FontSlantStyle aStyle)
+                                        WeightRange aWeightForEntry,
+                                        StretchRange aStretchForEntry,
+                                        SlantStyleRange aStyleForEntry)
 {
     nsAutoreleasePool localPool;
 
     NSString *faceName = GetNSStringForString(aFontName);
     MacOSFontEntry *newFontEntry;
 
     // lookup face based on postscript or full name
     CGFontRef fontRef = ::CGFontCreateWithFontName(CFStringRef(faceName));
     if (!fontRef) {
         return nullptr;
     }
 
-    MOZ_ASSERT(aWeight >= FontWeight(100) && aWeight <= FontWeight(900),
-               "bogus font weight value!");
-
     newFontEntry =
-        new MacOSFontEntry(aFontName, fontRef, aWeight, aStretch, aStyle,
+        new MacOSFontEntry(aFontName, fontRef, aWeightForEntry,
+                           aStretchForEntry, aStyleForEntry,
                            false, true);
     ::CFRelease(fontRef);
 
     return newFontEntry;
 }
 
 static void ReleaseData(void *info, const void *data, size_t size)
 {
     free((void*)data);
 }
 
 gfxFontEntry*
 gfxMacPlatformFontList::MakePlatformFont(const nsAString& aFontName,
-                                         FontWeight aWeight,
-                                         FontStretch aStretch,
-                                         FontSlantStyle aStyle,
+                                         WeightRange aWeightForEntry,
+                                         StretchRange aStretchForEntry,
+                                         SlantStyleRange aStyleForEntry,