Merge inbound to mozilla-central a=merge
authorCoroiu Cristina <ccoroiu@mozilla.com>
Fri, 22 Jun 2018 12:55:04 +0300
changeset 480065 6b6f3f6ecf142908b3e437d3bc3fac75540a9bcb
parent 480064 7f7a190b3d0de55a0e231d9a32b82e2ce0643697 (current diff)
parent 480036 d7b2d2d11109e2cd17e2f68960260631560d03f3 (diff)
child 480066 82a9a1027e2be734ae8c6488f54c251181a962dc
child 480068 a32be868e151ab9f1cf945976316f730292a8b09
child 480106 c45b1a3c054b18433585d44c743c712922953e79
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
6b6f3f6ecf14 / 62.0a1 / 20180622100109 / files
nightly linux64
6b6f3f6ecf14 / 62.0a1 / 20180622100109 / files
nightly mac
6b6f3f6ecf14 / 62.0a1 / 20180622100109 / files
nightly win32
6b6f3f6ecf14 / 62.0a1 / 20180622100109 / files
nightly win64
6b6f3f6ecf14 / 62.0a1 / 20180622100109 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5353,22 +5353,16 @@ already_AddRefed<AnonymousContent>
 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
 {
   nsIPresShell* shell = GetShell();
   if (!shell || !shell->GetCanvasFrame()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
-  // We're about to insert random content here that will be rendered. We're
-  // going to need more than svg.css here...
-  if (IsSVGDocument()) {
-    AsSVGDocument()->EnsureNonSVGUserAgentStyleSheetsLoaded();
-  }
-
   nsAutoScriptBlocker scriptBlocker;
   nsCOMPtr<Element> container = shell->GetCanvasFrame()
                                      ->GetCustomContentContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2012,17 +2012,23 @@ public:
     return mType == eXUL;
   }
   bool IsUnstyledDocument()
   {
     return IsLoadedAsData() || IsLoadedAsInteractiveData();
   }
   bool LoadsFullXULStyleSheetUpFront()
   {
-    return IsXULDocument() || AllowXULXBL();
+    if (IsXULDocument()) {
+      return true;
+    }
+    if (IsSVGDocument()) {
+      return false;
+    }
+    return AllowXULXBL();
   }
 
   bool IsScriptEnabled();
 
   bool IsTopLevelContentDocument() const { return mIsTopLevelContentDocument; }
   void SetIsTopLevelContentDocument(bool aIsTopLevelContentDocument)
   {
     mIsTopLevelContentDocument = aIsTopLevelContentDocument;
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -287,53 +287,50 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
 
   if (thisContent->IsInAnonymousSubtree() &&
       thisContent->IsAnonymousContentInSVGUseSubtree()) {
     // Stylesheets in <use>-cloned subtrees are disabled until we figure out
     // how they should behave.
     return Update { };
   }
 
-  // Check for a ShadowRoot because link elements are inert in a
-  // ShadowRoot.
-  ShadowRoot* containingShadow = thisContent->GetContainingShadow();
-  if (thisContent->IsHTMLElement(nsGkAtoms::link) &&
-      (aOldShadowRoot || containingShadow)) {
-    return Update { };
-  }
-
   if (mStyleSheet && (aOldDocument || aOldShadowRoot)) {
     MOZ_ASSERT(!(aOldDocument && aOldShadowRoot),
                "ShadowRoot content is never in document, thus "
                "there should not be a old document and old "
                "ShadowRoot simultaneously.");
 
     // We're removing the link element from the document or shadow tree,
     // unload the stylesheet.  We want to do this even if updates are
     // disabled, since otherwise a sheet with a stale linking element pointer
     // will be hanging around -- not good!
+    //
+    // TODO(emilio): We can reach this code with aOldShadowRoot ==
+    // thisContent->GetContainingShadowRoot(), when moving the shadow host
+    // around. We probably could optimize some of this stuff out, is it worth
+    // it?
     if (aOldShadowRoot) {
       aOldShadowRoot->RemoveSheet(mStyleSheet);
     } else {
       aOldDocument->RemoveStyleSheet(mStyleSheet);
     }
 
-    nsStyleLinkElement::SetStyleSheet(nullptr);
+    SetStyleSheet(nullptr);
+  }
+
+  nsIDocument* doc = thisContent->IsInShadowTree()
+    ? thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
+
+  // Loader could be null during unlink, see bug 1425866.
+  if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
+    return Update { };
   }
 
   // When static documents are created, stylesheets are cloned manually.
-  if (mDontLoadStyle || !mUpdatesEnabled ||
-      thisContent->OwnerDoc()->IsStaticDocument()) {
-    return Update { };
-  }
-
-  nsCOMPtr<nsIDocument> doc = thisContent->IsInShadowTree() ?
-    thisContent->OwnerDoc() : thisContent->GetUncomposedDoc();
-  // Loader could be null during unlink, see bug 1425866.
-  if (!doc || !doc->CSSLoader() || !doc->CSSLoader()->GetEnabled()) {
+  if (mDontLoadStyle || !mUpdatesEnabled || doc->IsStaticDocument()) {
     return Update { };
   }
 
   Maybe<SheetInfo> info = GetStyleSheetInfo();
   if (aForceUpdate == ForceUpdate::No &&
       mStyleSheet &&
       info &&
       !info->mIsInline &&
@@ -389,25 +386,25 @@ nsStyleLinkElement::DoUpdateStyleSheet(n
         return Err(rv);
       }
       return Update { };
     }
 
     // Parse the style sheet.
     return doc->CSSLoader()->LoadInlineStyle(*info, text, mLineNumber, aObserver);
   }
-  nsAutoString integrity;
   if (thisContent->IsElement()) {
+    nsAutoString integrity;
     thisContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::integrity,
                                       integrity);
-  }
-  if (!integrity.IsEmpty()) {
-    MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
-            ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
-             NS_ConvertUTF16toUTF8(integrity).get()));
+    if (!integrity.IsEmpty()) {
+      MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
+              ("nsStyleLinkElement::DoUpdateStyleSheet, integrity=%s",
+               NS_ConvertUTF16toUTF8(integrity).get()));
+    }
   }
   auto resultOrError = doc->CSSLoader()->LoadStyleLink(*info, aObserver);
   if (resultOrError.isErr()) {
     // Don't propagate LoadStyleLink() errors further than this, since some
     // consumers (e.g. nsXMLContentSink) will completely abort on innocuous
     // things like a stylesheet load being blocked by the security system.
     return Update { };
   }
