Merge autoland to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sat, 03 Dec 2016 13:37:47 -0800
changeset 325201 11d06cafe634dfa7981cfdca62c0b34d04a22a84
parent 325193 f664a80393daca803f15055ade0c85145883f3ff (current diff)
parent 325200 3976b04bf08a4442eb75cace5ad4899ca0eb0ced (diff)
child 325220 6bdef7ba8b4108a996b9f61ef9f81c5ea6c93017
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersmerge
milestone53.0a1
Merge autoland to m-c, a=merge MozReview-Commit-ID: 1loR89aNn8E
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -378,22 +378,20 @@ AllChildrenIterator::Seek(nsIContent* aC
 
 void
 AllChildrenIterator::AppendNativeAnonymousChildren()
 {
   AppendNativeAnonymousChildrenFromFrame(mOriginalContent->GetPrimaryFrame());
 
   // The root scroll frame is not the primary frame of the root element.
   // Detect and handle this case.
-  if (mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) {
-    nsIPresShell* presShell = mOriginalContent->OwnerDoc()->GetShell();
-    nsIFrame* scrollFrame = presShell ? presShell->GetRootScrollFrame() : nullptr;
-    if (scrollFrame) {
-      AppendNativeAnonymousChildrenFromFrame(scrollFrame);
-    }
+  if (!(mFlags & nsIContent::eSkipDocumentLevelNativeAnonymousContent) &&
+      mOriginalContent == mOriginalContent->OwnerDoc()->GetRootElement()) {
+    nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
+        mOriginalContent->OwnerDoc(), mAnonKids);
   }
 }
 
 void
 AllChildrenIterator::AppendNativeAnonymousChildrenFromFrame(nsIFrame* aFrame)
 {
   nsIAnonymousContentCreator* ac = do_QueryFrame(aFrame);
   if (ac) {
@@ -580,22 +578,16 @@ StyleChildrenIterator::IsNeeded(const El
   }
 
   // If the node has native anonymous content, return true.
   nsIAnonymousContentCreator* ac = do_QueryFrame(aElement->GetPrimaryFrame());
   if (ac) {
     return true;
   }
 
-  // The root element has a scroll frame that is not the primary frame, so we
-  // need to do special checking for that case.
-  if (aElement == aElement->OwnerDoc()->GetRootElement()) {
-    return true;
-  }
-
   return false;
 }
 
 
 nsIContent*
 StyleChildrenIterator::GetNextChild()
 {
   while (nsIContent* child = AllChildrenIterator::GetNextChild()) {
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -251,28 +251,32 @@ private:
   // there's no easy way to do that.
   nsMutationGuard mMutationGuard;
 #endif
 };
 
 /**
  * StyleChildrenIterator traverses the children of the element from the
  * perspective of the style system, particularly the children we need to traverse
- * during restyle. This is identical to AllChildrenIterator with eAllChildren,
- * _except_ that we detect and skip any native anonymous children that are used
- * to implement pseudo-elements (since the style system needs to cascade those
- * using different algorithms).
+ * during restyle. This is identical to AllChildrenIterator with
+ * (eAllChildren | eSkipDocumentLevelNativeAnonymousContent), _except_ that we
+ * detect and skip any native anonymous children that are used to implement
+ * pseudo-elements (since the style system needs to cascade those using
+ * different algorithms).
  *
  * Note: it assumes that no mutation of the DOM or frame tree takes place during
  * iteration, and will break horribly if that is not true.
  */
-class StyleChildrenIterator : private AllChildrenIterator {
+class StyleChildrenIterator : private AllChildrenIterator
+{
 public:
   explicit StyleChildrenIterator(const nsIContent* aContent)
-    : AllChildrenIterator(aContent, nsIContent::eAllChildren)
+    : AllChildrenIterator(aContent,
+                          nsIContent::eAllChildren |
+                          nsIContent::eSkipDocumentLevelNativeAnonymousContent)
   {
     MOZ_COUNT_CTOR(StyleChildrenIterator);
   }
   ~StyleChildrenIterator() { MOZ_COUNT_DTOR(StyleChildrenIterator); }
 
   nsIContent* GetNextChild();
 
   // Returns true if we cannot find all the children we need to style by
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -396,16 +396,17 @@ public:
   inline bool HasDirAuto() const {
     return (!HasFixedDir() &&
             (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi)));
   }
 
   Directionality GetComputedDirectionality() const;
 
   inline Element* GetFlattenedTreeParentElement() const;
+  inline Element* GetFlattenedTreeParentElementForStyle() const;
 
   bool HasDirtyDescendantsForServo() const
   {
     MOZ_ASSERT(IsStyledByServo());
     return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
   void SetHasDirtyDescendantsForServo() {
@@ -413,16 +414,18 @@ public:
     SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
   void UnsetHasDirtyDescendantsForServo() {
     MOZ_ASSERT(IsStyledByServo());
     UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 
+  inline bool ShouldTraverseForServo();
+
   inline void NoteDirtyDescendantsForServo();
 
 #ifdef DEBUG
   inline bool DirtyDescendantsBitIsPropagatedForServo();
 #endif
 
   bool HasServoData() {
 #ifdef MOZ_STYLO
--- a/dom/base/ElementInlines.h
+++ b/dom/base/ElementInlines.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_ElementInlines_h
 #define mozilla_dom_ElementInlines_h
 
 #include "mozilla/dom/Element.h"
+#include "mozilla/ServoBindings.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 
 namespace mozilla {
 namespace dom {
 
 inline void
 Element::RegisterActivityObserver()
@@ -32,40 +33,59 @@ Element::GetFlattenedTreeParentElement()
   nsINode* parentNode = GetFlattenedTreeParentNode();
   if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
     return parentNode->AsElement();
   }
 
   return nullptr;
 }
 
+inline Element*
+Element::GetFlattenedTreeParentElementForStyle() const
+{
+  nsINode* parentNode = GetFlattenedTreeParentNodeForStyle();
+  if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
+    return parentNode->AsElement();
+  }
+
+  return nullptr;
+}
+
+inline bool
+Element::ShouldTraverseForServo()
+{
+  return HasDirtyDescendantsForServo() || Servo_Element_ShouldTraverse(this);
+}
+
 inline void
 Element::NoteDirtyDescendantsForServo()
 {
   Element* curr = this;
   while (curr && !curr->HasDirtyDescendantsForServo()) {
     curr->SetHasDirtyDescendantsForServo();
-    curr = curr->GetFlattenedTreeParentElement();
+    curr = curr->GetFlattenedTreeParentElementForStyle();
   }
 
   MOZ_ASSERT(DirtyDescendantsBitIsPropagatedForServo());
 }
 
 #ifdef DEBUG
 inline bool
 Element::DirtyDescendantsBitIsPropagatedForServo()
 {
   Element* curr = this;
   while (curr) {
     if (!curr->HasDirtyDescendantsForServo()) {
       return false;
     }
     nsINode* parentNode = curr->GetParentNode();
-    curr = curr->GetFlattenedTreeParentElement();
-    MOZ_ASSERT_IF(!curr, parentNode == OwnerDoc());
+    curr = curr->GetFlattenedTreeParentElementForStyle();
+    MOZ_ASSERT_IF(!curr,
+                  parentNode == OwnerDoc() ||
+                  parentNode == parentNode->OwnerDoc()->GetRootElement());
   }
   return true;
 }
 #endif
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -147,25 +147,64 @@ nsIContent::FindFirstNonChromeOnlyAccess
       // non-const.  (Then again, so does GetChildAt(0)->GetParent().)
       return const_cast<nsIContent*>(content);
     }
   }
   return nullptr;
 }
 
 nsINode*
-nsIContent::GetFlattenedTreeParentNodeInternal() const
+nsIContent::GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const
 {
   nsINode* parentNode = GetParentNode();
   if (!parentNode || !parentNode->IsContent()) {
     MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
     return parentNode;
   }
   nsIContent* parent = parentNode->AsContent();
 
+  if (aType == eForStyle &&
+      IsRootOfNativeAnonymousSubtree() &&
+      OwnerDoc()->GetRootElement() == parent) {
+    // When getting the flattened tree parent for style, we return null
+    // for any "document level" native anonymous content subtree root.
+    // This is NAC generated by an ancestor frame of the document element's
+    // primary frame, and includes scrollbar elements created by the root
+    // scroll frame, and the "custom content container" and accessible caret
+    // generated by the nsCanvasFrame.  We distinguish document level NAC
+    // from NAC generated by the root element's primary frame below.
+    nsIFrame* parentFrame = parent->GetPrimaryFrame();
+    if (!parentFrame) {
+      // If the root element has no primary frame, it means it can't have
+      // generated any NAC itself.  Thus any NAC we have here must have
+      // been generated by an ancestor frame.
+      //
+      // If we are in here, then either the root element is display:none, or
+      // we are in the middle of constructing the root of the frame tree and
+      // we are trying to eagerly restyle document level NAC in
+      // nsCSSFrameConstructor::GetAnonymousContent before the root
+      // element's frame has been constructed.
+      return nullptr;
+    }
+    nsIAnonymousContentCreator* creator = do_QueryFrame(parentFrame);
+    if (!creator) {
+      // If the root element does have a frame, but does not implement
+      // nsIAnonymousContentCreator, then this must be document level NAC.
+      return nullptr;
+    }
+    AutoTArray<nsIContent*, 8> elements;
+    creator->AppendAnonymousContentTo(elements, 0);
+    if (!elements.Contains(this)) {
+      // If the root element does have a frame, and also does implement
+      // nsIAnonymousContentCreator, but didn't create this node, then
+      // it must be document level NAC.
+      return nullptr;
+    }
+  }
+
   if (parent && nsContentUtils::HasDistributedChildren(parent) &&
       nsContentUtils::IsInSameAnonymousTree(parent, this)) {
     // This node is distributed to insertion points, thus we
     // need to consult the destination insertion points list to
     // figure out where this node was inserted in the flattened tree.
     // It may be the case that |parent| distributes its children
     // but the child does not match any insertion points, thus
     // the flattened tree parent is nullptr.
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -97,16 +97,17 @@
 #include "nsFocusManager.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsGkAtoms.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsHtml5Module.h"
 #include "nsHtml5StringParser.h"
 #include "nsIAddonPolicyService.h"
+#include "nsIAnonymousContentCreator.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICategoryManager.h"
 #include "nsIChannelEventSink.h"
 #include "nsICharsetDetectionObserver.h"
 #include "nsIChromeRegistry.h"
 #include "nsIConsoleService.h"
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
@@ -9782,8 +9783,26 @@ nsContentUtils::AttemptLargeAllocationLo
 
   // Actually perform the cross process load
   bool reloadSucceeded = false;
   rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer, &reloadSucceeded);
   NS_ENSURE_SUCCESS(rv, false);
 
   return reloadSucceeded;
 }
+
+/* static */ void
+nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
+    nsIDocument* aDocument,
+    nsTArray<nsIContent*>& aElements)
+{
+  MOZ_ASSERT(aDocument);
+
+  // XXXheycam This probably needs to find the nsCanvasFrame's NAC too.
+  if (nsIPresShell* presShell = aDocument->GetShell()) {
+    if (nsIFrame* scrollFrame = presShell->GetRootScrollFrame()) {
+      nsIAnonymousContentCreator* creator = do_QueryFrame(scrollFrame);
+      MOZ_ASSERT(creator,
+                 "scroll frame should always implement nsIAnonymousContentCreator");
+      creator->AppendAnonymousContentTo(aElements, 0);
+    }
+  }
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2725,16 +2725,26 @@ public:
 
   static void GetCustomPrototype(nsIDocument* aDoc,
                                  int32_t aNamespaceID,
                                  nsIAtom* aAtom,
                                  JS::MutableHandle<JSObject*> prototype);
 
   static bool AttemptLargeAllocationLoad(nsIHttpChannel* aChannel);
 
+  /**
+   * Appends all "document level" native anonymous content subtree roots for
+   * aDocument to aElements.  Document level NAC subtrees are those created
+   * by ancestor frames of the document element's primary frame, such as
+   * the scrollbar elements created by the root scroll frame.
+   */
+  static void AppendDocumentLevelNativeAnonymousContentTo(
+      nsIDocument* aDocument,
+      nsTArray<nsIContent*>& aElements);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -140,17 +140,24 @@ public:
      *   4. :after generated node
      */
     eAllButXBL = 1,
 
     /**
      * Skip native anonymous content created for placeholder of HTML input,
      * used in conjunction with eAllChildren or eAllButXBL.
      */
-    eSkipPlaceholderContent = 2
+    eSkipPlaceholderContent = 2,
+
+    /**
+     * Skip native anonymous content created by ancestor frames of the root
+     * element's primary frame, such as scrollbar elements created by the root
+     * scroll frame.
+     */
+    eSkipDocumentLevelNativeAnonymousContent = 4,
   };
 
   /**
    * Return either the XBL explicit children of the node or the XBL flattened
    * tree children of the node, depending on the filter, as well as
    * native anonymous children.
    *
    * @note calling this method with eAllButXBL will return children that are
@@ -719,20 +726,19 @@ public:
   virtual void SetXBLInsertionParent(nsIContent* aContent) = 0;
 
   /**
    * Same as GetFlattenedTreeParentNode, but returns null if the parent is
    * non-nsIContent.
    */
   inline nsIContent *GetFlattenedTreeParent() const;
 
-  /**
-   * Helper method, which we leave public so that it's accessible from nsINode.
-   */
-  nsINode *GetFlattenedTreeParentNodeInternal() const;
+  // Helper method, which we leave public so that it's accessible from nsINode.
+  enum FlattenedParentType { eNotForStyle, eForStyle };
+  nsINode* GetFlattenedTreeParentNodeInternal(FlattenedParentType aType) const;
 
   /**
    * Gets the custom element data used by web components custom element.
    * Custom element data is created at the first attempt to enqueue a callback.
    *
    * @return The custom element data or null if none.
    */
   virtual mozilla::dom::CustomElementData *GetCustomElementData() const = 0;
--- a/dom/base/nsIContentInlines.h
+++ b/dom/base/nsIContentInlines.h
@@ -28,44 +28,63 @@ inline mozilla::dom::ShadowRoot* nsICont
 {
   if (!IsElement()) {
     return nullptr;
   }
 
   return AsElement()->FastGetShadowRoot();
 }
 
-inline nsINode* nsINode::GetFlattenedTreeParentNode() const
+template<nsIContent::FlattenedParentType Type>
+static inline nsINode*
+GetFlattenedTreeParentNode(const nsINode* aNode)
 {
-  nsINode* parent = GetParentNode();
-
+  nsINode* parent = aNode->GetParentNode();
   // Try to short-circuit past the complicated and not-exactly-fast logic for
   // computing the flattened parent.
   //
-  // There are three cases where we need might something other than parentNode:
+  // There are four cases where we need might something other than parentNode:
   //   (1) The node is an explicit child of an XBL-bound element, re-bound
   //       to an XBL insertion point.
   //   (2) The node is a top-level element in a shadow tree, whose flattened
   //       parent is the host element (as opposed to the actual parent which
   //       is the shadow root).
   //   (3) The node is an explicit child of an element with a shadow root,
   //       re-bound to an insertion point.
-  bool needSlowCall = HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
-                      IsInShadowTree() ||
-                      (parent && parent->IsContent() &&
-                       parent->AsContent()->GetShadowRoot());
+  //   (4) We want the flattened parent for style, and the node is the root
+  //       of a native anonymous content subtree parented to the document's
+  //       root element.
+  bool needSlowCall = aNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) ||
+                      aNode->IsInShadowTree() ||
+                      (parent &&
+                       parent->IsContent() &&
+                       parent->AsContent()->GetShadowRoot()) ||
+                      (Type == nsIContent::eForStyle &&
+                       aNode->IsContent() &&
+                       aNode->AsContent()->IsRootOfNativeAnonymousSubtree() &&
+                       aNode->OwnerDoc()->GetRootElement() == parent);
   if (MOZ_UNLIKELY(needSlowCall)) {
-    MOZ_ASSERT(IsContent());
-    return AsContent()->GetFlattenedTreeParentNodeInternal();
+    MOZ_ASSERT(aNode->IsContent());
+    return aNode->AsContent()->GetFlattenedTreeParentNodeInternal(Type);
   }
+  return parent;
+}
 
-  return parent;
+inline nsINode*
+nsINode::GetFlattenedTreeParentNode() const
+{
+  return ::GetFlattenedTreeParentNode<nsIContent::eNotForStyle>(this);
 }
 
 inline nsIContent*
 nsIContent::GetFlattenedTreeParent() const
 {
   nsINode* parent = GetFlattenedTreeParentNode();
   return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
 }
 
+inline nsINode*
+nsINode::GetFlattenedTreeParentNodeForStyle() const
+{
+  return ::GetFlattenedTreeParentNode<nsIContent::eForStyle>(this);
+}
 
 #endif // nsIContentInlines_h
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -914,16 +914,24 @@ public:
    * into an insertion point, or if the node is a direct child of a
    * shadow root.
    *
    * @return the flattened tree parent
    */
   inline nsINode* GetFlattenedTreeParentNode() const;
 
   /**
+   * Like GetFlattenedTreeParentNode, but returns null for any native
+   * anonymous content that was generated for ancestor frames of the
+   * root element's primary frame, such as scrollbar elements created
+   * by the root scroll frame.
+   */
+  inline nsINode* GetFlattenedTreeParentNodeForStyle() const;
+
+  /**
    * Get the parent nsINode for this node if it is an Element.
    * @return the parent node
    */
   mozilla::dom::Element* GetParentElement() const
   {
     return mParent && mParent->IsElement() ? mParent->AsElement() : nullptr;
   }
 
--- a/layout/base/RestyleManagerHandleInlines.h
+++ b/layout/base/RestyleManagerHandleInlines.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_RestyleManagerHandleInlines_h
 #define mozilla_RestyleManagerHandleInlines_h
 
 #include "mozilla/RestyleManager.h"
 #include "mozilla/ServoRestyleManager.h"
+#include "mozilla/ServoRestyleManagerInlines.h"
 
 #define FORWARD_CONCRETE(method_, geckoargs_, servoargs_) \
   if (IsGecko()) { \
     return AsGecko()->method_ geckoargs_; \
   } else { \
     return AsServo()->method_ servoargs_; \
   }
 
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -1,16 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoRestyleManager.h"
+
+#include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoBindings.h"
+#include "mozilla/ServoRestyleManagerInlines.h"
 #include "mozilla/ServoStyleSet.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
 #include "nsStyleChangeList.h"
 
 using namespace mozilla::dom;
 
@@ -284,37 +287,38 @@ ServoRestyleManager::ProcessPendingResty
   }
 
   if (!HasPendingRestyles()) {
     return;
   }
 
   ServoStyleSet* styleSet = StyleSet();
   nsIDocument* doc = PresContext()->Document();
-  Element* root = doc->GetRootElement();
 
   // XXXbholley: Should this be while() per bug 1316247?
   if (HasPendingRestyles()) {
-    MOZ_ASSERT(root);
     mInStyleRefresh = true;
     styleSet->StyleDocument();
 
     // First do any queued-up frame creation. (see bugs 827239 and 997506).
     //
     // XXXEmilio I'm calling this to avoid random behavior changes, since we
     // delay frame construction after styling we should re-check once our
     // model is more stable whether we can skip this call.
     //
     // Note this has to be *after* restyling, because otherwise frame
     // construction will find unstyled nodes, and that's not funny.
     PresContext()->FrameConstructor()->CreateNeededFrames();
 
     // Recreate style contexts and queue up change hints.
     nsStyleChangeList currentChanges;
-    RecreateStyleContexts(root, nullptr, styleSet, currentChanges);
+    DocumentStyleRootIterator iter(doc);
+    while (Element* root = iter.GetNextStyleRoot()) {
+      RecreateStyleContexts(root, nullptr, styleSet, currentChanges);
+    }
 
     // Process the change hints.
     //
     // Unfortunately, the frame constructor can generate new change hints while
     // processing existing ones. We redirect those into a secondary queue and
     // iterate until there's nothing left.
     ReentrantChangeList newChanges;
     mReentrantChanges = &newChanges;
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ServoRestyleManager_h
 #define mozilla_ServoRestyleManager_h
 
+#include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/RestyleManagerBase.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/ServoElementSnapshot.h"
 #include "nsChangeHint.h"
 #include "nsHashKeys.h"
 #include "nsINode.h"
 #include "nsISupportsImpl.h"
@@ -73,21 +74,17 @@ public:
                            const nsAttrValue* aNewValue);
 
   void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
                         nsIAtom* aAttribute, int32_t aModType,
                         const nsAttrValue* aOldValue);
 
   nsresult ReparentStyleContext(nsIFrame* aFrame);
 
