Merge autoland to mozilla-central. a=merge default tip
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 19 Jun 2019 18:49:12 +0300
changeset 479203 10e3ed789e2b5736f5e85b825c8f2e51a9556db4
parent 479176 8bce401b8833e8a2b4d187c507c075079275d8ca (current diff)
parent 479202 5fc7054fc05d67438c05a9adab43c6a7a217a07e (diff)
push id113467
push userrgurzau@mozilla.com
push dateWed, 19 Jun 2019 15:55:23 +0000
treeherdermozilla-inbound@10e3ed789e2b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone69.0a1
Merge autoland to mozilla-central. a=merge
testing/raptor/raptor/tests/raptor-assorted-dom.ini
testing/raptor/raptor/tests/raptor-motionmark-animometer.ini
testing/raptor/raptor/tests/raptor-motionmark-htmlsuite.ini
testing/raptor/raptor/tests/raptor-scn-power-idle.ini
testing/raptor/raptor/tests/raptor-speedometer.ini
testing/raptor/raptor/tests/raptor-stylebench.ini
testing/raptor/raptor/tests/raptor-sunspider.ini
testing/raptor/raptor/tests/raptor-tp6-1.ini
testing/raptor/raptor/tests/raptor-tp6-10.ini
testing/raptor/raptor/tests/raptor-tp6-2.ini
testing/raptor/raptor/tests/raptor-tp6-3.ini
testing/raptor/raptor/tests/raptor-tp6-4.ini
testing/raptor/raptor/tests/raptor-tp6-5.ini
testing/raptor/raptor/tests/raptor-tp6-6.ini
testing/raptor/raptor/tests/raptor-tp6-7.ini
testing/raptor/raptor/tests/raptor-tp6-8.ini
testing/raptor/raptor/tests/raptor-tp6-9.ini
testing/raptor/raptor/tests/raptor-tp6-binast-1.ini
testing/raptor/raptor/tests/raptor-tp6-cold-1.ini
testing/raptor/raptor/tests/raptor-tp6-cold-2.ini
testing/raptor/raptor/tests/raptor-tp6-cold-3.ini
testing/raptor/raptor/tests/raptor-tp6-cold-4.ini
testing/raptor/raptor/tests/raptor-tp6m-1-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-1.ini
testing/raptor/raptor/tests/raptor-tp6m-10-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-10.ini
testing/raptor/raptor/tests/raptor-tp6m-2-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-2.ini
testing/raptor/raptor/tests/raptor-tp6m-3-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-3.ini
testing/raptor/raptor/tests/raptor-tp6m-4-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-4.ini
testing/raptor/raptor/tests/raptor-tp6m-5-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-5.ini
testing/raptor/raptor/tests/raptor-tp6m-6-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-6.ini
testing/raptor/raptor/tests/raptor-tp6m-7-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-7.ini
testing/raptor/raptor/tests/raptor-tp6m-8-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-8.ini
testing/raptor/raptor/tests/raptor-tp6m-9-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-9.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-1-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-1.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-10-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-10.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-11-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-11.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-12-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-12.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-13-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-13.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-14-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-14.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-2-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-2.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-3-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-3.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-4-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-4.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-5-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-5.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-6-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-6.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-7-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-7.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-8-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-8.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-9-fennec64.ini
testing/raptor/raptor/tests/raptor-tp6m-cold-9.ini
testing/raptor/raptor/tests/raptor-unity-webgl.ini
testing/raptor/raptor/tests/raptor-wasm-godot-baseline.ini
testing/raptor/raptor/tests/raptor-wasm-godot-cranelift.ini
testing/raptor/raptor/tests/raptor-wasm-godot-ion.ini
testing/raptor/raptor/tests/raptor-wasm-godot.ini
testing/raptor/raptor/tests/raptor-wasm-misc-baseline.ini
testing/raptor/raptor/tests/raptor-wasm-misc-cranelift.ini
testing/raptor/raptor/tests/raptor-wasm-misc-ion.ini
testing/raptor/raptor/tests/raptor-wasm-misc.ini
testing/raptor/raptor/tests/raptor-webaudio.ini
testing/raptor/raptor/tests/raptor-youtube-playback.ini
--- a/.hgignore
+++ b/.hgignore
@@ -185,16 +185,16 @@ tps_result\.json
 ^infer-out/
 
 # https://bz.mercurial-scm.org/show_bug.cgi?id=5322
 ^comm/
 
 # Ignore various raptor performance framework files
 ^testing/raptor/.raptor-venv
 ^testing/raptor/raptor-venv