--- a/dom/bindings/DOMString.h
+++ b/dom/bindings/DOMString.h
@@ -216,20 +216,20 @@ public:
     MOZ_ASSERT(mState == State::Empty, "We're already set to a value");
     MOZ_ASSERT(!mStringBuffer, "Setting stringbuffer twice?");
     MOZ_ASSERT(aAtom || aNullHandling != eNullNotExpected);
     if (aNullHandling == eNullNotExpected || aAtom) {
       if (aAtom->IsStatic()) {
         // Static atoms are backed by literals.
         SetLiteralInternal(aAtom->GetUTF16String(), aAtom->GetLength());
       } else {
-        // Dynamic atoms always have a string buffer and never have 0 length,
-        // because nsGkAtoms::_empty is a static atom.
-        SetKnownLiveStringBuffer(
-          aAtom->AsDynamic()->GetStringBuffer(), aAtom->GetLength());
+        // Dynamic atoms own their own chars, and never have 0 length because
+        // nsGkAtoms::_empty is a static atom.
+        MOZ_ASSERT(aAtom->GetLength() > 0);
+        AsAString().Assign(aAtom->AsDynamic()->String(), aAtom->GetLength());
       }
     } else if (aNullHandling == eTreatNullAsNull) {
       SetNull();
     }
   }
 
   void SetNull()
   {
--- a/dom/html/HTMLLinkElement.cpp
+++ b/dom/html/HTMLLinkElement.cpp
@@ -120,33 +120,30 @@ HTMLLinkElement::OnDNSPrefetchDeferred()
 
 bool
 HTMLLinkElement::HasDeferredDNSPrefetchRequest()
 {
   return HasFlag(HTML_LINK_DNS_PREFETCH_DEFERRED);
 }
 
 nsresult
-HTMLLinkElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
+HTMLLinkElement::BindToTree(nsIDocument* aDocument,
+                            nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  // Link must be inert in ShadowRoot.
-  if (aDocument && !GetContainingShadow()) {
-    aDocument->RegisterPendingLinkUpdate(this);
-  }
-
-  if (IsInComposedDoc()) {
+  if (nsIDocument* doc = GetComposedDoc()) {
+    doc->RegisterPendingLinkUpdate(this);
     TryDNSPrefetchOrPreconnectOrPrefetchOrPreloadOrPrerender();
   }
 
   void (HTMLLinkElement::*update)() = &HTMLLinkElement::UpdateStyleSheetInternal;
   nsContentUtils::AddScriptRunner(
     NewRunnableMethod("dom::HTMLLinkElement::BindToTree", this, update));
 
   CreateAndDispatchEvent(aDocument, NS_LITERAL_STRING("DOMLinkAdded"));
@@ -177,22 +174,18 @@ HTMLLinkElement::UnbindFromTree(bool aDe
   CancelPrefetchOrPreload();
 
   // If this link is ever reinserted into a document, it might
   // be under a different xml:base, so forget the cached state now.
   Link::ResetLinkState(false, Link::ElementHasHref());
 
   // If this is reinserted back into the document it will not be
   // from the parser.
-  nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
-
-  // Check for a ShadowRoot because link elements are inert in a
-  // ShadowRoot.
-  ShadowRoot* oldShadowRoot = GetBindingParent() ?
-    GetBindingParent()->GetShadowRoot() : nullptr;
+  nsIDocument* oldDoc = GetUncomposedDoc();
+  ShadowRoot* oldShadowRoot = GetContainingShadow();
 
   CreateAndDispatchEvent(oldDoc, NS_LITERAL_STRING("DOMLinkRemoved"));
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   Unused << UpdateStyleSheetInternal(oldDoc, oldShadowRoot);
 }
 
 bool
--- a/dom/svg/SVGDocument.cpp
+++ b/dom/svg/SVGDocument.cpp
@@ -25,141 +25,30 @@ using namespace mozilla::css;
 using namespace mozilla::dom;
 
 namespace mozilla {
 namespace dom {
 
 //----------------------------------------------------------------------
 // Implementation
 
-//----------------------------------------------------------------------
-// nsISupports methods:
-
-nsresult
-SVGDocument::InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                               bool aNotify)
-{
-  if (aKid->IsElement() && !aKid->IsSVGElement()) {
-    // We can get here when well formed XML with a non-SVG root element is
-    // served with the SVG MIME type, for example. In that case we need to load
-    // the non-SVG UA sheets or else we can get bugs like bug 1016145.  Note
-    // that we have to do this _before_ the XMLDocument::InsertChildBefore,
-    // since that can try to construct frames, and we need to have the sheets
-    // loaded by then.
-    EnsureNonSVGUserAgentStyleSheetsLoaded();
-  }
-
-  return XMLDocument::InsertChildBefore(aKid, aBeforeThis, aNotify);
-}
-
 nsresult
 SVGDocument::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                    bool aPreallocateChildren) const
 {
   NS_ASSERTION(aNodeInfo->NodeInfoManager() == mNodeInfoManager,
                "Can't import this document into another document!");
 
   RefPtr<SVGDocument> clone = new SVGDocument();
   nsresult rv = CloneDocHelper(clone.get(), aPreallocateChildren);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return CallQueryInterface(clone.get(), aResult);
 }
 
-void
-SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded()
-{
-  if (mHasLoadedNonSVGUserAgentStyleSheets) {
-    return;
-  }
-
-  if (IsStaticDocument()) {
-    // If we're a static clone of a document, then
-    // nsIDocument::CreateStaticClone will handle cloning the original
-    // document's sheets, including the on-demand non-SVG UA sheets,
-    // for us.
-    return;
-  }
-
-  mHasLoadedNonSVGUserAgentStyleSheets = true;
-
-  if (IsBeingUsedAsImage()) {
-    // nsDocumentViewer::CreateStyleSet skipped loading all user-agent/user
-    // style sheets in this case, but we'll need B2G/Fennec's
-    // content.css. We could load all the sheets registered with the
-    // nsIStyleSheetService (and maybe we should) but most likely it isn't
-    // desirable or necessary for foreignObject in SVG-as-an-image. Instead we
-    // only load the "agent-style-sheets" that nsStyleSheetService::Init()
-    // pulls in from the category manager. That keeps memory use of
-    // SVG-as-an-image down.
-    //
-    // We do this before adding the other sheets below because
-    // EnsureOnDemandBuiltInUASheet prepends, and B2G/Fennec's/GeckoView's
-    // content.css must come after those UASheet() etc.
-    //
-    // FIXME(emilio, bug 1468133): We may already have loaded some of the other
-    // on-demand built-in UA sheets, including svg.css, so this looks somewhat
-    // bogus... Also, this should probably just use the stylesheet service which
-    // also has the right sheets cached and parsed here...
-    nsCOMPtr<nsICategoryManager> catMan =
-      do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-    if (catMan) {
-      nsCOMPtr<nsISimpleEnumerator> sheets;
-      catMan->EnumerateCategory("agent-style-sheets", getter_AddRefs(sheets));
-      if (sheets) {
-        bool hasMore;
-        while (NS_SUCCEEDED(sheets->HasMoreElements(&hasMore)) && hasMore) {
-          nsCOMPtr<nsISupports> sheet;
-          if (NS_FAILED(sheets->GetNext(getter_AddRefs(sheet))))
-            break;
-
-          nsCOMPtr<nsISupportsCString> icStr = do_QueryInterface(sheet);
-          MOZ_ASSERT(icStr,
-                     "category manager entries must be nsISupportsCStrings");
-
-          nsAutoCString name;
-          icStr->GetData(name);
-
-          nsCString spec;
-          catMan->GetCategoryEntry("agent-style-sheets", name.get(),
-                                   getter_Copies(spec));
-
-          mozilla::css::Loader* cssLoader = CSSLoader();
-          if (cssLoader->GetEnabled()) {
-            nsCOMPtr<nsIURI> uri;
-            NS_NewURI(getter_AddRefs(uri), spec);
-            if (uri) {
-              RefPtr<StyleSheet> sheet;
-              cssLoader->LoadSheetSync(uri,
-                                       mozilla::css::eAgentSheetFeatures,
-                                       true, &sheet);
-              if (sheet) {
-                EnsureOnDemandBuiltInUASheet(sheet);
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-
-  auto cache = nsLayoutStylesheetCache::Singleton();
-
-  EnsureOnDemandBuiltInUASheet(cache->FormsSheet());
-  EnsureOnDemandBuiltInUASheet(cache->CounterStylesSheet());
-  EnsureOnDemandBuiltInUASheet(cache->HTMLSheet());
-  if (nsLayoutUtils::ShouldUseNoFramesSheet(this)) {
-    EnsureOnDemandBuiltInUASheet(cache->NoFramesSheet());
-  }
-  if (nsLayoutUtils::ShouldUseNoScriptSheet(this)) {
-    EnsureOnDemandBuiltInUASheet(cache->NoScriptSheet());
-  }
-  EnsureOnDemandBuiltInUASheet(cache->UASheet());
-}
-
 } // namespace dom
 } // namespace mozilla
 
 ////////////////////////////////////////////////////////////////////////
 // Exported creation functions
 
 nsresult
 NS_NewSVGDocument(nsIDocument** aInstancePtrResult)
--- a/dom/svg/SVGDocument.h
+++ b/dom/svg/SVGDocument.h
@@ -17,46 +17,37 @@ namespace mozilla {
 class SVGContextPaint;
 
 namespace dom {
 
 class SVGForeignObjectElement;
 
 class SVGDocument final : public XMLDocument
 {
-  friend class SVGForeignObjectElement; // To call EnsureNonSVGUserAgentStyleSheetsLoaded
-  friend class nsIDocument; // Same reason.
-
 public:
   SVGDocument()
     : XMLDocument("image/svg+xml")
-    , mHasLoadedNonSVGUserAgentStyleSheets(false)
   {
     mType = eSVG;
   }
 
-  virtual nsresult InsertChildBefore(nsIContent* aKid, nsIContent* aBeforeThis,
-                                     bool aNotify) override;
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   void SetCurrentContextPaint(const SVGContextPaint* aContextPaint)
   {
     mCurrentContextPaint = aContextPaint;
   }
 
   const SVGContextPaint* GetCurrentContextPaint() const
   {
     return mCurrentContextPaint;
   }
 
 private:
-  void EnsureNonSVGUserAgentStyleSheetsLoaded();
-
-  bool mHasLoadedNonSVGUserAgentStyleSheets;
 
   // This is maintained by AutoSetRestoreSVGContextPaint.
   const SVGContextPaint* mCurrentContextPaint = nullptr;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/svg/SVGForeignObjectElement.cpp
+++ b/dom/svg/SVGForeignObjectElement.cpp
@@ -102,42 +102,16 @@ SVGForeignObjectElement::HasValidDimensi
          mLengthAttributes[ATTR_WIDTH].GetAnimValInSpecifiedUnits() > 0 &&
          mLengthAttributes[ATTR_HEIGHT].IsExplicitlySet() &&
          mLengthAttributes[ATTR_HEIGHT].GetAnimValInSpecifiedUnits() > 0;
 }
 
 //----------------------------------------------------------------------
 // nsIContent methods
 
-nsresult
-SVGForeignObjectElement::BindToTree(nsIDocument* aDocument,
-                                    nsIContent* aParent,
-                                    nsIContent* aBindingParent,
-                                    bool aCompileEventHandlers)
-{
-  nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent,
-                                               aBindingParent,
-                                               aCompileEventHandlers);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsIDocument* doc = GetComposedDoc();
-  if (doc && doc->IsSVGDocument()) {
-    // We assume that we're going to have HTML content, so we ensure that the
-    // UA style sheets that nsDocumentViewer::CreateStyleSet skipped when
-    // it saw the document was an SVG document are loaded.
-    //
-    // We setup these style sheets during binding, not element construction,
-    // because elements can be moved from the document that creates them to
-    // another document.
-    doc->AsSVGDocument()->EnsureNonSVGUserAgentStyleSheetsLoaded();
-  }
-
-  return rv;
-}
-
 NS_IMETHODIMP_(bool)
 SVGForeignObjectElement::IsAttributeMapped(const nsAtom* name) const
 {
   static const MappedAttributeEntry* const map[] = {
     sFEFloodMap,
     sFiltersMap,
     sFontSpecificationMap,
     sGradientStopMap,
--- a/dom/svg/SVGForeignObjectElement.h
+++ b/dom/svg/SVGForeignObjectElement.h
@@ -31,19 +31,16 @@ protected:
 public:
   // nsSVGElement specializations:
   virtual gfxMatrix PrependLocalTransformsTo(
     const gfxMatrix &aMatrix,
     SVGTransformTypes aWhich = eAllTransforms) const override;
   virtual bool HasValidDimensions() const override;
 
   // nsIContent interface
-  virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
-                              nsIContent* aBindingParent,
-                              bool aCompileEventHandlers) override;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsAtom* name) const override;
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult,
                          bool aPreallocateChildren) const override;
 
   // WebIDL
   already_AddRefed<SVGAnimatedLength> X();
   already_AddRefed<SVGAnimatedLength> Y();
--- a/dom/svg/SVGSVGElement.cpp
+++ b/dom/svg/SVGSVGElement.cpp
@@ -490,21 +490,20 @@ SVGSVGElement::BindToTree(nsIDocument* a
     }
   }
 
   nsresult rv = SVGGraphicsElement::BindToTree(aDocument, aParent,
                                               aBindingParent,
                                               aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv,rv);
 
-  nsIDocument* doc = GetComposedDoc();
-  if (doc) {
-    // Setup the style sheet during binding, not element construction,
-    // because we could move the root SVG element from the document
-    // that created it to another document.
+  if (nsIDocument* doc = GetComposedDoc()) {
+    // Setup the style sheet during binding, not element construction, because
+    // we could move the root SVG element from the document that created it to
+    // another document.
     auto cache = nsLayoutStylesheetCache::Singleton();
     doc->EnsureOnDemandBuiltInUASheet(cache->SVGSheet());
   }
 
   if (mTimedDocumentRoot && smilController) {
     rv = mTimedDocumentRoot->SetParent(smilController);
     if (mStartAnimationOnBindToTree) {
       mTimedDocumentRoot->Begin();
--- a/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
+++ b/dom/tests/mochitest/webcomponents/test_shadowroot_inert_element.html
@@ -11,39 +11,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=806506">Bug 806506</a>
 <script>
 
 SimpleTest.waitForExplicitFinish();
 
 var content = '<div id="grabme"></div>';
-setShadowDOMPrefAndCreateIframe(content)
-  .then((aDocument) => {
-    function runChecks() {
-      isnot(aDocument.defaultView.getComputedStyle(shadowSpan, null).getPropertyValue("padding-top"), "10px", "Link element should be inert.");
-      is(aDocument.styleSheets.length, numStyleBeforeLoad, "Document style count should remain the same because the style should not be in the doucment.");
-      is(shadow.styleSheets.length, 0, "Inert link should not add style to ShadowRoot.");
-      // Remove link to make sure we don't get assertions.
-      shadow.removeChild(shadowStyle);
-      SimpleTest.finish();
-    };
-
-    var element = aDocument.getElementById("grabme");
-    var shadow = element.attachShadow({mode: "open"});
+setShadowDOMPrefAndCreateIframe(content).then(aDocument => {
+  var element = aDocument.getElementById("grabme");
+  var shadow = element.attachShadow({mode: "open"});
 
-    // Check that <base> is inert.
-    shadow.innerHTML = '<base href="http://www.example.org/" />';
-    isnot(aDocument.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
-
-    // Check that <link> is inert.
-    var numStyleBeforeLoad = aDocument.styleSheets.length;
-
-    shadow.innerHTML = '<link id="shadowlink" rel="stylesheet" type="text/css" href="inert_style.css" /><span id="shadowspan"></span>';
-    shadow.applyAuthorStyles = true;
-    var shadowSpan = shadow.getElementById("shadowspan");
-    var shadowStyle = shadow.getElementById("shadowlink");
-
-    runChecks();
-  });
+  // Check that <base> is inert.
+  shadow.innerHTML = '<base href="http://www.example.org/" />';
+  isnot(aDocument.baseURI, "http://www.example.org/", "Base element should be inert in ShadowRoot.");
+  SimpleTest.finish();
+});
 </script>
 </body>
 </html>
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -760,20 +760,17 @@ nsXULElement::BindToTree(nsIDocument* aD
 
   nsresult rv = nsStyledElement::BindToTree(aDocument, aParent,
                                             aBindingParent,
                                             aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIDocument* doc = GetComposedDoc();
 #ifdef DEBUG
-  if (doc &&
-      !doc->LoadsFullXULStyleSheetUpFront() &&
-      !doc->IsUnstyledDocument()) {
-
+  if (doc && !doc->AllowXULXBL() && !doc->IsUnstyledDocument()) {
     // To save CPU cycles and memory, non-XUL documents only load the user
     // agent style sheet rules for a minimal set of XUL elements such as
     // 'scrollbar' that may be created implicitly for their content (those
     // rules being in minimal-xul.css).
     //
     // This assertion makes sure no other XUL element than the ones in the
     // minimal XUL sheet is used in the bindings.
     if (!XULElementsRulesInMinimalXULSheet(NodeInfo()->NameAtom())) {
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52804
+52808
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -16933,16 +16933,17 @@ blahs/M
 blame/BMGDRS
 blameable
 blameless/YP
 blamelessness/M
 blameworthiness/M
 blameworthy/P
 blammo
 blanch/GDS
+blanche/S
 blancmange/MS
 bland/PTRY
 blandish/DSLG
 blandishment/SM
 blandness/M
 blank/TGPMDRYS
 blanket/GMDS
 blankness/M
@@ -18609,16 +18610,17 @@ carry/ZGDRSM
 carryall/SM
 carrycot/S
 carryout
 carryover/MS
 carsick/P
 carsickness/M
 cart/SZGMDR
 cartage/M
+carte/S
 cartel/MS
 carter/M
 carthorse/SM
 cartilage/SM
 cartilaginous
 cartload/SM
 cartographer/SM
 cartographic
@@ -27755,16 +27757,17 @@ gay/TSPMR
 gayness/M
 gaze/MZGDRS
 gazebo/SM
 gazelle/MS
 gazer/M
 gazette/MGDS
 gazetteer/MS
 gazillion/S
+gazillionth
 gazpacho/M
 gazump/DGS
 gear/MDGS
 gearbox/MS
 gearing/M
 gearshift/MS
 gearwheel/SM
 gecko/SM
@@ -36400,16 +36403,17 @@ neuroscientist/MS
 neuroses
 neurosis/M
 neurosurgeon/MS
 neurosurgery/M
 neurosurgical
 neurotic/MS
 neurotically
 neuroticism
+neurotoxin/S
 neurotransmitter/SM
 neut
 neuter/MDGS
 neutral/SMY
 neutralism/M
 neutralist/SM
 neutrality/M
 neutralization/M
@@ -40452,17 +40456,17 @@ precipitant/MS
 precipitate/XMYGNDS
 precipitation/M
 precipitous/Y
 precis/M
 precise/DRSYTGNP
 preciseness/M
 precision/M
 preclude/GDS
-preclusion/M
+preclusion/MS
 precocious/YP
 precociousness/M
 precocity/M
 precognition/M
 precognitive
 precolonial
 preconceive/GDS
 preconception/SM
@@ -43958,17 +43962,17 @@ scrawniness/M
 scrawny/PTR
 scream/SMDRZG
 screamer/M
 screaming/Y
 scree/MDS
 screech/GMDS
 screechy/TR
 screed/S
-screen/SJMDG
+screen/SJMDGRZ
 screening/M
 screenplay/SM
 screensaver/SM
 screenshot/SM
 screenwriter/SM
 screenwriting/M
 screw's
 screw/UDSG
@@ -49409,17 +49413,17 @@ tragedy/SM
 tragic
 tragically
 tragicomedy/SM
 tragicomic
 trail/ZGSMDR
 trailblazer/MS
 trailblazing/M
 trailer/M
-train/ZGSMDRB
+train/ZGSMDRBJ
 trained/U
 trainee/SM
 trainer/M
 training/M
 trainload/MS
 trainman/M
 trainmen
 trainspotter/S
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -315,17 +315,20 @@ ClientLayerManager::BeginTransaction()
 bool
 ClientLayerManager::EndTransactionInternal(DrawPaintedLayerCallback aCallback,
                                            void* aCallbackData,
                                            EndTransactionFlags)
 {
   // Wait for any previous async paints to complete before starting to paint again.
   // Do this outside the profiler and telemetry block so this doesn't count as time
   // spent rasterizing.
-  FlushAsyncPaints();
+  {
+    PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::FlushRasterization);
+    FlushAsyncPaints();
+  }
 
   PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Rasterization);
   AUTO_PROFILER_TRACING("Paint", "Rasterize");
 
   Maybe<TimeStamp> startTime;
   if (gfxPrefs::LayersDrawFPS()) {
     startTime = Some(TimeStamp::Now());
   }
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2435,29 +2435,16 @@ nsDocumentViewer::CreateStyleSet(nsIDocu
   // Make sure this does the same thing as PresShell::AddSheet wrt ordering.
 
   // this should eventually get expanded to allow for creating
   // different sets for different media
 
   UniquePtr<ServoStyleSet> styleSet = MakeUnique<ServoStyleSet>();
 
   // The document will fill in the document sheets when we create the presshell
-
-  if (aDocument->IsBeingUsedAsImage()) {
-    MOZ_ASSERT(aDocument->IsSVGDocument(),
-               "Do we want to skip most sheets for this new image type?");
-
-    // SVG-as-an-image must be kept as light and small as possible. We
-    // deliberately skip loading everything and leave svg.css (and html.css and
-    // xul.css) to be loaded on-demand.
-    // XXXjwatt Nothing else is loaded on-demand, but I don't think that
-    // should matter for SVG-as-an-image. If it does, I want to know why!
-    return styleSet;
-  }
-
   auto cache = nsLayoutStylesheetCache::Singleton();
 
   // Handle the user sheets.
   StyleSheet* sheet = nullptr;
   if (nsContentUtils::IsInChromeDocshell(aDocument)) {
     sheet = cache->UserChromeSheet();
   } else {
     sheet = cache->UserContentSheet();
@@ -2468,91 +2455,73 @@ nsDocumentViewer::CreateStyleSet(nsIDocu
   }
 
   // Append chrome sheets (scrollbars + forms).
   sheet = cache->ScrollbarsSheet();
   if (sheet) {
     styleSet->PrependStyleSheet(SheetType::Agent, sheet);
   }
 
-  if (!aDocument->IsSVGDocument()) {
-    // !!! IMPORTANT - KEEP THIS BLOCK IN SYNC WITH
-    // !!! SVGDocument::EnsureNonSVGUserAgentStyleSheetsLoaded.
-
-    // SVGForeignObjectElement::BindToTree calls SVGDocument::
-    // EnsureNonSVGUserAgentStyleSheetsLoaded to loads these UA sheet
-    // on-demand. (Excluding the quirks sheet, which should never be loaded for
-    // an SVG document, and excluding xul.css which will be loaded on demand by
-    // nsXULElement::BindToTree.)
-
-    sheet = cache->FormsSheet();
+  sheet = cache->FormsSheet();
+  if (sheet) {
+    styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+  }
+
+  if (aDocument->LoadsFullXULStyleSheetUpFront()) {
+    // This is the only place components.css gets loaded, unlike xul.css
+    sheet = cache->XULComponentsSheet();
+    if (sheet) {
+      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+    }
+
+    // nsXULElement::BindToTree loads xul.css on-demand if we don't load it
+    // up-front here.
+    sheet = cache->XULSheet();
     if (sheet) {
       styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
-
-    if (aDocument->LoadsFullXULStyleSheetUpFront()) {
-      // This is the only place components.css gets loaded, unlike xul.css
-      sheet = cache->XULComponentsSheet();
-      if (sheet) {
-        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-      }
-
-      // nsXULElement::BindToTree loads xul.css on-demand if we don't load it
-      // up-front here.
-      sheet = cache->XULSheet();
-      if (sheet) {
-        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-      }
-    }
-
-    sheet = cache->MinimalXULSheet();
-    if (sheet) {
-      // Load the minimal XUL rules for scrollbars and a few other XUL things
-      // that non-XUL (typically HTML) documents commonly use.
-      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-    }
-
-    sheet = cache->CounterStylesSheet();
+  }
+
+  sheet = cache->MinimalXULSheet();
+  if (sheet) {
+    // Load the minimal XUL rules for scrollbars and a few other XUL things
+    // that non-XUL (typically HTML) documents commonly use.
+    styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+  }
+
+  sheet = cache->CounterStylesSheet();
+  if (sheet) {
+    styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+  }
+
+  if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
+    sheet = cache->NoScriptSheet();
     if (sheet) {
       styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
-
-    if (nsLayoutUtils::ShouldUseNoScriptSheet(aDocument)) {
-      sheet = cache->NoScriptSheet();
-      if (sheet) {
-        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-      }
-    }
-
-    if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
-      sheet = cache->NoFramesSheet();
-      if (sheet) {
-        styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-      }
-    }
-
-    // We don't add quirk.css here; nsPresContext::CompatibilityModeChanged will
-    // append it if needed.
-
-    sheet = cache->HTMLSheet();
+  }
+
+  if (nsLayoutUtils::ShouldUseNoFramesSheet(aDocument)) {
+    sheet = cache->NoFramesSheet();
     if (sheet) {
       styleSet->PrependStyleSheet(SheetType::Agent, sheet);
     }
-
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->UASheet());
-  } else {
-    // SVG documents may have scrollbars and need the scrollbar styling.
-    sheet = cache->MinimalXULSheet();
-    if (sheet) {
-      styleSet->PrependStyleSheet(SheetType::Agent, sheet);
-    }
-  }
-
-  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
-  if (sheetService) {
+  }
+
+  // We don't add quirk.css here; nsPresContext::CompatibilityModeChanged will
+  // append it if needed.
+
+  sheet = cache->HTMLSheet();
+  if (sheet) {
+    styleSet->PrependStyleSheet(SheetType::Agent, sheet);
+  }
+
+  styleSet->PrependStyleSheet(SheetType::Agent, cache->UASheet());
+
+  if (nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance()) {
     for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
       styleSet->AppendStyleSheet(SheetType::Agent, sheet);
     }
     for (StyleSheet* sheet : Reversed(*sheetService->UserStyleSheets())) {
       styleSet->PrependStyleSheet(SheetType::User, sheet);
     }
   }
 
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -10021,27 +10021,30 @@ PaintTelemetry::AutoRecordPaint::~AutoRe
   auto recordSmall = [=](const nsCString& aKey, double aDurationMs) -> void {
     MOZ_ASSERT(aDurationMs <= totalMs);
     uint32_t amount = static_cast<int32_t>((aDurationMs / totalMs) * 100.0);
     Telemetry::Accumulate(Telemetry::CONTENT_SMALL_PAINT_PHASE_WEIGHT, aKey, amount);
   };
 
   double dlMs = sMetrics[Metric::DisplayList];
   double flbMs = sMetrics[Metric::Layerization];
+  double frMs = sMetrics[Metric::FlushRasterization];
   double rMs = sMetrics[Metric::Rasterization];
 
   // If the total time was >= 16ms, then it's likely we missed a frame due to
   // painting. We bucket these metrics separately.
   if (totalMs >= 16.0) {
     recordLarge(NS_LITERAL_CSTRING("dl"), dlMs);
     recordLarge(NS_LITERAL_CSTRING("flb"), flbMs);
+    recordLarge(NS_LITERAL_CSTRING("fr"), frMs);
     recordLarge(NS_LITERAL_CSTRING("r"), rMs);
   } else {
     recordSmall(NS_LITERAL_CSTRING("dl"), dlMs);
     recordSmall(NS_LITERAL_CSTRING("flb"), flbMs);
+    recordSmall(NS_LITERAL_CSTRING("fr"), frMs);
     recordSmall(NS_LITERAL_CSTRING("r"), rMs);
   }
 }
 
 PaintTelemetry::AutoRecord::AutoRecord(Metric aMetric)
  : mMetric(aMetric)
 {
   // Don't double-record anything nested.
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -6851,16 +6851,17 @@ public:
 namespace mozilla {
 
 class PaintTelemetry
 {
  public:
   enum class Metric {
     DisplayList,
     Layerization,
+    FlushRasterization,
     Rasterization,
     COUNT,
   };
 
   class AutoRecord
   {
    public:
     explicit AutoRecord(Metric aMetric);
--- a/layout/printing/nsPrintJob.cpp
+++ b/layout/printing/nsPrintJob.cpp
@@ -2339,23 +2339,16 @@ nsPrintJob::ReflowPrintObject(const Uniq
   aPO->mViewManager = new nsViewManager();
 
   rv = aPO->mViewManager->Init(printData->mPrintDC);
   NS_ENSURE_SUCCESS(rv,rv);
 
   UniquePtr<ServoStyleSet> styleSet =
     mDocViewerPrint->CreateStyleSet(aPO->mDocument);
 
-  if (aPO->mDocument->IsSVGDocument()) {
-    // The SVG document only loads minimal-xul.css, so it doesn't apply other
-    // styles. We should add ua.css for applying style which related to print.
-    auto cache = nsLayoutStylesheetCache::Singleton();
-    styleSet->PrependStyleSheet(SheetType::Agent, cache->UASheet());
-  }
-
   aPO->mPresShell = aPO->mDocument->CreateShell(aPO->mPresContext,
                                                 aPO->mViewManager,
                                                 std::move(styleSet));
   if (!aPO->mPresShell) {
     return NS_ERROR_FAILURE;
   }
 
   // If we're printing selection then remove the unselected nodes from our
--- a/layout/style/Loader.cpp
+++ b/layout/style/Loader.cpp
@@ -822,19 +822,16 @@ SheetLoadData::VerifySheetReadyToParse(n
 Loader::IsAlternate
 Loader::IsAlternateSheet(const nsAString& aTitle, bool aHasAlternateRel)
 {
   // A sheet is alternate if it has a nonempty title that doesn't match the
   // currently selected style set.  But if there _is_ no currently selected
   // style set, the sheet wasn't marked as an alternate explicitly, and aTitle
   // is nonempty, we should select the style set corresponding to aTitle, since
   // that's a preferred sheet.
-  //
-  // FIXME(emilio): This should return false for Shadow DOM regardless of the
-  // document.
   if (aTitle.IsEmpty()) {
     return IsAlternate::No;
   }
 
   if (mDocument) {
     const nsString& currentSheetSet = mDocument->GetCurrentStyleSheetSet();
     if (!aHasAlternateRel && currentSheetSet.IsEmpty()) {
       // There's no preferred set yet, and we now have a sheet with a title.
@@ -1916,20 +1913,18 @@ Loader::LoadInlineStyle(const SheetInfo&
   NS_ASSERTION(state == eSheetNeedsParser,
                "Inline sheets should not be cached");
 
   LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
 
   auto matched =
     PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate);
 
-  if (aInfo.mContent->HasFlag(NODE_IS_IN_SHADOW_TREE)) {
-    ShadowRoot* containingShadow = aInfo.mContent->GetContainingShadow();
-    MOZ_ASSERT(containingShadow);
-    containingShadow->InsertSheet(sheet, aInfo.mContent);
+  if (aInfo.mContent->IsInShadowTree()) {
+    aInfo.mContent->GetContainingShadow()->InsertSheet(sheet, aInfo.mContent);
   } else {
     rv = InsertSheetInDoc(sheet, aInfo.mContent, mDocument);
     if (NS_FAILED(rv)) {
       return Err(rv);
     }
   }
 
   nsIPrincipal* principal = aInfo.mContent->NodePrincipal();
@@ -2035,20 +2030,23 @@ Loader::LoadStyleLink(const SheetInfo& a
     return Err(rv);
   }
 
   LOG(("  Sheet is alternate: %d", static_cast<int>(isAlternate)));
 
   auto matched =
     PrepareSheet(sheet, aInfo.mTitle, aInfo.mMedia, nullptr, isAlternate);
 
-  // FIXME(emilio, bug 1410578): Shadow DOM should be handled here too.
-  rv = InsertSheetInDoc(sheet, aInfo.mContent, mDocument);
-  if (NS_FAILED(rv)) {
-    return Err(rv);
+  if (aInfo.mContent && aInfo.mContent->IsInShadowTree()) {
+    aInfo.mContent->GetContainingShadow()->InsertSheet(sheet, aInfo.mContent);
+  } else {
+    rv = InsertSheetInDoc(sheet, aInfo.mContent, mDocument);
+    if (NS_FAILED(rv)) {
+      return Err(rv);
+    }
   }
 
   nsCOMPtr<nsIStyleSheetLinkingElement> owningElement(
     do_QueryInterface(aInfo.mContent));
 
   if (state == eSheetComplete) {
     LOG(("  Sheet already complete: 0x%p", sheet.get()));
     if (aObserver || !mObservers.IsEmpty() || owningElement) {
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -2142,16 +2142,25 @@ Gecko_GetComputedURLSpec(const URLValueD
     if (NS_SUCCEEDED(rv)) {
       return;
     }
   }
 
   aOut->AssignLiteral("about:invalid");
 }
 
+void
+Gecko_nsIURI_Debug(nsIURI* aURI, nsCString* aOut)
+{
+  // TODO(emilio): Do we have more useful stuff to put here, maybe?
+  if (aURI) {
+    *aOut = aURI->GetSpecOrDefault();
+  }
+}
+
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(css::URLValue, CSSURLValue);
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(URLExtraData, URLExtraData);
 
 NS_IMPL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
 nsCSSShadowArray*
 Gecko_NewCSSShadowArray(uint32_t aLen)
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -531,16 +531,18 @@ void Gecko_nsStyleSVGPaint_Reset(nsStyle
 void Gecko_nsStyleSVG_SetDashArrayLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyDashArray(nsStyleSVG* dst, const nsStyleSVG* src);
 void Gecko_nsStyleSVG_SetContextPropertiesLength(nsStyleSVG* svg, uint32_t len);
 void Gecko_nsStyleSVG_CopyContextProperties(nsStyleSVG* dst, const nsStyleSVG* src);
 
 mozilla::css::URLValue* Gecko_NewURLValue(ServoBundledURI uri);
 size_t Gecko_URLValue_SizeOfIncludingThis(mozilla::css::URLValue* url);
 void Gecko_GetComputedURLSpec(const mozilla::css::URLValueData* url, nsCString* spec);
+void Gecko_nsIURI_Debug(nsIURI*, nsCString* spec);
+
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(mozilla::css::URLValue, CSSURLValue);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(RawGeckoURLExtraData, URLExtraData);
 
 void Gecko_FillAllImageLayers(nsStyleImageLayers* layers, uint32_t max_len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsStyleCoord::Calc, Calc);
 
 nsCSSShadowArray* Gecko_NewCSSShadowArray(uint32_t len);
 NS_DECL_THREADSAFE_FFI_REFCOUNTING(nsCSSShadowArray, CSSShadowArray);
--- a/layout/style/res/noscript.css
+++ b/layout/style/res/noscript.css
@@ -1,9 +1,11 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* This sheet is added to the style set for documents with script disabled */
+/* This sheet is added to the style set for documents with script enabled */
+
+@namespace url(http://www.w3.org/1999/xhtml);
 
 noscript {
   display: none !important;
 }
--- a/layout/svg/svg.css
+++ b/layout/svg/svg.css
@@ -79,32 +79,8 @@ foreignObject {
   mask: inherit;
   opacity: inherit;
 }
 
 *:-moz-focusring {
   /* Don't specify the outline-color, we should always use initial value. */
   outline: 1px dotted;
 }
-
-/* nsDocumentViewer::CreateStyleSet doesn't load ua.css.
- * A few styles are common to html and SVG though
- * so we copy the rules below from that file.
- */
-
-/* Links */
-
-*|*:any-link {
-  cursor: pointer;
-}
-
-*|*:any-link:-moz-focusring {
-  /* Don't specify the outline-color, we should always use initial value. */
-  outline: 1px dotted;
-}
-
-/*
- * SVG-as-an-image needs this rule
- */
-*|*::-moz-viewport, *|*::-moz-viewport-scroll, *|*::-moz-canvas, *|*::-moz-scrolled-canvas {
-  display: block !important;
-  background-color: inherit;
-}
--- a/parser/html/nsHtml5AtomTable.cpp
+++ b/parser/html/nsHtml5AtomTable.cpp
@@ -2,30 +2,30 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsHtml5AtomTable.h"
 #include "nsThreadUtils.h"
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(KeyTypePointer aStr)
   : nsStringHashKey(aStr)
-  , mAtom(new nsDynamicAtom(*aStr))
+  , mAtom(nsDynamicAtom::Create(*aStr))
 {
 }
 
 nsHtml5AtomEntry::nsHtml5AtomEntry(const nsHtml5AtomEntry& aOther)
   : nsStringHashKey(aOther)
   , mAtom(nullptr)
 {
   NS_NOTREACHED("nsHtml5AtomTable is broken and tried to copy an entry");
 }
 
 nsHtml5AtomEntry::~nsHtml5AtomEntry()
 {
-  delete mAtom;
+  nsDynamicAtom::Destroy(mAtom);
 }
 
 nsHtml5AtomTable::nsHtml5AtomTable()
   : mRecentlyUsedParserAtoms{}
 {
 #ifdef DEBUG
   mPermittedLookupEventTarget = mozilla::GetCurrentThreadSerialEventTarget();
 #endif
--- a/parser/html/nsHtml5String.h
+++ b/parser/html/nsHtml5String.h
@@ -1,17 +1,18 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsHtml5String_h
 #define nsHtml5String_h
 
+#include "nsAtom.h"
 #include "nsString.h"
-#include "nsAtom.h"
+#include "nsStringBuffer.h"
 
 class nsHtml5TreeBuilder;
 
 /**
  * A pass-by-value type that can represent
  *  * nullptr
  *  * empty string
  *  * Non-empty string as exactly-sized (capacity is length) `nsStringBuffer*`
--- a/servo/components/style/error_reporting.rs
+++ b/servo/components/style/error_reporting.rs
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Types used to report parsing errors.
 
 #![deny(missing_docs)]
 
 use cssparser::{BasicParseErrorKind, ParseErrorKind, SourceLocation, Token};
-use log;
 use std::fmt;
 use style_traits::ParseError;
 use stylesheets::UrlExtraData;
 
 /// Errors that can be encountered while parsing CSS.
 #[derive(Debug)]
 pub enum ContextualParseError<'a> {
     /// A property declaration was not recognized.
@@ -224,25 +223,28 @@ pub trait ParseErrorReporter {
 }
 
 /// An error reporter that uses [the `log` crate](https://github.com/rust-lang-nursery/log)
 /// at `info` level.
 ///
 /// This logging is silent by default, and can be enabled with a `RUST_LOG=style=info`
 /// environment variable.
 /// (See [`env_logger`](https://rust-lang-nursery.github.io/log/env_logger/).)
+#[cfg(feature = "servo")]
 pub struct RustLogReporter;
 
+#[cfg(feature = "servo")]
 impl ParseErrorReporter for RustLogReporter {
     fn report_error(
         &self,
         url: &UrlExtraData,
         location: SourceLocation,
         error: ContextualParseError,
     ) {
+        use log;
         if log_enabled!(log::Level::Info) {
             info!(
                 "Url:\t{}\n{}:{} {}",
                 url.as_str(),
                 location.line,
                 location.column,
                 error
             )
--- a/servo/components/style/gecko/data.rs
+++ b/servo/components/style/gecko/data.rs
@@ -12,24 +12,35 @@ use gecko_bindings::structs::{self, RawG
 use gecko_bindings::structs::{StyleSheetInfo, nsIDocument};
 use gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI};
 use invalidation::media_queries::{MediaListKey, ToMediaListKey};
 use malloc_size_of::MallocSizeOfOps;
 use media_queries::{Device, MediaList};
 use properties::ComputedValues;
 use selector_parser::SnapshotMap;
 use servo_arc::Arc;
+use std::fmt;
 use shared_lock::{Locked, SharedRwLockReadGuard, StylesheetGuards};
 use stylesheets::{CssRule, Origin, StylesheetContents, StylesheetInDocument};
 use stylist::Stylist;
 
 /// Little wrapper to a Gecko style sheet.
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Eq, PartialEq)]
 pub struct GeckoStyleSheet(*const DomStyleSheet);
 
+impl fmt::Debug for GeckoStyleSheet {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        let contents = self.contents();
+        formatter.debug_struct("GeckoStyleSheet")
+            .field("origin", &contents.origin)
+            .field("url_data", &*contents.url_data.read())
+            .finish()
+    }
+}
+
 impl ToMediaListKey for ::gecko::data::GeckoStyleSheet {
     fn to_media_list_key(&self) -> MediaListKey {
         use std::mem;
         unsafe { MediaListKey::from_raw(mem::transmute(self.0)) }
     }
 }
 
 impl GeckoStyleSheet {
--- a/servo/components/style/gecko/media_queries.rs
+++ b/servo/components/style/gecko/media_queries.rs
@@ -59,16 +59,37 @@ pub struct Device {
     /// Whether any styles computed in the document relied on the root font-size
     /// by using rem units.
     used_root_font_size: AtomicBool,
     /// Whether any styles computed in the document relied on the viewport size
     /// by using vw/vh/vmin/vmax units.
     used_viewport_size: AtomicBool,
 }
 
+impl fmt::Debug for Device {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        use nsstring::nsCString;
+
+        let mut doc_uri = nsCString::new();
+        unsafe {
+            let doc =
+                &*self.pres_context().mDocument.raw::<structs::nsIDocument>();
+
+            bindings::Gecko_nsIURI_Debug(
+                doc.mDocumentURI.raw::<structs::nsIURI>(),
+                &mut doc_uri,
+            )
+        };
+
+        f.debug_struct("Device")
+            .field("document_url", &doc_uri)
+            .finish()
+    }
+}
+
 unsafe impl Sync for Device {}
 unsafe impl Send for Device {}
 
 impl Device {
     /// Trivially constructs a new `Device`.
     pub fn new(pres_context: RawGeckoPresContextOwned) -> Self {
         assert!(!pres_context.is_null());
         Device {
--- a/servo/components/style/gecko/url.rs
+++ b/servo/components/style/gecko/url.rs
@@ -1,43 +1,44 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //! Common handling for the specified value CSS url() values.
 
 use cssparser::Parser;
 use gecko_bindings::bindings;
-use gecko_bindings::structs::{ServoBundledURI, URLExtraData};
+use gecko_bindings::structs::ServoBundledURI;
 use gecko_bindings::structs::mozilla::css::URLValueData;
 use gecko_bindings::structs::root::{RustString, nsStyleImageRequest};
 use gecko_bindings::structs::root::mozilla::css::{ImageValue, URLValue};
 use gecko_bindings::sugar::refptr::RefPtr;
 use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
 use nsstring::nsCString;
 use parser::{Parse, ParserContext};
 use servo_arc::{Arc, RawOffsetArc};
 use std::fmt::{self, Write};
 use std::mem;
+use stylesheets::UrlExtraData;
 use style_traits::{CssWriter, ParseError, ToCss};
 use values::computed::{Context, ToComputedValue};
 
 /// A CSS url() value for gecko.
 #[css(function = "url")]
 #[derive(Clone, Debug, PartialEq, SpecifiedValueInfo, ToCss)]
 pub struct CssUrl {
     /// The URL in unresolved string form.
     ///
     /// Refcounted since cloning this should be cheap and data: uris can be
     /// really large.
     serialization: Arc<String>,
 
     /// The URL extra data.
     #[css(skip)]
-    pub extra_data: RefPtr<URLExtraData>,
+    pub extra_data: UrlExtraData,
 }
 
 impl CssUrl {
     /// Try to parse a URL from a string value that is a valid CSS token for a
     /// URL.
     pub fn parse_from_string(url: String, context: &ParserContext) -> Self {
         CssUrl {
             serialization: Arc::new(url),
@@ -53,17 +54,17 @@ impl CssUrl {
     }
 
     /// Convert from URLValueData to SpecifiedUrl.
     unsafe fn from_url_value_data(url: &URLValueData) -> Self {
         let arc_type =
             &url.mString as *const _ as *const RawOffsetArc<String>;
         CssUrl {
             serialization: Arc::from_raw_offset((*arc_type).clone()),
-            extra_data: url.mExtraData.to_safe(),
+            extra_data: UrlExtraData(url.mExtraData.to_safe()),
         }
     }
 
     /// Returns true if this URL looks like a fragment.
     /// See https://drafts.csswg.org/css-values/#local-urls
     pub fn is_fragment(&self) -> bool {
         self.as_str().chars().next().map_or(false, |c| c == '#')
     }
@@ -83,17 +84,17 @@ impl CssUrl {
     }
 
     /// Create a bundled URI suitable for sending to Gecko
     /// to be constructed into a css::URLValue
     pub fn for_ffi(&self) -> ServoBundledURI {
         let arc_offset = Arc::into_raw_offset(self.serialization.clone());
         ServoBundledURI {
             mURLString: unsafe { mem::transmute::<_, RawOffsetArc<RustString>>(arc_offset) },
-            mExtraData: self.extra_data.get(),
+            mExtraData: self.extra_data.0.get(),
         }
     }
 }
 
 impl Parse for CssUrl {
     fn parse<'i, 't>(
         context: &ParserContext,
         input: &mut Parser<'i, 't>,
--- a/servo/components/style/gecko_bindings/sugar/refptr.rs
+++ b/servo/components/style/gecko_bindings/sugar/refptr.rs
@@ -73,25 +73,16 @@ impl<T: RefCounted> RefPtr<T> {
         let ret = RefPtr {
             ptr: ptr,
             _marker: PhantomData,
         };
         ret.addref();
         ret
     }
 
-    /// Create a reference to RefPtr from a reference to pointer.
-    ///
-    /// The pointer must be valid and non null.
-    ///
-    /// This method doesn't touch refcount.
-    pub unsafe fn from_ptr_ref(ptr: &*mut T) -> &Self {
-        mem::transmute(ptr)
-    }
-
     /// Produces an FFI-compatible RefPtr that can be stored in style structs.
     ///
     /// structs::RefPtr does not have a destructor, so this may leak
     pub fn forget(self) -> structs::RefPtr<T> {
         let ret = structs::RefPtr {
             mRawPtr: self.ptr,
             _phantom_0: PhantomData,
         };
--- a/servo/components/style/gecko_string_cache/mod.rs
+++ b/servo/components/style/gecko_string_cache/mod.rs
@@ -117,17 +117,18 @@ impl WeakAtom {
             let string_offset = unsafe { (*atom_ptr).mStringOffset };
             let string_offset = -(string_offset as isize);
             let u8_ptr = atom_ptr as *const u8;
             // It is safe to use offset() here because both addresses are within
             // the same struct, e.g. mozilla::detail::gGkAtoms.
             unsafe { u8_ptr.offset(string_offset) as *const u16 }
         } else {
             let atom_ptr = self.as_ptr() as *const nsDynamicAtom;
-            unsafe { (*(atom_ptr)).mString }
+            // Dynamic atom chars are stored at the end of the object.
+            unsafe { atom_ptr.offset(1) as *const u16 }
         };
         unsafe { slice::from_raw_parts(string, self.len() as usize) }
     }
 
     // NOTE: don't expose this, since it's slow, and easy to be misused.
     fn chars(&self) -> DecodeUtf16<Cloned<slice::Iter<u16>>> {
         char::decode_utf16(self.as_slice().iter().cloned())
     }
--- a/servo/components/style/media_queries/media_list.rs
+++ b/servo/components/style/media_queries/media_list.rs
@@ -9,18 +9,18 @@
 use context::QuirksMode;
 use cssparser::{Delimiter, Parser};
 use cssparser::{ParserInput, Token};
 use error_reporting::ContextualParseError;
 use parser::ParserContext;
 use super::{Device, MediaQuery, Qualifier};
 
 /// A type that encapsulates a media query list.
-#[css(comma)]
-#[derive(Clone, Debug, MallocSizeOf, ToCss)]
+#[css(comma, derive_debug)]
+#[derive(Clone, MallocSizeOf, ToCss)]
 pub struct MediaList {
     /// The list of media queries.
     #[css(iterable)]
     pub media_queries: Vec<MediaQuery>,
 }
 
 impl MediaList {
     /// Parse a media query list from CSS.
--- a/servo/components/style/stylesheets/mod.rs
+++ b/servo/components/style/stylesheets/mod.rs
@@ -56,32 +56,62 @@ pub use self::supports_rule::SupportsRul
 pub use self::viewport_rule::ViewportRule;
 
 /// Extra data that the backend may need to resolve url values.
 #[cfg(not(feature = "gecko"))]
 pub type UrlExtraData = ::servo_url::ServoUrl;
 
 /// Extra data that the backend may need to resolve url values.
 #[cfg(feature = "gecko")]
-pub type UrlExtraData =
-    ::gecko_bindings::sugar::refptr::RefPtr<::gecko_bindings::structs::URLExtraData>;
+#[derive(Clone, PartialEq)]
+pub struct UrlExtraData(
+    pub ::gecko_bindings::sugar::refptr::RefPtr<::gecko_bindings::structs::URLExtraData>
+);
 
 #[cfg(feature = "gecko")]
 impl UrlExtraData {
-    /// Returns a string for the url.
-    ///
-    /// Unimplemented currently.
-    pub fn as_str(&self) -> &str {
-        // TODO
-        "(stylo: not supported)"
+    /// True if this URL scheme is chrome.
+    #[inline]
+    pub fn is_chrome(&self) -> bool {
+        self.0.mIsChrome
     }
 
-    /// True if this URL scheme is chrome.
-    pub fn is_chrome(&self) -> bool {
-        self.mIsChrome
+    /// Create a reference to this `UrlExtraData` from a reference to pointer.
+    ///
+    /// The pointer must be valid and non null.
+    ///
+    /// This method doesn't touch refcount.
+    #[inline]
+    pub unsafe fn from_ptr_ref(ptr: &*mut ::gecko_bindings::structs::URLExtraData) -> &Self {
+        ::std::mem::transmute(ptr)
+    }
+}
+
+#[cfg(feature = "gecko")]
+impl fmt::Debug for UrlExtraData {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        use gecko_bindings::{structs, bindings};
+
+        struct DebugURI(*mut structs::nsIURI);
+        impl fmt::Debug for DebugURI {
+            fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+                use nsstring::nsCString;
+                let mut spec = nsCString::new();
+                unsafe {
+                    bindings::Gecko_nsIURI_Debug(self.0, &mut spec);
+                }
+                spec.fmt(formatter)
+            }
+        }
+
+        formatter.debug_struct("URLExtraData")
+            .field("is_chrome", &self.is_chrome())
+            .field("base", &DebugURI(self.0.mBaseURI.raw::<structs::nsIURI>()))
+            .field("referrer", &DebugURI(self.0.mReferrer.raw::<structs::nsIURI>()))
+            .finish()
     }
 }
 
 // XXX We probably need to figure out whether we should mark Eq here.
 // It is currently marked so because properties::UnparsedValue wants Eq.
 #[cfg(feature = "gecko")]
 impl Eq for UrlExtraData {}
 
--- a/servo/components/style/stylesheets/stylesheet.rs
+++ b/servo/components/style/stylesheets/stylesheet.rs
@@ -171,17 +171,17 @@ macro_rules! rule_filter {
                     }
                 }
             }
         )+
     }
 }
 
 /// A trait to represent a given stylesheet in a document.
-pub trait StylesheetInDocument {
+pub trait StylesheetInDocument : ::std::fmt::Debug {
     /// Get the stylesheet origin.
     fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin;
 
     /// Get the stylesheet quirks mode.
     fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode;
 
     /// Get whether this stylesheet is enabled.
     fn enabled(&self) -> bool;
@@ -258,17 +258,17 @@ impl StylesheetInDocument for Stylesheet
     #[inline]
     fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
         self.contents.rules(guard)
     }
 }
 
 /// A simple wrapper over an `Arc<Stylesheet>`, with pointer comparison, and
 /// suitable for its use in a `StylesheetSet`.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 #[cfg_attr(feature = "servo", derive(MallocSizeOf))]
 pub struct DocumentStyleSheet(
     #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] pub Arc<Stylesheet>,
 );
 
 impl PartialEq for DocumentStyleSheet {
     fn eq(&self, other: &Self) -> bool {
         Arc::ptr_eq(&self.0, &other.0)
--- a/servo/components/style/stylist.rs
+++ b/servo/components/style/stylist.rs
@@ -70,59 +70,65 @@ struct UserAgentCascadeDataCache {
     entries: Vec<Arc<UserAgentCascadeData>>,
 }
 
 impl UserAgentCascadeDataCache {
     fn new() -> Self {
         Self { entries: vec![] }
     }
 
+    fn len(&self) -> usize {
+        self.entries.len()
+    }
+
     // FIXME(emilio): This may need to be keyed on quirks-mode too, though there
     // aren't class / id selectors on those sheets, usually, so it's probably
     // ok...
     fn lookup<'a, I, S>(
         &'a mut self,
         sheets: I,
         device: &Device,
         quirks_mode: QuirksMode,
         guard: &SharedRwLockReadGuard,
     ) -> Result<Arc<UserAgentCascadeData>, FailedAllocationError>
     where
         I: Iterator<Item = &'a S> + Clone,
         S: StylesheetInDocument + ToMediaListKey + PartialEq + 'static,
     {
         let mut key = EffectiveMediaQueryResults::new();
+        debug!("UserAgentCascadeDataCache::lookup({:?})", device);
         for sheet in sheets.clone() {
             CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key)
         }
 
         for entry in &self.entries {
             if entry.cascade_data.effective_media_query_results == key {
                 return Ok(entry.clone());
             }
         }
 
         let mut new_data = UserAgentCascadeData {
             cascade_data: CascadeData::new(),
             precomputed_pseudo_element_decls: PrecomputedPseudoElementDeclarations::default(),
         };
 
+        debug!("> Picking the slow path");
+
         for sheet in sheets {
             new_data.cascade_data.add_stylesheet(
                 device,
                 quirks_mode,
                 sheet,
                 guard,
                 SheetRebuildKind::Full,
                 Some(&mut new_data.precomputed_pseudo_element_decls),
             )?;
         }
 
         let new_data = Arc::new(new_data);
-
         self.entries.push(new_data.clone());
         Ok(new_data)
     }
 
     fn expire_unused(&mut self) {
         self.entries.retain(|e| !e.is_unique())
     }
 
@@ -239,18 +245,18 @@ impl DocumentCascadeData {
     {
         // First do UA sheets.
         {
             if flusher.flush_origin(Origin::UserAgent).dirty() {
                 let mut ua_cache = UA_CASCADE_DATA_CACHE.lock().unwrap();
                 let origin_sheets = flusher.origin_sheets(Origin::UserAgent);
                 let ua_cascade_data =
                     ua_cache.lookup(origin_sheets, device, quirks_mode, guards.ua_or_user)?;
-
                 ua_cache.expire_unused();
+                debug!("User agent data cache size {:?}", ua_cache.len());
                 self.user_agent = ua_cascade_data;
             }
         }
 
         // Now do the user sheets.
         self.user.rebuild(
             device,
             quirks_mode,
@@ -1080,17 +1086,17 @@ impl Stylist {
     /// document itself, which is what is kept up-to-date.
     ///
     /// Arguably XBL should use something more lightweight than a Stylist.
     pub fn media_features_change_changed_style(
         &self,
         guards: &StylesheetGuards,
         device: &Device,
     ) -> OriginSet {
-        debug!("Stylist::media_features_change_changed_style");
+        debug!("Stylist::media_features_change_changed_style {:?}", device);
 
         let mut origins = OriginSet::empty();
         let stylesheets = self.stylesheets.iter();
 
         for (stylesheet, origin) in stylesheets {
             if origins.contains(origin.into()) {
                 continue;
             }
@@ -2140,26 +2146,29 @@ impl CascadeData {
         results: &mut EffectiveMediaQueryResults,
     ) where
         S: StylesheetInDocument + ToMediaListKey + 'static,
     {
         if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
             return;
         }
 
+        debug!(" + {:?}", stylesheet);
         results.saw_effective(stylesheet);
 
         for rule in stylesheet.effective_rules(device, guard) {
             match *rule {
                 CssRule::Import(ref lock) => {
                     let import_rule = lock.read_with(guard);
+                    debug!(" + {:?}", import_rule.stylesheet.media(guard));
                     results.saw_effective(import_rule);
                 },
                 CssRule::Media(ref lock) => {
                     let media_rule = lock.read_with(guard);
+                    debug!(" + {:?}", media_rule.media_queries.read_with(guard));
                     results.saw_effective(media_rule);
                 },
                 _ => {},
             }
         }
     }
 
     // Returns Err(..) to signify OOM
@@ -2341,18 +2350,20 @@ impl CascadeData {
         use invalidation::media_queries::PotentiallyEffectiveMediaRules;
 
         let effective_now = stylesheet.is_effective_for_device(device, guard);
 
         let effective_then = self.effective_media_query_results.was_effective(stylesheet);
 
         if effective_now != effective_then {
             debug!(
-                " > Stylesheet changed -> {}, {}",
-                effective_then, effective_now
+                " > Stylesheet {:?} changed -> {}, {}",
+                stylesheet.media(guard),
+                effective_then,
+                effective_now
             );
             return false;
         }
 
         if !effective_now {
             return true;
         }
 
@@ -2377,18 +2388,20 @@ impl CascadeData {
                     let import_rule = lock.read_with(guard);
                     let effective_now = import_rule
                         .stylesheet
                         .is_effective_for_device(&device, guard);
                     let effective_then = self.effective_media_query_results
                         .was_effective(import_rule);
                     if effective_now != effective_then {
                         debug!(
-                            " > @import rule changed {} -> {}",
-                            effective_then, effective_now
+                            " > @import rule {:?} changed {} -> {}",
+                            import_rule.stylesheet.media(guard),
+                            effective_then,
+                            effective_now
                         );
                         return false;
                     }
 
                     if !effective_now {
                         iter.skip_children();
                     }
                 },
@@ -2396,18 +2409,20 @@ impl CascadeData {
                     let media_rule = lock.read_with(guard);
                     let mq = media_rule.media_queries.read_with(guard);
                     let effective_now = mq.evaluate(device, quirks_mode);
                     let effective_then =
                         self.effective_media_query_results.was_effective(media_rule);
 
                     if effective_now != effective_then {
                         debug!(
-                            " > @media rule changed {} -> {}",
-                            effective_then, effective_now
+                            " > @media rule {:?} changed {} -> {}",
+                            mq,
+                            effective_then,
+                            effective_now
                         );
                         return false;
                     }
 
                     if !effective_now {
                         iter.skip_children();
                     }
                 },
--- a/servo/components/style_derive/to_css.rs
+++ b/servo/components/style_derive/to_css.rs
@@ -226,16 +226,18 @@ pub struct CssInputAttrs {
     // Here because structs variants are also their whole type definition.
     pub comma: bool,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromVariant)]
 pub struct CssVariantAttrs {
     pub function: Option<Override<String>>,
+    // Here because structs variants are also their whole type definition.
+    pub derive_debug: bool,
     pub comma: bool,
     pub dimension: bool,
     pub keyword: Option<String>,
     pub skip: bool,
 }
 
 #[darling(attributes(css), default)]
 #[derive(Default, FromField)]
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -138,17 +138,17 @@ use style::rule_cache::RuleCacheConditio
 use style::rule_tree::{CascadeLevel, StrongRuleNode};
 use style::selector_parser::{PseudoElementCascadeType, SelectorImpl};
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::{Atom, WeakAtom};
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, CounterStyleRule};
 use style::stylesheets::{DocumentRule, FontFaceRule, FontFeatureValuesRule, ImportRule};
 use style::stylesheets::{KeyframesRule, MediaRule, NamespaceRule, Origin, OriginSet, PageRule};
-use style::stylesheets::{StyleRule, StylesheetContents, SupportsRule};
+use style::stylesheets::{StyleRule, StylesheetContents, SupportsRule, UrlExtraData};
 use style::stylesheets::StylesheetLoader as StyleStylesheetLoader;
 use style::stylesheets::import_rule::ImportSheet;
 use style::stylesheets::keyframes_rule::{Keyframe, KeyframeSelector, KeyframesStepValue};
 use style::stylesheets::supports_rule::parse_condition_or_declaration;
 use style::stylist::{add_size_of_ua_cache, AuthorStylesEnabled, RuleInclusion, Stylist};
 use style::thread_state;
 use style::timer::Timer;
 use style::traversal::DomTraversal;
@@ -184,54 +184,52 @@ impl ClosureHelper for DeclarationBlockM
  * the C header in Gecko. In order to catch accidental mismatches, we run rust-bindgen against
  * those signatures as well, giving us a second declaration of all the Servo_* functions in this
  * crate. If there's a mismatch, LLVM will assert and abort, which is a rather awful thing to
  * depend on but good enough for our purposes.
  */
 
 // A dummy url data for where we don't pass url data in.
 // We need to get rid of this sooner than later.
-static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut URLExtraData;
-
-#[no_mangle]
-pub extern "C" fn Servo_Initialize(dummy_url_data: *mut URLExtraData) {
+static mut DUMMY_URL_DATA: *mut URLExtraData = 0 as *mut _;
+
+#[no_mangle]
+pub unsafe extern "C" fn Servo_Initialize(dummy_url_data: *mut URLExtraData) {
     use style::gecko_bindings::sugar::origin_flags;
 
     // Pretend that we're a Servo Layout thread, to make some assertions happy.
     thread_state::initialize(thread_state::ThreadState::LAYOUT);
 
     // Perform some debug-only runtime assertions.
     restyle_hints::assert_restyle_hints_match();
     origin_flags::assert_flags_match();
     parser::assert_parsing_mode_match();
     traversal_flags::assert_traversal_flags_match();
     specified::font::assert_variant_east_asian_matches();
     specified::font::assert_variant_ligatures_matches();
     specified::box_::assert_touch_action_matches();
 
-    // Initialize the dummy url data
-    unsafe { DUMMY_URL_DATA = dummy_url_data; }
+    DUMMY_URL_DATA = dummy_url_data;
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_InitializeCooperativeThread() {
     // Pretend that we're a Servo Layout thread to make some assertions happy.
     thread_state::initialize(thread_state::ThreadState::LAYOUT);
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_Shutdown() {
-    // The dummy url will be released after shutdown, so clear the
-    // reference to avoid use-after-free.
-    unsafe { DUMMY_URL_DATA = ptr::null_mut(); }
+pub unsafe extern "C" fn Servo_Shutdown() {
+    DUMMY_URL_DATA = ptr::null_mut();
     Stylist::shutdown();
 }
 
-unsafe fn dummy_url_data() -> &'static RefPtr<URLExtraData> {
-    RefPtr::from_ptr_ref(&DUMMY_URL_DATA)
+#[inline(always)]
+unsafe fn dummy_url_data() -> &'static UrlExtraData {
+    UrlExtraData::from_ptr_ref(&DUMMY_URL_DATA)
 }
 
 #[allow(dead_code)]
 fn is_main_thread() -> bool {
     unsafe { bindings::Gecko_IsMainThread() }
 }
 
 #[allow(dead_code)]
@@ -1176,17 +1174,17 @@ pub extern "C" fn Servo_StyleSheet_FromU
     line_number_offset: u32,
     quirks_mode: nsCompatibility,
     reusable_sheets: *mut LoaderReusableStyleSheets,
 ) -> RawServoStyleSheetContentsStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let input: &str = unsafe { (*bytes).as_str_unchecked() };
 
     let reporter = ErrorReporter::new(stylesheet, loader, extra_data);
-    let url_data = unsafe { RefPtr::from_ptr_ref(&extra_data) };
+    let url_data = unsafe { UrlExtraData::from_ptr_ref(&extra_data) };
     let loader = if loader.is_null() {
         None
     } else {
         Some(StylesheetLoader::new(loader, stylesheet, load_data, reusable_sheets))
     };
 
     // FIXME(emilio): loader.as_ref() doesn't typecheck for some reason?
     let loader: Option<&StyleStylesheetLoader> = match loader {
@@ -1203,33 +1201,33 @@ pub extern "C" fn Servo_StyleSheet_FromU
         loader,
         reporter.as_ref().map(|r| r as &ParseErrorReporter),
         quirks_mode.into(),
         line_number_offset,
     )).into_strong()
 }
 
 #[no_mangle]
-pub extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
+pub unsafe extern "C" fn Servo_StyleSheet_FromUTF8BytesAsync(
     load_data: *mut SheetLoadDataHolder,
     extra_data: *mut URLExtraData,
     bytes: *const nsACString,
     mode: SheetParsingMode,
     line_number_offset: u32,
     quirks_mode: nsCompatibility,
 ) {
-    let (load_data, extra_data, bytes) = unsafe {
-        let mut b = nsCString::new();
-        b.assign(&*bytes);
-        (RefPtr::new(load_data), RefPtr::new(extra_data), b)
-    };
+    let load_data = RefPtr::new(load_data);
+    let extra_data = UrlExtraData(RefPtr::new(extra_data));
+
+    let mut sheet_bytes = nsCString::new();
+    sheet_bytes.assign(&*bytes);
     let async_parser = AsyncStylesheetParser::new(
         load_data,
         extra_data,
-        bytes,
+        sheet_bytes,
         mode_to_origin(mode),
         quirks_mode.into(),
         line_number_offset
     );
 
     if let Some(thread_pool) = STYLE_THREAD_POOL.style_thread_pool.as_ref() {
         thread_pool.spawn(|| {
             async_parser.parse();
@@ -2508,17 +2506,17 @@ pub unsafe extern "C" fn Servo_FontFaceR
     rule: RawServoFontFaceRuleBorrowed,
     desc: nsCSSFontDesc,
     value: *const nsACString,
     data: *mut URLExtraData,
 ) -> bool {
     let value = value.as_ref().unwrap().as_str_unchecked();
     let mut input = ParserInput::new(&value);
     let mut parser = Parser::new(&mut input);
-    let url_data = RefPtr::from_ptr_ref(&data);
+    let url_data = UrlExtraData::from_ptr_ref(&data);
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
     );
@@ -3219,17 +3217,17 @@ fn parse_property_into(
     value: *const nsACString,
     data: *mut URLExtraData,
     parsing_mode: structs::ParsingMode,
     quirks_mode: QuirksMode,
     reporter: Option<&ParseErrorReporter>,
 ) -> Result<(), ()> {
     use style_traits::ParsingMode;
     let value = unsafe { value.as_ref().unwrap().as_str_unchecked() };
-    let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
+    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,
         value,
         url_data,
         reporter,
@@ -3279,17 +3277,17 @@ pub extern "C" fn Servo_ParseProperty(
 pub extern "C" fn Servo_ParseEasing(
     easing: *const nsAString,
     data: *mut URLExtraData,
     output: nsTimingFunctionBorrowedMut
 ) -> bool {
     use style::properties::longhands::transition_timing_function;
 
     // FIXME Dummy URL data would work fine here.
-    let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
+    let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
     );
@@ -3372,17 +3370,17 @@ pub extern "C" fn Servo_ParseStyleAttrib
     data: *const nsACString,
     raw_extra_data: *mut URLExtraData,
     quirks_mode: nsCompatibility,
     loader: *mut Loader,
 ) -> RawServoDeclarationBlockStrong {
     let global_style_data = &*GLOBAL_STYLE_DATA;
     let value = unsafe { data.as_ref().unwrap().as_str_unchecked() };
     let reporter = ErrorReporter::new(ptr::null_mut(), loader, raw_extra_data);
-    let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
+    let url_data = unsafe { 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),
             quirks_mode.into(),
         )
     )).into_strong()
@@ -4211,17 +4209,17 @@ pub extern "C" fn Servo_DeclarationBlock
     raw_extra_data: *mut URLExtraData,
 ) {
     use style::properties::PropertyDeclaration;
     use style::properties::longhands::background_image::SpecifiedValue as BackgroundImage;
     use style::values::Either;
     use style::values::generics::image::Image;
     use style::values::specified::url::SpecifiedImageUrl;
 
-    let url_data = unsafe { RefPtr::from_ptr_ref(&raw_extra_data) };
+    let url_data = unsafe { UrlExtraData::from_ptr_ref(&raw_extra_data) };
     let string = unsafe { (*value).to_string() };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::Style),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
@@ -5534,17 +5532,17 @@ pub extern "C" fn Servo_ParseFontShortha
 ) -> bool {
     use style::properties::shorthands::font;
     use style::values::generics::font::FontStyle as GenericFontStyle;
     use style::values::specified::font::{FontFamily, FontWeight, FontStyle, SpecifiedFontStyle};
 
     let string = unsafe { (*value).to_string() };
     let mut input = ParserInput::new(&string);
     let mut parser = Parser::new(&mut input);
-    let url_data = unsafe { RefPtr::from_ptr_ref(&data) };
+    let url_data = unsafe { UrlExtraData::from_ptr_ref(&data) };
     let context = ParserContext::new(
         Origin::Author,
         url_data,
         Some(CssRuleType::FontFace),
         ParsingMode::DEFAULT,
         QuirksMode::NoQuirks,
         None,
     );
--- a/servo/ports/geckolib/stylesheet_loader.rs
+++ b/servo/ports/geckolib/stylesheet_loader.rs
@@ -7,24 +7,23 @@ use nsstring::nsCString;
 use servo_arc::Arc;
 use style::context::QuirksMode;
 use style::gecko::data::GeckoStyleSheet;
 use style::gecko::global_style_data::GLOBAL_STYLE_DATA;
 use style::gecko_bindings::bindings;
 use style::gecko_bindings::bindings::Gecko_LoadStyleSheet;
 use style::gecko_bindings::structs::{Loader, LoaderReusableStyleSheets};
 use style::gecko_bindings::structs::{StyleSheet as DomStyleSheet, SheetLoadData, SheetLoadDataHolder};
-use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::sugar::ownership::FFIArcHelpers;
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::media_queries::MediaList;
 use style::parser::ParserContext;
 use style::shared_lock::{Locked, SharedRwLock};
 use style::stylesheets::{ImportRule, Origin, StylesheetLoader as StyleStylesheetLoader};
-use style::stylesheets::StylesheetContents;
+use style::stylesheets::{StylesheetContents, UrlExtraData};
 use style::stylesheets::import_rule::ImportSheet;
 use style::values::CssUrl;
 
 pub struct StylesheetLoader(*mut Loader, *mut DomStyleSheet, *mut SheetLoadData, *mut LoaderReusableStyleSheets);
 
 impl StylesheetLoader {
     pub fn new(
         loader: *mut Loader,
@@ -64,27 +63,27 @@ impl StyleStylesheetLoader for Styleshee
         let sheet = unsafe { GeckoStyleSheet::from_addrefed(child_sheet) };
         let stylesheet = ImportSheet::new(sheet);
         Arc::new(lock.wrap(ImportRule { url, source_location, stylesheet }))
     }
 }
 
 pub struct AsyncStylesheetParser {
     load_data: RefPtr<SheetLoadDataHolder>,
-    extra_data: RefPtr<URLExtraData>,
+    extra_data: UrlExtraData,
     bytes: nsCString,
     origin: Origin,
     quirks_mode: QuirksMode,
     line_number_offset: u32,
 }
 
 impl AsyncStylesheetParser {
     pub fn new(
         load_data: RefPtr<SheetLoadDataHolder>,
-        extra_data: RefPtr<URLExtraData>,
+        extra_data: UrlExtraData,
         bytes: nsCString,
         origin: Origin,
         quirks_mode: QuirksMode,
         line_number_offset: u32,
     ) -> Self {
         AsyncStylesheetParser {
             load_data,
             extra_data,
new file mode 100644
--- /dev/null
+++ b/taskcluster/ci/release-early-tagging/kind.yml
@@ -0,0 +1,48 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+loader: taskgraph.loader.transform:loader
+
+transforms:
+    - taskgraph.transforms.release_deps:transforms
+    - taskgraph.transforms.release_version_bump:transforms
+    - taskgraph.transforms.task:transforms
+
+job-defaults:
+    description: Release Promotion version tag for buildN
+    run-on-projects: []
+    shipping-phase: promote
+    worker-type:
+        by-project:
+            mozilla-(beta|release|esr.*): scriptworker-prov-v1/treescript-v1
+            maple: scriptworker-prov-v1/treescript-v1
+            birch: scriptworker-prov-v1/treescript-v1
+            jamun: scriptworker-prov-v1/treescript-v1
+            default: scriptworker-prov-v1/treescript-dev
+    worker:
+        implementation: treescript
+        tags: ['buildN']
+        bump: false
+        push:
+            by-project:
+                mozilla-beta: true
+                mozilla-release: true
+                mozilla-esr52: true
+                mozilla-esr60: true
+                maple: true
+                birch: true
+                default: false
+
+jobs:
+    fennec:
+        name: fennec-tag-buildN
+        shipping-product: fennec
+
+    firefox:
+        name: firefox-tag-buildN
+        shipping-product: firefox
+
+    devedition:
+        name: devedition-tag-buildN
+        shipping-product: devedition
--- a/taskcluster/ci/release-version-bump/kind.yml
+++ b/taskcluster/ci/release-version-bump/kind.yml
@@ -20,17 +20,17 @@ job-defaults:
         by-project:
             mozilla-(beta|release|esr.*): scriptworker-prov-v1/treescript-v1
             maple: scriptworker-prov-v1/treescript-v1
             birch: scriptworker-prov-v1/treescript-v1
             jamun: scriptworker-prov-v1/treescript-v1
             default: scriptworker-prov-v1/treescript-dev
     worker:
         implementation: treescript
-        tag: true
+        tags: ['release']
         bump: true
         bump-files:
             by-project:
                 default: ["browser/config/version_display.txt"]
                 mozilla-release:
                     - "browser/config/version.txt"
                     - "browser/config/version_display.txt"
                     - "config/milestone.txt"
--- a/taskcluster/docs/kinds.rst
+++ b/taskcluster/docs/kinds.rst
@@ -389,16 +389,20 @@ Repackaging of partner repacks.
 release-partner-repack-repackage-signing
 ------------------------------
 External signing of partner repacks.
 
 release-partner-repack-beetmover
 ------------------------------
 Moves the partner repacks to S3 buckets.
 
+release-early-tagging
+---------------------
+Utilises treescript to perform tagging that should happen near the start of a release.
+
 release-eme-free-repack
 ----------------------
 Generates customized versions of releases for eme-free repacks.
 
 release-eme-free-repack-signing
 ------------------------------
 Internal signing of eme-free repacks
 
--- a/taskcluster/taskgraph/transforms/task.py
+++ b/taskcluster/taskgraph/transforms/task.py
@@ -540,17 +540,17 @@ task_description_schema = Schema({
             Required('taskType'): basestring,
             Required('paths'): [basestring],
         }],
     }, {
         Required('implementation'): 'shipit',
         Required('release-name'): basestring,
     }, {
         Required('implementation'): 'treescript',
-        Required('tag'): bool,
+        Required('tags'): [Any('buildN', 'release', None)],
         Required('bump'): bool,
         Optional('bump-files'): [basestring],
         Optional('repo-param-prefix'): basestring,
         Required('force-dry-run', default=True): bool,
         Required('push', default=False): bool
     }),
 })
 
@@ -1134,24 +1134,29 @@ def build_sign_and_push_addons_payload(c
 
 @payload_builder('treescript')
 def build_treescript_payload(config, task, task_def):
     worker = task['worker']
     release_config = get_release_config(config)
 
     task_def['payload'] = {}
     task_def.setdefault('scopes', [])
-    if worker['tag']:
+    if worker['tags']:
+        tag_names = []
         product = task['shipping-product'].upper()
         version = release_config['version'].replace('.', '_')
         buildnum = release_config['build_number']
-        tag_names = [
-            "{}_{}_BUILD{}".format(product, version, buildnum),
-            "{}_{}_RELEASE".format(product, version)
-        ]
+        if 'buildN' in worker['tags']:
+            tag_names.extend([
+                "{}_{}_BUILD{}".format(product, version, buildnum),
+            ])
+        if 'release' in worker['tags']:
+            tag_names.extend([
+              "{}_{}_RELEASE".format(product, version)
+            ])
         tag_info = {
             'tags': tag_names,
             'revision': config.params['{}head_rev'.format(worker.get('repo-param-prefix', ''))],
         }
         task_def['payload']['tag_info'] = tag_info
         task_def['scopes'].append(add_scope_prefix(config, 'treescript:action:tagging'))
 
     if worker['bump']:
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -129446,16 +129446,40 @@
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
      ],
      {}
     ]
    ],
+   "css/css-scoping/shadow-link-rel-stylesheet-no-style-leak.html": [
+    [
+     "/css/css-scoping/shadow-link-rel-stylesheet-no-style-leak.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-scoping/shadow-link-rel-stylesheet.html": [
+    [
+     "/css/css-scoping/shadow-link-rel-stylesheet.html",
+     [
+      [
+       "/css/css-scoping/reference/green-box.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-scoping/shadow-reassign-dynamic-001.html": [
     [
      "/css/css-scoping/shadow-reassign-dynamic-001.html",
      [
       [
        "/css/css-scoping/reference/green-box.html",
        "=="
       ]
@@ -324146,16 +324170,28 @@
     ]
    ],
    "css/cssom/getComputedStyle-detached-subtree.html": [
     [
      "/css/cssom/getComputedStyle-detached-subtree.html",
      {}
     ]
    ],
+   "css/cssom/getComputedStyle-display-none-001.html": [
+    [
+     "/css/cssom/getComputedStyle-display-none-001.html",
+     {}
+    ]
+   ],
+   "css/cssom/getComputedStyle-display-none-002.html": [
+    [
+     "/css/cssom/getComputedStyle-display-none-002.html",
+     {}
+    ]
+   ],
    "css/cssom/getComputedStyle-dynamic-subdoc.html": [
     [
      "/css/cssom/getComputedStyle-dynamic-subdoc.html",
      {}
     ]
    ],
    "css/cssom/getComputedStyle-pseudo.html": [
     [
@@ -340908,16 +340944,22 @@
     ]
    ],
    "html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html": [
     [
      "/html/rendering/replaced-elements/svg-inline-sizing/svg-inline.html",
      {}
     ]
    ],
+   "html/scripting/the-noscript-element/non-html-noscript.html": [
+    [
+     "/html/scripting/the-noscript-element/non-html-noscript.html",
+     {}
+    ]
+   ],
    "html/semantics/disabled-elements/disabledElement.html": [
     [
      "/html/semantics/disabled-elements/disabledElement.html",
      {}
     ]
    ],
    "html/semantics/document-metadata/interactions-of-styling-and-scripting/conditionally-block-rendering-on-link-media-attr.html": [
     [
@@ -523061,16 +523103,24 @@
   "css/css-scoping/shadow-fallback-dynamic-005.html": [
    "ab3c3d205e59df800ba5b4217245b83685521c31",
    "reftest"
   ],
   "css/css-scoping/shadow-host-with-before-after.html": [
    "99af6e29e69b3131b59dbdc2b0eead52931123c2",
    "reftest"
   ],
+  "css/css-scoping/shadow-link-rel-stylesheet-no-style-leak.html": [
+   "76a54dabd8bd09f7155ab0331e3d17d1a0cae243",
+   "reftest"
+  ],
+  "css/css-scoping/shadow-link-rel-stylesheet.html": [
+   "07862ce7d2a954988bdbce882869a4c5f097089a",
+   "reftest"
+  ],
   "css/css-scoping/shadow-reassign-dynamic-001.html": [
    "11ed4da2e6ce88d8a2b98a8f1c814417ef7770dd",
    "reftest"
   ],
   "css/css-scoping/shadow-reassign-dynamic-002.html": [
    "2a90e0623a99cfb46430f0236ceea44f93a25131",
    "reftest"
   ],
@@ -547457,16 +547507,24 @@
   "css/cssom/font-shorthand-serialization.html": [
    "9d0bce1c0d74bf90aca1eb8ee6aa2e2ed2b92b30",
    "testharness"
   ],
   "css/cssom/getComputedStyle-detached-subtree.html": [
    "886f72b4eaa82d3aeb4de5c5b27f71369dbe0186",
    "testharness"
   ],
+  "css/cssom/getComputedStyle-display-none-001.html": [
+   "0e1f381bb67d045c9a92461f664c3c4d61de280f",
+   "testharness"
+  ],
+  "css/cssom/getComputedStyle-display-none-002.html": [
+   "23a362d9423cdfe6d8c29809fdadca525054d175",
+   "testharness"
+  ],
   "css/cssom/getComputedStyle-dynamic-subdoc.html": [
    "3f379487727d9730de9e3569b26632c35d602d9d",
    "testharness"
   ],
   "css/cssom/getComputedStyle-pseudo.html": [
    "d3ef09fb6092078562f8923879b9ece97938df47",
    "testharness"
   ],
@@ -548538,17 +548596,17 @@
    "cd3f0233cc0eaf9295e602ca25aef87fb68df851",
    "support"
   ],
   "css/mediaqueries/support/min-width-tables-001-iframe.html": [
    "29e7fb34c94e2e8411514d1e71d09aca2ddb642e",
    "support"
   ],
   "css/mediaqueries/test_media_queries.html": [
-   "cff3585932589f611a7101329d3b5b6ca27820aa",
+   "a7d78b13e119f8cd1ffa8812a9af67e59280084d",
    "testharness"
   ],
   "css/mediaqueries/viewport-script-dynamic-ref.html": [
    "7d55c513e2de39c9b362fc864233a3008ca6ced2",
    "support"
   ],
   "css/mediaqueries/viewport-script-dynamic.html": [
    "1c2ba1a9116942599804ed29553e85628afadb04",
@@ -577785,16 +577843,20 @@
   "html/rendering/unstyled-xml-documents/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "html/resources/common.js": [
    "0f18ee2c61b99893cfe2a3d1ff549b170a8d715d",
    "support"
   ],
+  "html/scripting/the-noscript-element/non-html-noscript.html": [
+   "c0c5453111f29e5a0206f988f4d127ec8ebc2f13",
+   "testharness"
+  ],
   "html/semantics/.gitkeep": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "html/semantics/OWNERS": [
    "abd95839027a88741c4d351ff374d81b773c80fa",
    "support"
   ],
@@ -619114,17 +619176,17 @@
    "5a31a3917a5157516c10951a3b3d5ffb43b992d9",
    "support"
   ],
   "webdriver/tests/support/asserts.py": [
    "b7424061b41d9bb1b87f147d5b29786695249d10",
    "support"
   ],
   "webdriver/tests/support/fixtures.py": [
-   "f1c704ea7b44e08dd63eaac6b5e9a3370e4c0503",
+   "5ae91067cee6f4cf7e3c63e99aca65104bbc2fab",
    "support"
   ],
   "webdriver/tests/support/http_request.py": [
    "cb40c781fea2280b98135522def5e6a116d7b946",
    "support"
   ],
   "webdriver/tests/support/inline.py": [
    "48399821b7abca50df824e37c41829d7a4573be1",
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/css/cssom/getComputedStyle-display-none-001.html.ini
@@ -0,0 +1,2 @@
+[getComputedStyle-display-none-001.html]
+  prefs: [dom.webcomponents.shadowdom.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-link-rel-stylesheet-no-style-leak.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>CSS Test: &lt;link rel="stylesheet"&gt; in Shadow DOM doesn't affect the normal DOM</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-stylesheet">
+<link rel="help" href="https://drafts.csswg.org/css-scoping/#selectors-data-model">
+<link rel="match" href="reference/green-box.html">
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<style>
+  #light-dom {
+    width: 100px;
+    height: 100px;
+    background: green;
+    color: green;
+  }
+</style>
+<div id="host">FAIL</div>
+<div id="light-dom"></div>
+<script>
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <link rel="stylesheet" href="data:text/css,div { background: red !important }">
+  `;
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/css-scoping/shadow-link-rel-stylesheet.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<title>CSS Test: &lt;link rel="stylesheet"&gt; in Shadow DOM</title>
+<link rel="help" href="https://html.spec.whatwg.org/#link-type-stylesheet">
+<link rel="match" href="reference/green-box.html">
+<p>Test passes if you see a single 100px by 100px green box below.</p>
+<div id="host">FAIL</div>
+<script>
+  host.attachShadow({ mode: "open" }).innerHTML = `
+    <link rel="stylesheet" href="resources/host-green-box.css">
+  `;
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-001.html
@@ -0,0 +1,39 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle gets invalidated for display: none elements (inheritance)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#undisplayed, #host {
+  display: none;
+  color: red;
+}
+</style>
+<div id="undisplayed"><div id="child"></div></div>
+<div id="host"></div>
+<script>
+  test(function() {
+    let undisplayed_style = getComputedStyle(undisplayed);
+    let undisplayed_child_style = getComputedStyle(child);
+    assert_equals(undisplayed_style.color, "rgb(255, 0, 0)");
+    assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)");
+    undisplayed.style.color = "green";
+    assert_equals(undisplayed_style.color, "rgb(0, 128, 0)");
+    assert_equals(undisplayed_child_style.color, "rgb(0, 128, 0)");
+  }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor");
+  test(function() {
+    host.attachShadow({ mode: 'open' }).innerHTML = `
+      <div></div>
+    `;
+    let host_style = getComputedStyle(host);
+    let shadow_style = getComputedStyle(host.shadowRoot.firstElementChild);
+    assert_equals(host_style.color, "rgb(255, 0, 0)");
+    assert_equals(shadow_style.color, "rgb(255, 0, 0)");
+    host.style.color = "green";
+    assert_equals(host_style.color, "rgb(0, 128, 0)");
+    assert_equals(shadow_style.color, "rgb(0, 128, 0)");
+  }, "getComputedStyle gets invalidated in display: none subtrees due to inherited changes to an ancestor shadow host");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom/getComputedStyle-display-none-002.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSSOM: getComputedStyle gets invalidated for display: none elements (rules)</title>
+<link rel="help" href="https://drafts.csswg.org/cssom/#dom-window-getcomputedstyle">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=186882">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#undisplayed {
+  display: none;
+  color: red;
+}
+.sibling + #undisplayed {
+  color: green;
+}
+
+.sibling + #undisplayed > div {
+  color: yellow;
+}
+</style>
+<div id="helper"></div>
+<div id="undisplayed"><div id="child"></div></div>
+<script>
+  test(function() {
+    let undisplayed_style = getComputedStyle(undisplayed);
+    let undisplayed_child_style = getComputedStyle(child);
+    assert_equals(undisplayed_style.color, "rgb(255, 0, 0)");
+    assert_equals(undisplayed_child_style.color, "rgb(255, 0, 0)");
+    helper.classList.add("sibling");
+    assert_equals(undisplayed_style.color, "rgb(0, 128, 0)");
+    assert_equals(undisplayed_child_style.color, "rgb(255, 255, 0)");
+  }, "getComputedStyle gets invalidated in display: none subtrees due to rule matching changes");
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/html/scripting/the-noscript-element/non-html-noscript.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<title>noscript rules don't apply to non-html elements</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/#the-noscript-element">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1470150">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<script>
+test(function() {
+  let non_html_noscript = document.createElementNS("http://www.w3.org/2000/svg", "noscript");
+  document.body.appendChild(non_html_noscript);
+  assert_not_equals(getComputedStyle(non_html_noscript).display, "none");
+}, "Non-HTML <noscript> element shouldn't be undisplayed by a UA rule");
+</script>
+</body>
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
+++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul
@@ -96,18 +96,18 @@
     } else if (aPath.search(/^explicit\/js-non-window\/.*realm\(/) >= 0) {
       present.jsNonWindowRealms = true;
     } else if (aPath.search(/^explicit\/window-objects\/top\(.*\/js-realm\(/) >= 0) {
       present.windowObjectsJsRealms = true;
     } else if (aPath.search(/^explicit\/storage\/sqlite\/places.sqlite/) >= 0) {
       present.places = true;
     } else if (aPath.search(/^explicit\/images/) >= 0) {
       present.images = true;
-    } else if (aPath.search(/^explicit\/atoms\/dynamic\/atom-objects$/) >= 0) {
-      present.dynamicAtomObjects = true;
+    } else if (aPath.search(/^explicit\/atoms\/dynamic-objects-and-chars$/) >= 0) {
+      present.dynamicObjectsAndChars = true;
     } else if (/\[System Principal\].*this-is-a-sandbox-name/.test(aPath)) {
       // A system compartment with a location (such as a sandbox) should
       // show that location.
       present.sandboxLocation = true;
     } else if (aPath.includes(bigStringPrefix)) {
       present.bigString = true;
     } else if (aPath.includes("!)(*&")) {
       present.smallString1 = true;
@@ -254,17 +254,17 @@
     }
     checkSizeReasonable("js-main-runtime-gc-heap-committed/used/gc-things",
                         jsGcHeapUsedGcThingsTotal);
 
     ok(present.jsNonWindowRealms,           "js-non-window realms are present");
     ok(present.windowObjectsJsRealms,       "window-objects/.../js realms are present");
     ok(present.places,                      "places is present");
     ok(present.images,                      "images is present");
-    ok(present.dynamicAtomObjects,          "dynamic/atom-objects is present");
+    ok(present.dynamicObjectsAndChars,      "dynamic-objects-and-chars is present");
     ok(present.sandboxLocation,             "sandbox locations are present");
     ok(present.bigString,                   "large string is present");
     ok(present.smallString1,                "small string 1 is present");
     ok(present.smallString2,                "small string 2 is present");
 
     ok(!present.anonymizedWhenUnnecessary,
        "anonymized paths are not present when unnecessary. Failed case: " +
        present.anonymizedWhenUnnecessary);
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -12812,39 +12812,41 @@
   "CONTENT_PAINT_TIME": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["mwoodrow@mozilla.com","gfx-telemetry-alerts@mozilla.com"],
     "bug_numbers": [1309442],
     "expires_in_version": "66",
     "kind": "exponential",
     "high": 1000,
     "n_buckets": 50,
-    "description": "Time spent in the paint pipeline for content."
+    "description": "Time spent in the paint pipeline for content in milliseconds."
   },
   "CONTENT_LARGE_PAINT_PHASE_WEIGHT": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["mwoodrow@mozilla.com","gfx-telemetry-alerts@mozilla.com"],
     "bug_numbers": [1309442],
     "expires_in_version": "66",
     "keyed": true,
+    "keys": ["dl", "flb", "fr", "r"],
     "kind": "linear",
     "high": 100,
     "n_buckets": 10,
-    "description": "Percentage of time taken by phases in expensive content paints."
+    "description": "Percentages of times for phases in an expensive content paint relative to the time spent in the entire expensive paint. (\"dl\" = Display list, \"flb\" = Frame layer builder, \"fr\" = Flush rasterization, \"r\" = Rasterization)"
   },
   "CONTENT_SMALL_PAINT_PHASE_WEIGHT": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["mwoodrow@mozilla.com","gfx-telemetry-alerts@mozilla.com"],
     "bug_numbers": [1430897],
     "expires_in_version": "66",
     "keyed": true,
+    "keys": ["dl", "flb", "fr", "r"],
     "kind": "linear",
     "high": 100,
     "n_buckets": 10,
-    "description": "Percentage of time taken by phases in normal content paints."
+    "description": "Percentages of times for phases in a normal content paint relative to the time spent in the entire normal paint. (\"dl\" = Display list, \"flb\" = Frame layer builder, \"fr\" = Flush rasterization, \"r\" = Rasterization)"
   },
   "NARRATE_CONTENT_BY_LANGUAGE_2": {
     "record_in_processes": ["main", "content"],
     "alert_emails": ["eisaacson@mozilla.com"],
     "bug_numbers": [1308030, 1324868],
     "releaseChannelCollection": "opt-out",
     "expires_in_version": "60",
     "kind": "enumerated",
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -1416,25 +1416,20 @@ public:
     AtomsSizes sizes;
     NS_AddSizeOfAtoms(MallocSizeOf, sizes);
 
     MOZ_COLLECT_REPORT(
       "explicit/atoms/table", KIND_HEAP, UNITS_BYTES, sizes.mTable,
       "Memory used by the atom table.");
 
     MOZ_COLLECT_REPORT(
-      "explicit/atoms/dynamic/atom-objects", KIND_HEAP, UNITS_BYTES,
-      sizes.mDynamicAtomObjects,
-      "Memory used by dynamic atom objects.");
-
-    MOZ_COLLECT_REPORT(
-      "explicit/atoms/dynamic/unshared-buffers", KIND_HEAP, UNITS_BYTES,
-      sizes.mDynamicUnsharedBuffers,
-      "Memory used by unshared string buffers pointed to by dynamic atom "
-      "objects.");
+      "explicit/atoms/dynamic-objects-and-chars", KIND_HEAP, UNITS_BYTES,
+      sizes.mDynamicAtoms,
+      "Memory used by dynamic atom objects and chars (which are stored "
+      "at the end of each atom object).");
 
     return NS_OK;
   }
 };
 NS_IMPL_ISUPPORTS(AtomTablesReporter, nsIMemoryReporter)
 
 #ifdef DEBUG
 
--- a/xpcom/ds/nsAtom.h
+++ b/xpcom/ds/nsAtom.h
@@ -4,18 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsAtom_h
 #define nsAtom_h
 
 #include "nsISupportsImpl.h"
 #include "nsString.h"
-#include "nsStringBuffer.h"
 #include "mozilla/HashFunctions.h"
+#include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 struct AtomsSizes;
 }
 
 class nsStaticAtom;
 class nsDynamicAtom;
 
@@ -162,45 +162,42 @@ private:
 class nsDynamicAtom : public nsAtom
 {
 public:
   // We can't use NS_INLINE_DECL_THREADSAFE_REFCOUNTING because the refcounting
   // of this type is special.
   MozExternalRefCountType AddRef();
   MozExternalRefCountType Release();
 
-  ~nsDynamicAtom();
-
-  const char16_t* String() const { return mString; }
-
-  // The caller must *not* mutate the string buffer, otherwise all hell will
-  // break loose.
-  nsStringBuffer* GetStringBuffer() const
+  const char16_t* String() const
   {
-    // See the comment on |mString|'s declaration.
-    MOZ_ASSERT(IsDynamic());
-    return nsStringBuffer::FromData(const_cast<char16_t*>(mString));
+    return reinterpret_cast<const char16_t*>(this + 1);
   }
 
 private:
   friend class nsAtomTable;
   friend class nsAtomSubTable;
   // XXX: we'd like to remove nsHtml5AtomEntry. See bug 1392185.
   friend class nsHtml5AtomEntry;
 
-  // Construction is done by |friend|s.
-  // The first constructor is for dynamic normal atoms, the second is for
-  // dynamic HTML5 atoms.
+  // These shouldn't be used directly, even by friend classes. The
+  // Create()/Destroy() methods use them.
+  static nsDynamicAtom* CreateInner(const nsAString& aString, uint32_t aHash);
   nsDynamicAtom(const nsAString& aString, uint32_t aHash);
-  explicit nsDynamicAtom(const nsAString& aString);
+  ~nsDynamicAtom() {}
+
+  // Creation/destruction is done by friend classes. The first Create() is for
+  // dynamic normal atoms, the second is for dynamic HTML5 atoms.
+  static nsDynamicAtom* Create(const nsAString& aString, uint32_t aHash);
+  static nsDynamicAtom* Create(const nsAString& aString);
+  static void Destroy(nsDynamicAtom* aAtom);
 
   mozilla::ThreadSafeAutoRefCnt mRefCnt;
-  // Note: this points to the chars in an nsStringBuffer, which is obtained
-  // with nsStringBuffer::FromData(mString).
-  const char16_t* const mString;
+
+  // The atom's chars are stored at the end of the struct.
 };
 
 // The four forms of NS_Atomize (for use with |RefPtr<nsAtom>|) return the
 // atom for the string given. At any given time there will always be one atom
 // representing a given string. Atoms are intended to make string comparison
 // cheaper by simplifying it to pointer equality. A pointer to the atom that
 // does not own a reference is not guaranteed to be valid.
 
--- a/xpcom/ds/nsAtomTable.cpp
+++ b/xpcom/ds/nsAtomTable.cpp
@@ -25,17 +25,17 @@
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
 #include "PLDHashTable.h"
 #include "prenv.h"
 
 // There are two kinds of atoms handled by this module.
 //
-// - Dynamic: the atom itself is heap allocated, as is the nsStringBuffer it
+// - Dynamic: the atom itself is heap allocated, as is the char buffer it
 //   points to. |gAtomTable| holds weak references to dynamic atoms. When the
 //   refcount of a dynamic atom drops to zero, we increment a static counter.
 //   When that counter reaches a certain threshold, we iterate over the atom
 //   table, removing and deleting dynamic atoms with refcount zero. This allows
 //   us to avoid acquiring the atom table lock during normal refcounting.
 //
 // - Static: both the atom and its chars are statically allocated and
 //   immutable, so it ignores all AddRef/Release calls.
@@ -58,67 +58,59 @@ enum class GCKind {
 // (and thus turned into unused state), and decremented when an unused
 // atom gets a reference again. The atom table relies on this value to
 // schedule GC. This value can temporarily go below zero when multiple
 // threads are operating the same atom, so it has to be signed so that
 // we wouldn't use overflow value for comparison.
 // See nsAtom::AddRef() and nsAtom::Release().
 static Atomic<int32_t, ReleaseAcquire> gUnusedAtomCount(0);
 
-static char16_t*
-FromStringBuffer(const nsAString& aString)
-{
-  char16_t* str;
-  size_t length = aString.Length();
-  RefPtr<nsStringBuffer> buf = nsStringBuffer::FromString(aString);
-  if (buf) {
-    str = static_cast<char16_t*>(buf->Data());
-  } else {
-    const size_t size = (length + 1) * sizeof(char16_t);
-    buf = nsStringBuffer::Alloc(size);
-    if (MOZ_UNLIKELY(!buf)) {
-      NS_ABORT_OOM(size); // OOM because atom allocations should be small.
-    }
-    str = static_cast<char16_t*>(buf->Data());
-    CopyUnicodeTo(aString, 0, str, length);
-    str[length] = char16_t(0);
-  }
-
-  MOZ_ASSERT(buf && buf->StorageSize() >= (length + 1) * sizeof(char16_t),
-             "enough storage");
-
-  // Take ownership of the string buffer.
-  mozilla::Unused << buf.forget();
-
-  return str;
-}
-
 nsDynamicAtom::nsDynamicAtom(const nsAString& aString, uint32_t aHash)
   : nsAtom(AtomKind::DynamicNormal, aString, aHash)
   , mRefCnt(1)
-  , mString(FromStringBuffer(aString))
 {
-  MOZ_ASSERT(mHash == HashString(mString, mLength));
-
-  MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
-  MOZ_ASSERT(Equals(aString), "correct data");
 }
 
-nsDynamicAtom::nsDynamicAtom(const nsAString& aString)
-  : nsAtom(AtomKind::DynamicHTML5, aString, 0)
-  , mRefCnt(1)
-  , mString(FromStringBuffer(aString))
+nsDynamicAtom*
+nsDynamicAtom::CreateInner(const nsAString& aString, uint32_t aHash)
 {
-  MOZ_ASSERT(mString[mLength] == char16_t(0), "null terminated");
-  MOZ_ASSERT(Equals(aString), "correct data");
+  // We tack the chars onto the end of the nsDynamicAtom object.
+  size_t numCharBytes = (aString.Length() + 1) * sizeof(char16_t);
+  size_t numTotalBytes = sizeof(nsDynamicAtom) + numCharBytes;
+
+  nsDynamicAtom* atom = (nsDynamicAtom*)moz_xmalloc(numTotalBytes);
+  new (atom) nsDynamicAtom(aString, aHash);
+  memcpy(const_cast<char16_t*>(atom->String()),
+         PromiseFlatString(aString).get(), numCharBytes);
+
+  MOZ_ASSERT(atom->String()[atom->GetLength()] == char16_t(0));
+  MOZ_ASSERT(atom->Equals(aString));
+
+  return atom;
 }
 
-nsDynamicAtom::~nsDynamicAtom()
+nsDynamicAtom*
+nsDynamicAtom::Create(const nsAString& aString, uint32_t aHash)
 {
-  GetStringBuffer()->Release();
+  nsDynamicAtom* atom = CreateInner(aString, aHash);
+  MOZ_ASSERT(atom->mHash == HashString(atom->String(), atom->GetLength()));
+  return atom;
+}
+
+nsDynamicAtom*
+nsDynamicAtom::Create(const nsAString& aString)
+{
+  return CreateInner(aString, /* hash */ 0);
+}
+
+void
+nsDynamicAtom::Destroy(nsDynamicAtom* aAtom)
+{
+  aAtom->~nsDynamicAtom();
+  free(aAtom);
 }
 
 const nsStaticAtom*
 nsAtom::AsStatic() const
 {
   MOZ_ASSERT(IsStatic());
   return static_cast<const nsStaticAtom*>(this);
 }
@@ -142,17 +134,17 @@ nsAtom::ToString(nsAString& aString) con
 {
   // See the comment on |mString|'s declaration.
   if (IsStatic()) {
     // AssignLiteral() lets us assign without copying. This isn't a string
     // literal, but it's a static atom and thus has an unbounded lifetime,
     // which is what's important.
     aString.AssignLiteral(AsStatic()->String(), mLength);
   } else {
-    AsDynamic()->GetStringBuffer()->ToString(mLength, aString);
+    aString.Assign(AsDynamic()->String(), mLength);
   }
 }
 
 void
 nsAtom::ToUTF8String(nsACString& aBuf) const
 {
   MOZ_ASSERT(!IsDynamicHTML5(),
              "Called ToUTF8String() on a dynamic HTML5 atom");
@@ -163,20 +155,17 @@ void
 nsAtom::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, AtomsSizes& aSizes)
   const
 {
   MOZ_ASSERT(!IsDynamicHTML5(),
              "Called AddSizeOfIncludingThis() on a dynamic HTML5 atom");
 
   // Static atoms are in static memory, and so are not measured here.
   if (IsDynamic()) {
-    aSizes.mDynamicAtomObjects += aMallocSizeOf(this);
-    aSizes.mDynamicUnsharedBuffers +=
-      AsDynamic()->GetStringBuffer()->SizeOfIncludingThisIfUnshared(
-        aMallocSizeOf);
+    aSizes.mDynamicAtoms += aMallocSizeOf(this);
   }
 }
 
 char16ptr_t
 nsAtom::GetUTF16String() const
 {
   return IsStatic() ? AsStatic()->String() : AsDynamic()->String();
 }
@@ -496,17 +485,17 @@ nsAtomSubTable::GCLocked(GCKind aKind)
     if (entry->mAtom->IsStatic()) {
       continue;
     }
 
     nsAtom* atom = entry->mAtom;
     MOZ_ASSERT(!atom->IsDynamicHTML5());
     if (atom->IsDynamic() && atom->AsDynamic()->mRefCnt == 0) {
       i.Remove();
-      delete atom->AsDynamic();
+      nsDynamicAtom::Destroy(atom->AsDynamic());
       ++removedCount;
     }
 #ifdef NS_FREE_PERMANENT_DATA
     else if (aKind == GCKind::Shutdown && PR_GetEnv("XPCOM_MEM_BLOAT_LOG")) {
       // Only report leaking atoms in leak-checking builds in a run where we
       // are checking for leaks, during shutdown. If something is anomalous,
       // then we'll assert later in this function.
       nsAutoCString name;
@@ -701,22 +690,19 @@ nsAtomTable::Atomize(const nsACString& a
   AtomTableEntry* he = table.Add(key);
 
   if (he->mAtom) {
     RefPtr<nsAtom> atom = he->mAtom;
 
     return atom.forget();
   }
 
-  // This results in an extra addref/release of the nsStringBuffer.
-  // Unfortunately there doesn't seem to be any APIs to avoid that.
-  // Actually, now there is, sort of: ForgetSharedBuffer.
   nsString str;
   CopyUTF8toUTF16(aUTF8String, str);
-  RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(str, hash));
+  RefPtr<nsAtom> atom = dont_AddRef(nsDynamicAtom::Create(str, hash));
 
   he->mAtom = atom;
 
   return atom.forget();
 }
 
 already_AddRefed<nsAtom>
 NS_Atomize(const nsACString& aUTF8String)
@@ -742,17 +728,17 @@ nsAtomTable::Atomize(const nsAString& aU
   AtomTableEntry* he = table.Add(key);
 
   if (he->mAtom) {
     RefPtr<nsAtom> atom = he->mAtom;
 
     return atom.forget();
   }
 
-  RefPtr<nsAtom> atom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
+  RefPtr<nsAtom> atom = dont_AddRef(nsDynamicAtom::Create(aUTF16String, hash));
   he->mAtom = atom;
 
   return atom.forget();
 }
 
 already_AddRefed<nsAtom>
 NS_Atomize(const nsAString& aUTF16String)
 {
@@ -781,17 +767,18 @@ nsAtomTable::AtomizeMainThread(const nsA
 
   nsAtomSubTable& table = SelectSubTable(key);
   MutexAutoLock lock(table.mLock);
   AtomTableEntry* he = table.Add(key);
 
   if (he->mAtom) {
     retVal = he->mAtom;
   } else {
-    RefPtr<nsAtom> newAtom = dont_AddRef(new nsDynamicAtom(aUTF16String, hash));
+    RefPtr<nsAtom> newAtom =
+      dont_AddRef(nsDynamicAtom::Create(aUTF16String, hash));
     he->mAtom = newAtom;
     retVal = newAtom.forget();
   }
 
   sRecentlyUsedMainThreadAtoms[index] = he->mAtom;
   return retVal.forget();
 }
 
--- a/xpcom/ds/nsAtomTable.h
+++ b/xpcom/ds/nsAtomTable.h
@@ -12,23 +12,21 @@
 
 void NS_InitAtomTable();
 void NS_ShutdownAtomTable();
 
 namespace mozilla {
 struct AtomsSizes
 {
   size_t mTable;
-  size_t mDynamicAtomObjects;
-  size_t mDynamicUnsharedBuffers;
+  size_t mDynamicAtoms;
 
   AtomsSizes()
    : mTable(0)
-   , mDynamicAtomObjects(0)
-   , mDynamicUnsharedBuffers(0)
+   , mDynamicAtoms(0)
   {}
 };
 } // namespace mozilla
 
 void NS_AddSizeOfAtoms(mozilla::MallocSizeOf aMallocSizeOf,
                        mozilla::AtomsSizes& aSizes);
 
 #endif // nsAtomTable_h__
--- a/xpcom/tests/gtest/TestAtoms.cpp
+++ b/xpcom/tests/gtest/TestAtoms.cpp
@@ -49,26 +49,16 @@ TEST(Atoms, 16vs8)
 {
   for (unsigned int i = 0; i < ArrayLength(ValidStrings); ++i) {
     RefPtr<nsAtom> atom16 = NS_Atomize(ValidStrings[i].m16);
     RefPtr<nsAtom> atom8 = NS_Atomize(ValidStrings[i].m8);
     EXPECT_EQ(atom16, atom8);
   }
 }
 
-TEST(Atoms, BufferSharing)
-{
-  nsString unique;
-  unique.AssignLiteral("this is a unique string !@#$");
-
-  RefPtr<nsAtom> atom = NS_Atomize(unique);
-
-  EXPECT_EQ(unique.get(), atom->GetUTF16String());
-}
-
 TEST(Atoms, Null)
 {
   nsAutoString str(NS_LITERAL_STRING("string with a \0 char"));
   nsDependentString strCut(str.get());
 
   EXPECT_FALSE(str.Equals(strCut));
 
   RefPtr<nsAtom> atomCut = NS_Atomize(strCut);