-  bool HasPendingRestyles()
-  {
-    Element* root = PresContext()->Document()->GetRootElement();
-    return root && root->HasDirtyDescendantsForServo();
-  }
+  inline bool HasPendingRestyles();
 
 
   /**
    * Gets the appropriate frame given a content and a pseudo-element tag.
    *
    * Right now only supports a null tag, before or after. If the pseudo-element
    * is not null, the content needs to be an element.
    */
new file mode 100644
--- /dev/null
+++ b/layout/base/ServoRestyleManagerInlines.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef ServoRestyleManagerInlines_h
+#define ServoRestyleManagerInlines_h
+
+#include "ServoRestyleManager.h"
+
+#include "mozilla/dom/ElementInlines.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+inline bool
+ServoRestyleManager::HasPendingRestyles()
+{
+  nsIDocument* doc = PresContext()->Document();
+  DocumentStyleRootIterator iter(doc);
+  while (Element* root = iter.GetNextStyleRoot()) {
+    if (root->ShouldTraverseForServo()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+} // namespace mozilla
+
+#endif // ServoRestyleManagerInlines_h
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -73,16 +73,17 @@ EXPORTS.mozilla += [
     'OverflowChangedTracker.h',
     'PresShell.h',
     'RestyleLogging.h',
     'RestyleManager.h',
     'RestyleManagerBase.h',
     'RestyleManagerHandle.h',
     'RestyleManagerHandleInlines.h',
     'ServoRestyleManager.h',
+    'ServoRestyleManagerInlines.h',
     'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
     'AccessibleCaretEventHub.cpp',
     'AccessibleCaretManager.cpp',
     'FramePropertyTable.cpp',
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -71,16 +71,17 @@
 #include "nsContentUtils.h"
 #include "nsPIWindowRoot.h"
 #include "mozilla/Preferences.h"
 #include "gfxTextRun.h"
 #include "nsFontFaceUtils.h"
 #include "nsLayoutStylesheetCache.h"
 #include "mozilla/StyleSheet.h"
 #include "mozilla/StyleSheetInlines.h"
+#include "mozilla/ServoRestyleManagerInlines.h"
 
 #if defined(MOZ_WIDGET_GTK)
 #include "gfxPlatformGtk.h" // xxx - for UseFcFontList
 #endif
 
 
 // Needed for Start/Stop of Image Animation
 #include "imgIContainer.h"
--- a/layout/generic/nsIAnonymousContentCreator.h
+++ b/layout/generic/nsIAnonymousContentCreator.h
@@ -61,17 +61,19 @@ public:
    *       that.
    */
   virtual nsresult CreateAnonymousContent(nsTArray<ContentInfo>& aElements)=0;
 
   /**
    * Appends "native" anonymous children created by CreateAnonymousContent()
    * to the given content list depending on the filter.
    *
-   * @see nsIContent::GetChildren for set of values used for filter.
+   * @see nsIContent::GetChildren for set of values used for filter.  Currently,
+   *   eSkipPlaceholderContent is the only flag that any implementation of
+   *   this method heeds.
    */
   virtual void AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                         uint32_t aFilter) = 0;
 
   /**
    * Implementations can override this method to create special frames for the
    * anonymous content returned from CreateAnonymousContent.
    * By default this method returns nullptr, which means the default frame
new file mode 100644
--- /dev/null
+++ b/layout/style/DocumentStyleRootIterator.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DocumentStyleRootIterator.h"
+
+#include "nsContentUtils.h"
+
+namespace mozilla {
+
+DocumentStyleRootIterator::DocumentStyleRootIterator(nsIDocument* aDocument)
+  : mPosition(0)
+{
+  MOZ_COUNT_CTOR(DocumentStyleRootIterator);
+  if (Element* root = aDocument->GetRootElement()) {
+    mStyleRoots.AppendElement(root);
+  }
+  nsContentUtils::AppendDocumentLevelNativeAnonymousContentTo(
+      aDocument, mStyleRoots);
+}
+
+Element*
+DocumentStyleRootIterator::GetNextStyleRoot()
+{
+  for (;;) {
+    if (mPosition >= mStyleRoots.Length()) {
+      return nullptr;
+    }
+
+    nsIContent* next = mStyleRoots[mPosition];
+    ++mPosition;
+
+    if (next->IsElement()) {
+      return next->AsElement();
+    }
+  }
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/style/DocumentStyleRootIterator.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef DocumentStyleRootIterator_h
+#define DocumentStyleRootIterator_h
+
+#include "nsTArray.h"
+
+class nsIContent;
+class nsIDocument;
+
+namespace mozilla {
+
+/**
+ * DocumentStyleRootIterator traverses the roots of the document from the
+ * perspective of the Servo-backed style system.  This will first traverse
+ * the document root, followed by any document level native anonymous content.
+ */
+class DocumentStyleRootIterator
+{
+public:
+  explicit DocumentStyleRootIterator(nsIDocument* aDocument);
+  ~DocumentStyleRootIterator() { MOZ_COUNT_DTOR(DocumentStyleRootIterator); }
+
+  Element* GetNextStyleRoot();
+
+private:
+  AutoTArray<nsIContent*, 8> mStyleRoots;
+  uint32_t mPosition;
+};
+
+} // namespace mozilla
+
+#endif // DocumentStyleRootIterator_h
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -15,16 +15,17 @@
  *
  * Users of this list should define a macro
  * SERVO_BINDING_FUNC(name_, return_, ...)
  * before including this file.
  */
 
 // Element data
 SERVO_BINDING_FUNC(Servo_Element_ClearData, void, RawGeckoElementBorrowed node)
+SERVO_BINDING_FUNC(Servo_Element_ShouldTraverse, bool, RawGeckoElementBorrowed node)
 
 // Styleset and Stylesheet management
 SERVO_BINDING_FUNC(Servo_StyleSheet_Empty, RawServoStyleSheetStrong,
                    mozilla::css::SheetParsingMode parsing_mode)
 SERVO_BINDING_FUNC(Servo_StyleSheet_FromUTF8Bytes, RawServoStyleSheetStrong,
                    const nsACString* data,
                    mozilla::css::SheetParsingMode parsing_mode,
                    const nsACString* base_url,
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -57,17 +57,17 @@ bool
 Gecko_NodeIsElement(RawGeckoNodeBorrowed aNode)
 {
   return aNode->IsElement();
 }
 
 RawGeckoNodeBorrowedOrNull
 Gecko_GetParentNode(RawGeckoNodeBorrowed aNode)
 {
-  return aNode->GetFlattenedTreeParentNode();
+  return aNode->GetFlattenedTreeParentNodeForStyle();
 }
 
 RawGeckoNodeBorrowedOrNull
 Gecko_GetFirstChild(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetFirstChild();
 }
 
@@ -87,17 +87,17 @@ RawGeckoNodeBorrowedOrNull
 Gecko_GetNextSibling(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetNextSibling();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetParentElement(RawGeckoElementBorrowed aElement)
 {
-  return aElement->GetFlattenedTreeParentElement();
+  return aElement->GetFlattenedTreeParentElementForStyle();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetFirstChildElement(RawGeckoElementBorrowed aElement)
 {
   return aElement->GetFirstElementChild();
 }
 
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/ServoStyleSet.h"
 
+#include "mozilla/DocumentStyleRootIterator.h"
 #include "mozilla/ServoRestyleManager.h"
 #include "mozilla/dom/ChildIterator.h"
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIDocumentInlines.h"
 #include "nsPrintfCString.h"
 #include "nsStyleContext.h"
 #include "nsStyleSet.h"
@@ -41,17 +42,18 @@ ServoStyleSet::BeginShutdown()
   // frame tree has been destroyed, but before the script runners that delete
   // native anonymous content (which also could be holding on the RuleNodes)
   // have run.  By clearing style here, before the frame tree is destroyed,
   // the AllChildrenIterator will find the anonymous content.
   //
   // Note that this is pretty bad for performance; we should find a way to
   // get by with the ServoNodeDatas being dropped as part of the document
   // going away.
-  if (Element* root = mPresContext->Document()->GetRootElement()) {
+  DocumentStyleRootIterator iter(mPresContext->Document());
+  while (Element* root = iter.GetNextStyleRoot()) {
     ServoRestyleManager::ClearServoDataFromSubtree(root);
   }
 }
 
 void
 ServoStyleSet::Shutdown()
 {
   mRawSet = nullptr;
@@ -443,23 +445,24 @@ ServoStyleSet::HasStateDependentStyle(do
 {
   NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
   return nsRestyleHint(0);
 }
 
 void
 ServoStyleSet::StyleDocument()
 {
-  // Grab the root.
-  nsIDocument* doc = mPresContext->Document();
-  Element* root = doc->GetRootElement();
-  MOZ_ASSERT(root);
-
-  // Restyle the document.
-  Servo_TraverseSubtree(root, mRawSet.get(), SkipRootBehavior::DontSkip);
+  // Restyle the document from the root element and each of the document level
+  // NAC subtree roots.
+  DocumentStyleRootIterator iter(mPresContext->Document());
+  while (Element* root = iter.GetNextStyleRoot()) {
+    if (root->ShouldTraverseForServo()) {
+      Servo_TraverseSubtree(root, mRawSet.get(), SkipRootBehavior::DontSkip);
+    }
+  }
 }
 
 void
 ServoStyleSet::StyleNewSubtree(nsIContent* aContent)
 {
   if (aContent->IsElement()) {
     Servo_TraverseSubtree(aContent->AsElement(), mRawSet.get(), SkipRootBehavior::DontSkip);
   }
--- a/layout/style/moz.build
+++ b/layout/style/moz.build
@@ -83,16 +83,17 @@ EXPORTS.mozilla += [
     'AnimationCollection.h',
     'CSSEnabledState.h',
     'CSSStyleSheet.h',
     'CSSVariableDeclarations.h',
     'CSSVariableResolver.h',
     'CSSVariableValues.h',
     'DeclarationBlock.h',
     'DeclarationBlockInlines.h',
+    'DocumentStyleRootIterator.h',
     'HandleRefPtr.h',
     'IncrementalClearCOMRuleArray.h',
     'LayerAnimationInfo.h',
     'RuleNodeCacheConditions.h',
     'RuleProcessorCache.h',
     'ServoArcTypeList.h',
     'ServoBindingList.h',
     'ServoBindings.h',
@@ -149,16 +150,17 @@ UNIFIED_SOURCES += [
     'CSS.cpp',
     'CSSLexer.cpp',
     'CSSRuleList.cpp',
     'CSSStyleSheet.cpp',
     'CSSVariableDeclarations.cpp',
     'CSSVariableResolver.cpp',
     'CSSVariableValues.cpp',
     'Declaration.cpp',
+    'DocumentStyleRootIterator.cpp',
     'ErrorReporter.cpp',
     'FontFace.cpp',
     'FontFaceSet.cpp',
     'FontFaceSetIterator.cpp',
     'ImageLoader.cpp',
     'IncrementalClearCOMRuleArray.cpp',
     'LayerAnimationInfo.cpp',
     'Loader.cpp',