-^testing/raptor/raptor/tests/.*.json
+^testing/raptor/raptor/tests/json/
 ^testing/raptor/webext/raptor/auto_gen_test_config.js
 
 # Ignore browsertime output directory
 ^browsertime-results
 
 # Ignore the build directories of WebRender standalone builds.
 gfx/wr/target/
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2423,16 +2423,19 @@ function focusAndSelectUrlBar(userInitia
   gURLBar.userInitiatedFocus = userInitiatedFocus;
   gURLBar.select();
   gURLBar.userInitiatedFocus = false;
 }
 
 function openLocation() {
   if (window.location.href == AppConstants.BROWSER_CHROME_URL) {
     focusAndSelectUrlBar(true);
+    if (gURLBar.openViewOnFocus && !gURLBar.view.isOpen) {
+      gURLBar.startQuery();
+    }
     return;
   }
 
   // If there's an open browser window, redirect the command there.
   let win = getTopWin();
   if (win) {
     win.focus();
     win.openLocation();
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1172,19 +1172,17 @@ window._gBrowser = {
       }
     }
 
     // Focus the location bar if it was previously focused for that tab.
     // In full screen mode, only bother making the location bar visible
     // if the tab is a blank one.
     if (newBrowser._urlbarFocused && gURLBar) {
       // Explicitly close the popup if the URL bar retains focus
-      if (!gURLBar.openViewOnFocus) {
-        gURLBar.closePopup();
-      }
+      gURLBar.closePopup();
 
       // If the user happened to type into the URL bar for this browser
       // by the time we got here, focusing will cause the text to be
       // selected which could cause them to overwrite what they've
       // already typed in.
       if (gURLBar.focused && newBrowser.userTypedValue) {
         return;
       }
--- a/browser/components/urlbar/UrlbarInput.jsm
+++ b/browser/components/urlbar/UrlbarInput.jsm
@@ -151,25 +151,35 @@ class UrlbarInput {
 
     // The event bufferer can be used to defer events that may affect users
     // muscle memory; for example quickly pressing DOWN+ENTER should end up
     // on a predictable result, regardless of the search status. The event
     // bufferer will invoke the handling code at the right time.
     this.eventBufferer = new UrlbarEventBufferer(this);
 
     this._inputFieldEvents = [
-      "blur", "focus", "input", "keydown", "keyup", "mouseover", "paste",
-      "scrollend", "select", "overflow", "underflow", "dragstart", "dragover",
-      "drop", "compositionstart", "compositionend",
+      "compositionstart", "compositionend",
+      "dragover", "dragstart", "drop",
+      "focus", "blur",
+      "input",
+      "keydown", "keyup",
+      "mousedown", "mouseover",
+      "overflow", "underflow",
+      "paste",
+      "scrollend",
+      "select",
     ];
     for (let name of this._inputFieldEvents) {
       this.inputField.addEventListener(name, this);
     }
 
+    // This is needed for the dropmarker. Once we remove that (i.e. make
+    // openViewOnFocus = true the default), this won't be needed anymore.
     this.addEventListener("mousedown", this);
+
     this.view.panel.addEventListener("popupshowing", this);
     this.view.panel.addEventListener("popuphidden", this);
 
     this._copyCutController = new CopyCutController(this);
     this.inputField.controllers.insertControllerAt(0, this._copyCutController);
 
     this._initPasteAndGo();
 
@@ -1285,40 +1295,40 @@ class UrlbarInput {
   _on_focus(event) {
     this._updateUrlTooltip();
     this.formatValue();
 
     // Hide popup notifications, to reduce visual noise.
     if (this.getAttribute("pageproxystate") != "valid") {
       this.window.UpdatePopupNotificationsVisibility();
     }
-
-    if (this.openViewOnFocus) {
-      this.startQuery();
-    }
   }
 
   _on_mouseover(event) {
     this._updateUrlTooltip();
   }
 
   _on_mousedown(event) {
-    if ((event.target == this.inputField ||
-         // Can be removed after bug 1513337:
-         event.originalTarget.classList.contains("anonymous-div")) &&
-        event.button == 0 &&
-        event.detail == 2 &&
-        UrlbarPrefs.get("doubleClickSelectsAll")) {
-      this.editor.selectAll();
-      event.preventDefault();
+    // We only care about left clicks here.
+    if (event.button != 0) {
       return;
     }
 
-    if (event.originalTarget.classList.contains("urlbar-history-dropmarker") &&
-        event.button == 0) {
+    if (event.currentTarget == this.inputField) {
+      if (event.detail == 2 &&
+          UrlbarPrefs.get("doubleClickSelectsAll")) {
+        this.editor.selectAll();
+        event.preventDefault();
+      } else if (this.openViewOnFocus && !this.view.isOpen) {
+        this.startQuery();
+      }
+      return;
+    }
+
+    if (event.originalTarget.classList.contains("urlbar-history-dropmarker")) {
       if (this.view.isOpen) {
         this.view.close();
       } else {
         this.startQuery();
       }
     }
   }
 
@@ -1455,19 +1465,16 @@ class UrlbarInput {
 
   _on_scrollend(event) {
     this._updateTextOverflow();
   }
 
   _on_TabSelect(event) {
     this._resetSearchState();
     this.controller.viewContextChanged();
-    if (this.focused && this.openViewOnFocus) {
-      this.startQuery();
-    }
   }
 
   _on_keydown(event) {
     // Due to event deferring, it's possible preventDefault() won't be invoked
     // soon enough to actually prevent some of the default behaviors, thus we
     // have to handle the event "twice". This first immediate call passes false
     // as second argument so that handleKeyNavigation will only simulate the
     // event handling, without actually executing actions.
--- a/build/docs/sparse.rst
+++ b/build/docs/sparse.rst
@@ -95,17 +95,17 @@ automation) has support for sparse check
 
 TaskGraph tasks using ``run-task`` can specify a ``sparse-profile``
 attribute in YAML (or in code) to denote the sparse profile file to
 use. e.g.::
 
    run:
        using: run-command
        command: <command>
-       sparse-profile: tasgraph
+       sparse-profile: taskgraph
 
 This automagically results in ``run-task`` and ``hg robustcheckout``
 using the sparse profile defined in ``build/sparse-profiles/<value>``.
 
 Pros and Cons of Sparse Checkouts
 =================================
 
 The benefits of sparse checkout are that it makes the repository appear
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1674,25 +1674,23 @@ CanvasRenderingContext2D::GetSurfaceSnap
     if (!EnsureTarget()) {
       MOZ_ASSERT(
           mTarget == sErrorTarget,
           "On EnsureTarget failure mTarget should be set to sErrorTarget.");
       return mTarget->Snapshot();
     }
   }
 
+  // The concept of BorrowSnapshot seems a bit broken here, but the original
+  // code in GetSurfaceSnapshot just returned a snapshot from mTarget, which
+  // amounts to breaking the concept implicitly.
   RefPtr<SourceSurface> snapshot = mBufferProvider->BorrowSnapshot();
-  if (!snapshot) {
-    return nullptr;
-  }
-
-  RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
+  RefPtr<SourceSurface> retSurface = snapshot;
   mBufferProvider->ReturnSnapshot(snapshot.forget());
-
-  return dataSurface.forget();
+  return retSurface.forget();
 }
 
 SurfaceFormat CanvasRenderingContext2D::GetSurfaceFormat() const {
   return mOpaque ? SurfaceFormat::B8G8R8X8 : SurfaceFormat::B8G8R8A8;
 }
 
 //
 // state
--- a/dom/grid/Grid.cpp
+++ b/dom/grid/Grid.cpp
@@ -29,39 +29,39 @@ Grid::Grid(nsISupports* aParent, nsGridC
   MOZ_ASSERT(aFrame,
              "Should never be instantiated with a null nsGridContainerFrame");
 
   // Construct areas first, because lines may need to reference them
   // to extract additional names for boundary lines.
 
   // Add implicit areas first. Track the names that we add here, because
   // we will ignore future explicit areas with the same name.
-  nsTHashtable<nsCStringHashKey> namesSeen;
+  nsTHashtable<nsRefPtrHashKey<nsAtom>> namesSeen;
   nsGridContainerFrame::ImplicitNamedAreas* implicitAreas =
       aFrame->GetImplicitNamedAreas();
   if (implicitAreas) {
-    for (auto iter = implicitAreas->Iter(); !iter.Done(); iter.Next()) {
-      auto& areaInfo = iter.Data();
-      namesSeen.PutEntry(areaInfo.name.AsString());
-      GridArea* area = new GridArea(
-          this, areaInfo.name.AsString(), GridDeclaration::Implicit,
-          areaInfo.rows.start, areaInfo.rows.end, areaInfo.columns.start,
-          areaInfo.columns.end);
+    for (auto iter = implicitAreas->iter(); !iter.done(); iter.next()) {
+      auto& areaInfo = iter.get().value();
+      namesSeen.PutEntry(areaInfo.name.AsAtom());
+      GridArea* area =
+          new GridArea(this, areaInfo.name.AsAtom(), GridDeclaration::Implicit,
+                       areaInfo.rows.start, areaInfo.rows.end,
+                       areaInfo.columns.start, areaInfo.columns.end);
       mAreas.AppendElement(area);
     }
   }
 
   // Add explicit areas next, as long as they don't have the same name
   // as the implicit areas, because the implicit values override what was
   // initially available in the explicit areas.
   if (auto* explicitAreas = aFrame->GetExplicitNamedAreas()) {
     for (auto& areaInfo : explicitAreas->AsSpan()) {
-      if (!namesSeen.Contains(areaInfo.name.AsString())) {
+      if (!namesSeen.Contains(areaInfo.name.AsAtom())) {
         GridArea* area = new GridArea(
-            this, areaInfo.name.AsString(), GridDeclaration::Explicit,
+            this, areaInfo.name.AsAtom(), GridDeclaration::Explicit,
             areaInfo.rows.start, areaInfo.rows.end, areaInfo.columns.start,
             areaInfo.columns.end);
         mAreas.AppendElement(area);
       }
     }
   }
 
   // Now construct the tracks and lines.
--- a/dom/grid/GridArea.cpp
+++ b/dom/grid/GridArea.cpp
@@ -14,39 +14,35 @@ namespace dom {
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(GridArea, mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(GridArea)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(GridArea)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GridArea)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-GridArea::GridArea(Grid* aParent, const nsDependentCSubstring& aName,
-                   GridDeclaration aType, uint32_t aRowStart, uint32_t aRowEnd,
-                   uint32_t aColumnStart, uint32_t aColumnEnd)
+GridArea::GridArea(Grid* aParent, nsAtom* aName, GridDeclaration aType,
+                   uint32_t aRowStart, uint32_t aRowEnd, uint32_t aColumnStart,
+                   uint32_t aColumnEnd)
     : mParent(aParent),
       mName(aName),
       mType(aType),
       mRowStart(aRowStart),
       mRowEnd(aRowEnd),
       mColumnStart(aColumnStart),
       mColumnEnd(aColumnEnd) {}
 
 GridArea::~GridArea() {}
 
 JSObject* GridArea::WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) {
   return GridArea_Binding::Wrap(aCx, this, aGivenProto);
 }
 
-void GridArea::GetName(nsCString& aName) const { aName = mName; }
-
-void GridArea::GetName(nsString& aName) const {
-  aName = NS_ConvertUTF8toUTF16(mName);
-}
+void GridArea::GetName(nsString& aName) const { mName->ToString(aName); }
 
 GridDeclaration GridArea::Type() const { return mType; }
 
 uint32_t GridArea::RowStart() const { return mRowStart; }
 
 uint32_t GridArea::RowEnd() const { return mRowEnd; }
 
 uint32_t GridArea::ColumnStart() const { return mColumnStart; }
--- a/dom/grid/GridArea.h
+++ b/dom/grid/GridArea.h
@@ -12,42 +12,41 @@
 
 namespace mozilla {
 namespace dom {
 
 class Grid;
 
 class GridArea : public nsISupports, public nsWrapperCache {
  public:
-  explicit GridArea(Grid* aParent, const nsDependentCSubstring& aName,
-                    GridDeclaration aType, uint32_t aRowStart, uint32_t aRowEnd,
-                    uint32_t aColumnStart, uint32_t aColumnEnd);
+  explicit GridArea(Grid* aParent, nsAtom* aName, GridDeclaration aType,
+                    uint32_t aRowStart, uint32_t aRowEnd, uint32_t aColumnStart,
+                    uint32_t aColumnEnd);
 
  protected:
   virtual ~GridArea();
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridArea)
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   Grid* GetParentObject() { return mParent; }
 
-  void GetName(nsCString& aName) const;
   void GetName(nsString& aName) const;
   GridDeclaration Type() const;
   uint32_t RowStart() const;
   uint32_t RowEnd() const;
   uint32_t ColumnStart() const;
   uint32_t ColumnEnd() const;
 
  protected:
   RefPtr<Grid> mParent;
-  const nsCString mName;
+  const RefPtr<nsAtom> mName;
   const GridDeclaration mType;
   const uint32_t mRowStart;
   const uint32_t mRowEnd;
   const uint32_t mColumnStart;
   const uint32_t mColumnEnd;
 };
 
 }  // namespace dom
--- a/dom/grid/GridLine.cpp
+++ b/dom/grid/GridLine.cpp
@@ -27,21 +27,20 @@ GridLine::GridLine(GridLines* aParent)
       mType(GridDeclaration::Implicit),
       mNumber(0),
       mNegativeNumber(0) {
   MOZ_ASSERT(aParent, "Should never be instantiated with a null GridLines");
 }
 
 GridLine::~GridLine() {}
 
-void GridLine::GetNames(nsTArray<nsCString>& aNames) const { aNames = mNames; }
 void GridLine::GetNames(nsTArray<nsString>& aNames) const {
   aNames.SetCapacity(mNames.Length());
   for (auto& name : mNames) {
-    aNames.AppendElement(NS_ConvertUTF8toUTF16(name));
+    aNames.AppendElement(nsDependentAtomString(name));
   }
 }
 
 JSObject* GridLine::WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) {
   return GridLine_Binding::Wrap(aCx, this, aGivenProto);
 }
 
@@ -50,25 +49,25 @@ double GridLine::Start() const { return 
 double GridLine::Breadth() const { return mBreadth; }
 
 GridDeclaration GridLine::Type() const { return mType; }
 
 uint32_t GridLine::Number() const { return mNumber; }
 
 int32_t GridLine::NegativeNumber() const { return mNegativeNumber; }
 
-void GridLine::SetLineValues(const nsTArray<nsCString>& aNames, double aStart,
-                             double aBreadth, uint32_t aNumber,
+void GridLine::SetLineValues(const nsTArray<RefPtr<nsAtom>>& aNames,
+                             double aStart, double aBreadth, uint32_t aNumber,
                              int32_t aNegativeNumber, GridDeclaration aType) {
   mNames = aNames;
   mStart = aStart;
   mBreadth = aBreadth;
   mNumber = aNumber;
   mNegativeNumber = aNegativeNumber;
   mType = aType;
 }
 
-void GridLine::SetLineNames(const nsTArray<nsCString>& aNames) {
+void GridLine::SetLineNames(const nsTArray<RefPtr<nsAtom>>& aNames) {
   mNames = aNames;
 }
 
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/grid/GridLine.h
+++ b/dom/grid/GridLine.h
@@ -23,38 +23,38 @@ class GridLine : public nsISupports, pub
 
  protected:
   virtual ~GridLine();
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GridLine)
 
-  void GetNames(nsTArray<nsCString>& aNames) const;
   void GetNames(nsTArray<nsString>& aNames) const;
+  const nsTArray<RefPtr<nsAtom>>& Names() const { return mNames; }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
   GridLines* GetParentObject() { return mParent; }
 
   double Start() const;
   double Breadth() const;
   GridDeclaration Type() const;
   uint32_t Number() const;
   int32_t NegativeNumber() const;
 
-  void SetLineValues(const nsTArray<nsCString>& aNames, double aStart,
+  void SetLineValues(const nsTArray<RefPtr<nsAtom>>& aNames, double aStart,
                      double aBreadth, uint32_t aNumber, int32_t aNegativeNumber,
                      GridDeclaration aType);
 
-  void SetLineNames(const nsTArray<nsCString>& aNames);
+  void SetLineNames(const nsTArray<RefPtr<nsAtom>>& aNames);
 
  protected:
   RefPtr<GridLines> mParent;
-  nsTArray<nsCString> mNames;
+  nsTArray<RefPtr<nsAtom>> mNames;
   double mStart;
   double mBreadth;
   GridDeclaration mType;
   uint32_t mNumber;
   int32_t mNegativeNumber;
 };
 
 }  // namespace dom
--- a/dom/grid/GridLines.cpp
+++ b/dom/grid/GridLines.cpp
@@ -43,25 +43,25 @@ GridLine* GridLines::Item(uint32_t aInde
 GridLine* GridLines::IndexedGetter(uint32_t aIndex, bool& aFound) {
   aFound = aIndex < mLines.Length();
   if (!aFound) {
     return nullptr;
   }
   return mLines[aIndex];
 }
 
-static void AddLineNameIfNotPresent(nsTArray<nsCString>& aLineNames,
-                                    const nsCString& aName) {
+static void AddLineNameIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
+                                    nsAtom* aName) {
   if (!aLineNames.Contains(aName)) {
     aLineNames.AppendElement(aName);
   }
 }
 
-static void AddLineNamesIfNotPresent(nsTArray<nsCString>& aLineNames,
-                                     const nsTArray<nsCString>& aNames) {
+static void AddLineNamesIfNotPresent(nsTArray<RefPtr<nsAtom>>& aLineNames,
+                                     const nsTArray<RefPtr<nsAtom>>& aNames) {
   for (const auto& name : aNames) {
     AddLineNameIfNotPresent(aLineNames, name);
   }
 }
 
 void GridLines::SetLineInfo(const ComputedGridTrackInfo* aTrackInfo,
                             const ComputedGridLineInfo* aLineInfo,
                             const nsTArray<RefPtr<GridArea>>& aAreas,
@@ -112,34 +112,34 @@ void GridLines::SetLineInfo(const Comput
 
       // Get the line names for the current line. aLineInfo->mNames
       // may contain duplicate names. This is intentional, since grid
       // layout works fine with duplicate names, and we don't want to
       // detect and remove duplicates in layout since it is an O(n^2)
       // problem. We do the work here since this is only run when
       // requested by devtools, and slowness here will not affect
       // normal browsing.
-      const nsTArray<nsCString>& possiblyDuplicateLineNames(
-          aLineInfo->mNames.SafeElementAt(i, nsTArray<nsCString>()));
+      const nsTArray<RefPtr<nsAtom>>& possiblyDuplicateLineNames(
+          aLineInfo->mNames.SafeElementAt(i, nsTArray<RefPtr<nsAtom>>()));
 
-      nsTArray<nsCString> lineNames;
+      nsTArray<RefPtr<nsAtom>> lineNames;
       AddLineNamesIfNotPresent(lineNames, possiblyDuplicateLineNames);
 
       // Add in names from grid areas where this line is used as a boundary.
       for (auto area : aAreas) {
         // We specifically ignore line names from implicitly named areas,
         // because it can be confusing for designers who might naturally use
         // a named line of "-start" or "-end" and create an implicit named
         // area without meaning to.
         if (area->Type() == GridDeclaration::Implicit) {
           continue;
         }
 
         bool haveNameToAdd = false;
-        nsAutoCString nameToAdd;
+        nsAutoString nameToAdd;
         area->GetName(nameToAdd);
         if (aIsRow) {
           if (area->RowStart() == line1Index) {
             haveNameToAdd = true;
             nameToAdd.AppendLiteral("-start");
           } else if (area->RowEnd() == line1Index) {
             haveNameToAdd = true;
             nameToAdd.AppendLiteral("-end");
@@ -150,17 +150,18 @@ void GridLines::SetLineInfo(const Comput
             nameToAdd.AppendLiteral("-start");
           } else if (area->ColumnEnd() == line1Index) {
             haveNameToAdd = true;
             nameToAdd.AppendLiteral("-end");
           }
         }
 
         if (haveNameToAdd) {
-          AddLineNameIfNotPresent(lineNames, nameToAdd);
+          RefPtr<nsAtom> name = NS_Atomize(nameToAdd);
+          AddLineNameIfNotPresent(lineNames, name);
         }
       }
 
       if (i >= (aTrackInfo->mRepeatFirstTrack +
                 aTrackInfo->mNumLeadingImplicitTracks) &&
           repeatIndex < numRepeatTracks) {
         numAddedLines += AppendRemovedAutoFits(
             aTrackInfo, aLineInfo, lastTrackEdge, repeatIndex, numRepeatTracks,
@@ -245,71 +246,70 @@ void GridLines::SetLineInfo(const Comput
 
         // If both start and end indexes are -1, then stop here since we cannot
         // reason about the naming for either lines.
         if (startIndex < 0 && endIndex < 0) {
           break;
         }
 
         // Get the "-start" and "-end" line names of the grid area.
-        nsAutoCString startLineName;
+        nsAutoString startLineName;
         area->GetName(startLineName);
         startLineName.AppendLiteral("-start");
-        nsAutoCString endLineName;
+        nsAutoString endLineName;
         area->GetName(endLineName);
         endLineName.AppendLiteral("-end");
 
         // Get the list of existing line names for the start and end of the grid
         // area. In the case where one of the start or end indexes are -1, use a
         // dummy line as a substitute for the start/end line.
         RefPtr<GridLine> dummyLine = new GridLine(this);
         RefPtr<GridLine> areaStartLine =
             startIndex > -1 ? mLines[startIndex] : dummyLine;
-        nsTArray<nsCString> startLineNames;
-        areaStartLine->GetNames(startLineNames);
+        nsTArray<RefPtr<nsAtom>> startLineNames(areaStartLine->Names());
 
         RefPtr<GridLine> areaEndLine =
             endIndex > -1 ? mLines[endIndex] : dummyLine;
-        nsTArray<nsCString> endLineNames;
-        areaEndLine->GetNames(endLineNames);
+        nsTArray<RefPtr<nsAtom>> endLineNames(areaEndLine->Names());
 
-        if (startLineNames.Contains(endLineName) ||
-            endLineNames.Contains(startLineName)) {
+        RefPtr<nsAtom> start = NS_Atomize(startLineName);
+        RefPtr<nsAtom> end = NS_Atomize(endLineName);
+        if (startLineNames.Contains(end) || endLineNames.Contains(start)) {
           // Add the reversed line names.
-          AddLineNameIfNotPresent(startLineNames, endLineName);
-          AddLineNameIfNotPresent(endLineNames, startLineName);
+          AddLineNameIfNotPresent(startLineNames, end);
+          AddLineNameIfNotPresent(endLineNames, start);
         } else {
           // Add the normal line names.
-          AddLineNameIfNotPresent(startLineNames, startLineName);
-          AddLineNameIfNotPresent(endLineNames, endLineName);
+          AddLineNameIfNotPresent(startLineNames, start);
+          AddLineNameIfNotPresent(endLineNames, end);
         }
 
         areaStartLine->SetLineNames(startLineNames);
         areaEndLine->SetLineNames(endLineNames);
       }
     }
   }
 }
 
 uint32_t GridLines::AppendRemovedAutoFits(
     const ComputedGridTrackInfo* aTrackInfo,
     const ComputedGridLineInfo* aLineInfo, nscoord aLastTrackEdge,
     uint32_t& aRepeatIndex, uint32_t aNumRepeatTracks,
-    uint32_t aNumLeadingTracks, nsTArray<nsCString>& aLineNames) {
+    uint32_t aNumLeadingTracks, nsTArray<RefPtr<nsAtom>>& aLineNames) {
   // Check to see if lineNames contains ALL of the before line names.
   bool alreadyHasBeforeLineNames = true;
   for (const auto& beforeName : aLineInfo->mNamesBefore) {
     if (!aLineNames.Contains(beforeName)) {
       alreadyHasBeforeLineNames = false;
       break;
     }
   }
 
   bool extractedExplicitLineNames = false;
-  nsTArray<nsCString> explicitLineNames;
+  nsTArray<RefPtr<nsAtom>> explicitLineNames;
   uint32_t linesAdded = 0;
   while (aRepeatIndex < aNumRepeatTracks &&
          aTrackInfo->mRemovedRepeatTracks[aRepeatIndex]) {
     // If this is not the very first call to this function, and if we
     // haven't already added a line this call, pull all the explicit
     // names to pass along to the next line that will be added after
     // this function completes.
     if (aRepeatIndex > 0 && linesAdded == 0) {
--- a/dom/grid/GridLines.h
+++ b/dom/grid/GridLines.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_dom_GridLines_h
 #define mozilla_dom_GridLines_h
 
 #include "nsCoord.h"
 #include "nsTArray.h"
 #include "nsWrapperCache.h"
 
+class nsAtom;
+
 namespace mozilla {
 
 struct ComputedGridTrackInfo;
 struct ComputedGridLineInfo;
 
 namespace dom {
 
 class GridDimension;
@@ -46,17 +48,17 @@ class GridLines : public nsISupports, pu
                    const nsTArray<RefPtr<GridArea>>& aAreas, bool aIsRow);
 
  protected:
   uint32_t AppendRemovedAutoFits(const ComputedGridTrackInfo* aTrackInfo,
                                  const ComputedGridLineInfo* aLineInfo,
                                  nscoord aLastTrackEdge, uint32_t& aRepeatIndex,
                                  uint32_t aNumRepeatTracks,
                                  uint32_t aNumLeadingTracks,
-                                 nsTArray<nsCString>& aLineNames);
+                                 nsTArray<RefPtr<nsAtom>>& aLineNames);
 
   RefPtr<GridDimension> mParent;
   nsTArray<RefPtr<GridLine>> mLines;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
--- a/gfx/wr/webrender/src/display_list_flattener.rs
+++ b/gfx/wr/webrender/src/display_list_flattener.rs
@@ -408,21 +408,78 @@ impl<'a> DisplayListFlattener<'a> {
         // a small number of items in the root stacking context, since
         // most of the content is embedded in its own picture.
         //
 
         // Find the first primitive which has the desired scroll root.
         let mut first_index = None;
         let mut main_scroll_root = None;
 
+        // If the main primitive list that we split for picture caching has
+        // clip chain instances at the top level, it's possible that we will
+        // create a split resulting in one list having unmatched PushClipChain
+        // or PopClipChain instances. This causes panics in pop_surface(),
+        // which asserts that the clip chain instances are matched.
+
+        // The code below is a (very inelegant) solution to that. It records
+        // clip chain instances found during the initial scan for scroll roots.
+        // Later, it uses this information to fix up unopened or unclosed clip
+        // instances on the split primitive lists.
+
+        // This is far from ideal - but it fixes the crash for now. In future
+        // we will be simplifying and/or removing how clip chain instances
+        // and picture cache splitting works, so we can tidy this up as
+        // part of those future changes.
+
+        let mut clip_chain_instances = Vec::new();
+        let mut clip_chain_instance_stack = Vec::new();
+
+        /// Records the indices in the list of a push/pop clip chain instance pair.
+        #[derive(Debug)]
+        struct ClipChainPairInfo {
+            push_index: usize,
+            pop_index: usize,
+            spatial_node_index: SpatialNodeIndex,
+            clip_chain_id: ClipChainId,
+        }
+
         for (i, instance) in primitives.iter().enumerate() {
             let scroll_root = self.clip_scroll_tree.find_scroll_root(
                 instance.spatial_node_index,
             );
 
+            // If we encounter a push/pop clip, record where they occurred in the
+            // primitive list for later processing.
+            match instance.kind {
+                PrimitiveInstanceKind::PushClipChain => {
+                    clip_chain_instance_stack.push(clip_chain_instances.len());
+                    clip_chain_instances.push(ClipChainPairInfo {
+                        push_index: i,
+                        pop_index: usize::MAX,
+                        spatial_node_index: instance.spatial_node_index,
+                        clip_chain_id: instance.clip_chain_id,
+                    });
+                }
+                PrimitiveInstanceKind::PopClipChain => {
+                    let index = clip_chain_instance_stack.pop().unwrap();
+                    let clip_chain_instance = &mut clip_chain_instances[index];
+                    debug_assert_eq!(clip_chain_instance.pop_index, usize::MAX);
+                    debug_assert_eq!(
+                        clip_chain_instance.clip_chain_id,
+                        instance.clip_chain_id,
+                    );
+                    debug_assert_eq!(
+                        clip_chain_instance.spatial_node_index,
+                        instance.spatial_node_index,
+                    );
+                    clip_chain_instance.pop_index = i;
+                }
+                _ => {}
+            }
+
             if scroll_root != ROOT_SPATIAL_NODE_INDEX {
                 // If we find multiple scroll roots in this page, then skip
                 // picture caching for now. In future, we can handle picture
                 // caching on these sites by creating a tile cache per
                 // scroll root, or (more likely) selecting the common parent
                 // scroll root between the detected scroll roots.
                 match main_scroll_root {
                     Some(main_scroll_root) => {
@@ -449,19 +506,19 @@ impl<'a> DisplayListFlattener<'a> {
         // Get the list of existing primitives in the main stacking context.
         let mut old_prim_list = primitives.take();
 
         // In the simple case, there are no preceding or trailing primitives,
         // because everything is anchored to the root scroll node. Handle
         // this case specially to avoid underflow error in the Some(..)
         // path below.
 
-        let preceding_prims;
+        let mut preceding_prims;
         let mut remaining_prims;
-        let trailing_prims;
+        let mut trailing_prims;
 
         match first_index {
             Some(first_index) => {
                 // Split off the preceding primtives.
                 remaining_prims = old_prim_list.split_off(first_index);
 
                 // Find the first primitive in reverse order that is not the root scroll node.
                 let last_index = remaining_prims.iter().rposition(|instance| {
@@ -477,16 +534,146 @@ impl<'a> DisplayListFlattener<'a> {
             }
             None => {
                 preceding_prims = Vec::new();
                 remaining_prims = old_prim_list;
                 trailing_prims = Vec::new();
             }
         }
 
+        let mid_index = preceding_prims.len();
+        let post_index = mid_index + remaining_prims.len();
+
+        #[derive(Debug, Copy, Clone)]
+        enum ClipLocation {
+            Pre,        // The prims preceding the picture cache content slice.
+            Mid,        // Prims in the content / cache slice.
+            Post,       // Prims trailing the cache slice.
+        }
+
+        // Step through each clip chain pair, and see if it crosses a slice boundary.
+        for clip_chain_instance in clip_chain_instances {
+            // Get the location of the push / pop for this clip chain.
+            let push_location = if clip_chain_instance.push_index < mid_index {
+                ClipLocation::Pre
+            } else if clip_chain_instance.push_index < post_index {
+                ClipLocation::Mid
+            } else {
+                ClipLocation::Post
+            };
+
+            let pop_location = if clip_chain_instance.pop_index < mid_index {
+                ClipLocation::Pre
+            } else if clip_chain_instance.pop_index < post_index {
+                ClipLocation::Mid
+            } else {
+                ClipLocation::Post
+            };
+
+            // Apply fixups for any clip chain instances as required. Although this
+            // code can result in memcpys, it's unlikely to be a problem. The case
+            // itself where this occurs is rare, and the prim lists are typically
+            // quite short here. Nonetheless, we'll want to improve this as part
+            // of the changes to clip chain instances + picture cache slice splitting.
+            match (push_location, pop_location) {
+                (ClipLocation::Pre, ClipLocation::Pre) |
+                (ClipLocation::Mid, ClipLocation::Mid) |
+                (ClipLocation::Post, ClipLocation::Post) => {
+                    // If the clip exists within a slice, no fixup needed. This is the
+                    // common case for clip chain instances at the top level.
+                    continue;
+                }
+                (ClipLocation::Pre, ClipLocation::Post) => {
+                    // Close off the pre list, enclose the cache slice and
+                    // open a clip chain for the trailing prims.
+
+                    preceding_prims.push(
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PopClipChain,
+                        )
+                    );
+
+                    remaining_prims.insert(
+                        0,
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PushClipChain,
+                        )
+                    );
+
+                    remaining_prims.push(
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PopClipChain,
+                        )
+                    );
+
+                    trailing_prims.insert(
+                        0,
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PushClipChain,
+                        )
+                    );
+                }
+                (ClipLocation::Pre, ClipLocation::Mid) => {
+                    // Close off the preceding prims, and open a clip for the
+                    // content cache slice.
+
+                    preceding_prims.push(
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PopClipChain,
+                        )
+                    );
+
+                    remaining_prims.insert(
+                        0,
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PushClipChain,
+                        )
+                    );
+                }
+                (ClipLocation::Mid, ClipLocation::Post) => {
+                    // Close off the cache content slice, and open up a clip for
+                    // the trailing prims.
+
+                    remaining_prims.push(
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PopClipChain,
+                        )
+                    );
+
+                    trailing_prims.insert(
+                        0,
+                        create_clip_prim_instance(
+                            clip_chain_instance.spatial_node_index,
+                            clip_chain_instance.clip_chain_id,
+                            PrimitiveInstanceKind::PushClipChain,
+                        )
+                    );
+                }
+                (ClipLocation::Mid, ClipLocation::Pre) |
+                (ClipLocation::Post, ClipLocation::Pre) |
+                (ClipLocation::Post, ClipLocation::Mid) => {
+                    unreachable!();
+                }
+            }
+        }
+
         let prim_list = PrimitiveList::new(
             remaining_prims,
             &self.interners,
         );
 
         // Now, create a picture with tile caching enabled that will hold all
         // of the primitives selected as belonging to the main scroll root.
         let pic_key = PictureKey::new(
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -936,19 +936,20 @@ class MOZ_STACK_CLASS nsGridContainerFra
    *
    * E.g. to search for "A 2" forward from the start of the grid: aName is "A"
    * aNth is 2 and aFromIndex is zero.  To search for "A -2", aNth is -2 and
    * aFromIndex is ExplicitGridEnd + 1 (which is the line "before" the last
    * line when we're searching in reverse).  For "span A 2", aNth is 2 when
    * used on a grid-[row|column]-end property and -2 for a *-start property,
    * and aFromIndex is the line (which we should skip) on the opposite property.
    */
-  uint32_t FindNamedLine(const nsCString& aName, int32_t* aNth,
-                         uint32_t aFromIndex,
+  uint32_t FindNamedLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
                          const nsTArray<uint32_t>& aImplicitLines) const {
+    MOZ_ASSERT(aName);
+    MOZ_ASSERT(!aName->IsEmpty());
     MOZ_ASSERT(aNth && *aNth != 0);
     if (*aNth > 0) {
       return FindLine(aName, aNth, aFromIndex, aImplicitLines);
     }
     int32_t nth = -*aNth;
     int32_t line = RFindLine(aName, &nth, aFromIndex, aImplicitLines);
     *aNth = -nth;
     return line;
@@ -959,17 +960,17 @@ class MOZ_STACK_CLASS nsGridContainerFra
    * on aSide.  For example, for aName "a" and aSide being an end side, it
    * returns the line numbers which would match "a-end" in the relevant axis.
    * For subgrids it includes searching the relevant axis in all ancestor
    * grids too (within this subgrid's spanned area).  If an ancestor has
    * opposite direction, we switch aSide to the opposite logical side so we
    * match on the same physical side as the original subgrid we're resolving
    * the name for.
    */
-  void FindNamedAreas(const nsACString& aName, LogicalSide aSide,
+  void FindNamedAreas(nsAtom* aName, LogicalSide aSide,
                       nsTArray<uint32_t>& aImplicitLines) const {
     // True if we're currently in a map that has the same direction as 'this'.
     bool sameDirectionAsThis = true;
     uint32_t min = !mParentLineNameMap ? 1 : mClampMinLine;
     uint32_t max = mClampMaxLine;
     for (auto* map = this; true;) {
       uint32_t line = map->FindNamedArea(aName, aSide, min, max);
       if (line > 0) {
@@ -1012,20 +1013,20 @@ class MOZ_STACK_CLASS nsGridContainerFra
       map = parent;
     }
   }
 
   /**
    * Return true if any implicit named areas match aName, in this map or
    * in any of our ancestor maps.
    */
-  bool HasImplicitNamedArea(const nsCString& aName) const {
+  bool HasImplicitNamedArea(nsAtom* aName) const {
     const auto* map = this;
     do {
-      if (map->mAreas && map->mAreas->Contains(aName)) {
+      if (map->mAreas && map->mAreas->has(aName)) {
         return true;
       }
       map = map->mParentLineNameMap;
     } while (map);
     return false;
   }
 
   // The min/max line number (1-based) for clamping.
@@ -1034,17 +1035,17 @@ class MOZ_STACK_CLASS nsGridContainerFra
 
  private:
   // Return true if this map represents a subgridded axis.
   bool IsSubgridded() const { return mParentLineNameMap != nullptr; }
 
   /**
    * @see FindNamedLine, this function searches forward.
    */
-  uint32_t FindLine(const nsCString& aName, int32_t* aNth, uint32_t aFromIndex,
+  uint32_t FindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
                     const nsTArray<uint32_t>& aImplicitLines) const {
     MOZ_ASSERT(aNth && *aNth > 0);
     int32_t nth = *aNth;
     // For a subgrid we need to search to the end of the grid rather than
     // the end of the local name list, since ancestors might match.
     const uint32_t end = IsSubgridded() ? mClampMaxLine : mTemplateLinesEnd;
     uint32_t line;
     uint32_t i = aFromIndex;
@@ -1069,17 +1070,17 @@ class MOZ_STACK_CLASS nsGridContainerFra
     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
     *aNth = nth;
     return 0;
   }
 
   /**
    * @see FindNamedLine, this function searches in reverse.
    */
-  uint32_t RFindLine(const nsCString& aName, int32_t* aNth, uint32_t aFromIndex,
+  uint32_t RFindLine(nsAtom* aName, int32_t* aNth, uint32_t aFromIndex,
                      const nsTArray<uint32_t>& aImplicitLines) const {
     MOZ_ASSERT(aNth && *aNth > 0);
     if (MOZ_UNLIKELY(aFromIndex == 0)) {
       return 0;  // There are no named lines beyond the start of the explicit
                  // grid.
     }
     --aFromIndex;  // (shift aFromIndex so we can treat it as inclusive)
     int32_t nth = *aNth;
@@ -1107,17 +1108,17 @@ class MOZ_STACK_CLASS nsGridContainerFra
       }
     }
     MOZ_ASSERT(nth > 0, "should have returned a valid line above already");
     *aNth = nth;
     return 0;
   }
 
   // Return true if aName exists at aIndex in this map or any parent map.
-  bool Contains(uint32_t aIndex, const nsCString& aName) const {
+  bool Contains(uint32_t aIndex, nsAtom* aName) const {
     const auto* map = this;
     while (true) {
       if (aIndex < map->mTemplateLinesEnd && map->HasNameAt(aIndex, aName)) {
         return true;
       }
       auto* parent = map->mParentLineNameMap;
       if (!parent) {
         return false;
@@ -1126,17 +1127,17 @@ class MOZ_STACK_CLASS nsGridContainerFra
       MOZ_ASSERT(line >= 1, "expected a 1-based line number");
       aIndex = line - 1;
       map = parent;
     }
     MOZ_ASSERT_UNREACHABLE("we always return from inside the loop above");
   }
 
   // Return true if aName exists at aIndex in this map.
-  bool HasNameAt(uint32_t aIndex, const nsCString& aName) const {
+  bool HasNameAt(uint32_t aIndex, nsAtom* aName) const {
     if (!mHasRepeatAuto) {
       return mLineNameLists[aIndex].Contains(aName);
     }
     if (aIndex < mRepeatAutoEnd && aIndex >= mRepeatAutoStart &&
         mRepeatAutoLineNameListBefore.Contains(aName)) {
       return true;
     }
     if (aIndex <= mRepeatAutoEnd && aIndex > mRepeatAutoStart &&
@@ -1162,18 +1163,18 @@ class MOZ_STACK_CLASS nsGridContainerFra
   }
 
   /**
    * Return the 1-based line that match aName in 'grid-template-areas'
    * on the side aSide.  Clamp the result to aMin..aMax but require
    * that some part of the area is inside for it to match.
    * Return zero if there is no match.
    */
-  uint32_t FindNamedArea(const nsACString& aName, LogicalSide aSide,
-                         int32_t aMin, int32_t aMax) const {
+  uint32_t FindNamedArea(nsAtom* aName, LogicalSide aSide, int32_t aMin,
+                         int32_t aMax) const {
     if (const NamedArea* area = FindNamedArea(aName)) {
       int32_t start = IsBlock(aSide) ? area->rows.start : area->columns.start;
       int32_t end = IsBlock(aSide) ? area->rows.end : area->columns.end;
       if (IsStart(aSide)) {
         if (start >= aMin) {
           if (start <= aMax) {
             return start;
           }
@@ -1192,35 +1193,35 @@ class MOZ_STACK_CLASS nsGridContainerFra
     }
     return 0;  // no match
   }
 
   /**
    * A convenience method to lookup a name in 'grid-template-areas'.
    * @return null if not found
    */
-  const NamedArea* FindNamedArea(const nsACString& aName) const {
+  const NamedArea* FindNamedArea(nsAtom* aName) const {
     if (mStylePosition->mGridTemplateAreas.IsNone()) {
       return nullptr;
     }
     const auto areas = mStylePosition->mGridTemplateAreas.AsAreas();
     for (const NamedArea& area : areas->areas.AsSpan()) {
-      if (area.name.AsString() == aName) {
+      if (area.name.AsAtom() == aName) {
         return &area;
       }
     }
     return nullptr;
   }
 
   // Some style data references, for easy access.
   const nsStylePosition* mStylePosition;
   const ImplicitNamedAreas* mAreas;
-  const nsTArray<nsTArray<nsCString>>& mLineNameLists;
-  const nsTArray<nsCString>& mRepeatAutoLineNameListBefore;
-  const nsTArray<nsCString>& mRepeatAutoLineNameListAfter;
+  const nsTArray<nsTArray<RefPtr<nsAtom>>>& mLineNameLists;
+  const nsTArray<RefPtr<nsAtom>>& mRepeatAutoLineNameListBefore;
+  const nsTArray<RefPtr<nsAtom>>& mRepeatAutoLineNameListAfter;
   // The index of the repeat(auto-fill/fit) track, or zero if there is none.
   const uint32_t mRepeatAutoStart;
   // The (hypothetical) index of the last such repeat() track.
   const uint32_t mRepeatAutoEnd;
   // The difference between mTemplateLinesEnd and mLineNameLists.Length().
   const int32_t mRepeatEndDelta;
   // The end of the line name lists with repeat(auto-fill/fit) tracks accounted
   // for.  It is equal to mLineNameLists.Length() when a repeat() track
@@ -2098,23 +2099,23 @@ struct nsGridContainerFrame::Tracks {
   nscoord ResolveSize(const LineRange& aRange) const {
     MOZ_ASSERT(mCanResolveLineRangeSize);
     MOZ_ASSERT(aRange.Extent() > 0, "grid items cover at least one track");
     nscoord pos, size;
     aRange.ToPositionAndLength(mSizes, &pos, &size);
     return size;
   }
 
-  nsTArray<nsCString> GetExplicitLineNamesAtIndex(
+  nsTArray<RefPtr<nsAtom>> GetExplicitLineNamesAtIndex(
       const nsStyleGridTemplate& aGridTemplate,
       const TrackSizingFunctions& aFunctions, uint32_t aIndex) {
-    nsTArray<nsCString> lineNames;
+    nsTArray<RefPtr<nsAtom>> lineNames;
 
     bool hasRepeatAuto = aGridTemplate.HasRepeatAuto();
-    const nsTArray<nsTArray<nsCString>>& lineNameLists =
+    const nsTArray<nsTArray<RefPtr<nsAtom>>>& lineNameLists =
         aGridTemplate.mLineNameLists;
 
     if (!hasRepeatAuto) {
       if (aIndex < lineNameLists.Length()) {
         lineNames.AppendElements(lineNameLists[aIndex]);
       }
     } else {
       const uint32_t repeatTrackCount = aFunctions.NumRepeatTracks();
@@ -2705,38 +2706,37 @@ struct MOZ_STACK_CLASS nsGridContainerFr
    * be 'auto'.
    * @param aChild the grid item
    * @param aStyle the StylePosition() for the grid container
    */
   GridArea PlaceDefinite(nsIFrame* aChild, const LineNameMap& aColLineNameMap,
                          const LineNameMap& aRowLineNameMap,
                          const nsStylePosition* aStyle);
 
-  bool HasImplicitNamedArea(const nsCString& aName) const {
-    return mAreas && mAreas->Contains(aName);
+  bool HasImplicitNamedArea(nsAtom* aName) const {
+    return mAreas && mAreas->has(aName);
   }
 
   // Return true if aString ends in aSuffix and has at least one character
   // before the suffix. Assign aIndex to where the suffix starts.
-  static bool IsNameWithSuffix(const nsCString& aString,
-                               const nsCString& aSuffix,
+  static bool IsNameWithSuffix(nsAtom* aString, const nsString& aSuffix,
                                uint32_t* aIndex) {
-    if (StringEndsWith(aString, aSuffix)) {
-      *aIndex = aString.Length() - aSuffix.Length();
+    if (StringEndsWith(nsDependentAtomString(aString), aSuffix)) {
+      *aIndex = aString->GetLength() - aSuffix.Length();
       return *aIndex != 0;
     }
     return false;
   }
 
-  static bool IsNameWithEndSuffix(const nsCString& aString, uint32_t* aIndex) {
-    return IsNameWithSuffix(aString, NS_LITERAL_CSTRING("-end"), aIndex);
-  }
-
-  static bool IsNameWithStartSuffix(const nsCString& aString, uint32_t* aIndex) {
-    return IsNameWithSuffix(aString, NS_LITERAL_CSTRING("-start"), aIndex);
+  static bool IsNameWithEndSuffix(nsAtom* aString, uint32_t* aIndex) {
+    return IsNameWithSuffix(aString, NS_LITERAL_STRING("-end"), aIndex);
+  }
+
+  static bool IsNameWithStartSuffix(nsAtom* aString, uint32_t* aIndex) {
+    return IsNameWithSuffix(aString, NS_LITERAL_STRING("-start"), aIndex);
   }
 
   // Return the relevant parent LineNameMap for the given subgrid axis aAxis.
   const LineNameMap* ParentLineMapForAxis(bool aIsOrthogonal,
                                           LogicalAxis aAxis) const {
     if (!mParentGrid) {
       return nullptr;
     }
@@ -3323,111 +3323,112 @@ nsContainerFrame* NS_NewGridContainerFra
   nsRect* cb = aChild->GetProperty(GridItemContainingBlockRect());
   MOZ_ASSERT(cb,
              "this method must only be called on grid items, and the grid "
              "container should've reflowed this item by now and set up cb");
   return *cb;
 }
 
 void nsGridContainerFrame::AddImplicitNamedAreas(
-    const nsTArray<nsTArray<nsCString>>& aLineNameLists) {
+    const nsTArray<nsTArray<RefPtr<nsAtom>>>& aLineNameLists) {
   // http://dev.w3.org/csswg/css-grid/#implicit-named-areas
   // Note: recording these names for fast lookup later is just an optimization.
   const uint32_t len =
       std::min(aLineNameLists.Length(), size_t(nsStyleGridLine::kMaxLine));
   nsTHashtable<nsStringHashKey> currentStarts;
   ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   for (uint32_t i = 0; i < len; ++i) {
-    for (const nsCString& name : aLineNameLists[i]) {
+    for (nsAtom* name : aLineNameLists[i]) {
       uint32_t indexOfSuffix;
       if (Grid::IsNameWithStartSuffix(name, &indexOfSuffix) ||
           Grid::IsNameWithEndSuffix(name, &indexOfSuffix)) {
         // Extract the name that was found earlier.
-        nsDependentCSubstring areaName(name, 0, indexOfSuffix);
+        nsDependentSubstring areaName(nsDependentAtomString(name), 0,
+                                      indexOfSuffix);
 
         // Lazily create the ImplicitNamedAreas.
         if (!areas) {
           areas = new ImplicitNamedAreas;
           SetProperty(ImplicitNamedAreasProperty(), areas);
         }
 
-        NamedArea area;
-        if (!areas->Get(areaName, &area)) {
-          // Not found, so prep the newly-seen area with a name and empty
-          // boundary information, which will get filled in later.
-          Servo_MakeOwnedStr(&area.name, &areaName);
-          area.rows = {0, 0};
-          area.columns = {0, 0};
-
-          areas->Put(areaName, area);
+        RefPtr<nsAtom> name = NS_Atomize(areaName);
+        auto addPtr = areas->lookupForAdd(name);
+        if (!addPtr) {
+          if (!areas->add(
+                  addPtr, name,
+                  NamedArea{StyleAtom(do_AddRef(name)), {0, 0}, {0, 0}})) {
+            MOZ_CRASH("OOM while adding grid name lists");
+          }
         }
       }
     }
   }
 }
 
 void nsGridContainerFrame::InitImplicitNamedAreas(
     const nsStylePosition* aStyle) {
   ImplicitNamedAreas* areas = GetImplicitNamedAreas();
   if (areas) {
     // Clear it, but reuse the hashtable itself for now.  We'll remove it
     // below if it isn't needed anymore.
-    areas->Clear();
+    areas->clear();
   }
   AddImplicitNamedAreas(aStyle->GridTemplateColumns().mLineNameLists);
   AddImplicitNamedAreas(aStyle->GridTemplateRows().mLineNameLists);
-  if (areas && areas->Count() == 0) {
+  if (areas && areas->count() == 0) {
     DeleteProperty(ImplicitNamedAreasProperty());
   }
 }
 
 int32_t nsGridContainerFrame::Grid::ResolveLine(
     const nsStyleGridLine& aLine, int32_t aNth, uint32_t aFromIndex,
     const LineNameMap& aNameMap, LogicalSide aSide, uint32_t aExplicitGridEnd,
     const nsStylePosition* aStyle) {
   MOZ_ASSERT(!aLine.IsAuto());
   int32_t line = 0;
-  if (aLine.mLineName.IsEmpty()) {
+  if (aLine.mLineName->IsEmpty()) {
     MOZ_ASSERT(aNth != 0, "css-grid 9.2: <integer> must not be zero.");
     line = int32_t(aFromIndex) + aNth;
   } else {
     if (aNth == 0) {
       // <integer> was omitted; treat it as 1.
       aNth = 1;
     }
     bool isNameOnly = !aLine.mHasSpan && aLine.mInteger == 0;
     if (isNameOnly) {
       AutoTArray<uint32_t, 16> implicitLines;
       aNameMap.FindNamedAreas(aLine.mLineName, aSide, implicitLines);
       if (!implicitLines.IsEmpty() ||
           aNameMap.HasImplicitNamedArea(aLine.mLineName)) {
         // aName is a named area - look for explicit lines named
         // <name>-start/-end depending on which side we're resolving.
         // http://dev.w3.org/csswg/css-grid/#grid-placement-slot
-        nsAutoCString lineName(aLine.mLineName);
+        nsAutoString lineName(nsDependentAtomString(aLine.mLineName));
         if (IsStart(aSide)) {
           lineName.AppendLiteral("-start");
         } else {
           lineName.AppendLiteral("-end");
         }
-        line =
-            aNameMap.FindNamedLine(lineName, &aNth, aFromIndex, implicitLines);
+        RefPtr<nsAtom> name = NS_Atomize(lineName);
+        line = aNameMap.FindNamedLine(name, &aNth, aFromIndex, implicitLines);
       }
     }
 
     if (line == 0) {
       // If mLineName ends in -start/-end, try the prefix as a named area.
       AutoTArray<uint32_t, 16> implicitLines;
       uint32_t index;
       bool useStart = IsNameWithStartSuffix(aLine.mLineName, &index);
       if (useStart || IsNameWithEndSuffix(aLine.mLineName, &index)) {
         auto side = MakeLogicalSide(
             GetAxis(aSide), useStart ? eLogicalEdgeStart : eLogicalEdgeEnd);
-        aNameMap.FindNamedAreas(nsDependentCSubstring(aLine.mLineName, 0, index),
-                                side, implicitLines);
+        RefPtr<nsAtom> name = NS_Atomize(nsDependentSubstring(
+            nsDependentAtomString(aLine.mLineName), 0, index));
+        aNameMap.FindNamedAreas(name, side, implicitLines);
       }
       line = aNameMap.FindNamedLine(aLine.mLineName, &aNth, aFromIndex,
                                     implicitLines);
     }
 
     if (line == 0) {
       MOZ_ASSERT(aNth != 0, "we found all N named lines but 'line' is zero!");
       int32_t edgeLine;
@@ -3454,17 +3455,17 @@ nsGridContainerFrame::Grid::ResolveLineR
     const LineNameMap& aNameMap, LogicalAxis aAxis, uint32_t aExplicitGridEnd,
     const nsStylePosition* aStyle) {
   MOZ_ASSERT(int32_t(nsGridContainerFrame::kAutoLine) >
              nsStyleGridLine::kMaxLine);
 
   if (aStart.mHasSpan) {
     if (aEnd.mHasSpan || aEnd.IsAuto()) {
       // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
-      if (aStart.mLineName.IsEmpty()) {
+      if (aStart.mLineName->IsEmpty()) {
         // span <integer> / span *
         // span <integer> / auto
         return LinePair(kAutoLine, aStart.mInteger);
       }
       // span <custom-ident> / span *
       // span <custom-ident> / auto
       return LinePair(kAutoLine, 1);  // XXX subgrid explicit size instead of 1?
     }
@@ -3488,17 +3489,17 @@ nsGridContainerFrame::Grid::ResolveLineR
 
   int32_t start = kAutoLine;
   if (aStart.IsAuto()) {
     if (aEnd.IsAuto()) {
       // auto / auto
       return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
     }
     if (aEnd.mHasSpan) {
-      if (aEnd.mLineName.IsEmpty()) {
+      if (aEnd.mLineName->IsEmpty()) {
         // auto / span <integer>
         MOZ_ASSERT(aEnd.mInteger != 0);
         return LinePair(start, aEnd.mInteger);
       }
       // http://dev.w3.org/csswg/css-grid/#grid-placement-errors
       // auto / span <custom-ident>
       return LinePair(start, 1);  // XXX subgrid explicit size instead of 1?
     }
@@ -3514,17 +3515,17 @@ nsGridContainerFrame::Grid::ResolveLineR
       return LinePair(start, start);
     }
   }
 
   uint32_t from;
   int32_t nth = aEnd.mInteger == 0 ? 1 : aEnd.mInteger;
   if (aEnd.mHasSpan) {
     if (MOZ_UNLIKELY(start < 0)) {
-      if (aEnd.mLineName.IsEmpty()) {
+      if (aEnd.mLineName->IsEmpty()) {
         return LinePair(start, start + nth);
       }
       from = 0;
     } else {
       if (start >= int32_t(aExplicitGridEnd)) {
         // The start is at or after the last explicit line, thus all lines
         // after it match <custom-ident> since they're implicit.
         return LinePair(start, std::min(start + nth, aNameMap.mClampMaxLine));
@@ -4286,24 +4287,24 @@ void nsGridContainerFrame::Grid::PlaceGr
     aState.mColFunctions.SetNumRepeatTracks(colRepeatCount - numEmptyCols);
     auto rowRepeatCount = aState.mRowFunctions.NumRepeatTracks();
     aState.mRowFunctions.SetNumRepeatTracks(rowRepeatCount - numEmptyRows);
   }
 
   // Update the line boundaries of the implicit grid areas, if needed.
   if (mAreas &&
       aState.mFrame->HasAnyStateBits(NS_STATE_GRID_GENERATE_COMPUTED_VALUES)) {
-    for (auto iter = mAreas->Iter(); !iter.Done(); iter.Next()) {
-      auto& areaInfo = iter.Data();
+    for (auto iter = mAreas->iter(); !iter.done(); iter.next()) {
+      auto& areaInfo = iter.get().value();
 
       // Resolve the lines for the area. We use the name of the area as the
       // name of the lines, knowing that the line placement algorithm will
       // add the -start and -end suffixes as appropriate for layout.
       nsStyleGridLine lineStartAndEnd;
-      lineStartAndEnd.mLineName = areaInfo.name.AsString();
+      lineStartAndEnd.mLineName = areaInfo.name.AsAtom();
 
       LineRange columnLines =
           ResolveLineRange(lineStartAndEnd, lineStartAndEnd, colLineNameMap,
                            eLogicalAxisInline, mExplicitGridColEnd, gridStyle);
 
       LineRange rowLines =
           ResolveLineRange(lineStartAndEnd, lineStartAndEnd, rowLineNameMap,
                            eLogicalAxisBlock, mExplicitGridRowEnd, gridStyle);
@@ -7278,19 +7279,19 @@ void nsGridContainerFrame::Reflow(nsPres
           ((row >= gridReflowInput.mRowFunctions.mRepeatAutoStart) &&
            (row < gridReflowInput.mRowFunctions.mRepeatAutoEnd));
       rowTrackStates.AppendElement(
           isRepeat ? (uint32_t)mozilla::dom::GridTrackState::Repeat
                    : (uint32_t)mozilla::dom::GridTrackState::Static);
 
       row++;
     }
-    // Row info has to accomodate fragmentation of the grid, which may happen in
-    // later calls to Reflow. For now, presume that no more fragmentation will
-    // occur.
+    // Row info has to accommodate fragmentation of the grid, which may happen
+    // in later calls to Reflow. For now, presume that no more fragmentation
+    // will occur.
     ComputedGridTrackInfo* rowInfo = new ComputedGridTrackInfo(
         gridReflowInput.mRowFunctions.mExplicitGridOffset,
         IsSubgrid(eLogicalAxisBlock)
             ? rowTrackSizes.Length()
             : gridReflowInput.mRowFunctions.NumExplicitTracks(),
         gridReflowInput.mStartRow, row, std::move(rowTrackPositions),
         std::move(rowTrackSizes), std::move(rowTrackStates),
         std::move(rowRemovedRepeatTracks),
@@ -7330,28 +7331,28 @@ void nsGridContainerFrame::Reflow(nsPres
     // repeat tracks produced in the reflow. Only explicit names are assigned
     // to lines here; the mozilla::dom::GridLines class will later extract
     // implicit names from grid areas and assign them to the appropriate lines.
 
     // Generate column lines first.
     uint32_t capacity = gridReflowInput.mCols.mSizes.Length();
     const nsStyleGridTemplate& gridColTemplate =
         gridReflowInput.mGridStyle->GridTemplateColumns();
-    nsTArray<nsTArray<nsCString>> columnLineNames(capacity);
+    nsTArray<nsTArray<RefPtr<nsAtom>>> columnLineNames(capacity);
     for (col = 0; col <= gridReflowInput.mCols.mSizes.Length(); col++) {
       // Offset col by the explicit grid offset, to get the original names.
-      nsTArray<nsCString> explicitNames =
+      nsTArray<RefPtr<nsAtom>> explicitNames =
           gridReflowInput.mCols.GetExplicitLineNamesAtIndex(
               gridColTemplate, gridReflowInput.mColFunctions,
               col - gridReflowInput.mColFunctions.mExplicitGridOffset);
 
       columnLineNames.AppendElement(explicitNames);
     }
     // Get the explicit names that follow a repeat auto declaration.
-    nsTArray<nsCString> colNamesFollowingRepeat;
+    nsTArray<RefPtr<nsAtom>> colNamesFollowingRepeat;
     if (gridColTemplate.HasRepeatAuto()) {
       // The line name list after the repeatAutoIndex holds the line names
       // for the first explicit line after the repeat auto declaration.
       uint32_t repeatAutoEnd = gridColTemplate.mRepeatAutoIndex + 1;
       MOZ_ASSERT(repeatAutoEnd < gridColTemplate.mLineNameLists.Length());
       colNamesFollowingRepeat.AppendElements(
           gridColTemplate.mLineNameLists[repeatAutoEnd]);
     }
@@ -7362,28 +7363,28 @@ void nsGridContainerFrame::Reflow(nsPres
                                  gridColTemplate.mRepeatAutoLineNameListAfter,
                                  std::move(colNamesFollowingRepeat));
     SetProperty(GridColumnLineInfo(), columnLineInfo);
 
     // Generate row lines next.
     capacity = gridReflowInput.mRows.mSizes.Length();
     const nsStyleGridTemplate& gridRowTemplate =
         gridReflowInput.mGridStyle->GridTemplateRows();
-    nsTArray<nsTArray<nsCString>> rowLineNames(capacity);
+    nsTArray<nsTArray<RefPtr<nsAtom>>> rowLineNames(capacity);
     for (row = 0; row <= gridReflowInput.mRows.mSizes.Length(); row++) {
       // Offset row by the explicit grid offset, to get the original names.
-      nsTArray<nsCString> explicitNames =
+      nsTArray<RefPtr<nsAtom>> explicitNames =
           gridReflowInput.mRows.GetExplicitLineNamesAtIndex(
               gridRowTemplate, gridReflowInput.mRowFunctions,
               row - gridReflowInput.mRowFunctions.mExplicitGridOffset);
 
       rowLineNames.AppendElement(explicitNames);
     }
     // Get the explicit names that follow a repeat auto declaration.
-    nsTArray<nsCString> rowNamesFollowingRepeat;
+    nsTArray<RefPtr<nsAtom>> rowNamesFollowingRepeat;
     if (gridRowTemplate.HasRepeatAuto()) {
       // The line name list after the repeatAutoIndex holds the line names
       // for the first explicit line after the repeat auto declaration.
       uint32_t repeatAutoEnd = gridRowTemplate.mRepeatAutoIndex + 1;
       MOZ_ASSERT(repeatAutoEnd < gridRowTemplate.mLineNameLists.Length());
       rowNamesFollowingRepeat.AppendElements(
           gridRowTemplate.mLineNameLists[repeatAutoEnd]);
     }
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -6,19 +6,18 @@
 
 /* rendering object for CSS "display: grid | inline-grid" */
 
 #ifndef nsGridContainerFrame_h___
 #define nsGridContainerFrame_h___
 
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
+#include "mozilla/HashTable.h"
 #include "nsContainerFrame.h"
-#include "nsHashKeys.h"
-#include "nsTHashtable.h"
 
 namespace mozilla {
 class PresShell;
 }  // namespace mozilla
 
 /**
  * Factory function.
  * @return a newly allocated nsGridContainerFrame (infallible)
@@ -62,28 +61,29 @@ struct ComputedGridTrackInfo {
   nsTArray<nscoord> mPositions;
   nsTArray<nscoord> mSizes;
   nsTArray<uint32_t> mStates;
   nsTArray<bool> mRemovedRepeatTracks;
   uint32_t mRepeatFirstTrack;
 };
 
 struct ComputedGridLineInfo {
-  explicit ComputedGridLineInfo(nsTArray<nsTArray<nsCString>>&& aNames,
-                                const nsTArray<nsCString>& aNamesBefore,
-                                const nsTArray<nsCString>& aNamesAfter,
-                                nsTArray<nsCString>&& aNamesFollowingRepeat)
+  explicit ComputedGridLineInfo(
+      nsTArray<nsTArray<RefPtr<nsAtom>>>&& aNames,
+      const nsTArray<RefPtr<nsAtom>>& aNamesBefore,
+      const nsTArray<RefPtr<nsAtom>>& aNamesAfter,
+      nsTArray<RefPtr<nsAtom>>&& aNamesFollowingRepeat)
       : mNames(aNames),
         mNamesBefore(aNamesBefore),
         mNamesAfter(aNamesAfter),
         mNamesFollowingRepeat(aNamesFollowingRepeat) {}
-  nsTArray<nsTArray<nsCString>> mNames;
-  nsTArray<nsCString> mNamesBefore;
-  nsTArray<nsCString> mNamesAfter;
-  nsTArray<nsCString> mNamesFollowingRepeat;
+  nsTArray<nsTArray<RefPtr<nsAtom>>> mNames;
+  nsTArray<RefPtr<nsAtom>> mNamesBefore;
+  nsTArray<RefPtr<nsAtom>> mNamesAfter;
+  nsTArray<RefPtr<nsAtom>> mNamesFollowingRepeat;
 };
 }  // namespace mozilla
 
 class nsGridContainerFrame final : public nsContainerFrame {
  public:
   NS_DECL_FRAMEARENA_HELPERS(nsGridContainerFrame)
   NS_DECL_QUERYFRAME
   using ComputedGridTrackInfo = mozilla::ComputedGridTrackInfo;
@@ -197,17 +197,31 @@ class nsGridContainerFrame final : publi
 
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(GridRowLineInfo, ComputedGridLineInfo)
   const ComputedGridLineInfo* GetComputedTemplateRowLines() {
     const ComputedGridLineInfo* info = GetProperty(GridRowLineInfo());
     MOZ_ASSERT(info, "Property generation wasn't requested.");
     return info;
   }
 
-  using ImplicitNamedAreas = nsBaseHashtable<nsCStringHashKey, NamedArea, NamedArea>;
+  struct AtomKey {
+    RefPtr<nsAtom> mKey;
+
+    explicit AtomKey(nsAtom* aAtom) : mKey(aAtom) {}
+
+    using Lookup = nsAtom*;
+
+    static mozilla::HashNumber hash(const Lookup& aKey) { return aKey->hash(); }
+
+    static bool match(const AtomKey& aFirst, const Lookup& aSecond) {
+      return aFirst.mKey == aSecond;
+    }
+  };
+
+  using ImplicitNamedAreas = mozilla::HashMap<AtomKey, NamedArea, AtomKey>;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ImplicitNamedAreasProperty,
                                       ImplicitNamedAreas)
   ImplicitNamedAreas* GetImplicitNamedAreas() const {
     return GetProperty(ImplicitNamedAreasProperty());
   }
 
   using ExplicitNamedAreas = mozilla::StyleOwnedSlice<NamedArea>;
   NS_DECLARE_FRAME_PROPERTY_DELETABLE(ExplicitNamedAreasProperty,
@@ -310,17 +324,17 @@ class nsGridContainerFrame final : publi
   /**
    * XXX temporary - move the ImplicitNamedAreas stuff to the style system.
    * The implicit area names that come from x-start .. x-end lines in
    * grid-template-columns / grid-template-rows are stored in this frame
    * property when needed, as a ImplicitNamedAreas* value.
    */
   void InitImplicitNamedAreas(const nsStylePosition* aStyle);
   void AddImplicitNamedAreas(
-      const nsTArray<nsTArray<nsCString>>& aLineNameLists);
+      const nsTArray<nsTArray<RefPtr<nsAtom>>>& aLineNameLists);
 
   void NormalizeChildLists();
 
   /**
    * Reflow and place our children.
    * @return the consumed size of all of this grid container's continuations
    *         so far including this frame
    */
--- a/layout/reftests/w3c-css/submitted/ruby/reftest.list
+++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list
@@ -1,11 +1,11 @@
 # Tests for inlinizing block-level boxes
 == ruby-inlinize-blocks-001.html ruby-inlinize-blocks-001-ref.html
-fuzzy-if(winWidget,0-144,0-1) == ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
+== ruby-inlinize-blocks-002.html ruby-inlinize-blocks-002-ref.html
 == ruby-inlinize-blocks-003.html ruby-inlinize-blocks-003-ref.html
 == ruby-inlinize-blocks-004.html ruby-inlinize-blocks-004-ref.html
 == ruby-inlinize-blocks-005.html ruby-inlinize-blocks-005-ref.html
 
 # Tests for autohiding base-identical annotations
 == ruby-autohide-001.html ruby-autohide-001-ref.html
 == ruby-autohide-002.html ruby-autohide-002-ref.html
 == ruby-autohide-003.html ruby-autohide-003-ref.html
--- a/layout/reftests/w3c-css/submitted/ruby/ruby-inlinize-blocks-002-ref.html
+++ b/layout/reftests/w3c-css/submitted/ruby/ruby-inlinize-blocks-002-ref.html
@@ -1,15 +1,20 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <title>CSS Reference: Inlinize block-level boxes inside ruby</title>
   <link rel="author" title="Xidorn Quan" href="mailto:quanxunzhen@gmail.com">
   <style>
+    body {
+      /* Use a sans-serif font to avoid fuzzy pixels where e.g. the
+         letter "a" bottom-right serif might overlap the table border: */
+      font: 16px sans-serif;
+    }
     .block, table, .flex {
       background-color: yellow;
       width: 100px; height: 30px;
       border: 1px solid blue;
     }
     .block { display: inline-block; }
     table { display: inline-table; border-collapse: collapse; }
     td { border: 3px solid red; }
--- a/layout/reftests/w3c-css/submitted/ruby/ruby-inlinize-blocks-002.html
+++ b/layout/reftests/w3c-css/submitted/ruby/ruby-inlinize-blocks-002.html
@@ -2,16 +2,21 @@
 <html lang="en">
 <head>
   <meta charset="UTF-8">
   <title>CSS Test: Inlinize block-level boxes inside ruby</title>
   <link rel="author" title="Xidorn Quan" href="mailto:quanxunzhen@gmail.com">
   <link rel="help" href="http://www.w3.org/TR/css-ruby-1/#anon-gen-inlinize">
   <link rel="match" href="ruby-inlinize-blocks-002-ref.html">
   <style>
+    body {
+      /* Use a sans-serif font to avoid fuzzy pixels where e.g. the
+         letter "a" bottom-right serif might overlap the table border: */
+      font: 16px sans-serif;
+    }
     .block, table, .flex {
       background-color: yellow;
       width: 100px; height: 30px;
       border: 1px solid blue;
     }
     .block { display: block; }
     table { border-collapse: collapse; }
     td { border: 3px solid red; }
--- a/layout/style/GeckoBindings.cpp
+++ b/layout/style/GeckoBindings.cpp
@@ -1253,18 +1253,17 @@ void Gecko_ClearPODTArray(void* aArray, 
       0, base->Length(), 0, aElementSize, aElementAlign);
 }
 
 void Gecko_ResizeTArrayForStrings(nsTArray<nsString>* aArray,
                                   uint32_t aLength) {
   aArray->SetLength(aLength);
 }
 
-void Gecko_ResizeTArrayForCStrings(nsTArray<nsCString>* aArray,
-                                  uint32_t aLength) {
+void Gecko_ResizeAtomArray(nsTArray<RefPtr<nsAtom>>* aArray, uint32_t aLength) {
   aArray->SetLength(aLength);
 }
 
 void Gecko_SetStyleGridTemplate(UniquePtr<nsStyleGridTemplate>* aGridTemplate,
                                 nsStyleGridTemplate* aValue) {
   aGridTemplate->reset(aValue);
 }
 
--- a/layout/style/GeckoBindings.h
+++ b/layout/style/GeckoBindings.h
@@ -411,17 +411,17 @@ void Gecko_EnsureTArrayCapacity(void* ar
 
 // Same here, `array` must be an nsTArray<T>, for some T.
 //
 // Important note: Only valid for POD types, since destructors won't be run
 // otherwise. This is ensured with rust traits for the relevant structs.
 void Gecko_ClearPODTArray(void* array, size_t elem_size, size_t elem_align);
 
 void Gecko_ResizeTArrayForStrings(nsTArray<nsString>* array, uint32_t length);
-void Gecko_ResizeTArrayForCStrings(nsTArray<nsCString>* array, uint32_t length);
+void Gecko_ResizeAtomArray(nsTArray<RefPtr<nsAtom>>* array, uint32_t length);
 
 void Gecko_SetStyleGridTemplate(
     mozilla::UniquePtr<nsStyleGridTemplate>* grid_template,
     nsStyleGridTemplate* value);
 
 nsStyleGridTemplate* Gecko_CreateStyleGridTemplate(uint32_t track_sizes,
                                                    uint32_t name_size);
 
--- a/layout/style/ServoStyleConstsInlines.h
+++ b/layout/style/ServoStyleConstsInlines.h
@@ -237,16 +237,29 @@ inline nsAtom* StyleAtom::AsAtom() const
 }
 
 inline StyleAtom::~StyleAtom() {
   if (!IsStatic()) {
     AsAtom()->Release();
   }
 }
 
+inline StyleAtom::StyleAtom(already_AddRefed<nsAtom> aAtom) {
+  nsAtom* atom = aAtom.take();
+  if (atom->IsStatic()) {
+    ptrdiff_t offset = reinterpret_cast<const uint8_t*>(atom->AsStatic()) -
+        reinterpret_cast<const uint8_t*>(&detail::gGkAtoms);
+    _0 = (offset << 1) | 1;
+  } else {
+    _0 = reinterpret_cast<uintptr_t>(atom);
+  }
+  MOZ_ASSERT(IsStatic() == atom->IsStatic());
+  MOZ_ASSERT(AsAtom() == atom);
+}
+
 inline StyleAtom::StyleAtom(const StyleAtom& aOther) : _0(aOther._0) {
   if (!IsStatic()) {
     reinterpret_cast<nsAtom*>(_0)->AddRef();
   }
 }
 
 inline nsAtom* StyleCustomIdent::AsAtom() const { return _0.AsAtom(); }
 
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1240,49 +1240,49 @@ void nsComputedDOMStyle::SetValueToURLVa
   nsAutoString url;
   url.AppendLiteral(u"url(");
   nsStyleUtil::AppendEscapedCSSString(source, url, '"');
   url.Append(')');
   aValue->SetString(url);
 }
 
 void nsComputedDOMStyle::AppendGridLineNames(
-    nsString& aResult, const nsTArray<nsCString>& aLineNames) {
+    nsString& aResult, const nsTArray<RefPtr<nsAtom>>& aLineNames) {
   uint32_t numLines = aLineNames.Length();
   if (numLines == 0) {
     return;
   }
   for (uint32_t i = 0;;) {
-    nsStyleUtil::AppendEscapedCSSIdent(NS_ConvertUTF8toUTF16(aLineNames[i]),
+    nsStyleUtil::AppendEscapedCSSIdent(nsDependentAtomString(aLineNames[i]),
                                        aResult);
     if (++i == numLines) {
       break;
     }
     aResult.Append(' ');
   }
 }
 
 void nsComputedDOMStyle::AppendGridLineNames(
-    nsDOMCSSValueList* aValueList, const nsTArray<nsCString>& aLineNames,
+    nsDOMCSSValueList* aValueList, const nsTArray<RefPtr<nsAtom>>& aLineNames,
     bool aSuppressEmptyList) {
   if (aLineNames.IsEmpty() && aSuppressEmptyList) {
     return;
   }
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString lineNamesString;
   lineNamesString.Assign('[');
   AppendGridLineNames(lineNamesString, aLineNames);
   lineNamesString.Append(']');
   val->SetString(lineNamesString);
   aValueList->AppendCSSValue(val.forget());
 }
 
 void nsComputedDOMStyle::AppendGridLineNames(
-    nsDOMCSSValueList* aValueList, const nsTArray<nsCString>& aLineNames1,
-    const nsTArray<nsCString>& aLineNames2) {
+    nsDOMCSSValueList* aValueList, const nsTArray<RefPtr<nsAtom>>& aLineNames1,
+    const nsTArray<RefPtr<nsAtom>>& aLineNames2) {
   if (aLineNames1.IsEmpty() && aLineNames2.IsEmpty()) {
     return;
   }
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   nsAutoString lineNamesString;
   lineNamesString.Assign('[');
   if (!aLineNames1.IsEmpty()) {
     AppendGridLineNames(lineNamesString, aLineNames1);
@@ -1477,17 +1477,18 @@ already_AddRefed<CSSValue> nsComputedDOM
           repeatIndex++;
         }
         repeatIndex++;
       };
 
       for (int32_t i = 0;; i++) {
         if (aTrackList.HasRepeatAuto()) {
           if (i == aTrackList.mRepeatAutoIndex) {
-            const nsTArray<nsCString>& lineNames = aTrackList.mLineNameLists[i];
+            const nsTArray<RefPtr<nsAtom>>& lineNames =
+                aTrackList.mLineNameLists[i];
             if (i == endOfRepeat) {
               // All auto-fit tracks are empty, but we report them anyway.
               AppendGridLineNames(valueList, lineNames,
                                   aTrackList.mRepeatAutoLineNameListBefore);
 
               AppendRemovedAutoFits(LinesBetween);
 
               AppendGridLineNames(valueList,
@@ -1497,32 +1498,34 @@ already_AddRefed<CSSValue> nsComputedDOM
               AppendGridLineNames(valueList, lineNames,
                                   aTrackList.mRepeatAutoLineNameListBefore);
               AppendRemovedAutoFits(LinesFollow);
             }
           } else if (i == endOfRepeat) {
             // Before appending the last line, finish off any removed auto-fits.
             AppendRemovedAutoFits(LinesPrecede);
 
-            const nsTArray<nsCString>& lineNames =
+            const nsTArray<RefPtr<nsAtom>>& lineNames =
                 aTrackList.mLineNameLists[aTrackList.mRepeatAutoIndex + 1];
             AppendGridLineNames(
                 valueList, aTrackList.mRepeatAutoLineNameListAfter, lineNames);
           } else if (i > aTrackList.mRepeatAutoIndex && i < endOfRepeat) {
             AppendGridLineNames(valueList,
                                 aTrackList.mRepeatAutoLineNameListAfter,
                                 aTrackList.mRepeatAutoLineNameListBefore);
             AppendRemovedAutoFits(LinesFollow);
           } else {
             uint32_t j = i > endOfRepeat ? i - offsetToLastRepeat : i;
-            const nsTArray<nsCString>& lineNames = aTrackList.mLineNameLists[j];
+            const nsTArray<RefPtr<nsAtom>>& lineNames =
+                aTrackList.mLineNameLists[j];
             AppendGridLineNames(valueList, lineNames);
           }
         } else {
-          const nsTArray<nsCString>& lineNames = aTrackList.mLineNameLists[i];
+          const nsTArray<RefPtr<nsAtom>>& lineNames =
+              aTrackList.mLineNameLists[i];
           AppendGridLineNames(valueList, lineNames);
         }
         if (uint32_t(i) == numExplicitTracks) {
           break;
         }
         RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
         val->SetAppUnits(trackSizes[i + numLeadingImplicitTracks]);
         valueList->AppendCSSValue(val.forget());
@@ -1535,17 +1538,17 @@ already_AddRefed<CSSValue> nsComputedDOM
       RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
       val->SetAppUnits(trackSizes[i]);
       valueList->AppendCSSValue(val.forget());
     }
   } else {
     // We don't have a frame.  So, we'll just return a serialization of
     // the tracks from the style (without resolved sizes).
     for (uint32_t i = 0;; i++) {
-      const nsTArray<nsCString>& lineNames = aTrackList.mLineNameLists[i];
+      const nsTArray<RefPtr<nsAtom>>& lineNames = aTrackList.mLineNameLists[i];
       if (!lineNames.IsEmpty()) {
         AppendGridLineNames(valueList, lineNames);
       }
       if (i == numSizes) {
         break;
       }
       if (MOZ_UNLIKELY(aTrackList.IsRepeatAutoIndex(i))) {
         RefPtr<nsROCSSPrimitiveValue> start = new nsROCSSPrimitiveValue;
@@ -1633,21 +1636,21 @@ already_AddRefed<CSSValue> nsComputedDOM
   }
 
   if (aGridLine.mInteger != 0) {
     RefPtr<nsROCSSPrimitiveValue> integer = new nsROCSSPrimitiveValue;
     integer->SetNumber(aGridLine.mInteger);
     valueList->AppendCSSValue(integer.forget());
   }
 
-  if (!aGridLine.mLineName.IsEmpty()) {
+  if (aGridLine.mLineName != nsGkAtoms::_empty) {
     RefPtr<nsROCSSPrimitiveValue> lineName = new nsROCSSPrimitiveValue;
     nsString escapedLineName;
     nsStyleUtil::AppendEscapedCSSIdent(
-        NS_ConvertUTF8toUTF16(aGridLine.mLineName), escapedLineName);
+        nsDependentAtomString(aGridLine.mLineName), escapedLineName);
     lineName->SetString(escapedLineName);
     valueList->AppendCSSValue(lineName.forget());
   }
 
   NS_ASSERTION(valueList->Length() > 0,
                "Should have appended at least one value");
   return valueList.forget();
 }
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -186,26 +186,26 @@ class nsComputedDOMStyle final : public 
   already_AddRefed<CSSValue> GetBorderColorFor(mozilla::Side aSide);
 
   already_AddRefed<CSSValue> GetMarginWidthFor(mozilla::Side aSide);
 
   already_AddRefed<CSSValue> GetTransformValue(const mozilla::StyleTransform&);
 
   // Appends all aLineNames (may be empty) space-separated to aResult.
   void AppendGridLineNames(nsString& aResult,
-                           const nsTArray<nsCString>& aLineNames);
+                           const nsTArray<RefPtr<nsAtom>>& aLineNames);
   // Appends aLineNames as a CSSValue* to aValueList.  If aLineNames is empty
   // a value ("[]") is only appended if aSuppressEmptyList is false.
   void AppendGridLineNames(nsDOMCSSValueList* aValueList,
-                           const nsTArray<nsCString>& aLineNames,
+                           const nsTArray<RefPtr<nsAtom>>& aLineNames,
                            bool aSuppressEmptyList = true);
   // Appends aLineNames1/2 (if non-empty) as a CSSValue* to aValueList.
   void AppendGridLineNames(nsDOMCSSValueList* aValueList,
-                           const nsTArray<nsCString>& aLineNames1,
-                           const nsTArray<nsCString>& aLineNames2);
+                           const nsTArray<RefPtr<nsAtom>>& aLineNames1,
+                           const nsTArray<RefPtr<nsAtom>>& aLineNames2);
   already_AddRefed<CSSValue> GetGridTrackSize(const nsStyleCoord& aMinSize,
                                               const nsStyleCoord& aMaxSize);
   already_AddRefed<CSSValue> GetGridTemplateColumnsRows(
       const nsStyleGridTemplate& aTrackList,
       const mozilla::ComputedGridTrackInfo* aTrackInfo);
   already_AddRefed<CSSValue> GetGridLine(const nsStyleGridLine& aGridLine);
 
   bool GetLineHeightCoord(nscoord& aCoord);
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -945,45 +945,42 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
       mMozListReversed;  // true in an <ol reversed> scope
 };
 
 struct nsStyleGridLine {
   // http://dev.w3.org/csswg/css-grid/#typedef-grid-line
   // XXXmats we could optimize memory size here
   bool mHasSpan;
   int32_t mInteger;    // 0 means not provided
-  nsCString mLineName;  // Empty string means not provided.
+  RefPtr<nsAtom> mLineName;  // Empty string means not provided.
 
   // These are the limits that we choose to clamp grid line numbers to.
   // http://dev.w3.org/csswg/css-grid/#overlarge-grids
   // mInteger is clamped to this range:
   static const int32_t kMinLine = -10000;
   static const int32_t kMaxLine = 10000;
 
   nsStyleGridLine()
-      : mHasSpan(false),
-        mInteger(0)
-  // mLineName get its default constructor, the empty string
-  {}
+      : mHasSpan(false), mInteger(0), mLineName(nsGkAtoms::_empty) {}
 
   nsStyleGridLine(const nsStyleGridLine& aOther) { (*this) = aOther; }
 
   void operator=(const nsStyleGridLine& aOther) {
     mHasSpan = aOther.mHasSpan;
     mInteger = aOther.mInteger;
     mLineName = aOther.mLineName;
   }
 
   bool operator!=(const nsStyleGridLine& aOther) const {
     return mHasSpan != aOther.mHasSpan || mInteger != aOther.mInteger ||
            mLineName != aOther.mLineName;
   }
 
   bool IsAuto() const {
-    bool haveInitialValues = mInteger == 0 && mLineName.IsEmpty();
+    bool haveInitialValues = mInteger == 0 && mLineName == nsGkAtoms::_empty;
     MOZ_ASSERT(!(haveInitialValues && mHasSpan),
                "should not have 'span' when other components are "
                "at their initial values");
     return haveInitialValues;
   }
 };
 
 // Computed value of the grid-template-columns or grid-template-rows property
@@ -1022,21 +1019,21 @@ struct nsStyleGridLine {
 // If mRepeatAutoIndex != -1 then that index is an <auto-repeat> and
 // mIsAutoFill == true means it's an 'auto-fill', otherwise 'auto-fit'.
 // mRepeatAutoLineNameListBefore is the list of line names before the track
 // size, mRepeatAutoLineNameListAfter the names after.  (They are empty
 // when there is no <auto-repeat> track, i.e. when mRepeatAutoIndex == -1).
 // When mIsSubgrid is true, mRepeatAutoLineNameListBefore contains the line
 // names and mRepeatAutoLineNameListAfter is empty.
 struct nsStyleGridTemplate {
-  nsTArray<nsTArray<nsCString>> mLineNameLists;
+  nsTArray<nsTArray<RefPtr<nsAtom>>> mLineNameLists;
   nsTArray<nsStyleCoord> mMinTrackSizingFunctions;
   nsTArray<nsStyleCoord> mMaxTrackSizingFunctions;
-  nsTArray<nsCString> mRepeatAutoLineNameListBefore;
-  nsTArray<nsCString> mRepeatAutoLineNameListAfter;
+  nsTArray<RefPtr<nsAtom>> mRepeatAutoLineNameListBefore;
+  nsTArray<RefPtr<nsAtom>> mRepeatAutoLineNameListAfter;
   int16_t mRepeatAutoIndex;  // -1 or the track index for an auto-fill/fit track
   bool mIsAutoFill : 1;
   bool mIsSubgrid : 1;
 
   nsStyleGridTemplate()
       : mRepeatAutoIndex(-1), mIsAutoFill(false), mIsSubgrid(false) {}
 
   inline bool operator==(const nsStyleGridTemplate& aOther) const {
--- a/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs
+++ b/media/webrtc/signaling/src/sdp/rsdparsa_capi/src/lib.rs
@@ -180,17 +180,17 @@ pub unsafe extern "C" fn sdp_add_media_s
       }
     };
 
     // Check that the provided address type is valid. The rust parser will find out
     // on his own which address type was provided
     match addr_type {
       // enum AddrType { kAddrTypeNone, kIPv4, kIPv6 };
       // kAddrTypeNone is explicitly not covered as it is an 'invalid' flag
-      1...2 => (),
+      1 | 2 => (),
       _ => {
           return NS_ERROR_INVALID_ARG;
       }
     }
 
     match (*session).add_media(media_type, direction, port as u32, protocol, addr_str) {
         Ok(_) => NS_OK,
         Err(_) => NS_ERROR_INVALID_ARG
--- a/remote/domains/parent/Target.jsm
+++ b/remote/domains/parent/Target.jsm
@@ -12,57 +12,72 @@ const {TabSession} = ChromeUtils.import(
 
 let sessionIds = 1;
 
 class Target extends Domain {
   constructor(session) {
     super(session);
 
     this.onTargetCreated = this.onTargetCreated.bind(this);
+    this.onTargetDestroyed = this.onTargetDestroyed.bind(this);
   }
 
   getBrowserContexts() {
     return {
       browserContextIds: [],
     };
   }
 
   setDiscoverTargets({ discover }) {
     const { targets } = this.session.target;
     if (discover) {
       targets.on("connect", this.onTargetCreated);
+      targets.on("disconnect", this.onTargetDestroyed);
     } else {
       targets.off("connect", this.onTargetCreated);
+      targets.off("disconnect", this.onTargetDestroyed);
     }
     for (const target of targets) {
       this.onTargetCreated("connect", target);
     }
   }
 
   onTargetCreated(eventName, target) {
     this.emit("Target.targetCreated", {
       targetInfo: {
         browserContextId: target.id,
         targetId: target.id,
         type: "page",
       },
     });
   }
 
+  onTargetDestroyed(eventName, target) {
+    this.emit("Target.targetDestroyed", {
+      targetId: target.id,
+    });
+  }
+
   async createTarget() {
-    const {targets} = this.session.target;
+    const { targets } = this.session.target;
     const onTarget = targets.once("connect");
     const tab = TabManager.addTab();
     const target = await onTarget;
     if (tab.linkedBrowser != target.browser) {
       throw new Error("Unexpected tab opened: " + tab.linkedBrowser.currentURI.spec);
     }
     return {targetId: target.id};
   }
 
+  closeTarget({ targetId }) {
+    const { targets } = this.session.target;
+    const target = targets.getById(targetId);
+    target.window.gBrowser.removeTab(target.tab);
+  }
+
   attachToTarget({ targetId }) {
     const { targets } = this.session.target;
     const target = targets.getById(targetId);
     if (!target) {
       return new Error(`Unable to find target with id '${targetId}'`);
     }
 
     const session = new TabSession(this.session.connection, target, sessionIds++, this.session);
--- a/remote/targets/TabTarget.jsm
+++ b/remote/targets/TabTarget.jsm
@@ -52,16 +52,20 @@ class TabTarget extends Target {
   get mm() {
     return this.browser.messageManager;
   }
 
   get window() {
     return this.browser.ownerGlobal;
   }
 
+  get tab() {
+    return this.window.gBrowser.getTabForBrowser(this.browser);
+  }
+
   /**
    * Determines if the content browser remains attached
    * to its parent chrome window.
    *
    * We determine this by checking if the <browser> element
    * is still attached to the DOM.
    *
    * @return {boolean}
--- a/remote/test/browser/browser.ini
+++ b/remote/test/browser/browser.ini
@@ -15,8 +15,9 @@ support-files =
 [browser_runtime_evaluate.js]
 [browser_runtime_remote_objects.js]
 [browser_runtime_callFunctionOn.js]
 [browser_runtime_executionContext.js]
 skip-if = os == "mac" || (verify && os == 'win') # bug 1547961
 [browser_session.js]
 [browser_tabs.js]
 [browser_target.js]
+[browser_target_close.js]
new file mode 100644
--- /dev/null
+++ b/remote/test/browser/browser_target_close.js
@@ -0,0 +1,42 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
+
+// Test the Target closeTarget method and the targetDestroyed event.
+add_task(async function() {
+  info("Start the CDP server");
+  RemoteAgent.listen(Services.io.newURI("http://localhost:9222"));
+  const CDP = await getCDP();
+  const client = await CDP({});
+
+  info("Setup Target domain");
+  const { Target } = client;
+  const targetCreated = Target.targetCreated();
+  Target.setDiscoverTargets({ discover: true });
+  await targetCreated;
+
+  info("Create a new tab and wait for the target to be created");
+  const otherTargetCreated = Target.targetCreated();
+  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
+  const {targetInfo} = await otherTargetCreated;
+
+  const onTabClose = BrowserTestUtils.waitForEvent(tab, "TabClose");
+  const targetDestroyed = Target.targetDestroyed();
+
+  info("Close the target");
+  Target.closeTarget({ targetId: targetInfo.targetId });
+
+  await onTabClose;
+  ok(true, "Tab was closed");
+
+  await targetDestroyed;
+  ok(true, "Received the expected Target.targetDestroyed event");
+
+  await client.close();
+  ok(true, "The client is closed");
+
+  await RemoteAgent.close();
+});
--- a/remote/test/demo.js
+++ b/remote/test/demo.js
@@ -1,38 +1,68 @@
 "use strict";
 
+/**
+ * nodejs script to test basic CDP behaviors against Firefox and Chromium.
+ *
+ * Install chrome-remote-interface, the npm package for a CDP client in node:
+ * $ npm install chrome-remote-interface
+ *
+ * Run Firefox or Chromium with server turned on:
+ * $ ./mach run --setpref "remote.enabled=true" --remote-debugging-port 9222
+ * $ firefox --remote-debugging-port 9222
+ * $ chromium-browser --remote-debugging-port=9222
+ *
+ * Run this script:
+ * $ node demo.js
+ */
 const CDP = require("chrome-remote-interface");
 
 async function demo() {
   let client;
   try {
     client = await CDP();
     const {Log, Network, Page, Runtime} = client;
-    let { result } = await Runtime.evaluate({expression: "this.obj = {foo:true}; this.obj"});
+
+    // Bug 1553756, Firefox requires `contextId` argument to be passed to
+    // Runtime.evaluate, so fetch the current context id it first.
+    Runtime.enable();
+    const { context } = await Runtime.executionContextCreated();
+    const contextId = context.id;
+
+    let { result } = await Runtime.evaluate({
+      expression: "this.obj = {foo:true}; this.obj",
+      contextId,
+    });
     console.log("1", result);
-    ({ result } = await Runtime.evaluate({expression: "this.obj"}));
+    ({ result } = await Runtime.evaluate({
+      expression: "this.obj",
+      contextId,
+    }));
     console.log("2", result);
-    ({ result } = await Runtime.evaluate({expression: "this.obj.foo"}));
+    ({ result } = await Runtime.evaluate({
+      expression: "this.obj.foo",
+      contextId,
+    }));
     console.log("3", result);
 
-
     // receive console.log messages and print them
     Log.enable();
     Log.entryAdded(({entry}) => {
       const {timestamp, level, text, args} = entry;
       const msg = text || args.join(" ");
       console.log(`${new Date(timestamp)}\t${level.toUpperCase()}\t${msg}`);
     });
 
     // turn on navigation related events, such as DOMContentLoaded et al.
     await Page.enable();
 
+    const onLoad = Page.loadEventFired();
     await Page.navigate({url: "data:text/html,test-page<script>console.log('foo');</script><script>'</script>"});
-    await Page.loadEventFired();
+    await onLoad;
   } catch (e) {
     console.error(e);
   } finally {
     if (client) {
       await client.close();
     }
   }
 }
--- a/servo/components/malloc_size_of/lib.rs
+++ b/servo/components/malloc_size_of/lib.rs
@@ -87,17 +87,17 @@ use std::ops::Range;
 use std::ops::{Deref, DerefMut};
 use std::os::raw::c_void;
 use void::Void;
 
 /// A C function that takes a pointer to a heap allocation and returns its size.
 type VoidPtrToSizeFn = unsafe extern "C" fn(ptr: *const c_void) -> usize;
 
 /// A closure implementing a stateful predicate on pointers.
-type VoidPtrToBoolFnMut = FnMut(*const c_void) -> bool;
+type VoidPtrToBoolFnMut = dyn FnMut(*const c_void) -> bool;
 
 /// Operations used when measuring heap usage of data structures.
 pub struct MallocSizeOfOps {
     /// A function that returns the size of a heap allocation.
     size_of_op: VoidPtrToSizeFn,
 
     /// Like `size_of_op`, but can take an interior pointer. Optional because
     /// not all allocators support this operation. If it's not provided, some
--- a/servo/components/selectors/context.rs
+++ b/servo/components/selectors/context.rs
@@ -129,17 +129,17 @@ where
     /// MatchingContext immutable again.
     nesting_level: usize,
 
     /// Whether we're inside a negation or not.
     in_negation: bool,
 
     /// An optional hook function for checking whether a pseudo-element
     /// should match when matching_mode is ForStatelessPseudoElement.
-    pub pseudo_element_matching_fn: Option<&'a Fn(&Impl::PseudoElement) -> bool>,
+    pub pseudo_element_matching_fn: Option<&'a dyn Fn(&Impl::PseudoElement) -> bool>,
 
     /// Extra implementation-dependent matching data.
     pub extra_data: Impl::ExtraMatchingData,
 
     quirks_mode: QuirksMode,
     classes_and_ids_case_sensitivity: CaseSensitivity,
     _impl: ::std::marker::PhantomData<Impl>,
 }
--- a/servo/components/style/animation.rs
+++ b/servo/components/style/animation.rs
@@ -483,17 +483,17 @@ pub fn start_transitions_if_applicable(
     had_animations
 }
 
 fn compute_style_for_animation_step<E>(
     context: &SharedStyleContext,
     step: &KeyframesStep,
     previous_style: &ComputedValues,
     style_from_cascade: &Arc<ComputedValues>,
-    font_metrics_provider: &FontMetricsProvider,
+    font_metrics_provider: &dyn FontMetricsProvider,
 ) -> Arc<ComputedValues>
 where
     E: TElement,
 {
     match step.value {
         KeyframesStepValue::ComputedValues => style_from_cascade.clone(),
         KeyframesStepValue::Declarations {
             block: ref declarations,
@@ -668,17 +668,17 @@ pub enum AnimationUpdate {
 /// transitions in response to a style change (that is,
 /// consider_starting_transitions + maybe_start_animations, but handling
 /// canceled animations, duration changes, etc, there instead of here), and this
 /// function should be only about the style update in response of a transition.
 pub fn update_style_for_animation<E>(
     context: &SharedStyleContext,
     animation: &Animation,
     style: &mut Arc<ComputedValues>,
-    font_metrics_provider: &FontMetricsProvider,
+    font_metrics_provider: &dyn FontMetricsProvider,
 ) -> AnimationUpdate
 where
     E: TElement,
 {
     debug!("update_style_for_animation: {:?}", animation);
     debug_assert!(!animation.is_expired());
 
     match *animation {
--- a/servo/components/style/parser.rs
+++ b/servo/components/style/parser.rs
@@ -46,33 +46,33 @@ pub struct ParserContext<'a> {
     pub url_data: &'a UrlExtraData,
     /// The current rule type, if any.
     pub rule_type: Option<CssRuleType>,
     /// The mode to use when parsing.
     pub parsing_mode: ParsingMode,
     /// The quirks mode of this stylesheet.
     pub quirks_mode: QuirksMode,
     /// The active error reporter, or none if error reporting is disabled.
-    error_reporter: Option<&'a ParseErrorReporter>,
+    error_reporter: Option<&'a dyn ParseErrorReporter>,
     /// The currently active namespaces.
     pub namespaces: Option<&'a Namespaces>,
     /// The use counters we want to record while parsing style rules, if any.
     pub use_counters: Option<&'a UseCounters>,
 }
 
 impl<'a> ParserContext<'a> {
     /// Create a parser context.
     #[inline]
     pub fn new(
         stylesheet_origin: Origin,
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
-        error_reporter: Option<&'a ParseErrorReporter>,
+        error_reporter: Option<&'a dyn ParseErrorReporter>,
         use_counters: Option<&'a UseCounters>,
     ) -> Self {
         Self {
             stylesheet_origin,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
@@ -84,17 +84,17 @@ impl<'a> ParserContext<'a> {
 
     /// Create a parser context for on-the-fly parsing in CSSOM
     #[inline]
     pub fn new_for_cssom(
         url_data: &'a UrlExtraData,
         rule_type: Option<CssRuleType>,
         parsing_mode: ParsingMode,
         quirks_mode: QuirksMode,
-        error_reporter: Option<&'a ParseErrorReporter>,
+        error_reporter: Option<&'a dyn ParseErrorReporter>,
         use_counters: Option<&'a UseCounters>,
     ) -> Self {
         Self::new(
             Origin::Author,
             url_data,
             rule_type,
             parsing_mode,
             quirks_mode,
--- a/servo/components/style/properties/cascade.rs
+++ b/servo/components/style/properties/cascade.rs
@@ -77,17 +77,17 @@ pub fn cascade<E>(
     device: &Device,
     pseudo: Option<&PseudoElement>,
     rule_node: &StrongRuleNode,
     guards: &StylesheetGuards,
     parent_style: Option<&ComputedValues>,
     parent_style_ignoring_first_line: Option<&ComputedValues>,
     layout_parent_style: Option<&ComputedValues>,
     visited_rules: Option<&StrongRuleNode>,
-    font_metrics_provider: &FontMetricsProvider,
+    font_metrics_provider: &dyn FontMetricsProvider,
     quirks_mode: QuirksMode,
     rule_cache: Option<&RuleCache>,
     rule_cache_conditions: &mut RuleCacheConditions,
     element: Option<E>,
 ) -> Arc<ComputedValues>
 where
     E: TElement,
 {
@@ -111,17 +111,17 @@ where
 fn cascade_rules<E>(
     device: &Device,
     pseudo: Option<&PseudoElement>,
     rule_node: &StrongRuleNode,
     guards: &StylesheetGuards,
     parent_style: Option<&ComputedValues>,
     parent_style_ignoring_first_line: Option<&ComputedValues>,
     layout_parent_style: Option<&ComputedValues>,
-    font_metrics_provider: &FontMetricsProvider,
+    font_metrics_provider: &dyn FontMetricsProvider,
     cascade_mode: CascadeMode,
     quirks_mode: QuirksMode,
     rule_cache: Option<&RuleCache>,
     rule_cache_conditions: &mut RuleCacheConditions,
     element: Option<E>,
 ) -> Arc<ComputedValues>
 where
     E: TElement,
@@ -208,17 +208,17 @@ pub fn apply_declarations<'a, E, F, I>(
     device: &Device,
     pseudo: Option<&PseudoElement>,
     rules: &StrongRuleNode,
     guards: &StylesheetGuards,
     iter_declarations: F,
     parent_style: Option<&ComputedValues>,
     parent_style_ignoring_first_line: Option<&ComputedValues>,
     layout_parent_style: Option<&ComputedValues>,
-    font_metrics_provider: &FontMetricsProvider,
+    font_metrics_provider: &dyn FontMetricsProvider,
     cascade_mode: CascadeMode,
     quirks_mode: QuirksMode,
     rule_cache: Option<&RuleCache>,
     rule_cache_conditions: &mut RuleCacheConditions,
     element: Option<E>,
 ) -> Arc<ComputedValues>
 where
     E: TElement,
--- a/servo/components/style/properties/declaration_block.rs
+++ b/servo/components/style/properties/declaration_block.rs
@@ -1194,17 +1194,17 @@ where
 /// A helper to parse the style attribute of an element, in order for this to be
 /// shared between Servo and Gecko.
 ///
 /// Inline because we call this cross-crate.
 #[inline]
 pub fn parse_style_attribute(
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: Option<&ParseErrorReporter>,
+    error_reporter: Option<&dyn ParseErrorReporter>,
     quirks_mode: QuirksMode,
 ) -> PropertyDeclarationBlock {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         quirks_mode,
@@ -1221,17 +1221,17 @@ pub fn parse_style_attribute(
 ///
 /// This does not attempt to parse !important at all.
 #[inline]
 pub fn parse_one_declaration_into(
     declarations: &mut SourcePropertyDeclaration,
     id: PropertyId,
     input: &str,
     url_data: &UrlExtraData,
-    error_reporter: Option<&ParseErrorReporter>,
+    error_reporter: Option<&dyn ParseErrorReporter>,
     parsing_mode: ParsingMode,
     quirks_mode: QuirksMode
 ) -> Result<(), ()> {
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         parsing_mode,
--- a/servo/components/style/properties/gecko.mako.rs
+++ b/servo/components/style/properties/gecko.mako.rs
@@ -1132,52 +1132,55 @@ fn static_assert() {
 
     ${impl_simple_copy('order', 'mOrder')}
 
     % for value in GRID_LINES:
     pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) {
         use crate::gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
 
         let line = &mut self.gecko.${value.gecko};
-        let line_name = &mut line.mLineName;
-        match v.ident.as_ref() {
-            Some(i) => i.0.with_str(|s| line_name.assign(s)),
-            None => line_name.assign(""),
-        };
+        line.mLineName.set_move(unsafe {
+            RefPtr::from_addrefed(match v.ident {
+                Some(i) => i.0,
+                None => atom!(""),
+            }.into_addrefed())
+        });
         line.mHasSpan = v.is_span;
         if let Some(integer) = v.line_num {
             // clamping the integer between a range
             line.mInteger = cmp::max(
                 nsStyleGridLine_kMinLine,
                 cmp::min(integer, nsStyleGridLine_kMaxLine),
             );
         }
     }
 
     pub fn copy_${value.name}_from(&mut self, other: &Self) {
         self.gecko.${value.gecko}.mHasSpan = other.gecko.${value.gecko}.mHasSpan;
         self.gecko.${value.gecko}.mInteger = other.gecko.${value.gecko}.mInteger;
-        self.gecko.${value.gecko}.mLineName.assign(&*other.gecko.${value.gecko}.mLineName);
+        unsafe {
+            self.gecko.${value.gecko}.mLineName.set(&other.gecko.${value.gecko}.mLineName);
+        }
     }
 
     pub fn reset_${value.name}(&mut self, other: &Self) {
         self.copy_${value.name}_from(other)
     }
 
     pub fn clone_${value.name}(&self) -> longhands::${value.name}::computed_value::T {
         use crate::gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine};
 
         longhands::${value.name}::computed_value::T {
             is_span: self.gecko.${value.gecko}.mHasSpan,
             ident: {
-                let name = self.gecko.${value.gecko}.mLineName.to_string();
-                if name.len() == 0 {
+                let name = unsafe { Atom::from_raw(self.gecko.${value.gecko}.mLineName.mRawPtr) };
+                if name == atom!("") {
                     None
                 } else {
-                    Some(CustomIdent(Atom::from(name)))
+                    Some(CustomIdent(name))
                 }
             },
             line_num:
                 if self.gecko.${value.gecko}.mInteger == 0 {
                     None
                 } else {
                     debug_assert!(nsStyleGridLine_kMinLine <= self.gecko.${value.gecko}.mInteger);
                     debug_assert!(self.gecko.${value.gecko}.mInteger <= nsStyleGridLine_kMaxLine);
@@ -1206,30 +1209,31 @@ fn static_assert() {
     pub fn clone_grid_auto_${kind}(&self) -> longhands::grid_auto_${kind}::computed_value::T {
         crate::values::generics::grid::TrackSize::from_gecko_style_coords(&self.gecko.mGridAuto${kind.title()}Min,
                                                                      &self.gecko.mGridAuto${kind.title()}Max)
     }
 
     pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) {
         <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
         use crate::gecko_bindings::structs::{nsTArray, nsStyleGridLine_kMaxLine};
-        use nsstring::nsCString;
         use std::usize;
         use crate::values::CustomIdent;
         use crate::values::generics::grid::TrackListType::Auto;
         use crate::values::generics::grid::{GridTemplateComponent, RepeatCount};
 
         #[inline]
-        fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray<nsCString>) {
+        fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray<structs::RefPtr<structs::nsAtom>>) {
             unsafe {
-                bindings::Gecko_ResizeTArrayForCStrings(gecko_names, servo_names.len() as u32);
+                bindings::Gecko_ResizeAtomArray(gecko_names, servo_names.len() as u32);
             }
 
             for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) {
-                servo_name.0.with_str(|s| gecko_name.assign(s))
+                gecko_name.set_move(unsafe {
+                    RefPtr::from_addrefed(servo_name.0.clone().into_addrefed())
+                });
             }
         }
 
         let max_lines = nsStyleGridLine_kMaxLine as usize - 1;      // for accounting the final <line-names>
 
         let result = match v {
             GridTemplateComponent::None => ptr::null_mut(),
             GridTemplateComponent::TrackList(track) => {
@@ -1257,19 +1261,19 @@ fn static_assert() {
                     value.mRepeatAutoIndex = idx as i16;
                     // NOTE: Gecko supports only one set of values in <auto-repeat>
                     // i.e., it can only take repeat(auto-fill, [a] 10px [b]), and no more.
                     set_line_names(&auto_repeat.line_names[0], &mut value.mRepeatAutoLineNameListBefore);
                     set_line_names(&auto_repeat.line_names[1], &mut value.mRepeatAutoLineNameListAfter);
                     auto_track_size = Some(auto_repeat.track_sizes.get(0).unwrap().clone());
                 } else {
                     unsafe {
-                        bindings::Gecko_ResizeTArrayForCStrings(
+                        bindings::Gecko_ResizeAtomArray(
                             &mut value.mRepeatAutoLineNameListBefore, 0);
-                        bindings::Gecko_ResizeTArrayForCStrings(
+                        bindings::Gecko_ResizeAtomArray(
                             &mut value.mRepeatAutoLineNameListAfter, 0);
                     }
                 }
 
                 let mut line_names = track.line_names.into_iter();
                 let mut values_iter = track.values.into_iter();
                 {
                     let min_max_iter = value.mMinTrackSizingFunctions.iter_mut()
@@ -1333,37 +1337,37 @@ fn static_assert() {
 
     pub fn reset_grid_template_${kind}(&mut self, other: &Self) {
         self.copy_grid_template_${kind}_from(other)
     }
 
     pub fn clone_grid_template_${kind}(&self) -> longhands::grid_template_${kind}::computed_value::T {
         <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %>
         use crate::gecko_bindings::structs::nsTArray;
-        use nsstring::nsCString;
         use crate::values::CustomIdent;
         use crate::values::generics::grid::{GridTemplateComponent, LineNameList, RepeatCount};
         use crate::values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat, TrackSize};
 
         let value = match unsafe { ${self_grid}.mPtr.as_ref() } {
             None => return GridTemplateComponent::None,
             Some(value) => value,
         };
 
         #[inline]
-        fn to_boxed_customident_slice(gecko_names: &nsTArray<nsCString>) -> Box<[CustomIdent]> {
+        fn to_boxed_customident_slice(gecko_names: &nsTArray<structs::RefPtr<structs::nsAtom>>) -> Box<[CustomIdent]> {
             let idents: Vec<CustomIdent> = gecko_names.iter().map(|gecko_name| {
-                CustomIdent(Atom::from(unsafe { gecko_name.as_str_unchecked() }))
+                CustomIdent(unsafe { Atom::from_raw(gecko_name.mRawPtr) })
             }).collect();
             idents.into_boxed_slice()
         }
 
         #[inline]
-        fn to_line_names_vec(gecko_line_names: &nsTArray<nsTArray<nsCString>>)
-            -> Vec<Box<[CustomIdent]>> {
+        fn to_line_names_vec(
+            gecko_line_names: &nsTArray<nsTArray<structs::RefPtr<structs::nsAtom>>>,
+        ) -> Vec<Box<[CustomIdent]>> {
             gecko_line_names.iter().map(|gecko_names| {
                 to_boxed_customident_slice(gecko_names)
             }).collect()
         }
 
         let repeat_auto_index = value.mRepeatAutoIndex as usize;
         if value.mIsSubgrid() {
             let mut names_vec = to_line_names_vec(&value.mLineNameLists);
--- a/servo/components/style/str.rs
+++ b/servo/components/style/str.rs
@@ -55,17 +55,17 @@ pub fn split_html_space_chars<'a>(
 #[inline]
 pub fn split_commas<'a>(s: &'a str) -> Filter<Split<'a, char>, fn(&&str) -> bool> {
     s.split(',').filter(not_empty as fn(&&str) -> bool)
 }
 
 /// Character is ascii digit
 pub fn is_ascii_digit(c: &char) -> bool {
     match *c {
-        '0'...'9' => true,
+        '0'..='9' => true,
         _ => false,
     }
 }
 
 fn is_decimal_point(c: char) -> bool {
     c == '.'
 }
 
@@ -156,17 +156,17 @@ where
 /// Returns true if a given string has a given prefix with case-insensitive match.
 pub fn starts_with_ignore_ascii_case(string: &str, prefix: &str) -> bool {
     string.len() >= prefix.len() &&
         string.as_bytes()[0..prefix.len()].eq_ignore_ascii_case(prefix.as_bytes())
 }
 
 /// Returns an ascii lowercase version of a string, only allocating if needed.
 pub fn string_as_ascii_lowercase<'a>(input: &'a str) -> Cow<'a, str> {
-    if input.bytes().any(|c| matches!(c, b'A'...b'Z')) {
+    if input.bytes().any(|c| matches!(c, b'A'..=b'Z')) {
         input.to_ascii_lowercase().into()
     } else {
         // Already ascii lowercase.
         Cow::Borrowed(input)
     }
 }
 
 /// To avoid accidentally instantiating multiple monomorphizations of large
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -347,17 +347,17 @@ impl CssRule {
     ///
     /// Input state is None for a nested rule
     pub fn parse(
         css: &str,
         insert_rule_context: InsertRuleContext,
         parent_stylesheet_contents: &StylesheetContents,
         shared_lock: &SharedRwLock,
         state: State,
-        loader: Option<&StylesheetLoader>,
+        loader: Option<&dyn StylesheetLoader>,
     ) -> Result<Self, RulesMutateError> {
         let url_data = parent_stylesheet_contents.url_data.read();
         let context = ParserContext::new(
             parent_stylesheet_contents.origin,
             &url_data,
             None,
             ParsingMode::DEFAULT,
             parent_stylesheet_contents.quirks_mode,
--- a/servo/components/style/stylesheets/rule_list.rs
+++ b/servo/components/style/stylesheets/rule_list.rs
@@ -122,29 +122,29 @@ pub trait CssRulesHelpers {
     /// instead, but that seems overkill.
     fn insert_rule(
         &self,
         lock: &SharedRwLock,
         rule: &str,
         parent_stylesheet_contents: &StylesheetContents,
         index: usize,
         nested: bool,
-        loader: Option<&StylesheetLoader>,
+        loader: Option<&dyn StylesheetLoader>,
     ) -> Result<CssRule, RulesMutateError>;
 }
 
 impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
     fn insert_rule(
         &self,
         lock: &SharedRwLock,
         rule: &str,
         parent_stylesheet_contents: &StylesheetContents,
         index: usize,
         nested: bool,
-        loader: Option<&StylesheetLoader>,
+        loader: Option<&dyn StylesheetLoader>,
     ) -> Result<CssRule, RulesMutateError> {
         let new_rule = {
             let read_guard = lock.read();
             let rules = self.read_with(&read_guard);
 
             // Step 1, 2
             if index > rules.0.len() {
                 return Err(RulesMutateError::IndexSize);
--- a/servo/components/style/stylesheets/rule_parser.rs
+++ b/servo/components/style/stylesheets/rule_parser.rs
@@ -39,17 +39,17 @@ pub struct InsertRuleContext<'a> {
     pub index: usize,
 }
 
 /// The parser for the top-level rules in a stylesheet.
 pub struct TopLevelRuleParser<'a> {
     /// A reference to the lock we need to use to create rules.
     pub shared_lock: &'a SharedRwLock,
     /// A reference to a stylesheet loader if applicable, for `@import` rules.
-    pub loader: Option<&'a StylesheetLoader>,
+    pub loader: Option<&'a dyn StylesheetLoader>,
     /// The top-level parser context.
     ///
     /// This won't contain any namespaces, and only nested parsers created with
     /// `ParserContext::new_with_rule_type` will.
     pub context: ParserContext<'a>,
     /// The current state of the parser.
     pub state: State,
     /// Whether we have tried to parse was invalid due to being in the wrong
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -71,18 +71,18 @@ pub struct StylesheetContents {
 impl StylesheetContents {
     /// Parse a given CSS string, with a given url-data, origin, and
     /// quirks mode.
     pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         shared_lock: &SharedRwLock,
-        stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: Option<&ParseErrorReporter>,
+        stylesheet_loader: Option<&dyn StylesheetLoader>,
+        error_reporter: Option<&dyn ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
         use_counters: Option<&UseCounters>,
     ) -> Self {
         let namespaces = RwLock::new(Namespaces::default());
         let (rules, source_map_url, source_url) = Stylesheet::parse_rules(
             css,
             &url_data,
@@ -338,18 +338,18 @@ impl StylesheetInDocument for DocumentSt
 }
 
 impl Stylesheet {
     /// Updates an empty stylesheet from a given string of text.
     pub fn update_from_str(
         existing: &Stylesheet,
         css: &str,
         url_data: UrlExtraData,
-        stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: Option<&ParseErrorReporter>,
+        stylesheet_loader: Option<&dyn StylesheetLoader>,
+        error_reporter: Option<&dyn ParseErrorReporter>,
         line_number_offset: u32,
     ) {
         let namespaces = RwLock::new(Namespaces::default());
 
         // FIXME: Consider adding use counters to Servo?
         let (rules, source_map_url, source_url) = Self::parse_rules(
             css,
             &url_data,
@@ -377,18 +377,18 @@ impl Stylesheet {
     }
 
     fn parse_rules(
         css: &str,
         url_data: &UrlExtraData,
         origin: Origin,
         namespaces: &mut Namespaces,
         shared_lock: &SharedRwLock,
-        stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: Option<&ParseErrorReporter>,
+        stylesheet_loader: Option<&dyn StylesheetLoader>,
+        error_reporter: Option<&dyn ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
         use_counters: Option<&UseCounters>,
     ) -> (Vec<CssRule>, Option<String>, Option<String>) {
         let mut rules = Vec::new();
         let mut input = ParserInput::new_with_line_number_offset(css, line_number_offset);
         let mut input = Parser::new(&mut input);
 
@@ -445,18 +445,18 @@ impl Stylesheet {
     /// Effectively creates a new stylesheet and forwards the hard work to
     /// `Stylesheet::update_from_str`.
     pub fn from_str(
         css: &str,
         url_data: UrlExtraData,
         origin: Origin,
         media: Arc<Locked<MediaList>>,
         shared_lock: SharedRwLock,
-        stylesheet_loader: Option<&StylesheetLoader>,
-        error_reporter: Option<&ParseErrorReporter>,
+        stylesheet_loader: Option<&dyn StylesheetLoader>,
+        error_reporter: Option<&dyn ParseErrorReporter>,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> Self {
         // FIXME: Consider adding use counters to Servo?
         let contents = StylesheetContents::from_str(
             css,
             url_data,
             origin,
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -646,17 +646,17 @@ impl Stylist {
 
     /// Computes the style for a given "precomputed" pseudo-element, taking the
     /// universal rules and applying them.
     pub fn precomputed_values_for_pseudo<E>(
         &self,
         guards: &StylesheetGuards,
         pseudo: &PseudoElement,
         parent: Option<&ComputedValues>,
-        font_metrics: &FontMetricsProvider,
+        font_metrics: &dyn FontMetricsProvider,
     ) -> Arc<ComputedValues>
     where
         E: TElement,
     {
         debug_assert!(pseudo.is_precomputed());
 
         let rule_node = self.rule_node_for_precomputed_pseudo(guards, pseudo, None);
 
@@ -674,17 +674,17 @@ impl Stylist {
     ///
     /// TODO(emilio): The type parameter could go away with a void type
     /// implementing TElement.
     pub fn precomputed_values_for_pseudo_with_rule_node<E>(
         &self,
         guards: &StylesheetGuards,
         pseudo: &PseudoElement,
         parent: Option<&ComputedValues>,
-        font_metrics: &FontMetricsProvider,
+        font_metrics: &dyn FontMetricsProvider,
         rules: StrongRuleNode,
     ) -> Arc<ComputedValues>
     where
         E: TElement,
     {
         self.compute_pseudo_element_style_with_inputs::<E>(
             CascadeInputs {
                 rules: Some(rules),
@@ -769,18 +769,18 @@ impl Stylist {
     pub fn lazily_compute_pseudo_element_style<E>(
         &self,
         guards: &StylesheetGuards,
         element: E,
         pseudo: &PseudoElement,
         rule_inclusion: RuleInclusion,
         parent_style: &ComputedValues,
         is_probe: bool,
-        font_metrics: &FontMetricsProvider,
-        matching_fn: Option<&Fn(&PseudoElement) -> bool>,
+        font_metrics: &dyn FontMetricsProvider,
+        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
     ) -> Option<Arc<ComputedValues>>
     where
         E: TElement,
     {
         let cascade_inputs = self.lazy_pseudo_rules(
             guards,
             element,
             parent_style,
@@ -805,17 +805,17 @@ impl Stylist {
     /// selector matching for eager pseudo-elements when we need to recompute
     /// their style with a new parent style.
     pub fn compute_pseudo_element_style_with_inputs<E>(
         &self,
         inputs: CascadeInputs,
         pseudo: &PseudoElement,
         guards: &StylesheetGuards,
         parent_style: Option<&ComputedValues>,
-        font_metrics: &FontMetricsProvider,
+        font_metrics: &dyn FontMetricsProvider,
         element: Option<E>,
     ) -> Arc<ComputedValues>
     where
         E: TElement,
     {
         // FIXME(emilio): The lack of layout_parent_style here could be
         // worrying, but we're probably dropping the display fixup for
         // pseudos other than before and after, so it's probably ok.
@@ -858,17 +858,17 @@ impl Stylist {
         &self,
         element: Option<E>,
         pseudo: Option<&PseudoElement>,
         inputs: CascadeInputs,
         guards: &StylesheetGuards,
         parent_style: Option<&ComputedValues>,
         parent_style_ignoring_first_line: Option<&ComputedValues>,
         layout_parent_style: Option<&ComputedValues>,
-        font_metrics: &FontMetricsProvider,
+        font_metrics: &dyn FontMetricsProvider,
         rule_cache: Option<&RuleCache>,
         rule_cache_conditions: &mut RuleCacheConditions,
     ) -> Arc<ComputedValues>
     where
         E: TElement,
     {
         debug_assert!(pseudo.is_some() || element.is_some(), "Huh?");
 
@@ -915,17 +915,17 @@ impl Stylist {
     fn lazy_pseudo_rules<E>(
         &self,
         guards: &StylesheetGuards,
         element: E,
         parent_style: &ComputedValues,
         pseudo: &PseudoElement,
         is_probe: bool,
         rule_inclusion: RuleInclusion,
-        matching_fn: Option<&Fn(&PseudoElement) -> bool>,
+        matching_fn: Option<&dyn Fn(&PseudoElement) -> bool>,
     ) -> Option<CascadeInputs>
     where
         E: TElement,
     {
         debug_assert!(pseudo.is_lazy());
 
         // Apply the selector flags. We should be in sequential mode
         // already, so we can directly apply the parent flags.
--- a/servo/components/style/values/computed/mod.rs
+++ b/servo/components/style/values/computed/mod.rs
@@ -142,17 +142,17 @@ pub struct Context<'a> {
     /// painful.
     ///
     /// TODO(emilio): Make constructors for Context, and drop this.
     #[cfg(feature = "servo")]
     pub cached_system_font: Option<()>,
 
     /// A font metrics provider, used to access font metrics to implement
     /// font-relative units.
-    pub font_metrics_provider: &'a FontMetricsProvider,
+    pub font_metrics_provider: &'a dyn FontMetricsProvider,
 
     /// Whether or not we are computing the media list in a media query
     pub in_media_query: bool,
 
     /// The quirks mode of this context.
     pub quirks_mode: QuirksMode,
 
     /// Whether this computation is being done for a SMIL animation.
--- a/servo/components/style/values/specified/position.rs
+++ b/servo/components/style/values/specified/position.rs
@@ -2,17 +2,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
 
 //! CSS handling for the specified value of
 //! [`position`][position]s
 //!
 //! [position]: https://drafts.csswg.org/css-backgrounds-3/#position
 
-use crate::hash::FxHashMap;
+use crate::Atom;
+use crate::selector_map::PrecomputedHashMap;
 use crate::parser::{Parse, ParserContext};
 use crate::str::HTML_SPACE_CHARACTERS;
 use crate::values::computed::LengthPercentage as ComputedLengthPercentage;
 use crate::values::computed::{Context, Percentage, ToComputedValue};
 use crate::values::generics::position::Position as GenericPosition;
 use crate::values::generics::position::ZIndex as GenericZIndex;
 use crate::values::specified::{AllowQuirks, Integer, LengthPercentage};
 use crate::Zero;
@@ -495,65 +496,65 @@ impl TemplateAreas {
     pub fn from_vec(strings: Vec<crate::OwnedStr>) -> Result<Self, ()> {
         if strings.is_empty() {
             return Err(());
         }
         let mut areas: Vec<NamedArea> = vec![];
         let mut width = 0;
         {
             let mut row = 0u32;
-            let mut area_indices = FxHashMap::<&str, usize>::default();
+            let mut area_indices = PrecomputedHashMap::<Atom, usize>::default();
             for string in &strings {
                 let mut current_area_index: Option<usize> = None;
                 row += 1;
                 let mut column = 0u32;
                 for token in TemplateAreasTokenizer(string) {
                     column += 1;
-                    let token = if let Some(token) = token? {
-                        token
+                    let name = if let Some(token) = token? {
+                        Atom::from(token)
                     } else {
                         if let Some(index) = current_area_index.take() {
                             if areas[index].columns.end != column {
                                 return Err(());
                             }
                         }
                         continue;
                     };
                     if let Some(index) = current_area_index {
-                        if &*areas[index].name == token {
+                        if areas[index].name == name {
                             if areas[index].rows.start == row {
                                 areas[index].columns.end += 1;
                             }
                             continue;
                         }
                         if areas[index].columns.end != column {
                             return Err(());
                         }
                     }
-                    if let Some(index) = area_indices.get(token).cloned() {
+                    if let Some(index) = area_indices.get(&name).cloned() {
                         if areas[index].columns.start != column || areas[index].rows.end != row {
                             return Err(());
                         }
                         areas[index].rows.end += 1;
                         current_area_index = Some(index);
                         continue;
                     }
                     let index = areas.len();
+                    assert!(area_indices.insert(name.clone(), index).is_none());
                     areas.push(NamedArea {
-                        name: token.to_owned().into(),
+                        name,
                         columns: UnsignedRange {
                             start: column,
                             end: column + 1,
                         },
                         rows: UnsignedRange {
                             start: row,
                             end: row + 1,
                         },
                     });
-                    assert!(area_indices.insert(token, index).is_none());
                     current_area_index = Some(index);
                 }
                 if let Some(index) = current_area_index {
                     if areas[index].columns.end != column + 1 {
                         assert_ne!(areas[index].rows.start, row);
                         return Err(());
                     }
                 }
@@ -624,17 +625,17 @@ pub struct UnsignedRange {
 }
 
 #[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
 #[repr(C)]
 /// Not associated with any particular grid item, but can be referenced from the
 /// grid-placement properties.
 pub struct NamedArea {
     /// Name of the `named area`
-    pub name: crate::OwnedStr,
+    pub name: Atom,
     /// Rows of the `named area`
     pub rows: UnsignedRange,
     /// Columns of the `named area`
     pub columns: UnsignedRange,
 }
 
 /// Tokenize the string into a list of the tokens,
 /// using longest-match semantics
--- a/servo/components/style_traits/specified_value_info.rs
+++ b/servo/components/style_traits/specified_value_info.rs
@@ -22,17 +22,17 @@ pub mod CssType {
     pub const COLOR: u8 = 1 << 0;
     /// <gradient>
     pub const GRADIENT: u8 = 1 << 1;
     /// <timing-function>
     pub const TIMING_FUNCTION: u8 = 1 << 2;
 }
 
 /// See SpecifiedValueInfo::collect_completion_keywords.
-pub type KeywordsCollectFn<'a> = &'a mut FnMut(&[&'static str]);
+pub type KeywordsCollectFn<'a> = &'a mut dyn FnMut(&[&'static str]);
 
 /// Information of values of a given specified value type.
 ///
 /// This trait is derivable with `#[derive(SpecifiedValueInfo)]`.
 ///
 /// The algorithm traverses the type definition. For `SUPPORTED_TYPES`,
 /// it puts an or'ed value of `SUPPORTED_TYPES` of all types it finds.
 /// For `collect_completion_keywords`, it recursively invokes this
--- a/servo/ports/geckolib/cbindgen.toml
+++ b/servo/ports/geckolib/cbindgen.toml
@@ -461,16 +461,18 @@ renaming_overrides_prefixing = true
 "Atom" = """
   StyleAtom(size_t) = delete;
   StyleAtom() = delete;
 
   // NOTE(emilio): For now we don't need to expose anything else, but it'd be trivial if we wanted to.
   inline bool IsStatic() const;
   inline nsAtom* AsAtom() const;
 
+  inline explicit StyleAtom(already_AddRefed<nsAtom> aAtom);
+
   // Could be implemented if wanted.
   StyleAtom& operator=(const StyleAtom&) = delete;
 
   inline StyleAtom(const StyleAtom& aOther);
   inline ~StyleAtom();
 """
 
 "OwnedStr" = """
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -1378,28 +1378,28 @@ pub extern "C" fn Servo_StyleSheet_FromU
             loader,
             stylesheet,
             load_data,
             reusable_sheets,
         ))
     };
 
     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
-    let loader: Option<&StyleStylesheetLoader> = match loader {
+    let loader: Option<&dyn StyleStylesheetLoader> = match loader {
         None => None,
         Some(ref s) => Some(s),
     };
 
     Arc::new(StylesheetContents::from_str(
         input,
         url_data.clone(),
         mode_to_origin(mode),
         &global_style_data.shared_lock,
         loader,
-        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
         quirks_mode.into(),
         line_number_offset,
         use_counters,
     ))
     .into_strong()
 }
 
 #[no_mangle]
@@ -1903,17 +1903,17 @@ pub extern "C" fn Servo_CssRules_InsertR
             loader,
             gecko_stylesheet,
             ptr::null_mut(),
             ptr::null_mut(),
         ))
     };
     let loader = loader
         .as_ref()
-        .map(|loader| loader as &StyleStylesheetLoader);
+        .map(|loader| loader as &dyn StyleStylesheetLoader);
     let rule = unsafe { rule.as_ref().unwrap().as_str_unchecked() };
 
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let contents = StylesheetContents::as_arc(&contents);
     let result = Locked::<CssRules>::as_arc(&rules).insert_rule(
         &global_style_data.shared_lock,
         rule,
         contents,
@@ -3583,17 +3583,17 @@ fn get_pseudo_style(
     guard: &SharedRwLockReadGuard,
     element: GeckoElement,
     pseudo: &PseudoElement,
     rule_inclusion: RuleInclusion,
     styles: &ElementStyles,
     inherited_styles: Option<&ComputedValues>,
     doc_data: &PerDocumentStyleDataImpl,
     is_probe: bool,
-    matching_func: Option<&Fn(&PseudoElement) -> bool>,
+    matching_func: Option<&dyn Fn(&PseudoElement) -> bool>,
 ) -> Option<Arc<ComputedValues>> {
     let style = match pseudo.cascade_type() {
         PseudoElementCascadeType::Eager => {
             match *pseudo {
                 PseudoElement::FirstLetter => {
                     styles.pseudos.get(&pseudo).map(|pseudo_styles| {
                         // inherited_styles can be None when doing lazy resolution
                         // (e.g. for computed style) or when probing.  In that case
@@ -3772,17 +3772,17 @@ pub unsafe extern "C" fn Servo_StyleSet_
 
 fn parse_property_into(
     declarations: &mut SourcePropertyDeclaration,
     property_id: PropertyId,
     value: *const nsACString,
     data: *mut URLExtraData,
     parsing_mode: structs::ParsingMode,
     quirks_mode: QuirksMode,
-    reporter: Option<&ParseErrorReporter>,
+    reporter: Option<&dyn ParseErrorReporter>,
 ) -> Result<(), ()> {
     let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
     let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
     let parsing_mode = ParsingMode::from_bits_truncate(parsing_mode);
 
     parse_one_declaration_into(
         declarations,
         property_id,
@@ -3809,17 +3809,17 @@ pub extern "C" fn Servo_ParseProperty(
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
     let result = parse_property_into(
         &mut declarations,
         id,
         value,
         data,
         parsing_mode,
         quirks_mode.into(),
-        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
     );
 
     match result {
         Ok(()) => {
             let global_style_data = &*GLOBAL_STYLE_DATA;
             let mut block = PropertyDeclarationBlock::new();
             block.extend(declarations.drain(), Importance::Normal);
             Arc::new(global_style_data.shared_lock.wrap(block)).into_strong()
@@ -3961,17 +3961,17 @@ pub unsafe extern "C" fn Servo_ParseStyl
 ) -> Strong<RawServoDeclarationBlock> {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let value = (*data).as_str_unchecked();
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
     let url_data = UrlExtraData::from_ptr_ref(&raw_extra_data);
     Arc::new(global_style_data.shared_lock.wrap(parse_style_attribute(
         value,
         url_data,
-        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
         quirks_mode.into(),
     )))
     .into_strong()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_DeclarationBlock_CreateEmpty() -> Strong<RawServoDeclarationBlock> {
     let global_style_data = &*GLOBAL_STYLE_DATA;
@@ -4190,17 +4190,17 @@ fn set_property(
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, data);
     let result = parse_property_into(
         &mut source_declarations,
         property_id,
         value,
         data,
         parsing_mode,
         quirks_mode,
-        reporter.as_ref().map(|r| r as &ParseErrorReporter),
+        reporter.as_ref().map(|r| r as &dyn ParseErrorReporter),
     );
 
     if result.is_err() {
         return false;
     }
 
     let importance = if is_important {
         Importance::Important
@@ -5224,17 +5224,17 @@ fn simulate_compute_values_failure(prope
 
 #[cfg(not(feature = "gecko_debug"))]
 fn simulate_compute_values_failure(_: &PropertyValuePair) -> bool {
     false
 }
 
 fn create_context_for_animation<'a>(
     per_doc_data: &'a PerDocumentStyleDataImpl,
-    font_metrics_provider: &'a FontMetricsProvider,
+    font_metrics_provider: &'a dyn FontMetricsProvider,
     style: &'a ComputedValues,
     parent_style: Option<&'a ComputedValues>,
     for_smil_animation: bool,
     rule_cache_conditions: &'a mut RuleCacheConditions,
 ) -> Context<'a> {
     Context {
         is_root_element: false,
         builder: StyleBuilder::for_animation(per_doc_data.stylist.device(), style, parent_style),
@@ -6158,17 +6158,17 @@ pub unsafe extern "C" fn Servo_SelectorL
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_SelectorList_Drop(list: *mut RawServoSelectorList) {
     SelectorList::drop_ffi(list)
 }
 
 fn parse_color(
     value: &str,
-    error_reporter: Option<&ParseErrorReporter>,
+    error_reporter: Option<&dyn ParseErrorReporter>,
 ) -> Result<specified::Color, ()> {
     let mut input = ParserInput::new(value);
     let mut parser = Parser::new(&mut input);
     let url_data = unsafe { dummy_url_data() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
@@ -6222,17 +6222,17 @@ pub extern "C" fn Servo_ComputeColor(
     let value = unsafe { (*value).to_string() };
     let result_color = unsafe { result_color.as_mut().unwrap() };
 
     let reporter = unsafe { loader.as_mut() }.and_then(|loader| {
         // Make an ErrorReporter that will report errors as being "from DOM".
         ErrorReporter::new(ptr::null_mut(), loader, ptr::null_mut())
     });
 
-    match parse_color(&value, reporter.as_ref().map(|r| r as &ParseErrorReporter)) {
+    match parse_color(&value, reporter.as_ref().map(|r| r as &dyn ParseErrorReporter)) {
         Ok(specified_color) => {
             let computed_color = match raw_data {
                 Some(raw_data) => {
                     let data = PerDocumentStyleData::from_ffi(raw_data).borrow();
                     let device = data.stylist.device();
                     let quirks_mode = data.stylist.quirks_mode();
                     Context::for_media_query_evaluation(device, quirks_mode, |context| {
                         specified_color.to_computed_color(Some(&context))
@@ -6612,13 +6612,8 @@ pub unsafe extern "C" fn Servo_CloneBasi
 pub unsafe extern "C" fn Servo_StyleArcSlice_EmptyPtr() -> *mut c_void {
     style_traits::arc_slice::ArcSlice::<u64>::leaked_empty_ptr()
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn Servo_LoadData_GetLazy(source: &url::LoadDataSource) -> *const url::LoadData {
     source.get()
 }
-
-#[no_mangle]
-pub unsafe extern "C" fn Servo_MakeOwnedStr(out: &mut style::OwnedStr, in_: &nsACString) {
-    *out = in_.to_string().into()
-}
--- a/taskcluster/ci/test/raptor.yml
+++ b/taskcluster/ci/test/raptor.yml
@@ -1334,17 +1334,17 @@ raptor-scn-power-idle-fennec:
 
 raptor-scn-power-idle-geckoview:
     description: "Raptor idle-browser power-use measurement on GeckoView"
     try-name: raptor-scn-power-idle-geckoview
     treeherder-symbol: Rap-P(idl)
     target: geckoview_example.apk
     run-on-projects:
         by-test-platform:
-            android-hw.*/pgo: ['try', 'trunk', 'mozilla-beta', 'mozilla-central']
+            android-hw.*/pgo: ['try', 'mozilla-central']
             default: ['try']
     tier: 3
     max-run-time: 1800
     mozharness:
         extra-options:
             - --test=raptor-scn-power-idle
             - --app=geckoview
             - --binary=org.mozilla.geckoview_example
--- a/testing/geckodriver/src/marionette.rs
+++ b/testing/geckodriver/src/marionette.rs
@@ -1273,17 +1273,17 @@ impl MarionetteConnection {
                         ErrorKind::Other,
                         "EOF reading marionette message",
                     ))
                 }
                 1 => buf[0] as char,
                 _ => panic!("Expected one byte got more"),
             };
             match byte {
-                '0'...'9' => {
+                '0'..='9' => {
                     bytes = bytes * 10;
                     bytes += byte as usize - '0' as usize;
                 }
                 ':' => break,
                 _ => {}
             }
         }
 
--- a/testing/mozbase/rust/mozprofile/src/prefreader.rs
+++ b/testing/mozbase/rust/mozprofile/src/prefreader.rs
@@ -9,17 +9,17 @@ use std::iter::Iterator;
 use std::mem;
 use std::ops::Deref;
 use std::str;
 
 impl PrefReaderError {
     fn new(
         message: &'static str,
         position: Position,
-        parent: Option<Box<Error>>,
+        parent: Option<Box<dyn Error>>,
     ) -> PrefReaderError {
         PrefReaderError {
             message,
             position,
             parent,
         }
     }
 }
@@ -34,17 +34,17 @@ impl fmt::Display for PrefReaderError {
     }
 }
 
 impl Error for PrefReaderError {
     fn description(&self) -> &str {
         self.message
     }
 
-    fn cause(&self) -> Option<&Error> {
+    fn cause(&self) -> Option<&dyn Error> {
         match self.parent {
             None => None,
             Some(ref cause) => Some(cause.deref()),
         }
     }
 }
 
 impl From<io::Error> for PrefReaderError {
@@ -138,17 +138,17 @@ impl<'a> PrefToken<'a> {
         }
     }
 }
 
 #[derive(Debug)]
 pub struct PrefReaderError {
     message: &'static str,
     position: Position,
-    parent: Option<Box<Error>>,
+    parent: Option<Box<dyn Error>>,
 }
 
 struct TokenData<'a> {
     token_type: TokenType,
     complete: bool,
     position: Position,
     data: Cow<'a, str>,
     start_pos: usize,
@@ -353,19 +353,19 @@ impl<'a> PrefTokenizer<'a> {
 
     fn read_hex_escape(&mut self, hex_chars: isize, first: bool) -> Result<u32, PrefReaderError> {
         let mut value = 0;
         for _ in 0..hex_chars {
             match self.get_char() {
                 Some(x) => {
                     value = value << 4;
                     match x {
-                        '0'...'9' => value += x as u32 - '0' as u32,
-                        'a'...'f' => value += x as u32 - 'a' as u32,
-                        'A'...'F' => value += x as u32 - 'A' as u32,
+                        '0'..='9' => value += x as u32 - '0' as u32,
+                        'a'..='f' => value += x as u32 - 'a' as u32,
+                        'A'..='F' => value += x as u32 - 'A' as u32,
                         _ => {
                             return Err(PrefReaderError::new(
                                 "Unexpected character in escape",
                                 self.position,
                                 None,
                             ))
                         }
                     }
@@ -589,17 +589,17 @@ impl<'a> PrefTokenizer<'a> {
                         token_data.start(&self, TokenType::String);
                         token_data.start_pos = self.pos + 1;
                         TokenizerState::SingleQuotedString
                     }
                     't' | 'f' => {
                         self.unget_char();
                         TokenizerState::Bool
                     }
-                    '0'...'9' | '-' | '+' => {
+                    '0'..='9' | '-' | '+' => {
                         token_data.start(&self, TokenType::Int);
                         TokenizerState::Number
                     }
                     _ => {
                         return Err(PrefReaderError::new(
                             "Invalid character at start of function argument",
                             self.position,
                             None,
@@ -640,17 +640,17 @@ impl<'a> PrefTokenizer<'a> {
                     }
                     '\\' => {
                         self.consume_escape(&mut token_data)?;
                         TokenizerState::SingleQuotedString
                     }
                     _ => TokenizerState::SingleQuotedString,
                 },
                 TokenizerState::Number => match c {
-                    '0'...'9' => TokenizerState::Number,
+                    '0'..='9' => TokenizerState::Number,
                     ')' | ',' => {
                         token_data.end(&self.data, self.pos)?;
                         self.unget_char();
                         self.next_state = Some(TokenizerState::AfterFunctionArg);
                         TokenizerState::Junk
                     }
                     x if PrefTokenizer::is_space(x) => {
                         token_data.end(&self.data, self.pos)?;
--- a/testing/mozbase/rust/mozrunner/src/bin/firefox-default-path.rs
+++ b/testing/mozbase/rust/mozrunner/src/bin/firefox-default-path.rs
@@ -3,15 +3,15 @@ extern crate mozrunner;
 use mozrunner::runner::platform;
 use std::io::Write;
 
 fn main() {
     let (path, code) = platform::firefox_default_path()
         .map(|x| (x.to_string_lossy().into_owned(), 0))
         .unwrap_or(("Firefox binary not found".to_owned(), 1));
 
-    let mut writer: Box<Write> = match code {
+    let mut writer: Box<dyn Write> = match code {
         0 => Box::new(std::io::stdout()),
         _ => Box::new(std::io::stderr())
     };
     writeln!(&mut writer, "{}", &*path).unwrap();
     std::process::exit(code);
 }
--- a/testing/mozbase/rust/mozrunner/src/runner.rs
+++ b/testing/mozbase/rust/mozrunner/src/runner.rs
@@ -101,20 +101,20 @@ impl Error for RunnerError {
                     ErrorKind::NotFound => "no such file or directory",
                     _ => err.description(),
                 }
             }
             RunnerError::PrefReader(ref err) => err.description(),
         }
     }
 
-    fn cause(&self) -> Option<&Error> {
+    fn cause(&self) -> Option<&dyn Error> {
         Some(match *self {
-            RunnerError::Io(ref err) => err as &Error,
-            RunnerError::PrefReader(ref err) => err as &Error,
+            RunnerError::Io(ref err) => err as &dyn Error,
+            RunnerError::PrefReader(ref err) => err as &dyn Error,
         })
     }
 }
 
 impl From<io::Error> for RunnerError {
     fn from(value: io::Error) -> RunnerError {
         RunnerError::Io(value)
     }
--- a/testing/raptor/raptor/control_server.py
+++ b/testing/raptor/raptor/control_server.py
@@ -111,19 +111,19 @@ def MakeCustomHandlerClass(results_handl
                 super(MyHandler, self).log_request(code, size)
 
         def do_GET(self):
             # get handler, received request for test settings from web ext runner
             self.send_response(200)
             head, tail = os.path.split(self.path)
 
             if tail.startswith('raptor') and tail.endswith('.json'):
-                LOG.info('reading test settings from ' + tail)
+                LOG.info('reading test settings from json/' + tail)
                 try:
-                    with open(tail) as json_settings:
+                    with open("json/{}".format(tail)) as json_settings:
                         self.send_header('Access-Control-Allow-Origin', '*')
                         self.send_header('Content-type', 'application/json')
                         self.end_headers()
                         self.wfile.write(json.dumps(json.load(json_settings)))
                         self.wfile.close()
                         LOG.info('sent test settings to web ext runner')
                 except Exception as ex:
                     LOG.info('control server exception')
--- a/testing/raptor/raptor/gen_test_config.py
+++ b/testing/raptor/raptor/gen_test_config.py
@@ -18,17 +18,17 @@ def gen_test_config(browser, test, cs_po
                     browser_cycle=1):
     LOG.info("writing test settings into background js, so webext can get it")
 
     data = """// this file is auto-generated by raptor, do not edit directly
 function getTestConfig() {
     return {"browser": "%s",
             "cs_port": "%d",
             "test_name": "%s",
-            "test_settings_url": "http://%s:%d/%s.json",
+            "test_settings_url": "http://%s:%d/json/%s.json",
             "post_startup_delay": "%s",
             "benchmark_port": "%d",
             "host": "%s",
             "debug_mode": "%d",
             "browser_cycle": "%d"};
 }
 
 """ % (browser, cs_port, test, host, cs_port, test, post_startup_delay, b_port, host, debug_mode,
--- a/testing/raptor/raptor/manifest.py
+++ b/testing/raptor/raptor/manifest.py
@@ -207,17 +207,22 @@ def write_test_settings_json(args, test_
 
     if test_details.get("newtab_per_cycle", None) is not None:
         test_settings['raptor-options']['newtab_per_cycle'] = \
             bool(test_details['newtab_per_cycle'])
 
     if test_details['type'] == "scenario":
         test_settings['raptor-options']['scenario_time'] = test_details['scenario_time']
 
-    settings_file = os.path.join(tests_dir, test_details['name'] + '.json')
+    jsons_dir = os.path.join(tests_dir, 'json')
+
+    if not os.path.exists(jsons_dir):
+        os.mkdir(os.path.join(tests_dir, 'json'))
+
+    settings_file = os.path.join(jsons_dir, test_details['name'] + '.json')
     try:
         with open(settings_file, 'w') as out_file:
             json.dump(test_settings, out_file, indent=4, ensure_ascii=False)
             out_file.close()
     except IOError:
         LOG.info("abort: exception writing test settings json!")
 
 
--- a/testing/raptor/raptor/playback/alternate-server-replay-2.0.2.py
+++ b/testing/raptor/raptor/playback/alternate-server-replay-2.0.2.py
@@ -85,17 +85,21 @@ Http2Layer._handle_remote_settings_chang
 class ServerPlayback:
     def __init__(self, replayfiles):
         if len(replayfiles) > 0:
             for path in replayfiles:
                 proto = os.path.splitext(path)[0] + ".json"
                 if os.path.exists(proto):
                     ctx.log.info("Loading proto info from %s" % proto)
                     with open(proto) as f:
-                        _PROTO.update(json.loads(f.read()))
+                        p = json.loads(f.read()).get('http_protocol')
+
+                        if p is not None:
+                            _PROTO.update(p)
+
         self.options = None
         self.replayfiles = replayfiles
         self.flowmap = {}
 
     def load(self, flows):
         for i in flows:
             if i.response:
                 l = self.flowmap.setdefault(self._hash(i.request), [])
--- a/testing/raptor/raptor/playback/alternate-server-replay-4.0.4.py
+++ b/testing/raptor/raptor/playback/alternate-server-replay-4.0.4.py
@@ -121,17 +121,23 @@ class AlternateServerPlayback:
                 flows = io.read_flows_from_paths([path])
             except exceptions.FlowReadException as e:
                 raise exceptions.CommandError(str(e))
             self.load_flows(flows)
             proto = os.path.splitext(path)[0] + ".json"
             if os.path.exists(proto):
                 ctx.log.info("Loading proto info from %s" % proto)
                 with open(proto) as f:
-                    _PROTO.update(json.loads(f.read()))
+                    recording_info = json.loads(f.read())
+                ctx.log.info(
+                    "Replaying file {} recorded on {}".format(
+                        os.path.basename(path), recording_info["recording_date"]
+                    )
+                )
+                _PROTO.update(recording_info["http_protocol"])
 
     def _hash(self, flow):
         """
             Calculates a loose hash of the flow request.
         """
         r = flow.request
 
         # unquote url
--- a/testing/raptor/raptor/playback/mitm4-linux-firefox-netflix.manifest
+++ b/testing/raptor/raptor/playback/mitm4-linux-firefox-netflix.manifest
@@ -1,10 +1,10 @@
 [
     {
-    "size": 87375104,
+    "size": 63313140,
     "visibility": "public",
-    "digest": "b281ee5f9ca3c7b8fba30b29714f752880ae8d92aaa97fab70edb5dfb69742882276b7df332de6a76fb7b6d9aebc50185bddbcc080a9d531e5f08e92b1a2c593",
+    "digest": "24236b7cb2b6f5656fa87173b887801dd3ee1a6dd43d9cabf055735813aaf3975ac1daf743f3dd281c4d0f93fa32900377b17f3711e38b32d90f7271b3f118c1",
     "algorithm": "sha512",
     "filename": "mitm4-linux-firefox-netflix.zip",
     "unpack": true
     }
 ]
--- a/testing/raptor/raptor/raptor.ini
+++ b/testing/raptor/raptor/raptor.ini
@@ -1,94 +1,94 @@
 # raptor warm pageload tests desktop
-[include:tests/raptor-tp6-1.ini]
-[include:tests/raptor-tp6-2.ini]
-[include:tests/raptor-tp6-3.ini]
-[include:tests/raptor-tp6-4.ini]
-[include:tests/raptor-tp6-5.ini]
-[include:tests/raptor-tp6-6.ini]
-[include:tests/raptor-tp6-7.ini]
-[include:tests/raptor-tp6-8.ini]
-[include:tests/raptor-tp6-9.ini]
-[include:tests/raptor-tp6-10.ini]
+[include:tests/tp6/desktop/raptor-tp6-1.ini]
+[include:tests/tp6/desktop/raptor-tp6-2.ini]
+[include:tests/tp6/desktop/raptor-tp6-3.ini]
+[include:tests/tp6/desktop/raptor-tp6-4.ini]
+[include:tests/tp6/desktop/raptor-tp6-5.ini]
+[include:tests/tp6/desktop/raptor-tp6-6.ini]
+[include:tests/tp6/desktop/raptor-tp6-7.ini]
+[include:tests/tp6/desktop/raptor-tp6-8.ini]
+[include:tests/tp6/desktop/raptor-tp6-9.ini]
+[include:tests/tp6/desktop/raptor-tp6-10.ini]
 
 # raptor cold pageload tests desktop
-[include:tests/raptor-tp6-cold-1.ini]
-[include:tests/raptor-tp6-cold-2.ini]
-[include:tests/raptor-tp6-cold-3.ini]
-[include:tests/raptor-tp6-cold-4.ini]
+[include:tests/tp6/desktop/raptor-tp6-cold-1.ini]
+[include:tests/tp6/desktop/raptor-tp6-cold-2.ini]
+[include:tests/tp6/desktop/raptor-tp6-cold-3.ini]
+[include:tests/tp6/desktop/raptor-tp6-cold-4.ini]
 
 # raptor pageload binast tests desktop
-[include:tests/raptor-tp6-binast-1.ini]
+[include:tests/tp6/desktop/raptor-tp6-binast-1.ini]
 
 # raptor warm pageload tests mobile
-[include:tests/raptor-tp6m-1.ini]
-[include:tests/raptor-tp6m-2.ini]
-[include:tests/raptor-tp6m-3.ini]
-[include:tests/raptor-tp6m-4.ini]
-[include:tests/raptor-tp6m-5.ini]
-[include:tests/raptor-tp6m-6.ini]
-[include:tests/raptor-tp6m-7.ini]
-[include:tests/raptor-tp6m-8.ini]
-[include:tests/raptor-tp6m-9.ini]
-[include:tests/raptor-tp6m-10.ini]
-[include:tests/raptor-tp6m-1-fennec64.ini]
-[include:tests/raptor-tp6m-2-fennec64.ini]
-[include:tests/raptor-tp6m-3-fennec64.ini]
-[include:tests/raptor-tp6m-4-fennec64.ini]
-[include:tests/raptor-tp6m-5-fennec64.ini]
-[include:tests/raptor-tp6m-6-fennec64.ini]
-[include:tests/raptor-tp6m-7-fennec64.ini]
-[include:tests/raptor-tp6m-8-fennec64.ini]
-[include:tests/raptor-tp6m-9-fennec64.ini]
-[include:tests/raptor-tp6m-10-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-1.ini]
+[include:tests/tp6/mobile/raptor-tp6m-2.ini]
+[include:tests/tp6/mobile/raptor-tp6m-3.ini]
+[include:tests/tp6/mobile/raptor-tp6m-4.ini]
+[include:tests/tp6/mobile/raptor-tp6m-5.ini]
+[include:tests/tp6/mobile/raptor-tp6m-6.ini]
+[include:tests/tp6/mobile/raptor-tp6m-7.ini]
+[include:tests/tp6/mobile/raptor-tp6m-8.ini]
+[include:tests/tp6/mobile/raptor-tp6m-9.ini]
+[include:tests/tp6/mobile/raptor-tp6m-10.ini]
+[include:tests/tp6/mobile/raptor-tp6m-1-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-2-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-3-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-4-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-5-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-6-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-7-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-8-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-9-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-10-fennec64.ini]
 
 # raptor cold pageload tests mobile
-[include:tests/raptor-tp6m-cold-1.ini]
-[include:tests/raptor-tp6m-cold-2.ini]
-[include:tests/raptor-tp6m-cold-3.ini]
-[include:tests/raptor-tp6m-cold-4.ini]
-[include:tests/raptor-tp6m-cold-5.ini]
-[include:tests/raptor-tp6m-cold-6.ini]
-[include:tests/raptor-tp6m-cold-7.ini]
-[include:tests/raptor-tp6m-cold-8.ini]
-[include:tests/raptor-tp6m-cold-9.ini]
-[include:tests/raptor-tp6m-cold-10.ini]
-[include:tests/raptor-tp6m-cold-11.ini]
-[include:tests/raptor-tp6m-cold-12.ini]
-[include:tests/raptor-tp6m-cold-13.ini]
-[include:tests/raptor-tp6m-cold-14.ini]
-[include:tests/raptor-tp6m-cold-1-fennec64.ini]
-[include:tests/raptor-tp6m-cold-2-fennec64.ini]
-[include:tests/raptor-tp6m-cold-3-fennec64.ini]
-[include:tests/raptor-tp6m-cold-4-fennec64.ini]
-[include:tests/raptor-tp6m-cold-5-fennec64.ini]
-[include:tests/raptor-tp6m-cold-6-fennec64.ini]
-[include:tests/raptor-tp6m-cold-7-fennec64.ini]
-[include:tests/raptor-tp6m-cold-8-fennec64.ini]
-[include:tests/raptor-tp6m-cold-9-fennec64.ini]
-[include:tests/raptor-tp6m-cold-10-fennec64.ini]
-[include:tests/raptor-tp6m-cold-11-fennec64.ini]
-[include:tests/raptor-tp6m-cold-12-fennec64.ini]
-[include:tests/raptor-tp6m-cold-13-fennec64.ini]
-[include:tests/raptor-tp6m-cold-14-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-1.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-2.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-3.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-4.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-5.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-6.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-7.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-8.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-9.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-10.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-11.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-12.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-13.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-14.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-1-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-2-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-3-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-4-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-5-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-6-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-7-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-8-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-9-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-10-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-11-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-12-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-13-fennec64.ini]
+[include:tests/tp6/mobile/raptor-tp6m-cold-14-fennec64.ini]
 
 # raptor benchmark tests
-[include:tests/raptor-assorted-dom.ini]
-[include:tests/raptor-motionmark-animometer.ini]
-[include:tests/raptor-motionmark-htmlsuite.ini]
-[include:tests/raptor-speedometer.ini]
-[include:tests/raptor-stylebench.ini]
-[include:tests/raptor-sunspider.ini]
-[include:tests/raptor-unity-webgl.ini]
-[include:tests/raptor-youtube-playback.ini]
-[include:tests/raptor-wasm-godot.ini]
-[include:tests/raptor-wasm-godot-baseline.ini]
-[include:tests/raptor-wasm-godot-ion.ini]
-[include:tests/raptor-wasm-godot-cranelift.ini]
-[include:tests/raptor-wasm-misc.ini]
-[include:tests/raptor-wasm-misc-baseline.ini]
-[include:tests/raptor-wasm-misc-ion.ini]
-[include:tests/raptor-wasm-misc-cranelift.ini]
-[include:tests/raptor-webaudio.ini]
+[include:tests/benchmarks/raptor-assorted-dom.ini]
+[include:tests/benchmarks/raptor-motionmark-animometer.ini]
+[include:tests/benchmarks/raptor-motionmark-htmlsuite.ini]
+[include:tests/benchmarks/raptor-speedometer.ini]
+[include:tests/benchmarks/raptor-stylebench.ini]
+[include:tests/benchmarks/raptor-sunspider.ini]
+[include:tests/benchmarks/raptor-unity-webgl.ini]
+[include:tests/benchmarks/raptor-youtube-playback.ini]
+[include:tests/benchmarks/raptor-wasm-godot.ini]
+[include:tests/benchmarks/raptor-wasm-godot-baseline.ini]
+[include:tests/benchmarks/raptor-wasm-godot-ion.ini]
+[include:tests/benchmarks/raptor-wasm-godot-cranelift.ini]
+[include:tests/benchmarks/raptor-wasm-misc.ini]
+[include:tests/benchmarks/raptor-wasm-misc-baseline.ini]
+[include:tests/benchmarks/raptor-wasm-misc-ion.ini]
+[include:tests/benchmarks/raptor-wasm-misc-cranelift.ini]
+[include:tests/benchmarks/raptor-webaudio.ini]
 
 # raptor scenario tests
-[include:tests/raptor-scn-power-idle.ini]
+[include:tests/scenario/raptor-scn-power-idle.ini]
--- a/testing/raptor/raptor/raptor.py
+++ b/testing/raptor/raptor/raptor.py
@@ -55,17 +55,16 @@ from gen_test_config import gen_test_con
 from outputhandler import OutputHandler
 from manifest import get_raptor_test_list
 from memory import generate_android_memory_profile
 from power import init_android_power_test, finish_android_power_test
 from results import RaptorResultsHandler
 from utils import view_gecko_profile
 from cpu import generate_android_cpu_profile
 
-
 LOG = RaptorLogger(component='raptor-main')
 
 
 class SignalHandler:
 
     def __init__(self):
         signal.signal(signal.SIGINT, self.handle_signal)
         signal.signal(signal.SIGTERM, self.handle_signal)
@@ -316,24 +315,18 @@ class Raptor(object):
         # for chrome the addon is just a list (appended to cmd line)
         chrome_apps = CHROMIUM_DISTROS + ["chrome-android", "chromium-android"]
         if self.config['app'] in chrome_apps:
             self.profile.addons.remove(self.raptor_webext)
 
     def get_proxy_command_for_mitm(self, test, version):
         # Generate Mitmproxy playback args
         script = os.path.join(here, "playback", "alternate-server-replay-{}.py".format(version))
-        recordings = test.get("playback_recordings")
-        if recordings:
-            recording_paths = []
-            proxy_dir = self.playback.mozproxy_dir
-            for recording in recordings.split():
-                if not recording:
-                    continue
-                recording_paths.append(os.path.join(proxy_dir, recording))
+
+        recording_paths = self.get_recording_paths(test)
 
         # this part is platform-specific
         if mozinfo.os == "win":
             script = script.replace("\\", "\\\\\\")
             recording_paths = [recording_path.replace("\\", "\\\\\\")
                                for recording_path in recording_paths]
 
         if version == "2.0.2":
@@ -370,16 +363,48 @@ class Raptor(object):
         self.get_playback_config(test)
         self.playback = get_playback(self.config, self.device)
 
         self.get_proxy_command_for_mitm(test, self.config['playback_version'])
 
         # let's start it!
         self.playback.start()
 
+        self.log_recording_dates(test)
+
+    def get_recording_paths(self, test):
+        recordings = test.get("playback_recordings")
+
+        if recordings:
+            recording_paths = []
+            proxy_dir = self.playback.mozproxy_dir
+
+            for recording in recordings.split():
+                if not recording:
+                    continue
+                recording_paths.append(os.path.join(proxy_dir, recording))
+
+            return recording_paths
+
+    def log_recording_dates(self, test):
+        for r in self.get_recording_paths(test):
+            json_path = '{}.json'.format(r.split('.')[0])
+
+            if os.path.exists(json_path):
+                with open(json_path) as f:
+                    recording_date = json.loads(f.read()).get('recording_date')
+
+                    if recording_date is not None:
+                        LOG.info('Playback recording date: {} '.
+                                 format(recording_date.split(' ')[0]))
+                    else:
+                        LOG.info('Playback recording date not available')
+            else:
+                LOG.info('Playback recording information not available')
+
     def delete_proxy_settings_from_profile(self):
         # Must delete the proxy settings from the profile if running
         # the test with a host different from localhost.
         userjspath = os.path.join(self.profile.profile, 'user.js')
         with open(userjspath) as userjsfile:
             prefs = userjsfile.readlines()
         prefs = [pref for pref in prefs if 'network.proxy' not in pref]
         with open(userjspath, 'w') as userjsfile:
rename from testing/raptor/raptor/tests/raptor-assorted-dom.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-assorted-dom.ini
rename from testing/raptor/raptor/tests/raptor-motionmark-animometer.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-motionmark-animometer.ini
rename from testing/raptor/raptor/tests/raptor-motionmark-htmlsuite.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-motionmark-htmlsuite.ini
rename from testing/raptor/raptor/tests/raptor-speedometer.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-speedometer.ini
rename from testing/raptor/raptor/tests/raptor-stylebench.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-stylebench.ini
rename from testing/raptor/raptor/tests/raptor-sunspider.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-sunspider.ini
rename from testing/raptor/raptor/tests/raptor-unity-webgl.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-unity-webgl.ini
rename from testing/raptor/raptor/tests/raptor-wasm-godot-baseline.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-godot-baseline.ini
rename from testing/raptor/raptor/tests/raptor-wasm-godot-cranelift.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-godot-cranelift.ini
rename from testing/raptor/raptor/tests/raptor-wasm-godot-ion.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-godot-ion.ini
rename from testing/raptor/raptor/tests/raptor-wasm-godot.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-godot.ini
rename from testing/raptor/raptor/tests/raptor-wasm-misc-baseline.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-misc-baseline.ini
rename from testing/raptor/raptor/tests/raptor-wasm-misc-cranelift.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-misc-cranelift.ini
rename from testing/raptor/raptor/tests/raptor-wasm-misc-ion.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-misc-ion.ini
rename from testing/raptor/raptor/tests/raptor-wasm-misc.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-wasm-misc.ini
rename from testing/raptor/raptor/tests/raptor-webaudio.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-webaudio.ini
rename from testing/raptor/raptor/tests/raptor-youtube-playback.ini
rename to testing/raptor/raptor/tests/benchmarks/raptor-youtube-playback.ini
rename from testing/raptor/raptor/tests/raptor-scn-power-idle.ini
rename to testing/raptor/raptor/tests/scenario/raptor-scn-power-idle.ini
rename from testing/raptor/raptor/tests/raptor-tp6-1.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-1.ini
rename from testing/raptor/raptor/tests/raptor-tp6-10.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-10.ini
rename from testing/raptor/raptor/tests/raptor-tp6-2.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-2.ini
rename from testing/raptor/raptor/tests/raptor-tp6-3.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-3.ini
rename from testing/raptor/raptor/tests/raptor-tp6-4.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-4.ini
rename from testing/raptor/raptor/tests/raptor-tp6-5.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-5.ini
rename from testing/raptor/raptor/tests/raptor-tp6-6.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-6.ini
rename from testing/raptor/raptor/tests/raptor-tp6-7.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-7.ini
rename from testing/raptor/raptor/tests/raptor-tp6-8.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-8.ini
rename from testing/raptor/raptor/tests/raptor-tp6-9.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-9.ini
rename from testing/raptor/raptor/tests/raptor-tp6-binast-1.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-binast-1.ini
rename from testing/raptor/raptor/tests/raptor-tp6-cold-1.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-cold-1.ini
rename from testing/raptor/raptor/tests/raptor-tp6-cold-2.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-cold-2.ini
rename from testing/raptor/raptor/tests/raptor-tp6-cold-3.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-cold-3.ini
rename from testing/raptor/raptor/tests/raptor-tp6-cold-4.ini
rename to testing/raptor/raptor/tests/tp6/desktop/raptor-tp6-cold-4.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-1-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-1-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-1.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-1.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-10-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-10-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-10.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-10.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-2-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-2-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-2.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-2.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-3-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-3-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-3.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-3.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-4-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-4-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-4.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-4.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-5-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-5-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-5.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-5.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-6-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-6-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-6.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-6.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-7-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-7-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-7.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-7.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-8-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-8-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-8.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-8.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-9-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-9-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-9.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-9.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-1-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-1-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-1.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-1.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-10-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-10-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-10.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-10.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-11-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-11-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-11.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-11.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-12-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-12-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-12.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-12.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-13-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-13-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-13.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-13.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-14-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-14-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-14.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-14.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-2-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-2-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-2.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-2.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-3-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-3-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-3.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-3.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-4-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-4-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-4.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-4.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-5-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-5-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-5.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-5.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-6-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-6-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-6.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-6.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-7-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-7-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-7.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-7.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-8-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-8-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-8.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-8.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-9-fennec64.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-9-fennec64.ini
rename from testing/raptor/raptor/tests/raptor-tp6m-cold-9.ini
rename to testing/raptor/raptor/tests/tp6/mobile/raptor-tp6m-cold-9.ini
--- a/testing/web-platform/tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
+++ b/testing/web-platform/tests/shadow-dom/scroll-to-the-fragment-in-shadow-tree.html
@@ -1,15 +1,16 @@
 <!DOCTYPE html>
 <html>
 <head>
 <title>Shadow DOM: The indicated part of the document should not match an element inside a shadow tree</title>
 <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org">
 <meta name="assert" content="An element inside a shadow tree should not be the indicated part of the document even if its ID is exactly equal to the decoded fragid or its name attribute is exactly equal to the fragid">
 <link rel="help" href="https://html.spec.whatwg.org/multipage/browsers.html#scroll-to-the-fragment-identifier">
+<meta name="viewport" content="width=device-width,initial-scale=1">
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 </head>
 <body>
 <div id="log"></div>
 <div id="testContainer"></div>
 <script>
 
--- a/testing/webdriver/src/error.rs
+++ b/testing/webdriver/src/error.rs
@@ -320,17 +320,17 @@ impl WebDriverError {
     }
 }
 
 impl Error for WebDriverError {
     fn description(&self) -> &str {
         self.error_code()
     }
 
-    fn cause(&self) -> Option<&Error> {
+    fn cause(&self) -> Option<&dyn Error> {
         None
     }
 }
 
 impl fmt::Display for WebDriverError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         self.message.fmt(f)
     }
@@ -349,18 +349,18 @@ impl From<io::Error> for WebDriverError 
 }
 
 impl From<DecodeError> for WebDriverError {
     fn from(err: DecodeError) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
 }
 
-impl From<Box<Error>> for WebDriverError {
-    fn from(err: Box<Error>) -> WebDriverError {
+impl From<Box<dyn Error>> for WebDriverError {
+    fn from(err: Box<dyn Error>) -> WebDriverError {
         WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
     }
 }
 
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::test::check_serialize;
--- a/testing/webdriver/src/server.rs
+++ b/testing/webdriver/src/server.rs
@@ -171,17 +171,17 @@ impl<U: WebDriverExtensionRoute> HttpHan
     }
 }
 
 impl<U: WebDriverExtensionRoute + 'static> Service for HttpHandler<U> {
     type ReqBody = Body;
     type ResBody = Body;
 
     type Error = hyper::Error;
-    type Future = Box<future::Future<Item = Response<Self::ResBody>, Error = hyper::Error> + Send>;
+    type Future = Box<dyn future::Future<Item = Response<Self::ResBody>, Error = hyper::Error> + Send>;
 
     fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
         let uri = req.uri().clone();
         let method = req.method().clone();
         let api = self.api.clone();
         let chan = self.chan.clone();
 
         Box::new(req.into_body().concat2().and_then(move |body| {
--- a/xpcom/ds/nsAtom.h
+++ b/xpcom/ds/nsAtom.h
@@ -70,16 +70,23 @@ class nsAtom {
   // BloomFilter requires elements to implement a function called hash().
   //
   uint32_t hash() const { return mHash; }
 
   // This function returns true if ToLowercaseASCII would return the string
   // unchanged.
   bool IsAsciiLowercase() const { return mIsAsciiLowercase; }
 
+  // This function returns true if this is the empty atom. This is exactly
+  // equivalent to `this == nsGkAtoms::_empty`, but it's a bit less foot-gunny,
+  // since we also have `nsGkAtoms::empty`.
+  //
+  // Defined in nsGkAtoms.h
+  inline bool IsEmpty() const;
+
   // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
   // of this type is special.
   inline MozExternalRefCountType AddRef();
   inline MozExternalRefCountType Release();
 
   typedef mozilla::TrueType HasThreadSafeRefCnt;
 
  protected:
--- a/xpcom/ds/nsGkAtoms.h
+++ b/xpcom/ds/nsGkAtoms.h
@@ -175,9 +175,13 @@ class nsGkAtoms {
 #define GK_ATOM(name_, value_, hash_, is_ascii_lower_, type_, atom_type_) \
   static constexpr nsStaticAtom* name_ = const_cast<nsStaticAtom*>(       \
       &mozilla::detail::gGkAtoms.mAtoms[static_cast<size_t>(              \
           mozilla::detail::GkAtoms::Atoms::name_)]);
 #include "nsGkAtomList.h"
 #undef GK_ATOM
 };
 
+inline bool nsAtom::IsEmpty() const {
+  return this == nsGkAtoms::_empty;
+}
+
 #endif /* nsGkAtoms_h___ */
--- a/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
+++ b/xpcom/rust/xpcom/xpcom_macros/src/lib.rs
@@ -189,17 +189,17 @@ struct Method {
 #[derive(Debug)]
 struct Interface {
     name: &'static str,
     base: Option<&'static str>,
     methods: Result<&'static [Method], &'static str>,
 }
 
 impl Interface {
-    fn base(&self) -> Result<Option<&'static Interface>, Box<Error>> {
+    fn base(&self) -> Result<Option<&'static Interface>, Box<dyn Error>> {
         Ok(if let Some(base) = self.base {
             Some(*IFACES.get(base).ok_or_else(
                 || format!("Base interface {} does not exist", base)
             )?)
         } else {
             None
         })
     }
@@ -235,17 +235,17 @@ impl ToTokens for RefcntKind {
         match *self {
             RefcntKind::NonAtomic => quote!(xpcom::Refcnt).to_tokens(tokens),
             RefcntKind::Atomic => quote!(xpcom::AtomicRefcnt).to_tokens(tokens),
         }
     }
 }
 
 /// Scans through the attributes on a struct, and extracts the type of the refcount to use.
-fn get_refcnt_kind(attrs: &[Attribute]) -> Result<RefcntKind, Box<Error>> {
+fn get_refcnt_kind(attrs: &[Attribute]) -> Result<RefcntKind, Box<dyn Error>> {
     for attr in attrs {
         if let Some(Meta::NameValue(ref attr)) = attr.interpret_meta() {
             if attr.ident != "refcnt" {
                 continue;
             }
 
             let value = if let Lit::Str(ref s) = attr.lit {
                 s.value()
@@ -265,17 +265,17 @@ fn get_refcnt_kind(attrs: &[Attribute]) 
     }
 
     Err("Expected #[refcnt] attribute")?
 }
 
 /// Scan the attributes looking for an #[xpimplements] attribute. The identifier
 /// arguments passed to this attribute are the interfaces which the type wants to
 /// directly implement.
-fn get_bases(attrs: &[Attribute]) -> Result<Vec<&'static Interface>, Box<Error>> {
+fn get_bases(attrs: &[Attribute]) -> Result<Vec<&'static Interface>, Box<dyn Error>> {
     let mut inherits = Vec::new();
     for attr in attrs {
         if let Some(Meta::List(ref attr)) = attr.interpret_meta() {
             if attr.ident != "xpimplements" {
                 continue;
             }
 
             for item in &attr.nested {
@@ -291,28 +291,28 @@ fn get_bases(attrs: &[Attribute]) -> Res
                 }
             }
         }
     }
     Ok(inherits)
 }
 
 /// Extract the fields list from the input struct.
-fn get_fields(di: &DeriveInput) -> Result<&Punctuated<Field, Token![,]>, Box<Error>> {
+fn get_fields(di: &DeriveInput) -> Result<&Punctuated<Field, Token![,]>, Box<dyn Error>> {
     match di.data {
         Data::Struct(DataStruct {
             fields: Fields::Named(ref named), ..
         }) => Ok(&named.named),
         _ => Err("The initializer struct must be a standard \
                   named value struct definition".into())
     }
 }
 
 /// Takes the `Init*` struct in, and generates a `DeriveInput` for the "real" struct.
-fn gen_real_struct(init: &DeriveInput, bases: &[&Interface], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<Error>> {
+fn gen_real_struct(init: &DeriveInput, bases: &[&Interface], refcnt_ty: RefcntKind) -> Result<DeriveInput, Box<dyn Error>> {
     // Determine the name for the real struct based on the name of the
     // initializer struct's name.
     if !init.ident.to_string().starts_with("Init") {
         Err("The target struct's name must begin with Init")?
     }
     let name: Ident = Ident::new(&init.ident.to_string()[4..], Span::call_site());
     let vis = &init.vis;
 
@@ -334,17 +334,17 @@ fn gen_real_struct(init: &DeriveInput, b
 }
 
 /// Generates the `extern "system"` methods which are actually included in the
 /// VTable for the given interface.
 ///
 /// These methods attempt to invoke the `recover_self` method to translate from
 /// the passed-in raw pointer to the actual `&self` value, and it is expected to
 /// be in scope.
-fn gen_vtable_methods(iface: &Interface) -> Result<TokenStream2, Box<Error>> {
+fn gen_vtable_methods(iface: &Interface) -> Result<TokenStream2, Box<dyn Error>> {
     let base_ty = Ident::new(iface.name, Span::call_site());
 
     let base_methods = if let Some(base) = iface.base()? {
         gen_vtable_methods(base)?
     } else {
         quote!{}
     };
 
@@ -378,17 +378,17 @@ fn gen_vtable_methods(iface: &Interface)
     Ok(quote!{
         #base_methods
         #(#method_defs)*
     })
 }
 
 /// Generates the VTable for a given base interface. This assumes that the
 /// implementations of each of the `extern "system"` methods are in scope.
-fn gen_inner_vtable(iface: &Interface) -> Result<TokenStream2, Box<Error>> {
+fn gen_inner_vtable(iface: &Interface) -> Result<TokenStream2, Box<dyn Error>> {
     let vtable_ty = Ident::new(&format!("{}VTable", iface.name), Span::call_site());
 
     let methods = iface.methods
         .map_err(|reason| format!("Interface {} cannot be implemented in rust \
                                    because {} is not supported yet", iface.name, reason))?;
 
     // Generate the vtable for the base interface.
     let base_vtable = if let Some(base) = iface.base()? {
@@ -405,17 +405,17 @@ fn gen_inner_vtable(iface: &Interface) -
     }).collect::<Vec<_>>();
 
     Ok(quote!(#vtable_ty {
         #base_vtable
         #(#vtable_init)*
     }))
 }
 
-fn gen_root_vtable(name: &Ident, base: &Interface) -> Result<TokenStream2, Box<Error>> {
+fn gen_root_vtable(name: &Ident, base: &Interface) -> Result<TokenStream2, Box<dyn Error>> {
     let field = Ident::new(&format!("__base_{}", base.name), Span::call_site());
     let vtable_ty = Ident::new(&format!("{}VTable", base.name), Span::call_site());
     let methods = gen_vtable_methods(base)?;
     let value = gen_inner_vtable(base)?;
 
     // Define the `recover_self` method. This performs an offset calculation to
     // recover a pointer to the original struct from a pointer to the given
     // VTable field.
@@ -452,17 +452,17 @@ fn gen_root_vtable(name: &Ident, base: &
 /// value is the `QueryInterface` implementation, and the second is the `Coerce`
 /// implementation.
 fn gen_casts(
     seen: &mut HashSet<&'static str>,
     iface: &Interface,
     name: &Ident,
     coerce_name: &Ident,
     vtable_field: &Ident,
-) -> Result<(TokenStream2, TokenStream2), Box<Error>> {
+) -> Result<(TokenStream2, TokenStream2), Box<dyn Error>> {
     if !seen.insert(iface.name) {
         return Ok((quote!{}, quote!{}));
     }
 
     // Generate the cast implementations for the base interfaces.
     let (base_qi, base_coerce) = if let Some(base) = iface.base()? {
         gen_casts(
             seen,
@@ -508,17 +508,17 @@ fn gen_casts(
             }
         }
     };
 
     Ok((qi, coerce))
 }
 
 /// The root xpcom procedural macro definition.
-fn xpcom(init: DeriveInput) -> Result<TokenStream2, Box<Error>> {
+fn xpcom(init: DeriveInput) -> Result<TokenStream2, Box<dyn Error>> {
     if !init.generics.params.is_empty() || !init.generics.where_clause.is_none() {
         return Err("Cannot #[derive(xpcom)] on a generic type, due to \
                     rust limitations. It is not possible to instantiate \
                     a static with a generic type parameter, meaning that \
                     generic types cannot have their VTables instantiated \
                     correctly.".into());
     }