Bug 1317016 - Basic infrastructure for RestyleHint-driven traversal. r=emilio
authorBobby Holley <bobbyholley@gmail.com>
Tue, 01 Nov 2016 23:11:24 -0700
changeset 324318 7c852ac4acf83d5d1776e98a6af36193bd97a9e5
parent 324317 8144961dead7492bdbfc6e7dde081e555d13ac98
child 324319 dcecd20fd2db6e6271f06837121b287244a55a21
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewersemilio
bugs1317016
milestone53.0a1
Bug 1317016 - Basic infrastructure for RestyleHint-driven traversal. r=emilio MozReview-Commit-ID: 7wH5XcILVmX
dom/base/Element.cpp
dom/base/Element.h
dom/base/ElementInlines.h
dom/base/FragmentOrElement.cpp
dom/base/nsGenericDOMDataNode.cpp
dom/base/nsIContent.h
dom/base/nsINode.cpp
dom/base/nsINode.h
dom/html/nsGenericHTMLElement.cpp
dom/xbl/nsXBLBinding.cpp
layout/base/RestyleManagerBase.h
layout/base/RestyleManagerHandle.h
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsChangeHint.h
layout/base/nsDocumentViewer.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresShell.cpp
layout/generic/nsFrameSetFrame.cpp
layout/generic/nsImageFrame.cpp
layout/style/ServoBindingList.h
layout/style/ServoBindingTypes.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/ServoElementSnapshot.cpp
layout/style/ServoElementSnapshot.h
layout/style/ServoStyleSet.cpp
layout/style/ServoStyleSet.h
layout/style/ServoTypes.h
layout/style/StyleSetHandle.h
layout/style/StyleSetHandleInlines.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsRuleNode.cpp
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
layout/style/nsStyleSet.h
layout/xul/nsListBoxBodyFrame.cpp
layout/xul/nsSplitterFrame.cpp
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1731,28 +1731,16 @@ Element::BindToTree(nsIDocument* aDocume
          child = child->GetNextSibling()) {
       rv = child->BindToTree(nullptr, shadowRoot,
                              shadowRoot->GetBindingParent(),
                              aCompileEventHandlers);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  // It would be cleanest to mark nodes as dirty when (a) they're created and
-  // (b) they're unbound from a tree. However, we can't easily do (a) right now,
-  // because IsStyledByServo() is not always easy to check at node creation time,
-  // and the bits have different meaning in the non-IsStyledByServo case.
-  //
-  // So for now, we just mark nodes as dirty when they're inserted into a
-  // document or shadow tree.
-  if (IsStyledByServo() && IsInComposedDoc()) {
-    MOZ_ASSERT(!HasServoData());
-    SetIsDirtyForServo();
-  }
-
   // XXXbz script execution during binding can trigger some of these
   // postcondition asserts....  But we do want that, since things will
   // generally be quite broken when that happens.
   NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document");
   NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
   NS_POSTCONDITION(aBindingParent == GetBindingParent(),
                    "Bound to wrong binding parent");
 
@@ -3917,8 +3905,17 @@ Element::UpdateIntersectionObservation(D
   for (auto& reg : *observers) {
     if (reg.observer == aObserver && reg.previousThreshold != aThreshold) {
       reg.previousThreshold = aThreshold;
       return true;
     }
   }
   return false;
 }
+
+void
+Element::ClearServoData() {
+#ifdef MOZ_STYLO
+  Servo_Element_ClearData(this);
+#else
+  MOZ_CRASH("Accessing servo node data in non-stylo build");
+#endif
+}
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -11,16 +11,17 @@
  */
 
 #ifndef mozilla_dom_Element_h__
 #define mozilla_dom_Element_h__
 
 #include "mozilla/dom/FragmentOrElement.h" // for base class
 #include "nsChangeHint.h"                  // for enum
 #include "mozilla/EventStates.h"           // for member
+#include "mozilla/ServoTypes.h"
 #include "mozilla/dom/DirectionalityUtils.h"
 #include "nsIDOMElement.h"
 #include "nsILinkHandler.h"
 #include "nsINodeList.h"
 #include "nsNodeUtils.h"
 #include "nsAttrAndChildArray.h"
 #include "mozFlushType.h"
 #include "nsDOMAttributeMap.h"
@@ -159,16 +160,24 @@ public:
   explicit Element(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo) :
     FragmentOrElement(aNodeInfo),
     mState(NS_EVENT_STATE_MOZ_READONLY)
   {
     MOZ_ASSERT(mNodeInfo->NodeType() == nsIDOMNode::ELEMENT_NODE,
                "Bad NodeType in aNodeInfo");
     SetIsElement();
   }
+
+  ~Element()
+  {
+#ifdef MOZ_STYLO
+    NS_ASSERTION(!HasServoData(), "expected ServoData to be cleared earlier");
+#endif
+  }
+
 #endif // MOZILLA_INTERNAL_API
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID)
 
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
 
   /**
    * Method to get the full state of this element.  See mozilla/EventStates.h
@@ -386,16 +395,50 @@ public:
   // false
   inline bool HasDirAuto() const {
     return (!HasFixedDir() &&
             (HasValidDir() || IsHTMLElement(nsGkAtoms::bdi)));
   }
 
   Directionality GetComputedDirectionality() const;
 
+  inline Element* GetFlattenedTreeParentElement() const;
+
+  bool HasDirtyDescendantsForServo() const
+  {
+    MOZ_ASSERT(IsStyledByServo());
+    return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+  }
+
+  void SetHasDirtyDescendantsForServo() {
+    MOZ_ASSERT(IsStyledByServo());
+    SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+  }
+
+  void UnsetHasDirtyDescendantsForServo() {
+    MOZ_ASSERT(IsStyledByServo());
+    UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+  }
+
+  inline void NoteDirtyDescendantsForServo();
+
+#ifdef DEBUG
+  inline bool DirtyDescendantsBitIsPropagatedForServo();
+#endif
+
+  bool HasServoData() {
+#ifdef MOZ_STYLO
+    return !!mServoData.Get();
+#else
+    MOZ_CRASH("Accessing servo node data in non-stylo build");
+#endif
+  }
+
+  void ClearServoData();
+
 protected:
   /**
    * Method to get the _intrinsic_ content state of this element.  This is the
    * state that is independent of the element's presentation.  To get the full
    * content state, use State().  See mozilla/EventStates.h for
    * the possible bits that could be set here.
    */
   virtual EventStates IntrinsicState() const;
@@ -1386,16 +1429,20 @@ private:
    */
   nsRect GetClientAreaRect();
 
   nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr,
                                      bool aFlushLayout = true);
 
   // Data members
   EventStates mState;
+#ifdef MOZ_STYLO
+  // Per-node data managed by Servo.
+  mozilla::ServoCell<ServoNodeData*> mServoData;
+#endif
 };
 
 class RemoveFromBindingManagerRunnable : public mozilla::Runnable
 {
 public:
   RemoveFromBindingManagerRunnable(nsBindingManager* aManager,
                                    nsIContent* aContent,
                                    nsIDocument* aDoc);
@@ -1483,17 +1530,17 @@ inline mozilla::dom::Element* nsINode::A
 inline const mozilla::dom::Element* nsINode::AsElement() const
 {
   MOZ_ASSERT(IsElement());
   return static_cast<const mozilla::dom::Element*>(this);
 }
 
 inline void nsINode::UnsetRestyleFlagsIfGecko()
 {
-  if (IsElement() && !IsStyledByServo()) {
+  if (IsElement() && !AsElement()->IsStyledByServo()) {
     UnsetFlags(ELEMENT_ALL_RESTYLE_FLAGS);
   }
 }
 
 /**
  * Macros to implement Clone(). _elementName is the class for which to implement
  * Clone.
  */
--- 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 "nsIContentInlines.h"
 #include "nsIDocument.h"
 
 namespace mozilla {
 namespace dom {
 
 inline void
 Element::RegisterActivityObserver()
 {
@@ -20,12 +21,52 @@ Element::RegisterActivityObserver()
 }
 
 inline void
 Element::UnregisterActivityObserver()
 {
   OwnerDoc()->UnregisterActivityObserver(this);
 }
 
+inline Element*
+Element::GetFlattenedTreeParentElement() const
+{
+  nsINode* parentNode = GetFlattenedTreeParentNode();
+  if MOZ_LIKELY(parentNode && parentNode->IsElement()) {
+    return parentNode->AsElement();
+  }
+
+  return nullptr;
+}
+
+inline void
+Element::NoteDirtyDescendantsForServo()
+{
+  Element* curr = this;
+  while (curr && !curr->HasDirtyDescendantsForServo()) {
+    curr->SetHasDirtyDescendantsForServo();
+    curr = curr->GetFlattenedTreeParentElement();
+  }
+
+  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());
+  }
+  return true;
+}
+#endif
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ElementInlines_h
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -2339,56 +2339,8 @@ FragmentOrElement::SetIsElementInStyleSc
   NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
                             "on a non-Element is useless");
   ShadowRoot* shadowRoot = GetShadowRoot();
   while (shadowRoot) {
     shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope);
     shadowRoot = shadowRoot->GetOlderShadowRoot();
   }
 }
-
-#ifdef DEBUG
-static void
-AssertDirtyDescendantsBitPropagated(nsINode* aNode)
-{
-  MOZ_ASSERT(aNode->HasDirtyDescendantsForServo());
-  nsINode* parent = aNode->GetFlattenedTreeParentNode();
-  if (!parent->IsContent()) {
-    MOZ_ASSERT(parent == aNode->OwnerDoc());
-    MOZ_ASSERT(parent->HasDirtyDescendantsForServo());
-  } else {
-    AssertDirtyDescendantsBitPropagated(parent);
-  }
-}
-#else
-static void AssertDirtyDescendantsBitPropagated(nsINode* aNode) {}
-#endif
-
-void
-nsIContent::MarkAncestorsAsHavingDirtyDescendantsForServo()
-{
-  MOZ_ASSERT(IsInComposedDoc());
-
-  // Get the parent in the flattened tree.
-  nsINode* parent = GetFlattenedTreeParentNode();
-
-  // Loop until we hit a base case.
-  while (true) {
-
-    // Base case: the document.
-    if (!parent->IsContent()) {
-      MOZ_ASSERT(parent == OwnerDoc());
-      parent->SetHasDirtyDescendantsForServo();
-      return;
-    }
-
-    // Base case: the parent is already marked, and therefore
-    // so are all its ancestors.
-    if (parent->HasDirtyDescendantsForServo()) {
-      AssertDirtyDescendantsBitPropagated(parent);
-      return;
-    }
-
-    // Mark the parent and iterate.
-    parent->SetHasDirtyDescendantsForServo();
-    parent = parent->GetFlattenedTreeParentNode();
-  }
-}
--- a/dom/base/nsGenericDOMDataNode.cpp
+++ b/dom/base/nsGenericDOMDataNode.cpp
@@ -547,28 +547,16 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
 
   nsNodeUtils::ParentChainChanged(this);
   if (!hadParent && IsRootOfNativeAnonymousSubtree()) {
     nsNodeUtils::NativeAnonymousChildListChange(this, false);
   }
 
   UpdateEditableState(false);
 
-  // It would be cleanest to mark nodes as dirty when (a) they're created and
-  // (b) they're unbound from a tree. However, we can't easily do (a) right now,
-  // because IsStyledByServo() is not always easy to check at node creation time,
-  // and the bits have different meaning in the non-IsStyledByServo case.
-  //
-  // So for now, we just mark nodes as dirty when they're inserted into a
-  // document or shadow tree.
-  if (IsStyledByServo() && IsInComposedDoc()) {
-    MOZ_ASSERT(!HasServoData());
-    SetIsDirtyForServo();
-  }
-
   NS_POSTCONDITION(aDocument == GetUncomposedDoc(), "Bound to wrong document");
   NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
   NS_POSTCONDITION(aBindingParent == GetBindingParent(),
                    "Bound to wrong binding parent");
 
   return NS_OK;
 }
 
@@ -590,26 +578,16 @@ nsGenericDOMDataNode::UnbindFromTree(boo
       NS_RELEASE(mParent);
     } else {
       mParent = nullptr;
     }
     SetParentIsContent(false);
   }
   ClearInDocument();
 
-  // Computed styled data isn't useful for detached nodes, and we'll need to
-  // recomputed it anyway if we ever insert the nodes back into a document.
-  if (IsStyledByServo()) {
-    ClearServoData();
-  } else {
-#ifdef MOZ_STYLO
-    MOZ_ASSERT(!HasServoData());
-#endif
-  }
-
   if (aNullParent || !mParent->IsInShadowTree()) {
     UnsetFlags(NODE_IS_IN_SHADOW_TREE);
 
     // Begin keeping track of our subtree root.
     SetSubtreeRootPointer(aNullParent ? this : mParent->SubtreeRoot());
   }
 
   if (document && !GetContainingShadow()) {
--- a/dom/base/nsIContent.h
+++ b/dom/base/nsIContent.h
@@ -933,24 +933,16 @@ public:
 
   /**
    * If the content is a part of HTML editor, this returns editing
    * host content.  When the content is in designMode, this returns its body
    * element.  Also, when the content isn't editable, this returns null.
    */
   mozilla::dom::Element* GetEditingHost();
 
-
-  /**
-   * Set NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO all the way up the flattened
-   * parent chain to the document. If an ancestor is found with the bit already
-   * set, this method asserts that all of its ancestors also have the bit set.
-   */
-  void MarkAncestorsAsHavingDirtyDescendantsForServo();
-
   /**
    * Determining language. Look at the nearest ancestor element that has a lang
    * attribute in the XML namespace or is an HTML/SVG element and has a lang in
    * no namespace attribute.  Returns false if no language was specified.
    */
   bool GetLang(nsAString& aResult) const {
     for (const nsIContent* content = this; content; content = content->GetParent()) {
       if (content->GetAttrCount() > 0) {
--- a/dom/base/nsINode.cpp
+++ b/dom/base/nsINode.cpp
@@ -147,20 +147,16 @@ nsINode::nsSlots::Unlink()
 }
 
 //----------------------------------------------------------------------
 
 nsINode::~nsINode()
 {
   MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
   MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?");
-#ifdef MOZ_STYLO
-  NS_ASSERTION(!HasServoData(), "expected ServoNodeData to be cleared earlier");
-  ClearServoData();
-#endif
 }
 
 void*
 nsINode::GetProperty(uint16_t aCategory, nsIAtom *aPropertyName,
                      nsresult *aStatus) const
 {
   return OwnerDoc()->PropertyTable(aCategory)->GetProperty(this, aPropertyName,
                                                            aStatus);
@@ -1395,25 +1391,16 @@ nsINode::UnoptimizableCCNode() const
                                       NODE_IS_IN_SHADOW_TREE);
   return HasFlag(problematicFlags) ||
          NodeType() == nsIDOMNode::ATTRIBUTE_NODE ||
          // For strange cases like xbl:content/xbl:children
          (IsElement() &&
           AsElement()->IsInNamespace(kNameSpaceID_XBL));
 }
 
-void
-nsINode::ClearServoData() {
-#ifdef MOZ_STYLO
-  Servo_Node_ClearNodeData(this);
-#else
-  MOZ_CRASH("Accessing servo node data in non-stylo build");
-#endif
-}
-
 /* static */
 bool
 nsINode::Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb)
 {
   if (MOZ_LIKELY(!cb.WantAllTraces())) {
     nsIDocument *currentDoc = tmp->GetUncomposedDoc();
     if (currentDoc &&
         nsCCUncollectableMarker::InGeneration(currentDoc->GetMarkedCCGeneration())) {
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -3,17 +3,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsINode_h___
 #define nsINode_h___
 
 #include "mozilla/Likely.h"
-#include "mozilla/ServoTypes.h"
 #include "mozilla/UniquePtr.h"
 #include "nsCOMPtr.h"               // for member, local
 #include "nsGkAtoms.h"              // for nsGkAtoms::baseURIProperty
 #include "nsIDOMNode.h"
 #include "mozilla/dom/NodeInfo.h"            // member (in nsCOMPtr)
 #include "nsIVariant.h"             // for use in GetUserData()
 #include "nsNodeInfoManager.h"      // for use in NodePrincipal()
 #include "nsPropertyTable.h"        // for typedefs
@@ -181,24 +180,23 @@ enum {
 
   NODE_CHROME_ONLY_ACCESS =               NODE_FLAG_BIT(19),
 
   NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS =    NODE_FLAG_BIT(20),
 
   // These two bits are shared by Gecko's and Servo's restyle systems for
   // different purposes. They should not be accessed directly, and access to
   // them should be properly guarded by asserts.
+  //
+  // FIXME(bholley): These should move to Element, and we only need one now.
   NODE_SHARED_RESTYLE_BIT_1 =             NODE_FLAG_BIT(21),
   NODE_SHARED_RESTYLE_BIT_2 =             NODE_FLAG_BIT(22),
 
-  // Whether this node is dirty for Servo's style system.
-  NODE_IS_DIRTY_FOR_SERVO =               NODE_SHARED_RESTYLE_BIT_1,
-
   // Whether this node has dirty descendants for Servo's style system.
-  NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO =  NODE_SHARED_RESTYLE_BIT_2,
+  NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO =  NODE_SHARED_RESTYLE_BIT_1,
 
   // Remaining bits are node type specific.
   NODE_TYPE_SPECIFIC_BITS_OFFSET =        23
 };
 
 // Make sure we have space for our bits
 #define ASSERT_NODE_FLAGS_SPACE(n) \
   static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <=                          \
@@ -976,58 +974,16 @@ public:
    * style system.
    */
 #ifdef MOZ_STYLO
   bool IsStyledByServo() const;
 #else
   bool IsStyledByServo() const { return false; }
 #endif
 
-  bool IsDirtyForServo() const
-  {
-    MOZ_ASSERT(IsStyledByServo());
-    return HasFlag(NODE_IS_DIRTY_FOR_SERVO);
-  }
-
-  bool HasDirtyDescendantsForServo() const
-  {
-    MOZ_ASSERT(IsStyledByServo());
-    return HasFlag(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
-  }
-
-  void SetIsDirtyForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    SetFlags(NODE_IS_DIRTY_FOR_SERVO);
-  }
-
-  void SetHasDirtyDescendantsForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
-  }
-
-  void SetIsDirtyAndHasDirtyDescendantsForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    SetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
-  }
-
-  void UnsetIsDirtyForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
-  }
-
-  void UnsetHasDirtyDescendantsForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
-  }
-
-  void UnsetIsDirtyAndHasDirtyDescendantsForServo() {
-    MOZ_ASSERT(IsStyledByServo());
-    UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO | NODE_IS_DIRTY_FOR_SERVO);
-  }
-
   inline void UnsetRestyleFlagsIfGecko();
 
   /**
    * Adds a mutation observer to be notified when this node, or any of its
    * descendants, are modified. The node will hold a weak reference to the
    * observer, which means that it is the responsibility of the observer to
    * remove itself in case it dies before the node.  If an observer is added
    * while observers are being notified, it may also be notified.  In general,
@@ -2056,26 +2012,16 @@ public:
   void SetOn##name_(mozilla::dom::EventHandlerNonNull* listener);
 #define TOUCH_EVENT EVENT
 #define DOCUMENT_ONLY_EVENT EVENT
 #include "mozilla/EventNameList.h"
 #undef DOCUMENT_ONLY_EVENT
 #undef TOUCH_EVENT
 #undef EVENT
 
-  bool HasServoData() {
-#ifdef MOZ_STYLO
-    return !!mServoData.Get();
-#else
-    MOZ_CRASH("Accessing servo node data in non-stylo build");
-#endif
-  }
-
-  void ClearServoData();
-
 protected:
   static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb);
   static void Unlink(nsINode *tmp);
 
   RefPtr<mozilla::dom::NodeInfo> mNodeInfo;
 
   // mParent is an owning ref most of the time, except for the case of document
   // nodes, so it cannot be represented by nsCOMPtr, so mark is as
@@ -2103,21 +2049,16 @@ protected:
     // Pointer to the root of our subtree.  Might be null.
     // This reference is non-owning and safe, since it either points to the
     // object itself, or is reset by ClearSubtreeRootPointer.
     nsINode* MOZ_NON_OWNING_REF mSubtreeRoot;
   };
 
   // Storage for more members that are usually not needed; allocated lazily.
   nsSlots* mSlots;
-
-#ifdef MOZ_STYLO
-  // Per-node data managed by Servo.
-  mozilla::ServoCell<ServoNodeData*> mServoData;
-#endif
 };
 
 inline nsIDOMNode* GetAsDOMNode(nsINode* aNode)
 {
   return aNode ? aNode->AsDOMNode() : nullptr;
 }
 
 // Useful inline function for getting a node given an nsIContent and an
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -2937,21 +2937,24 @@ IsOrHasAncestorWithDisplayNone(Element* 
     }
     elementsToCheck.AppendElement(e);
   }
 
   if (elementsToCheck.IsEmpty()) {
     return false;
   }
 
+  // XXXbholley: This could be done more directly with Servo's style system.
   StyleSetHandle styleSet = aPresShell->StyleSet();
   RefPtr<nsStyleContext> sc;
   for (int32_t i = elementsToCheck.Length() - 1; i >= 0; --i) {
     if (sc) {
-      sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc);
+      sc = styleSet->ResolveStyleFor(elementsToCheck[i], sc,
+                                     ConsumeStyleBehavior::DontConsume,
+                                     LazyComputeBehavior::Assert);
     } else {
       sc = nsComputedDOMStyle::GetStyleContextForElementNoFlush(elementsToCheck[i],
                                                                 nullptr, aPresShell);
     }
     if (sc->StyleDisplay()->mDisplay == StyleDisplay::None) {
       return true;
     }
   }
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -421,17 +421,16 @@ nsXBLBinding::GenerateAnonymousContent()
       mContent->UnsetAttr(namespaceID, name, false);
   }
 
   // Now that we've finished shuffling the tree around, go ahead and restyle it
   // since frame construction is about to happen.
   nsIPresShell* presShell = mBoundElement->OwnerDoc()->GetShell();
   ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo();
   if (servoSet) {
-    mBoundElement->SetHasDirtyDescendantsForServo();
     servoSet->StyleNewChildren(mBoundElement);
   }
 }
 
 nsIURI*
 nsXBLBinding::GetSourceDocURI()
 {
   nsIContent* targetContent =
--- a/layout/base/RestyleManagerBase.h
+++ b/layout/base/RestyleManagerBase.h
@@ -76,16 +76,18 @@ public:
 
   // Note: It's the caller's responsibility to make sure to wrap a
   // ProcessRestyledFrames call in a view update batch and a script blocker.
   // This function does not call ProcessAttachedQueue() on the binding manager.
   // If the caller wants that to happen synchronously, it needs to handle that
   // itself.
   nsresult ProcessRestyledFrames(nsStyleChangeList& aChangeList);
 
+  bool IsInStyleRefresh() const { return mInStyleRefresh; }
+
 protected:
   void ContentStateChangedInternal(Element* aElement,
                                    EventStates aStateMask,
                                    nsChangeHint* aOutChangeHint,
                                    nsRestyleHint* aOutRestyleHint);
 
   bool IsDisconnected() { return mPresContext == nullptr; }
 
--- a/layout/base/RestyleManagerHandle.h
+++ b/layout/base/RestyleManagerHandle.h
@@ -12,16 +12,17 @@
 #include "mozilla/HandleRefPtr.h"
 #include "mozilla/RefCountType.h"
 #include "mozilla/StyleBackendType.h"
 #include "nsChangeHint.h"
 
 namespace mozilla {
 class RestyleManager;
 class ServoRestyleManager;
+class RestyleManagerBase;
 namespace dom {
 class Element;
 } // namespace dom
 } // namespace mozilla
 class nsAttrValue;
 class nsIAtom;
 class nsIContent;
 class nsIFrame;
@@ -90,16 +91,24 @@ public:
     {
       MOZ_ASSERT(IsServo());
       return const_cast<Ptr*>(this)->AsServo();
     }
 
     const RestyleManager* GetAsGecko() const { return IsGecko() ? AsGecko() : nullptr; }
     const ServoRestyleManager* GetAsServo() const { return IsServo() ? AsServo() : nullptr; }
 
+    const mozilla::RestyleManagerBase* AsBase() const {
+      return reinterpret_cast<const RestyleManagerBase*>(mValue & ~SERVO_BIT);
+    }
+
+    mozilla::RestyleManagerBase* AsBase() {
+      return reinterpret_cast<RestyleManagerBase*>(mValue & ~SERVO_BIT);
+    }
+
     // These inline methods are defined in RestyleManagerHandleInlines.h.
     inline MozExternalRefCountType AddRef();
     inline MozExternalRefCountType Release();
 
     // Restyle manager interface.  These inline methods are defined in
     // RestyleManagerHandleInlines.h and just forward to the underlying
     // RestyleManager or ServoRestyleManager.  See corresponding comments in
     // RestyleManager.h for descriptions of these methods.
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -13,16 +13,17 @@
 #include "nsStyleChangeList.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
 ServoRestyleManager::ServoRestyleManager(nsPresContext* aPresContext)
   : RestyleManagerBase(aPresContext)
+  , mReentrantChanges(nullptr)
 {
 }
 
 void
 ServoRestyleManager::PostRestyleEvent(Element* aElement,
                                       nsRestyleHint aRestyleHint,
                                       nsChangeHint aMinChangeHint)
 {
@@ -30,29 +31,38 @@ ServoRestyleManager::PostRestyleEvent(El
       MOZ_UNLIKELY(PresContext()->PresShell()->IsDestroying())) {
     return;
   }
 
   if (aRestyleHint == 0 && !aMinChangeHint && !HasPendingRestyles()) {
     return; // Nothing to do.
   }
 
+  // We allow posting change hints during restyling, but not restyle hints
+  // themselves, since those would require us to re-traverse the tree.
+  MOZ_ASSERT_IF(mInStyleRefresh, aRestyleHint == 0);
+
+  // Processing change hints sometimes causes new change hints to be generated.
+  // Doing this after the gecko post-traversal is problematic, so instead we just
+  // queue them up for special handling.
+  if (mReentrantChanges) {
+    MOZ_ASSERT(aRestyleHint == 0);
+    mReentrantChanges->AppendElement(ReentrantChange { aElement, aMinChangeHint });
+    return;
+  }
+
   // XXX This is a temporary hack to make style attribute change works.
   //     In the future, we should be able to use this hint directly.
   if (aRestyleHint & eRestyle_StyleAttribute) {
     aRestyleHint &= ~eRestyle_StyleAttribute;
     aRestyleHint |= eRestyle_Self | eRestyle_Subtree;
   }
 
-  // Note that unlike in Servo, we don't mark elements as dirty until we process
-  // the restyle hints in ProcessPendingRestyles.
   if (aRestyleHint || aMinChangeHint) {
-    ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
-    snapshot->AddExplicitRestyleHint(aRestyleHint);
-    snapshot->AddExplicitChangeHint(aMinChangeHint);
+    Servo_NoteExplicitHints(aElement, aRestyleHint, aMinChangeHint);
   }
 
   PostRestyleEventInternal(false);
 }
 
 void
 ServoRestyleManager::PostRestyleEventForLazyConstruction()
 {
@@ -68,133 +78,77 @@ ServoRestyleManager::RebuildAllStyleData
 
 void
 ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                                   nsRestyleHint aRestyleHint)
 {
   NS_WARNING("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
 }
 
-static void
-MarkSelfAndDescendantsAsNotDirtyForServo(nsIContent* aContent)
+/* static */ void
+ServoRestyleManager::ClearServoDataFromSubtree(Element* aElement)
 {
-  aContent->UnsetIsDirtyForServo();
+  aElement->ClearServoData();
 
-  if (aContent->HasDirtyDescendantsForServo()) {
-    aContent->UnsetHasDirtyDescendantsForServo();
-
-    StyleChildrenIterator it(aContent);
-    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-      MarkSelfAndDescendantsAsNotDirtyForServo(n);
+  StyleChildrenIterator it(aElement);
+  for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
+    if (n->IsElement()) {
+      ClearServoDataFromSubtree(n->AsElement());
     }
   }
+
+  aElement->UnsetHasDirtyDescendantsForServo();
 }
 
 /* static */ void
-ServoRestyleManager::ClearServoDataFromSubtree(nsIContent* aContent)
+ServoRestyleManager::ClearDirtyDescendantsFromSubtree(Element* aElement)
 {
-  aContent->ClearServoData();
-  aContent->SetIsDirtyForServo();
-  aContent->UnsetHasDirtyDescendantsForServo();
+  StyleChildrenIterator it(aElement);
+  for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
+    if (n->IsElement()) {
+      ClearDirtyDescendantsFromSubtree(n->AsElement());
+    }
+  }
 
-  AllChildrenIterator it(aContent, nsIContent::eAllChildren);
-  for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-    ClearServoDataFromSubtree(n);
-  }
+  aElement->UnsetHasDirtyDescendantsForServo();
 }
 
 void
-ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
+ServoRestyleManager::RecreateStyleContexts(Element* aElement,
                                            nsStyleContext* aParentContext,
                                            ServoStyleSet* aStyleSet,
                                            nsStyleChangeList& aChangeListToProcess)
 {
-  MOZ_ASSERT(aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT));
+  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
 
-  nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
-  if (!primaryFrame && !aContent->IsDirtyForServo()) {
-    // This happens when, for example, a display: none child of a
-    // HAS_DIRTY_DESCENDANTS content is reached as part of the traversal.
-    MarkSelfAndDescendantsAsNotDirtyForServo(aContent);
-    return;
+  // FIXME(bholley): Once we transfer ownership of the styles to the frame, we
+  // can fast-reject without the FFI call by checking mServoData for null.
+  nsChangeHint changeHint = Servo_CheckChangeHint(aElement);
+  if (changeHint) {
+      aChangeListToProcess.AppendChange(primaryFrame, aElement, changeHint);
   }
 
-  // Work on text before.
-  if (!aContent->IsElement()) {
-    if (primaryFrame) {
-      RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
-      RefPtr<nsStyleContext> newContext =
-        aStyleSet->ResolveStyleForText(aContent, aParentContext);
-
-      for (nsIFrame* f = primaryFrame; f;
-           f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
-        f->SetStyleContext(newContext);
-      }
-    }
-
-    aContent->UnsetIsDirtyForServo();
+  // If our change hint is reconstruct, we delegate to the frame constructor,
+  // which consumes the new style and expects the old style to be on the frame.
+  //
+  // XXXbholley: We should teach the frame constructor how to clear the dirty
+  // descendants bit to avoid the traversal here.
+  if (changeHint & nsChangeHint_ReconstructFrame) {
+    ClearDirtyDescendantsFromSubtree(aElement);
     return;
   }
 
-  Element* element = aContent->AsElement();
-  if (element->IsDirtyForServo()) {
-    RefPtr<ServoComputedValues> computedValues =
-      Servo_ComputedValues_Get(aContent).Consume();
-    MOZ_ASSERT(computedValues);
-
-    nsChangeHint changeHint = nsChangeHint(0);
-
-    // Add an explicit change hint if appropriate.
-    ServoElementSnapshot* snapshot;
-    if (mModifiedElements.Get(element, &snapshot)) {
-      changeHint |= snapshot->ExplicitChangeHint();
-    }
-
-    // Add the stored change hint if there's a frame. If there isn't a frame,
-    // generate a ReconstructFrame change hint if the new display value
-    // (which we can get from the ComputedValues stored on the node) is not
-    // none.
-    if (primaryFrame) {
-      changeHint |= primaryFrame->StyleContext()->ConsumeStoredChangeHint();
-    } else {
-      const nsStyleDisplay* currentDisplay =
-        Servo_GetStyleDisplay(computedValues);
-      if (currentDisplay->mDisplay != StyleDisplay::None) {
-        changeHint |= nsChangeHint_ReconstructFrame;
-      }
-    }
-
-    // Add the new change hint to the list of elements to process if
-    // we need to do any work.
-    if (changeHint) {
-      aChangeListToProcess.AppendChange(primaryFrame, element, changeHint);
-    }
-
-    // The frame reconstruction step (if needed) will ask for the descendants'
-    // style correctly. If not needed, we're done too.
-    //
-    // Note that we must leave the old style on an existing frame that is
-    // about to be reframed, since some frame constructor code wants to
-    // inspect the old style to work out what to do.
-    if (changeHint & nsChangeHint_ReconstructFrame) {
-      // Since we might still have some dirty bits set on descendants,
-      // inconsistent with the clearing of HasDirtyDescendants we will do as
-      // we return from these recursive RecreateStyleContexts calls, we
-      // explicitly clear them here.  Otherwise we will trigger assertions
-      // when we soon process the frame reconstruction.
-      MarkSelfAndDescendantsAsNotDirtyForServo(element);
-      return;
-    }
-
-    // If there is no frame, and we didn't generate a ReconstructFrame change
-    // hint, then we don't need to do any more work.
-    if (!primaryFrame) {
-      aContent->UnsetIsDirtyForServo();
-      return;
-    }
+  // If we have a frame and a non-zero + non-reconstruct change hint, we need to
+  // attach a new style context.
+  bool recreateContext = primaryFrame && changeHint;
+  if (recreateContext) {
+    RefPtr<ServoComputedValues> computedValues
+      = Servo_ResolveStyle(aElement, aStyleSet->mRawSet.get(),
+                           ConsumeStyleBehavior::Consume,
+                           LazyComputeBehavior::Assert).Consume();
 
     // Hold the old style context alive, because it could become a dangling
     // pointer during the replacement. In practice it's not a huge deal (on
     // GetNextContinuationWithSameStyle the pointer is not dereferenced, only
     // compared), but better not playing with dangling pointers if not needed.
     RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
     MOZ_ASSERT(oldStyleContext);
 
@@ -214,79 +168,78 @@ ServoRestyleManager::RecreateStyleContex
     const static CSSPseudoElementType pseudosToRestyle[] = {
       CSSPseudoElementType::before,
       CSSPseudoElementType::after,
     };
 
     for (CSSPseudoElementType pseudoType : pseudosToRestyle) {
       nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
 
-      if (nsIFrame* pseudoFrame = FrameForPseudoElement(element, pseudoTag)) {
+      if (nsIFrame* pseudoFrame = FrameForPseudoElement(aElement, pseudoTag)) {
         // TODO: we could maybe make this more performant via calling into
         // Servo just once to know which pseudo-elements we've got to restyle?
         RefPtr<nsStyleContext> pseudoContext =
-          aStyleSet->ProbePseudoElementStyle(element, pseudoType, newContext);
-
-        // If pseudoContext is null here, it means the frame is going away, so
-        // our change hint computation should have already indicated we need
-        // to reframe.
-        MOZ_ASSERT_IF(!pseudoContext,
-                      changeHint & nsChangeHint_ReconstructFrame);
-        if (pseudoContext) {
-          pseudoFrame->SetStyleContext(pseudoContext);
+          aStyleSet->ProbePseudoElementStyle(aElement, pseudoType, newContext);
+        MOZ_ASSERT(pseudoContext, "should have taken the ReconstructFrame path above");
+        pseudoFrame->SetStyleContext(pseudoContext);
 
-          // We only care restyling text nodes, since other type of nodes
-          // (images), are still not supported. If that eventually changes, we
-          // may have to write more code here... Or not, I don't think too
-          // many inherited properties can affect those other frames.
-          StyleChildrenIterator it(pseudoFrame->GetContent());
-          for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-            if (n->IsNodeOfType(nsINode::eTEXT)) {
-              RefPtr<nsStyleContext> childContext =
-                aStyleSet->ResolveStyleForText(n, pseudoContext);
-              MOZ_ASSERT(n->GetPrimaryFrame(),
-                         "How? This node is created at FC time!");
-              n->GetPrimaryFrame()->SetStyleContext(childContext);
-            }
+        // We only care restyling text nodes, since other type of nodes
+        // (images), are still not supported. If that eventually changes, we
+        // may have to write more code here... Or not, I don't think too
+        // many inherited properties can affect those other frames.
+        StyleChildrenIterator it(pseudoFrame->GetContent());
+        for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
+          if (n->IsNodeOfType(nsINode::eTEXT)) {
+            RefPtr<nsStyleContext> childContext =
+              aStyleSet->ResolveStyleForText(n, pseudoContext);
+            MOZ_ASSERT(n->GetPrimaryFrame(),
+                       "How? This node is created at FC time!");
+            n->GetPrimaryFrame()->SetStyleContext(childContext);
           }
         }
       }
     }
-
-    aContent->UnsetIsDirtyForServo();
   }
 
-  if (aContent->HasDirtyDescendantsForServo()) {
-    MOZ_ASSERT(primaryFrame,
-               "Frame construction should be scheduled, and it takes the "
-               "correct style for the children, so no need to be here.");
-    StyleChildrenIterator it(aContent);
+  bool traverseElementChildren = aElement->HasDirtyDescendantsForServo();
+  bool traverseTextChildren = recreateContext;
+  if (traverseElementChildren || traverseTextChildren) {
+    StyleChildrenIterator it(aElement);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-      if (n->IsElement() || n->IsNodeOfType(nsINode::eTEXT)) {
-        RecreateStyleContexts(n, primaryFrame->StyleContext(),
+      if (traverseElementChildren && n->IsElement()) {
+        MOZ_ASSERT(primaryFrame,
+                   "Frame construction should be scheduled, and it takes the "
+                   "correct style for the children, so no need to be here.");
+        RecreateStyleContexts(n->AsElement(), primaryFrame->StyleContext(),
                               aStyleSet, aChangeListToProcess);
+      } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
+        RecreateStyleContextsForText(n, primaryFrame->StyleContext(),
+                                     aStyleSet);
       }
     }
-    aContent->UnsetHasDirtyDescendantsForServo();
   }
+
+  aElement->UnsetHasDirtyDescendantsForServo();
 }
 
-static void
-MarkChildrenAsDirtyForServo(nsIContent* aContent)
+void
+ServoRestyleManager::RecreateStyleContextsForText(nsIContent* aTextNode,
+                                                  nsStyleContext* aParentContext,
+                                                  ServoStyleSet* aStyleSet)
 {
-  StyleChildrenIterator it(aContent);
+  nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
+  if (primaryFrame) {
+    RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
+    RefPtr<nsStyleContext> newContext =
+      aStyleSet->ResolveStyleForText(aTextNode, aParentContext);
 
-  nsIContent* n = it.GetNextChild();
-  bool hadChildren = bool(n);
-  for (; n; n = it.GetNextChild()) {
-    n->SetIsDirtyForServo();
-  }
-
-  if (hadChildren) {
-    aContent->SetHasDirtyDescendantsForServo();
+    for (nsIFrame* f = primaryFrame; f;
+         f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
+      f->SetStyleContext(newContext);
+    }
   }
 }
 
 /* static */ nsIFrame*
 ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent,
                                            nsIAtom* aPseudoTagOrNull)
 {
   MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement());
@@ -310,53 +263,16 @@ ServoRestyleManager::FrameForPseudoEleme
     return nsLayoutUtils::GetAfterFrameForContent(primaryFrame, aContent);
   }
 
   MOZ_CRASH("Unkown pseudo-element given to "
             "ServoRestyleManager::FrameForPseudoElement");
   return nullptr;
 }
 
-/* static */ void
-ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
-{
-  const nsRestyleHint HANDLED_RESTYLE_HINTS = eRestyle_Self |
-                                              eRestyle_Subtree |
-                                              eRestyle_LaterSiblings |
-                                              eRestyle_SomeDescendants;
-  // NB: For Servo, at least for now, restyling and running selector-matching
-  // against the subtree is necessary as part of restyling the element, so
-  // processing eRestyle_Self will perform at least as much work as
-  // eRestyle_Subtree.
-  if (aHint & (eRestyle_Self | eRestyle_Subtree)) {
-    aElement->SetIsDirtyForServo();
-    aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
-  // NB: Servo gives us a eRestyle_SomeDescendants when it expects us to run
-  // selector matching on all the descendants. There's a bug on Servo to align
-  // meanings here (#12710) to avoid this potential source of confusion.
-  } else if (aHint & eRestyle_SomeDescendants) {
-    MarkChildrenAsDirtyForServo(aElement);
-    aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
-  }
-
-  if (aHint & eRestyle_LaterSiblings) {
-    aElement->MarkAncestorsAsHavingDirtyDescendantsForServo();
-    for (nsIContent* cur = aElement->GetNextSibling(); cur;
-         cur = cur->GetNextSibling()) {
-      cur->SetIsDirtyForServo();
-    }
-  }
-
-  // TODO: Handle all other nsRestyleHint values.
-  if (aHint & ~HANDLED_RESTYLE_HINTS) {
-    NS_WARNING(nsPrintfCString("stylo: Unhandled restyle hint %s",
-                               RestyleManagerBase::RestyleHintToString(aHint).get()).get());
-  }
-}
-
 void
 ServoRestyleManager::ProcessPendingRestyles()
 {
   MOZ_ASSERT(PresContext()->Document(), "No document?  Pshaw!");
   MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
 
   if (MOZ_UNLIKELY(!PresContext()->PresShell()->DidInitialize())) {
     // PresShell::FlushPendingNotifications doesn't early-return in the case
@@ -369,77 +285,59 @@ ServoRestyleManager::ProcessPendingResty
 
   if (!HasPendingRestyles()) {
     return;
   }
 
   ServoStyleSet* styleSet = StyleSet();
   nsIDocument* doc = PresContext()->Document();
   Element* root = doc->GetRootElement();
-  if (root) {
-    // ProcessPendingRestyles can generate new restyles (e.g. from the
-    // frame constructor if it decides that a ReconstructFrame change must
-    // apply to the parent of the element that generated that hint).  So
-    // we loop while mModifiedElements still has some restyles in it, clearing
-    // it after each RecreateStyleContexts call below.
-    while (!mModifiedElements.IsEmpty()) {
-      for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
-        ServoElementSnapshot* snapshot = iter.UserData();
-        Element* element = iter.Key();
+
+  // XXXbholley: Should this be while() per bug 1316247?
+  if (HasPendingRestyles()) {
+    MOZ_ASSERT(root);
+    mInStyleRefresh = true;
+    styleSet->StyleDocument();
 
-        // The element is no longer in the document, so don't bother computing
-        // a final restyle hint for it.
-        //
-        // XXXheycam RestyleTracker checks that the element's GetComposedDoc()
-        // matches the document we're restyling.  Do we need to do that too?
-        if (!element->IsInComposedDoc()) {
-          continue;
-        }
+    // 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();
 
-        // TODO: avoid the ComputeRestyleHint call if we already have the highest
-        // explicit restyle hint?
-        nsRestyleHint hint = styleSet->ComputeRestyleHint(element, snapshot);
-        hint |= snapshot->ExplicitRestyleHint();
-
-        if (hint) {
-          NoteRestyleHint(element, hint);
-        }
-      }
+    // Recreate style contexts and queue up change hints.
+    nsStyleChangeList currentChanges;
+    RecreateStyleContexts(root, nullptr, styleSet, currentChanges);
 
-      if (!root->IsDirtyForServo() && !root->HasDirtyDescendantsForServo()) {
-        mModifiedElements.Clear();
-        break;
+    // 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;
+    while (!currentChanges.IsEmpty()) {
+      ProcessRestyledFrames(currentChanges);
+      MOZ_ASSERT(currentChanges.IsEmpty());
+      for (ReentrantChange& change: newChanges)  {
+        currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
+                                    change.mContent, change.mHint);
       }
-
-      mInStyleRefresh = true;
-      styleSet->StyleDocument(/* aLeaveDirtyBits = */ true);
+      newChanges.Clear();
+    }
+    mReentrantChanges = nullptr;
 
-      // 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();
-
-      nsStyleChangeList changeList;
-      RecreateStyleContexts(root, nullptr, styleSet, changeList);
-
-      mModifiedElements.Clear();
-      ProcessRestyledFrames(changeList);
-
-      mInStyleRefresh = false;
-    }
+    styleSet->AssertTreeIsClean();
+    mInStyleRefresh = false;
   }
 
-  MOZ_ASSERT(!doc->IsDirtyForServo());
-  doc->UnsetHasDirtyDescendantsForServo();
-
   IncrementRestyleGeneration();
 }
 
 void
 ServoRestyleManager::RestyleForInsertOrChange(nsINode* aContainer,
                                               nsIContent* aChild)
 {
   //
@@ -449,41 +347,16 @@ ServoRestyleManager::RestyleForInsertOrC
   //
   // Bug 1297899 tracks this work.
   //
 }
 
 void
 ServoRestyleManager::ContentInserted(nsINode* aContainer, nsIContent* aChild)
 {
-  if (aContainer == aContainer->OwnerDoc()) {
-    // If we're getting this notification for the insertion of a root element,
-    // that means either:
-    //   (a) We initialized the PresShell before the root element existed, or
-    //   (b) The root element was removed and it or another root is being
-    //       inserted.
-    //
-    // Either way the whole tree is dirty, so we should style the document.
-    MOZ_ASSERT(aChild == aChild->OwnerDoc()->GetRootElement());
-    MOZ_ASSERT(aChild->IsDirtyForServo());
-    StyleSet()->StyleDocument(/* aLeaveDirtyBits = */ false);
-    return;
-  }
-
-  if (!aContainer->HasServoData()) {
-    // This can happen with display:none. Bug 1297249 tracks more investigation
-    // and assertions here.
-    return;
-  }
-
-  // Style the new subtree because we will most likely need it during subsequent
-  // frame construction. Bug 1298281 tracks deferring this work in the lazy
-  // frame construction case.
-  StyleSet()->StyleNewSubtree(aChild);
-
   RestyleForInsertOrChange(aContainer, aChild);
 }
 
 void
 ServoRestyleManager::RestyleForAppend(nsIContent* aContainer,
                                       nsIContent* aFirstNewContent)
 {
   //
@@ -494,32 +367,16 @@ ServoRestyleManager::RestyleForAppend(ns
   // Bug 1297899 tracks this work.
   //
 }
 
 void
 ServoRestyleManager::ContentAppended(nsIContent* aContainer,
                                      nsIContent* aFirstNewContent)
 {
-  if (!aContainer->HasServoData()) {
-    // This can happen with display:none. Bug 1297249 tracks more investigation
-    // and assertions here.
-    return;
-  }
-
-  // Style the new subtree because we will most likely need it during subsequent
-  // frame construction. Bug 1298281 tracks deferring this work in the lazy
-  // frame construction case.
-  if (aFirstNewContent->GetNextSibling()) {
-    aContainer->SetHasDirtyDescendantsForServo();
-    StyleSet()->StyleNewChildren(aContainer);
-  } else {
-    StyleSet()->StyleNewSubtree(aFirstNewContent);
-  }
-
   RestyleForAppend(aContainer, aFirstNewContent);
 }
 
 void
 ServoRestyleManager::ContentRemoved(nsINode* aContainer,
                                     nsIContent* aOldChild,
                                     nsIContent* aFollowingSibling)
 {
@@ -556,52 +413,51 @@ ServoRestyleManager::ContentStateChanged
   // PostRestyleEvent.
   //
   // If we definitely take the snapshot approach, we should take rid of
   // HasStateDependentStyle, etc (though right now they're no-ops).
   ContentStateChangedInternal(aElement, aChangedBits, &changeHint,
                               &restyleHint);
 
   EventStates previousState = aElement->StyleState() ^ aChangedBits;
-  ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
-  snapshot->AddState(previousState);
+  ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement);
+  if (snapshot) {
+    snapshot->AddState(previousState);
+    PostRestyleEvent(aElement, restyleHint, changeHint);
+  }
 
-  PostRestyleEvent(aElement, restyleHint, changeHint);
   return NS_OK;
 }
 
 void
 ServoRestyleManager::AttributeWillChange(Element* aElement,
                                          int32_t aNameSpaceID,
                                          nsIAtom* aAttribute, int32_t aModType,
                                          const nsAttrValue* aNewValue)
 {
-  ServoElementSnapshot* snapshot = SnapshotForElement(aElement);
-  snapshot->AddAttrs(aElement);
+  ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement);
+  if (snapshot) {
+    snapshot->AddAttrs(aElement);
+  }
 }
 
 void
 ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                       nsIAtom* aAttribute, int32_t aModType,
                                       const nsAttrValue* aOldValue)
 {
-  MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs());
+#ifdef DEBUG
+  ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement);
+  MOZ_ASSERT_IF(snapshot, snapshot->HasAttrs());
+#endif
   if (aAttribute == nsGkAtoms::style) {
     PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0));
   }
 }
 
 nsresult
 ServoRestyleManager::ReparentStyleContext(nsIFrame* aFrame)
 {
   NS_WARNING("stylo: ServoRestyleManager::ReparentStyleContext not implemented");
   return NS_OK;
 }
 
-ServoElementSnapshot*
-ServoRestyleManager::SnapshotForElement(Element* aElement)
-{
-  // NB: aElement is the argument for the construction of the snapshot in the
-  // not found case.
-  return mModifiedElements.LookupOrAdd(aElement, aElement);
-}
-
 } // namespace mozilla
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.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_ServoRestyleManager_h
 #define mozilla_ServoRestyleManager_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"
 #include "nsPresContext.h"
 
 namespace mozilla {
@@ -74,67 +75,76 @@ public:
   void AttributeChanged(dom::Element* aElement, int32_t aNameSpaceID,
                         nsIAtom* aAttribute, int32_t aModType,
                         const nsAttrValue* aOldValue);
 
   nsresult ReparentStyleContext(nsIFrame* aFrame);
 
   bool HasPendingRestyles()
   {
-    return !mModifiedElements.IsEmpty() ||
-           PresContext()->Document()->HasDirtyDescendantsForServo();
+    Element* root = PresContext()->Document()->GetRootElement();
+    return root && root->HasDirtyDescendantsForServo();
   }
 
 
   /**
    * 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.
    */
   static nsIFrame* FrameForPseudoElement(const nsIContent* aContent,
                                          nsIAtom* aPseudoTagOrNull);
 
   /**
-   * Clears the ServoNodeData from all content nodes in the subtree rooted
-   * at aContent, and sets the IsDirtyForServo bit and clears the
-   * HasDirtyDescendantsForServo bit on them too.
+   * Clears the ServoElementData and HasDirtyDescendants from all elements
+   * in the subtree rooted at aElement.
    */
-  static void ClearServoDataFromSubtree(nsIContent* aContent);
+  static void ClearServoDataFromSubtree(Element* aElement);
+
+  /**
+   * Clears HasDirtyDescendants from all elements in the subtree rooted at
+   * aElement.
+   */
+  static void ClearDirtyDescendantsFromSubtree(Element* aElement);
 
 protected:
-  ~ServoRestyleManager() {}
+  ~ServoRestyleManager() { MOZ_ASSERT(!mReentrantChanges); }
 
 private:
-  ServoElementSnapshot* SnapshotForElement(Element* aElement);
-
-  /**
-   * The element-to-element snapshot table to compute restyle hints.
-   */
-  nsClassHashtable<nsRefPtrHashKey<Element>, ServoElementSnapshot>
-    mModifiedElements;
-
   /**
    * Traverses a tree of content that Servo has just restyled, recreating style
    * contexts for their frames with the new style data.
    */
-  void RecreateStyleContexts(nsIContent* aContent,
+  void RecreateStyleContexts(Element* aElement,
                              nsStyleContext* aParentContext,
                              ServoStyleSet* aStyleSet,
                              nsStyleChangeList& aChangeList);
 
-  /**
-   * Marks the tree with the appropriate dirty flags for the given restyle hint.
-   */
-  static void NoteRestyleHint(Element* aElement, nsRestyleHint aRestyleHint);
+  void RecreateStyleContextsForText(nsIContent* aTextNode,
+                                    nsStyleContext* aParentContext,
+                                    ServoStyleSet* aStyleSet);
 
   inline ServoStyleSet* StyleSet() const
   {
     MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
                "ServoRestyleManager should only be used with a Servo-flavored "
                "style backend");
     return PresContext()->StyleSet()->AsServo();
   }
+
+  // We use a separate data structure from nsStyleChangeList because we need a
+  // frame to create nsStyleChangeList entries, and the primary frame may not be
+  // attached yet.
+  struct ReentrantChange {
+    nsCOMPtr<nsIContent> mContent;
+    nsChangeHint mHint;
+  };
+  typedef AutoTArray<ReentrantChange, 10> ReentrantChangeList;
+
+  // Only non-null while processing change hints. See the comment in
+  // ProcessPendingRestyles.
+  ReentrantChangeList* mReentrantChanges;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ServoRestyleManager_h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2424,17 +2424,19 @@ nsCSSFrameConstructor::ConstructDocEleme
   aDocElement->UnsetRestyleFlagsIfGecko();
 
   // --------- CREATE AREA OR BOX FRAME -------
   // FIXME: Should this use ResolveStyleContext?  (The calls in this
   // function are the only case in nsCSSFrameConstructor where we don't
   // do so for the construction of a style context for an element.)
   RefPtr<nsStyleContext> styleContext;
   styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
-                                                         nullptr);
+                                                         nullptr,
+                                                         ConsumeStyleBehavior::Consume,
+                                                         LazyComputeBehavior::Allow);
 
   const nsStyleDisplay* display = styleContext->StyleDisplay();
 
   // Ensure that our XBL bindings are installed.
   if (display->mBinding) {
     // Get the XBL loader.
     nsresult rv;
     bool resolveStyle;
@@ -2459,21 +2461,29 @@ nsCSSFrameConstructor::ConstructDocEleme
     }
 
     if (resolveStyle) {
       // FIXME: Should this use ResolveStyleContext?  (The calls in this
       // function are the only case in nsCSSFrameConstructor where we
       // don't do so for the construction of a style context for an
       // element.)
       styleContext = mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
-                                                             nullptr);
+                                                             nullptr,
+                                                             ConsumeStyleBehavior::Consume,
+                                                             LazyComputeBehavior::Allow);
       display = styleContext->StyleDisplay();
     }
   }
 
+  // We delay traversing the entire document until here, since we per above we
+  // may invalidate the root style when we load doc stylesheets.
+  if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) {
+    set->StyleDocument();
+  }
+
   // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
 
   NS_ASSERTION(!display->IsScrollableOverflow() ||
                state.mPresContext->IsPaginated() ||
                propagatedScrollFrom == aDocElement,
                "Scrollbars should have been propagated to the viewport");
 
   if (MOZ_UNLIKELY(display->mDisplay == StyleDisplay::None)) {
@@ -5017,20 +5027,24 @@ nsCSSFrameConstructor::ResolveStyleConte
   StyleSetHandle styleSet = mPresShell->StyleSet();
   aContent->OwnerDoc()->FlushPendingLinkUpdates();
 
   RefPtr<nsStyleContext> result;
   if (aContent->IsElement()) {
     if (aState) {
       result = styleSet->ResolveStyleFor(aContent->AsElement(),
                                          aParentStyleContext,
+                                         ConsumeStyleBehavior::Consume,
+                                         LazyComputeBehavior::Assert,
                                          aState->mTreeMatchContext);
     } else {
       result = styleSet->ResolveStyleFor(aContent->AsElement(),
-                                         aParentStyleContext);
+                                         aParentStyleContext,
+                                         ConsumeStyleBehavior::Consume,
+                                         LazyComputeBehavior::Assert);
     }
   } else {
     NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
                  "shouldn't waste time creating style contexts for "
                  "comments and processing instructions");
     result = styleSet->ResolveStyleForText(aContent, aParentStyleContext);
   }
 
@@ -5708,16 +5722,21 @@ nsCSSFrameConstructor::AddFrameConstruct
       return;
 
     if (newPendingBinding->mBinding) {
       pendingBinding = newPendingBinding;
       // aState takes over owning newPendingBinding
       aState.AddPendingBinding(newPendingBinding.forget());
     }
 
+    if (aContent->IsStyledByServo()) {
+      NS_WARNING("stylo: Skipping Unsupported binding re-resolve. This needs fixing.");
+      resolveStyle = false;
+    }
+
     if (resolveStyle) {
       styleContext =
         ResolveStyleContext(styleContext->GetParent(), aContent, &aState);
       display = styleContext->StyleDisplay();
       aStyleContext = styleContext;
     }
 
     aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID);
@@ -7337,19 +7356,38 @@ nsCSSFrameConstructor::ContentAppended(n
   // See comment in ContentRangeInserted for why this is necessary.
   if (!GetContentInsertionFrameFor(aContainer) &&
       !aContainer->IsActiveChildrenElement()) {
     return NS_OK;
   }
 
   if (aAllowLazyConstruction &&
       MaybeConstructLazily(CONTENTAPPEND, aContainer, aFirstNewContent)) {
+    if (aContainer->IsStyledByServo()) {
+      aContainer->AsElement()->NoteDirtyDescendantsForServo();
+    }
     return NS_OK;
   }
 
+  // We couldn't construct lazily. Make Servo eagerly traverse the subtree.
+  if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) {
+    // We use the same codepaths to handle both of the following cases:
+    //   (a) Newly-appended content for which lazy frame construction is disallowed.
+    //   (b) Lazy frame construction driven by the restyle manager.
+    // We need the styles for (a). In the case of (b), the Servo traversal has
+    // already happened, so we don't need to do it again.
+    if (!RestyleManager()->AsBase()->IsInStyleRefresh()) {
+      if (aFirstNewContent->GetNextSibling()) {
+        set->StyleNewChildren(aContainer);
+      } else {
+        set->StyleNewSubtree(aFirstNewContent);
+      }
+    }
+  }
+
   LAYOUT_PHASE_TEMP_EXIT();
   InsertionPoint insertion =
     GetRangeInsertionPoint(aContainer, aFirstNewContent, nullptr,
                            aAllowLazyConstruction);
   nsContainerFrame*& parentFrame = insertion.mParentFrame;
   LAYOUT_PHASE_TEMP_REENTER();
   if (!parentFrame) {
     return NS_OK;
@@ -7782,20 +7820,35 @@ nsCSSFrameConstructor::ContentRangeInser
     }
 
     // Otherwise, we've got parent content. Find its frame.
     NS_ASSERTION(!parentFrame || parentFrame->GetContent() == aContainer ||
                  GetDisplayContentsStyleFor(aContainer), "New XBL code is possibly wrong!");
 
     if (aAllowLazyConstruction &&
         MaybeConstructLazily(CONTENTINSERT, aContainer, aStartChild)) {
+      if (aContainer->IsStyledByServo()) {
+        aContainer->AsElement()->NoteDirtyDescendantsForServo();
+      }
       return NS_OK;
     }
   }
 
+  // We couldn't construct lazily. Make Servo eagerly traverse the subtree.
+  if (ServoStyleSet* set = mPresShell->StyleSet()->GetAsServo()) {
+    // We use the same codepaths to handle both of the following cases:
+    //   (a) Newly-appended content for which lazy frame construction is disallowed.
+    //   (b) Lazy frame construction driven by the restyle manager.
+    // We need the styles for (a). In the case of (b), the Servo traversal has
+    // already happened, so we don't need to do it again.
+    if (!RestyleManager()->AsBase()->IsInStyleRefresh()) {
+      set->StyleNewSubtree(aStartChild);
+    }
+  }
+
   InsertionPoint insertion;
   if (isSingleInsert) {
     // See if we have an XBL insertion point. If so, then that's our
     // real parent frame; if not, then the frame hasn't been built yet
     // and we just bail.
     insertion = GetInsertionPoint(aContainer, aStartChild);
   } else {
     // Get our insertion point. If we need to issue single ContentInserted's
@@ -9250,17 +9303,19 @@ nsCSSFrameConstructor::MaybeRecreateFram
     if (!oldContext) {
       return nullptr;
     }
     oldDisplay = StyleDisplay::Contents;
   }
 
   // The parent has a frame, so try resolving a new context.
   RefPtr<nsStyleContext> newContext = mPresShell->StyleSet()->
-    ResolveStyleFor(aElement, oldContext->GetParent());
+    ResolveStyleFor(aElement, oldContext->GetParent(),
+                    ConsumeStyleBehavior::Consume,
+                    LazyComputeBehavior::Assert);
 
   if (oldDisplay == StyleDisplay::None) {
     ChangeUndisplayedContent(aElement, newContext);
   } else {
     ChangeDisplayContents(aElement, newContext);
   }
 
   const nsStyleDisplay* disp = newContext->StyleDisplay();
@@ -10622,24 +10677,25 @@ nsCSSFrameConstructor::AddFCItemsForAnon
       parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext);
     if (aAnonymousItems[i].mStyleContext) {
       // If we have an explicit style context, that means that the anonymous
       // content creator had its own plan for the style, and doesn't need the
       // computed style obtained by cascading this content as a normal node.
       // This happens when a native anonymous node is used to implement a
       // pseudo-element. Allowing Servo to traverse these nodes would be wasted
       // work, so assert that we didn't do that.
-      MOZ_ASSERT_IF(content->IsStyledByServo(), !content->HasServoData());
+      MOZ_ASSERT_IF(content->IsStyledByServo(),
+                    !content->IsElement() || !content->AsElement()->HasServoData());
       styleContext = aAnonymousItems[i].mStyleContext.forget();
     } else {
       // If we don't have an explicit style context, that means we need the
       // ordinary computed values. Make sure we eagerly cascaded them when the
       // anonymous nodes were created.
       MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(),
-                    content->HasServoData());
+                    content->AsElement()->HasServoData());
       styleContext = ResolveStyleContext(aFrame, content, &aState);
     }
 
     nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
     if (!aAnonymousItems[i].mChildren.IsEmpty()) {
       anonChildren = &aAnonymousItems[i].mChildren;
     }
 
--- a/layout/base/nsChangeHint.h
+++ b/layout/base/nsChangeHint.h
@@ -12,16 +12,18 @@
 #include "nsDebug.h"
 #include "nsTArray.h"
 
 struct nsCSSSelector;
 
 // Defines for various style related constants
 
 enum nsChangeHint {
+  nsChangeHint_Empty = 0,
+
   // change was visual only (e.g., COLOR=)
   // Invalidates all descendant frames (including following
   // placeholders to out-of-flow frames).
   nsChangeHint_RepaintFrame = 1 << 0,
 
   // For reflow, we want flags to give us arbitrary FrameNeedsReflow behavior.
   // just do a FrameNeedsReflow.
   nsChangeHint_NeedReflow = 1 << 1,
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et 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/. */
 
 /* container for a document and its presentation */
 
+#include "mozilla/ServoRestyleManager.h"
 #include "mozilla/ServoStyleSet.h"
 #include "nsAutoPtr.h"
 #include "nscore.h"
 #include "nsCOMPtr.h"
 #include "nsCRT.h"
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsIContent.h"
@@ -4464,16 +4465,25 @@ NS_IMETHODIMP nsDocumentViewer::SetPageM
 
   if (mPresContext) {
     DestroyPresContext();
   }
 
   mViewManager  = nullptr;
   mWindow       = nullptr;
 
+  // We're creating a new presentation context for an existing document.
+  // Drop any associated Servo data.
+#ifdef MOZ_STYLO
+  Element* root = mDocument->GetRootElement();
+  if (root && root->IsStyledByServo()) {
+    ServoRestyleManager::ClearServoDataFromSubtree(root);
+  }
+#endif
+
   NS_ENSURE_STATE(mDocument);
   if (aPageMode)
   {    
     mPresContext = CreatePresContext(mDocument,
         nsPresContext::eContext_PageLayout, FindContainerView());
     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
     mPresContext->SetPaginatedScrolling(true);
     mPresContext->SetPrintSettings(aPrintSettings);
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1379,17 +1379,19 @@ GetPropagatedScrollbarStylesForViewport(
   // docElement might be null if we're doing this after removing it.
   if (!docElement) {
     return nullptr;
   }
 
   // Check the style on the document root element
   StyleSetHandle styleSet = aPresContext->StyleSet();
   RefPtr<nsStyleContext> rootStyle;
-  rootStyle = styleSet->ResolveStyleFor(docElement, nullptr);
+  rootStyle = styleSet->ResolveStyleFor(docElement, nullptr,
+                                        ConsumeStyleBehavior::DontConsume,
+                                        LazyComputeBehavior::Allow);
   if (CheckOverflow(rootStyle->StyleDisplay(), aStyles)) {
     // tell caller we stole the overflow style from the root element
     return docElement;
   }
 
   // Don't look in the BODY for non-HTML documents or HTML documents
   // with non-HTML roots
   // XXX this should be earlier; we shouldn't even look at the document root
@@ -1407,17 +1409,19 @@ GetPropagatedScrollbarStylesForViewport(
 
   if (!bodyElement ||
       !bodyElement->NodeInfo()->Equals(nsGkAtoms::body)) {
     // The body is not a <body> tag, it's a <frameset>.
     return nullptr;
   }
 
   RefPtr<nsStyleContext> bodyStyle;
-  bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle);
+  bodyStyle = styleSet->ResolveStyleFor(bodyElement->AsElement(), rootStyle,
+                                        ConsumeStyleBehavior::DontConsume,
+                                        LazyComputeBehavior::Allow);
 
   if (CheckOverflow(bodyStyle->StyleDisplay(), aStyles)) {
     // tell caller we stole the overflow style from the body element
     return bodyElement;
   }
 
   return nullptr;
 }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1687,27 +1687,16 @@ PresShell::Initialize(nscoord aWidth, ns
   }
 #endif
 
   // XXX Do a full invalidate at the beginning so that invalidates along
   // the way don't have region accumulation issues?
 
   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
 
-  if (mStyleSet->IsServo() && mDocument->GetRootElement()) {
-    // If we have the root element already, go ahead style it along with any
-    // descendants.
-    //
-    // Some things, like nsDocumentViewer::GetPageMode, recreate the PresShell
-    // while keeping the content tree alive (see bug 1292280) - so we
-    // unconditionally mark the root as dirty.
-    mDocument->GetRootElement()->SetIsDirtyForServo();
-    mStyleSet->AsServo()->StyleDocument(/* aLeaveDirtyBits = */ false);
-  }
-
   // Get the root frame from the frame manager
   // XXXbz it would be nice to move this somewhere else... like frame manager
   // Init(), say.  But we need to make sure our views are all set up by the
   // time we do this!
   nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
   NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
   if (!rootFrame) {
     nsAutoScriptBlocker scriptBlocker;
--- a/layout/generic/nsFrameSetFrame.cpp
+++ b/layout/generic/nsFrameSetFrame.cpp
@@ -297,17 +297,19 @@ nsHTMLFramesetFrame::Init(nsIContent*   
     // nsCSSFrameConstructor::ContentAppended/Inserted/Removed
     if (!child->IsHTMLElement())
       continue;
 
     if (child->IsAnyOfHTMLElements(nsGkAtoms::frameset, nsGkAtoms::frame)) {
       RefPtr<nsStyleContext> kidSC;
 
       kidSC = shell->StyleSet()->ResolveStyleFor(child->AsElement(),
-                                                 mStyleContext);
+                                                 mStyleContext,
+                                                 ConsumeStyleBehavior::DontConsume,
+                                                 LazyComputeBehavior::Allow);
       if (child->IsHTMLElement(nsGkAtoms::frameset)) {
         frame = NS_NewHTMLFramesetFrame(shell, kidSC);
 
         nsHTMLFramesetFrame* childFrame = (nsHTMLFramesetFrame*)frame;
         childFrame->SetParentFrameborder(frameborder);
         childFrame->SetParentBorderWidth(borderWidth);
         childFrame->SetParentBorderColor(borderColor);
         frame->Init(child, this, nullptr);
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -2053,17 +2053,19 @@ nsImageFrame::GetCursor(const nsPoint& a
     if (area) {
       // Use the cursor from the style of the *area* element.
       // XXX Using the image as the parent style context isn't
       // technically correct, but it's probably the right thing to do
       // here, since it means that areas on which the cursor isn't
       // specified will inherit the style from the image.
       RefPtr<nsStyleContext> areaStyle = 
         PresContext()->PresShell()->StyleSet()->
-          ResolveStyleFor(area->AsElement(), StyleContext());
+          ResolveStyleFor(area->AsElement(), StyleContext(),
+                          ConsumeStyleBehavior::DontConsume,
+                          LazyComputeBehavior::Allow);
       FillCursorInformationFromStyle(areaStyle->StyleUserInterface(),
                                      aCursor);
       if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) {
         aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
       }
       return NS_OK;
     }
   }
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -13,18 +13,18 @@
  * - 'return_' the return type of the binding function
  * and the parameter list of the function.
  *
  * Users of this list should define a macro
  * SERVO_BINDING_FUNC(name_, return_, ...)
  * before including this file.
  */
 
-// Node data
-SERVO_BINDING_FUNC(Servo_Node_ClearNodeData, void, RawGeckoNodeBorrowed node)
+// Element data
+SERVO_BINDING_FUNC(Servo_Element_ClearData, void, 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,
@@ -128,18 +128,16 @@ SERVO_BINDING_FUNC(Servo_DeclarationBloc
                    RawServoDeclarationBlockBorrowed declarations,
                    nsIAtom* property, bool is_custom)
 
 // CSS supports()
 SERVO_BINDING_FUNC(Servo_CSSSupports, bool,
                    const nsACString* name, const nsACString* value)
 
 // Computed style data
-SERVO_BINDING_FUNC(Servo_ComputedValues_Get, ServoComputedValuesStrong,
-                   RawGeckoNodeBorrowed node)
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetForAnonymousBox,
                    ServoComputedValuesStrong,
                    ServoComputedValuesBorrowedOrNull parent_style_or_null,
                    nsIAtom* pseudoTag, RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_ComputedValues_GetForPseudoElement,
                    ServoComputedValuesStrong,
                    ServoComputedValuesBorrowed parent_style,
                    RawGeckoElementBorrowed match_element, nsIAtom* pseudo_tag,
@@ -151,24 +149,36 @@ SERVO_BINDING_FUNC(Servo_ComputedValues_
 SERVO_BINDING_FUNC(Servo_ComputedValues_Release, void,
                    ServoComputedValuesBorrowed computed_values)
 
 // Initialize Servo components. Should be called exactly once at startup.
 SERVO_BINDING_FUNC(Servo_Initialize, void)
 // Shut down Servo components. Should be called exactly once at shutdown.
 SERVO_BINDING_FUNC(Servo_Shutdown, void)
 
-// Restyle hints
-SERVO_BINDING_FUNC(Servo_ComputeRestyleHint, nsRestyleHint,
-                   RawGeckoElementBorrowed element, ServoElementSnapshot* snapshot,
-                   RawServoStyleSetBorrowed set)
+// Gets the snapshot for the element. This will return null if the element
+// has never been styled, since snapshotting in that case is wasted work.
+SERVO_BINDING_FUNC(Servo_Element_GetSnapshot, ServoElementSnapshot*,
+                   RawGeckoElementBorrowed element)
+
+// Restyle and change hints.
+SERVO_BINDING_FUNC(Servo_NoteExplicitHints, void, RawGeckoElementBorrowed element,
+                   nsRestyleHint restyle_hint, nsChangeHint change_hint)
+SERVO_BINDING_FUNC(Servo_CheckChangeHint, nsChangeHint, RawGeckoElementBorrowed element)
+SERVO_BINDING_FUNC(Servo_ResolveStyle, ServoComputedValuesStrong,
+                   RawGeckoElementBorrowed element, RawServoStyleSetBorrowed set,
+                   mozilla::ConsumeStyleBehavior consume, mozilla::LazyComputeBehavior compute)
 
 // Restyle the given subtree.
-SERVO_BINDING_FUNC(Servo_RestyleSubtree, void,
-                   RawGeckoNodeBorrowed node, RawServoStyleSetBorrowed set)
+SERVO_BINDING_FUNC(Servo_TraverseSubtree, void,
+                   RawGeckoElementBorrowed root, RawServoStyleSetBorrowed set,
+                   mozilla::SkipRootBehavior skip_root)
+
+// Assert that the tree has no pending or unconsumed restyles.
+SERVO_BINDING_FUNC(Servo_AssertTreeIsClean, void, RawGeckoElementBorrowed root)
 
 // Style-struct management.
 #define STYLE_STRUCT(name, checkdata_cb)                            \
   struct nsStyle##name;                                             \
   SERVO_BINDING_FUNC(Servo_GetStyle##name, const nsStyle##name*,  \
                      ServoComputedValuesBorrowedOrNull computed_values)
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
--- a/layout/style/ServoBindingTypes.h
+++ b/layout/style/ServoBindingTypes.h
@@ -13,27 +13,29 @@
 struct ServoComputedValues;
 struct ServoCssRules;
 struct RawServoStyleSheet;
 struct RawServoStyleSet;
 struct RawServoDeclarationBlock;
 struct RawServoStyleRule;
 
 namespace mozilla {
+  class ServoElementSnapshot;
 namespace dom {
 class Element;
 class StyleChildrenIterator;
 } // namespace dom
 } // namespace mozilla
 
 class nsCSSValue;
 class nsIDocument;
 class nsINode;
 
 using mozilla::dom::StyleChildrenIterator;
+using mozilla::ServoElementSnapshot;
 
 typedef nsINode RawGeckoNode;
 typedef mozilla::dom::Element RawGeckoElement;
 typedef nsIDocument RawGeckoDocument;
 
 // We have these helper types so that we can directly generate
 // things like &T or Borrowed<T> on the Rust side in the function, providing
 // additional safety benefits.
@@ -80,29 +82,31 @@ DECL_ARC_REF_TYPE_FOR(RawServoDeclaratio
 DECL_ARC_REF_TYPE_FOR(RawServoStyleRule)
 // This is a reference to a reference of RawServoDeclarationBlock, which
 // corresponds to Option<&Arc<RawServoDeclarationBlock>> in Servo side.
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawServoDeclarationBlockStrong)
 
 DECL_OWNED_REF_TYPE_FOR(RawServoStyleSet)
 DECL_NULLABLE_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
 DECL_OWNED_REF_TYPE_FOR(StyleChildrenIterator)
+DECL_OWNED_REF_TYPE_FOR(ServoElementSnapshot)
 
 // We don't use BorrowedMut because the nodes may alias
 // Servo itself doesn't directly read or mutate these;
 // it only asks Gecko to do so. In case we wish to in
 // the future, we should ensure that things being mutated
 // are protected from noalias violations by a cell type
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoNode)
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoNode)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoElement)
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoElement)
 DECL_BORROWED_REF_TYPE_FOR(RawGeckoDocument)
 DECL_NULLABLE_BORROWED_REF_TYPE_FOR(RawGeckoDocument)
 DECL_BORROWED_MUT_REF_TYPE_FOR(StyleChildrenIterator)
+DECL_BORROWED_MUT_REF_TYPE_FOR(ServoElementSnapshot)
 DECL_BORROWED_REF_TYPE_FOR(nsCSSValue)
 DECL_BORROWED_MUT_REF_TYPE_FOR(nsCSSValue)
 
 #undef DECL_ARC_REF_TYPE_FOR
 #undef DECL_OWNED_REF_TYPE_FOR
 #undef DECL_NULLABLE_OWNED_REF_TYPE_FOR
 #undef DECL_BORROWED_REF_TYPE_FOR
 #undef DECL_NULLABLE_BORROWED_REF_TYPE_FOR
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -92,18 +92,17 @@ RawGeckoNodeBorrowedOrNull
 Gecko_GetNextSibling(RawGeckoNodeBorrowed aNode)
 {
   return aNode->GetNextSibling();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetParentElement(RawGeckoElementBorrowed aElement)
 {
-  nsINode* parentNode = aElement->GetFlattenedTreeParentNode();
-  return parentNode->IsElement() ? parentNode->AsElement() : nullptr;
+  return aElement->GetFlattenedTreeParentElement();
 }
 
 RawGeckoElementBorrowedOrNull
 Gecko_GetFirstChildElement(RawGeckoElementBorrowed aElement)
 {
   return aElement->GetFirstElementChild();
 }
 
@@ -270,55 +269,28 @@ Gecko_CalcStyleDifference(nsStyleContext
     aOldStyleContext->CalcStyleDifference(aComputedValues,
                                           forDescendants,
                                           &equalStructs,
                                           &samePointerStructs);
 
   return result;
 }
 
-void
-Gecko_StoreStyleDifference(RawGeckoNodeBorrowed aNode, nsChangeHint aChangeHintToStore)
+ServoElementSnapshotOwned
+Gecko_CreateElementSnapshot(RawGeckoElementBorrowed aElement)
 {
-#ifdef MOZ_STYLO
-  MOZ_ASSERT(aNode->IsElement());
-  MOZ_ASSERT(aNode->IsDirtyForServo(),
-             "Change hint stored in a not-dirty node");
+  return new ServoElementSnapshot(aElement);
+}
 
-  const Element* aElement = aNode->AsElement();
-  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
-  if (!primaryFrame) {
-    // If there's no primary frame, that means that either this content is
-    // undisplayed (so we only need to check at the restyling phase for the
-    // display value on the element), or is a display: contents element.
-    //
-    // In this second case, we should store it in the frame constructor display
-    // contents map. Note that while this operation looks hairy, this would be
-    // thread-safe because the content should be there already (we'd only need
-    // to read the map and modify our entry).
-    //
-    // That being said, we still don't support display: contents anyway, so it's
-    // probably not worth it to do all the roundtrip just yet until we have a
-    // more concrete plan.
-    return;
-  }
-
-  if ((aChangeHintToStore & nsChangeHint_ReconstructFrame) &&
-      aNode->IsInNativeAnonymousSubtree())
-  {
-    NS_WARNING("stylo: Removing forbidden frame reconstruction hint on native "
-               "anonymous content. Fix this in bug 1297857!");
-    aChangeHintToStore &= ~nsChangeHint_ReconstructFrame;
-  }
-
-  primaryFrame->StyleContext()->StoreChangeHint(aChangeHintToStore);
-#else
-  MOZ_CRASH("stylo: Shouldn't call Gecko_StoreStyleDifference in "
-            "non-stylo build");
-#endif
+void
+Gecko_DropElementSnapshot(ServoElementSnapshotOwned aSnapshot)
+{
+  MOZ_ASSERT(NS_IsMainThread(),
+             "ServoAttrSnapshots can only be dropped on the main thread");
+  delete aSnapshot;
 }
 
 RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed aElement)
 {
   const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
   if (!attr || attr->Type() != nsAttrValue::eCSSDeclaration) {
     return nullptr;
@@ -576,17 +548,17 @@ ClassOrClassList(Implementor* aElement, 
   }                                                                            \
   uint32_t prefix_##ClassOrClassList(implementor_ aElement, nsIAtom** aClass,  \
                                      nsIAtom*** aClassList)                    \
   {                                                                            \
     return ClassOrClassList(aElement, aClass, aClassList);                     \
   }
 
 SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
-SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, ServoElementSnapshot*)
+SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot, const ServoElementSnapshot*)
 
 #undef SERVO_IMPL_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 nsIAtom*
 Gecko_Atomize(const char* aString, uint32_t aLength)
 {
   return NS_Atomize(nsDependentCSubstring(aString, aLength)).take();
 }
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -133,17 +133,17 @@ nsIAtom* Gecko_GetElementId(RawGeckoElem
                               nsIAtom* name, nsIAtom* str);                   \
   bool prefix_##AttrHasSuffix(implementor_ element, nsIAtom* ns,              \
                               nsIAtom* name, nsIAtom* str);                   \
   uint32_t prefix_##ClassOrClassList(implementor_ element, nsIAtom** class_,  \
                                      nsIAtom*** classList);
 
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_, RawGeckoElementBorrowed)
 SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS(Gecko_Snapshot,
-                                              ServoElementSnapshot*)
+                                              const ServoElementSnapshot*)
 
 #undef SERVO_DECLARE_ELEMENT_ATTR_MATCHING_FUNCTIONS
 
 // Style attributes.
 RawServoDeclarationBlockStrongBorrowedOrNull
 Gecko_GetServoDeclarationBlock(RawGeckoElementBorrowed element);
 
 // Atoms.
@@ -215,27 +215,26 @@ void Gecko_SetMozBinding(nsStyleDisplay*
 void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
 
 // Dirtiness tracking.
 uint32_t Gecko_GetNodeFlags(RawGeckoNodeBorrowed node);
 void Gecko_SetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNodeBorrowed node, uint32_t flags);
 
 // Incremental restyle.
-// TODO: We would avoid a few ffi calls if we decide to make an API like the
-// former CalcAndStoreStyleDifference, but that would effectively mean breaking
-// some safety guarantees in the servo side.
-//
 // Also, we might want a ComputedValues to ComputedValues API for animations?
 // Not if we do them in Gecko...
 nsStyleContext* Gecko_GetStyleContext(RawGeckoNodeBorrowed node,
                                       nsIAtom* aPseudoTagOrNull);
 nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
                                        ServoComputedValuesBorrowed newstyle);
-void Gecko_StoreStyleDifference(RawGeckoNodeBorrowed node, nsChangeHint change);
+
+// Element snapshot.
+ServoElementSnapshotOwned Gecko_CreateElementSnapshot(RawGeckoElementBorrowed element);
+void Gecko_DropElementSnapshot(ServoElementSnapshotOwned snapshot);
 
 // `array` must be an nsTArray
 // If changing this signature, please update the
 // friend function declaration in nsTArray.h
 void Gecko_EnsureTArrayCapacity(void* array, size_t capacity, size_t elem_size);
 
 // Same here, `array` must be an nsTArray<T>, for some T.
 //
--- a/layout/style/ServoElementSnapshot.cpp
+++ b/layout/style/ServoElementSnapshot.cpp
@@ -6,28 +6,32 @@
 
 #include "mozilla/ServoElementSnapshot.h"
 #include "mozilla/dom/Element.h"
 #include "nsIContentInlines.h"
 #include "nsContentUtils.h"
 
 namespace mozilla {
 
-ServoElementSnapshot::ServoElementSnapshot(Element* aElement)
+ServoElementSnapshot::ServoElementSnapshot(const Element* aElement)
   : mContains(Flags(0))
   , mState(0)
-  , mExplicitRestyleHint(nsRestyleHint(0))
-  , mExplicitChangeHint(nsChangeHint(0))
 {
+  MOZ_COUNT_CTOR(ServoElementSnapshot);
   mIsHTMLElementInHTMLDocument =
     aElement->IsHTMLElement() && aElement->IsInHTMLDocument();
   mIsInChromeDocument =
     nsContentUtils::IsChromeDoc(aElement->OwnerDoc());
 }
 
+ServoElementSnapshot::~ServoElementSnapshot()
+{
+  MOZ_COUNT_DTOR(ServoElementSnapshot);
+}
+
 void
 ServoElementSnapshot::AddAttrs(Element* aElement)
 {
   MOZ_ASSERT(aElement);
 
   if (HasAny(Flags::Attributes)) {
     return;
   }
--- a/layout/style/ServoElementSnapshot.h
+++ b/layout/style/ServoElementSnapshot.h
@@ -62,17 +62,18 @@ class ServoElementSnapshot
 {
   typedef dom::BorrowedAttrInfo BorrowedAttrInfo;
   typedef dom::Element Element;
   typedef EventStates::ServoType ServoStateType;
 
 public:
   typedef ServoElementSnapshotFlags Flags;
 
-  explicit ServoElementSnapshot(Element* aElement);
+  explicit ServoElementSnapshot(const Element* aElement);
+  ~ServoElementSnapshot();
 
   bool HasAttrs() { return HasAny(Flags::Attributes); }
 
   bool HasState() { return HasAny(Flags::State); }
 
   /**
    * Captures the given state (if not previously captured).
    */
@@ -84,30 +85,16 @@ public:
     }
   }
 
   /**
    * Captures the given element attributes (if not previously captured).
    */
   void AddAttrs(Element* aElement);
 
-  void AddExplicitChangeHint(nsChangeHint aMinChangeHint)
-  {
-    mExplicitChangeHint |= aMinChangeHint;
-  }
-
-  void AddExplicitRestyleHint(nsRestyleHint aRestyleHint)
-  {
-    mExplicitRestyleHint |= aRestyleHint;
-  }
-
-  nsRestyleHint ExplicitRestyleHint() { return mExplicitRestyleHint; }
-
-  nsChangeHint ExplicitChangeHint() { return mExplicitChangeHint; }
-
   /**
    * Needed methods for attribute matching.
    */
   BorrowedAttrInfo GetAttrInfoAt(uint32_t aIndex) const
   {
     if (aIndex >= mAttrs.Length()) {
       return BorrowedAttrInfo(nullptr, nullptr);
     }
@@ -153,17 +140,15 @@ public:
 private:
   // TODO: Profile, a 1 or 2 element AutoTArray could be worth it, given we know
   // we're dealing with attribute changes when we take snapshots of attributes,
   // though it can be wasted space if we deal with a lot of state-only
   // snapshots.
   Flags mContains;
   nsTArray<ServoAttrSnapshot> mAttrs;
   ServoStateType mState;
-  nsRestyleHint mExplicitRestyleHint;
-  nsChangeHint mExplicitChangeHint;
   bool mIsHTMLElementInHTMLDocument;
   bool mIsInChromeDocument;
 };
 
 } // namespace mozilla
 
 #endif
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -84,30 +84,36 @@ ServoStyleSet::EndUpdate()
   }
 
   // ... do something ...
   return NS_OK;
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveStyleFor(Element* aElement,
-                               nsStyleContext* aParentContext)
+                               nsStyleContext* aParentContext,
+                               ConsumeStyleBehavior aConsume,
+                               LazyComputeBehavior aMayCompute)
 {
   return GetContext(aElement, aParentContext, nullptr,
-                    CSSPseudoElementType::NotPseudo);
+                    CSSPseudoElementType::NotPseudo, aConsume, aMayCompute);
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::GetContext(nsIContent* aContent,
                           nsStyleContext* aParentContext,
                           nsIAtom* aPseudoTag,
-                          CSSPseudoElementType aPseudoType)
+                          CSSPseudoElementType aPseudoType,
+                          ConsumeStyleBehavior aConsume,
+                          LazyComputeBehavior aMayCompute)
 {
+  MOZ_ASSERT(aContent->IsElement());
+  Element* element = aContent->AsElement();
   RefPtr<ServoComputedValues> computedValues =
-    Servo_ComputedValues_Get(aContent).Consume();
+    Servo_ResolveStyle(element, mRawSet.get(), aConsume, aMayCompute).Consume();
   MOZ_ASSERT(computedValues);
   return GetContext(computedValues.forget(), aParentContext, aPseudoTag, aPseudoType);
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::GetContext(already_AddRefed<ServoComputedValues> aComputedValues,
                           nsStyleContext* aParentContext,
                           nsIAtom* aPseudoTag,
@@ -121,22 +127,24 @@ ServoStyleSet::GetContext(already_AddRef
 
   return NS_NewStyleContext(aParentContext, mPresContext, aPseudoTag,
                             aPseudoType, Move(aComputedValues), skipFixup);
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveStyleFor(Element* aElement,
                                nsStyleContext* aParentContext,
+                               ConsumeStyleBehavior aConsume,
+                               LazyComputeBehavior aMayCompute,
                                TreeMatchContext& aTreeMatchContext)
 {
   // aTreeMatchContext is used to speed up selector matching,
   // but if the element already has a ServoComputedValues computed in
   // advance, then we shouldn't need to use it.
-  return ResolveStyleFor(aElement, aParentContext);
+  return ResolveStyleFor(aElement, aParentContext, aConsume, aMayCompute);
 }
 
 already_AddRefed<nsStyleContext>
 ServoStyleSet::ResolveStyleForText(nsIContent* aTextNode,
                                    nsStyleContext* aParentContext)
 {
   MOZ_ASSERT(aTextNode && aTextNode->IsNodeOfType(nsINode::eTEXT));
   MOZ_ASSERT(aTextNode->GetParent());
@@ -432,63 +440,44 @@ ServoStyleSet::HasStateDependentStyle(do
                                       CSSPseudoElementType aPseudoType,
                                      dom::Element* aPseudoElement,
                                      EventStates aStateMask)
 {
   NS_WARNING("stylo: HasStateDependentStyle always returns zero!");
   return nsRestyleHint(0);
 }
 
-nsRestyleHint
-ServoStyleSet::ComputeRestyleHint(dom::Element* aElement,
-                                  ServoElementSnapshot* aSnapshot)
-{
-  return Servo_ComputeRestyleHint(aElement, aSnapshot, mRawSet.get());
-}
-
-static void
-ClearDirtyBits(nsIContent* aContent)
-{
-  bool traverseDescendants = aContent->HasDirtyDescendantsForServo();
-  aContent->UnsetIsDirtyAndHasDirtyDescendantsForServo();
-  if (!traverseDescendants) {
-    return;
-  }
-
-  StyleChildrenIterator it(aContent);
-  for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-    ClearDirtyBits(n);
-  }
-}
-
 void
-ServoStyleSet::StyleDocument(bool aLeaveDirtyBits)
+ServoStyleSet::StyleDocument()
 {
   // Grab the root.
   nsIDocument* doc = mPresContext->Document();
-  nsIContent* root = doc->GetRootElement();
+  Element* root = doc->GetRootElement();
   MOZ_ASSERT(root);
 
-  // Restyle the document, clearing the dirty bits if requested.
-  Servo_RestyleSubtree(root, mRawSet.get());
-  if (!aLeaveDirtyBits) {
-    ClearDirtyBits(root);
-    doc->UnsetHasDirtyDescendantsForServo();
-  }
+  // Restyle the document.
+  Servo_TraverseSubtree(root, mRawSet.get(), SkipRootBehavior::DontSkip);
 }
 
 void
 ServoStyleSet::StyleNewSubtree(nsIContent* aContent)
 {
-  MOZ_ASSERT(aContent->IsDirtyForServo());
-  if (aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT)) {
-    Servo_RestyleSubtree(aContent, mRawSet.get());
+  if (aContent->IsElement()) {
+    Servo_TraverseSubtree(aContent->AsElement(), mRawSet.get(), SkipRootBehavior::DontSkip);
   }
-  ClearDirtyBits(aContent);
 }
 
 void
 ServoStyleSet::StyleNewChildren(nsIContent* aParent)
 {
-  MOZ_ASSERT(aParent->HasDirtyDescendantsForServo());
-  Servo_RestyleSubtree(aParent, mRawSet.get());
-  ClearDirtyBits(aParent);
+  MOZ_ASSERT(aParent->IsElement());
+  Servo_TraverseSubtree(aParent->AsElement(), mRawSet.get(), SkipRootBehavior::Skip);
 }
+
+#ifdef DEBUG
+void
+ServoStyleSet::AssertTreeIsClean()
+{
+  if (Element* root = mPresContext->Document()->GetRootElement()) {
+    Servo_AssertTreeIsClean(root);
+  }
+}
+#endif
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -51,21 +51,25 @@ public:
   bool GetAuthorStyleDisabled() const;
   nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
 
   void BeginUpdate();
   nsresult EndUpdate();
 
   already_AddRefed<nsStyleContext>
   ResolveStyleFor(dom::Element* aElement,
-                  nsStyleContext* aParentContext);
+                  nsStyleContext* aParentContext,
+                  ConsumeStyleBehavior aConsume,
+                  LazyComputeBehavior aMayCompute);
 
   already_AddRefed<nsStyleContext>
   ResolveStyleFor(dom::Element* aElement,
                   nsStyleContext* aParentContext,
+                  ConsumeStyleBehavior aConsume,
+                  LazyComputeBehavior aMayCompute,
                   TreeMatchContext& aTreeMatchContext);
 
   already_AddRefed<nsStyleContext>
   ResolveStyleForText(nsIContent* aTextNode,
                       nsStyleContext* aParentContext);
 
   already_AddRefed<nsStyleContext>
   ResolveStyleForOtherNonElement(nsStyleContext* aParentContext);
@@ -113,29 +117,20 @@ public:
   // Test if style is dependent on content state
   nsRestyleHint HasStateDependentStyle(dom::Element* aElement,
                                        EventStates aStateMask);
   nsRestyleHint HasStateDependentStyle(
     dom::Element* aElement, mozilla::CSSPseudoElementType aPseudoType,
     dom::Element* aPseudoElement, EventStates aStateMask);
 
   /**
-   * Computes a restyle hint given a element and a previous element snapshot.
-   */
-  nsRestyleHint ComputeRestyleHint(dom::Element* aElement,
-                                   ServoElementSnapshot* aSnapshot);
-
-  /**
    * Performs a Servo traversal to compute style for all dirty nodes in the
    * document. The root element must be non-null.
-   *
-   * If aLeaveDirtyBits is true, the dirty/dirty-descendant bits are not
-   * cleared.
    */
-  void StyleDocument(bool aLeaveDirtyBits);
+  void StyleDocument();
 
   /**
    * Eagerly styles a subtree of dirty nodes that were just appended to the
    * tree. This is used in situations where we need the style immediately and
    * cannot wait for a future batch restyle.
    *
    * The subtree must have the root dirty bit set, which currently gets
    * propagated to all descendants. The dirty bits are cleared before
@@ -147,26 +142,34 @@ public:
    * Like the above, but does not assume that the root node is dirty. When
    * appending multiple children to a potentially-non-dirty node, it's
    * preferable to call StyleNewChildren on the node rather than making multiple
    * calls to StyleNewSubtree on each child, since it allows for more
    * parallelism.
    */
   void StyleNewChildren(nsIContent* aParent);
 
+#ifdef DEBUG
+  void AssertTreeIsClean();
+#else
+  void AssertTreeIsClean() {}
+#endif
+
 private:
   already_AddRefed<nsStyleContext> GetContext(already_AddRefed<ServoComputedValues>,
                                               nsStyleContext* aParentContext,
                                               nsIAtom* aPseudoTag,
                                               CSSPseudoElementType aPseudoType);
 
   already_AddRefed<nsStyleContext> GetContext(nsIContent* aContent,
                                               nsStyleContext* aParentContext,
                                               nsIAtom* aPseudoTag,
-                                              CSSPseudoElementType aPseudoType);
+                                              CSSPseudoElementType aPseudoType,
+                                              ConsumeStyleBehavior aConsume,
+                                              LazyComputeBehavior aMayCompute);
 
   nsPresContext* mPresContext;
   UniquePtr<RawServoStyleSet> mRawSet;
   EnumeratedArray<SheetType, SheetType::Count,
                   nsTArray<RefPtr<ServoStyleSheet>>> mSheets;
   int32_t mBatching;
 };
 
--- a/layout/style/ServoTypes.h
+++ b/layout/style/ServoTypes.h
@@ -30,11 +30,34 @@ struct ServoUnsafeCell {
 template<typename T>
 struct ServoCell {
   ServoUnsafeCell<T> value;
   T Get() const { return value.value; }
   void Set(T arg) { value.value = arg; }
   ServoCell() : value() {};
 };
 
+// Indicates whether a style resolution should be considered to consume the style
+// for the construction of a layout frame, or whether the caller is just
+// peeking.
+enum class ConsumeStyleBehavior {
+  Consume,
+  DontConsume,
+};
+
+// Indicates whether the Servo style system should expect the style on an element
+// to have already been resolved (i.e. via a parallel traversal), or whether it
+// may be lazily computed.
+enum class LazyComputeBehavior {
+  Allow,
+  Assert,
+};
+
+// Indicates whether the Servo style system should skip processing on the root
+// element and start processing with its children.
+enum class SkipRootBehavior {
+  Skip,
+  DontSkip,
+};
+
 } // namespace mozilla
 
 #endif // mozilla_ServoTypes_h
--- a/layout/style/StyleSetHandle.h
+++ b/layout/style/StyleSetHandle.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_StyleSetHandle_h
 #define mozilla_StyleSetHandle_h
 
 #include "mozilla/EventStates.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/ServoTypes.h"
 #include "mozilla/SheetType.h"
 #include "mozilla/StyleBackendType.h"
 #include "mozilla/StyleSheet.h"
 #include "nsChangeHint.h"
 #include "nsCSSPseudoElements.h"
 #include "nsTArray.h"
 
 namespace mozilla {
@@ -109,20 +110,24 @@ public:
     inline void BeginShutdown();
     inline void Shutdown();
     inline bool GetAuthorStyleDisabled() const;
     inline nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
     inline void BeginUpdate();
     inline nsresult EndUpdate();
     inline already_AddRefed<nsStyleContext>
     ResolveStyleFor(dom::Element* aElement,
-                    nsStyleContext* aParentContext);
+                    nsStyleContext* aParentContext,
+                    ConsumeStyleBehavior aConsume,
+                    LazyComputeBehavior aMayCompute);
     inline already_AddRefed<nsStyleContext>
     ResolveStyleFor(dom::Element* aElement,
                     nsStyleContext* aParentContext,
+                    ConsumeStyleBehavior aConsume,
+                    LazyComputeBehavior aMayCompute,
                     TreeMatchContext& aTreeMatchContext);
     inline already_AddRefed<nsStyleContext>
     ResolveStyleForText(nsIContent* aTextNode,
                         nsStyleContext* aParentContext);
     inline already_AddRefed<nsStyleContext>
     ResolveStyleForOtherNonElement(nsStyleContext* aParentContext);
     inline already_AddRefed<nsStyleContext>
     ResolvePseudoElementStyle(dom::Element* aParentElement,
--- a/layout/style/StyleSetHandleInlines.h
+++ b/layout/style/StyleSetHandleInlines.h
@@ -74,27 +74,31 @@ nsresult
 StyleSetHandle::Ptr::EndUpdate()
 {
   FORWARD(EndUpdate, ());
 }
 
 // resolve a style context
 already_AddRefed<nsStyleContext>
 StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement,
-                                     nsStyleContext* aParentContext)
+                                     nsStyleContext* aParentContext,
+                                     ConsumeStyleBehavior aConsume,
+                                     LazyComputeBehavior aMayCompute)
 {
-  FORWARD(ResolveStyleFor, (aElement, aParentContext));
+  FORWARD(ResolveStyleFor, (aElement, aParentContext, aConsume, aMayCompute));
 }
 
 already_AddRefed<nsStyleContext>
 StyleSetHandle::Ptr::ResolveStyleFor(dom::Element* aElement,
                                      nsStyleContext* aParentContext,
+                                     ConsumeStyleBehavior aConsume,
+                                     LazyComputeBehavior aMayCompute,
                                      TreeMatchContext& aTreeMatchContext)
 {
-  FORWARD(ResolveStyleFor, (aElement, aParentContext, aTreeMatchContext));
+  FORWARD(ResolveStyleFor, (aElement, aParentContext, aConsume, aMayCompute, aTreeMatchContext));
 }
 
 already_AddRefed<nsStyleContext>
 StyleSetHandle::Ptr::ResolveStyleForText(nsIContent* aTextNode,
                                          nsStyleContext* aParentContext)
 {
   FORWARD(ResolveStyleForText, (aTextNode, aParentContext));
 }
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -490,17 +490,18 @@ nsComputedDOMStyle::GetStyleContextForEl
       return nullptr;
     }
     nsIFrame* frame = nsLayoutUtils::GetStyleFrame(aElement);
     Element* pseudoElement =
       frame && inDocWithShell ? frame->GetPseudoElement(type) : nullptr;
     sc = styleSet->ResolvePseudoElementStyle(aElement, type, parentContext,
                                              pseudoElement);
   } else {
-    sc = styleSet->ResolveStyleFor(aElement, parentContext);
+    sc = styleSet->ResolveStyleFor(aElement, parentContext, ConsumeStyleBehavior::DontConsume,
+                                   LazyComputeBehavior::Allow);
   }
 
   if (aStyleType == eDefaultOnly) {
     if (styleSet->IsServo()) {
       NS_ERROR("stylo: ServoStyleSets cannot supply UA-only styles yet");
       return nullptr;
     }
 
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -558,18 +558,18 @@ static nscoord CalcLengthWith(const nsCS
         const nsStyleFont *rootStyleFont = styleFont;
         Element* docElement = aPresContext->Document()->GetRootElement();
 
         if (docElement) {
           nsIFrame* rootFrame = docElement->GetPrimaryFrame();
           if (rootFrame) {
             rootStyle = rootFrame->StyleContext();
           } else {
-            rootStyle = aPresContext->StyleSet()->ResolveStyleFor(docElement,
-                                                                  nullptr);
+            rootStyle = aPresContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement,
+                                                                             nullptr);
           }
           rootStyleFont = rootStyle->StyleFont();
         }
 
         rootFontSize = rootStyleFont->mFont.size;
       }
 
       return ScaleCoordRound(aValue, float(rootFontSize));
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -86,22 +86,16 @@ nsStyleContext::nsStyleContext(nsStyleCo
   , mPseudoTag(aPseudoTag)
   , mSource(Move(aSource))
 #ifdef MOZ_STYLO
   , mPresContext(nullptr)
 #endif
   , mCachedResetData(nullptr)
   , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
   , mRefCnt(0)
-#ifdef MOZ_STYLO
-  , mStoredChangeHint(nsChangeHint(0))
-#ifdef DEBUG
-  , mConsumedChangeHint(false)
-#endif
-#endif
 #ifdef DEBUG
   , mFrameRefCnt(0)
   , mComputingStruct(nsStyleStructID_None)
 #endif
 {
   MOZ_COUNT_CTOR(nsStyleContext);
 }
 
@@ -791,17 +785,17 @@ nsStyleContext::ApplyStyleFixups(bool aS
 
   // CSS 2.1 10.1: Propagate the root element's 'direction' to the ICB.
   // (PageContentFrame/CanvasFrame etc will inherit 'direction')
   if (mPseudoTag == nsCSSAnonBoxes::viewport) {
     nsPresContext* presContext = PresContext();
     mozilla::dom::Element* docElement = presContext->Document()->GetRootElement();
     if (docElement) {
       RefPtr<nsStyleContext> rootStyle =
-        presContext->StyleSet()->ResolveStyleFor(docElement, nullptr);
+        presContext->StyleSet()->AsGecko()->ResolveStyleFor(docElement, nullptr);
       auto dir = rootStyle->StyleVisibility()->mDirection;
       if (dir != StyleVisibility()->mDirection) {
         nsStyleVisibility* uniqueVisibility = GET_UNIQUE_STYLE_DATA(Visibility);
         uniqueVisibility->mDirection = dir;
       }
     }
   }
 
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -507,55 +507,16 @@ public:
     } else {
       cachedData = mCachedInheritedData.mStyleStructs[aSID];
     }
     return cachedData;
   }
 
   mozilla::NonOwningStyleContextSource StyleSource() const { return mSource.AsRaw(); }
 
-#ifdef MOZ_STYLO
-  // NOTE: It'd be great to assert here that the previous change hint is always
-  // consumed.
-  //
-  // This is not the case right now, since the changes of childs of frames that
-  // go through frame construction are not consumed.
-  void StoreChangeHint(nsChangeHint aHint)
-  {
-    MOZ_ASSERT(!IsShared());
-    mStoredChangeHint = aHint;
-#ifdef DEBUG
-    mConsumedChangeHint = false;
-#endif
-  }
-
-  nsChangeHint ConsumeStoredChangeHint()
-  {
-    MOZ_ASSERT(!mConsumedChangeHint, "Re-consuming the same change hint!");
-    nsChangeHint result = mStoredChangeHint;
-    mStoredChangeHint = nsChangeHint(0);
-#ifdef DEBUG
-    mConsumedChangeHint = true;
-#endif
-    return result;
-  }
-#else
-  void StoreChangeHint(nsChangeHint aHint)
-  {
-    MOZ_CRASH("stylo: Called nsStyleContext::StoreChangeHint in a non MOZ_STYLO "
-              "build.");
-  }
-
-  nsChangeHint ConsumeStoredChangeHint()
-  {
-    MOZ_CRASH("stylo: Called nsStyleContext::ComsumeStoredChangeHint in a non "
-               "MOZ_STYLO build.");
-  }
-#endif
-
 private:
   // Private destructor, to discourage deletion outside of Release():
   ~nsStyleContext();
 
   // Delegated Helper constructor.
   nsStyleContext(nsStyleContext* aParent,
                  mozilla::OwningStyleContextSource&& aSource,
                  nsIAtom* aPseudoTag,
@@ -804,25 +765,16 @@ private:
   //    inherited from the parent context or owned by the rule node (i.e.,
   //    not owned by the style context).
   //  - It also stores the additional bits listed at the top of
   //    nsStyleStruct.h.
   uint64_t                mBits;
 
   uint32_t                mRefCnt;
 
-  // For now we store change hints on the style context during parallel traversal.
-  // We should improve this - see bug 1289861.
-#ifdef MOZ_STYLO
-  nsChangeHint            mStoredChangeHint;
-#ifdef DEBUG
-  bool                    mConsumedChangeHint;
-#endif
-#endif
-
 #ifdef DEBUG
   uint32_t                mFrameRefCnt; // number of frames that use this
                                         // as their style context
 
   nsStyleStructID         mComputingStruct;
 
   static bool DependencyAllowed(nsStyleStructID aOuterSID,
                                 nsStyleStructID aInnerSID)
--- a/layout/style/nsStyleSet.h
+++ b/layout/style/nsStyleSet.h
@@ -12,16 +12,17 @@
 #ifndef nsStyleSet_h_
 #define nsStyleSet_h_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/EnumeratedArray.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/ServoTypes.h"
 #include "mozilla/SheetType.h"
 
 #include "nsIStyleRuleProcessor.h"
 #include "nsBindingManager.h"
 #include "nsRuleNode.h"
 #include "nsTArray.h"
 #include "nsCOMArray.h"
 #include "nsIStyleRule.h"
@@ -110,18 +111,37 @@ class nsStyleSet final
   // get a style context for a non-pseudo frame.
   already_AddRefed<nsStyleContext>
   ResolveStyleFor(mozilla::dom::Element* aElement,
                   nsStyleContext* aParentContext);
 
   already_AddRefed<nsStyleContext>
   ResolveStyleFor(mozilla::dom::Element* aElement,
                   nsStyleContext* aParentContext,
+                  mozilla::ConsumeStyleBehavior,
+                  mozilla::LazyComputeBehavior)
+  {
+    return ResolveStyleFor(aElement, aParentContext);
+  }
+
+  already_AddRefed<nsStyleContext>
+  ResolveStyleFor(mozilla::dom::Element* aElement,
+                  nsStyleContext* aParentContext,
                   TreeMatchContext& aTreeMatchContext);
 
+  already_AddRefed<nsStyleContext>
+  ResolveStyleFor(mozilla::dom::Element* aElement,
+                  nsStyleContext* aParentContext,
+                  mozilla::ConsumeStyleBehavior aConsume,
+                  mozilla::LazyComputeBehavior aMayCompute,
+                  TreeMatchContext& aTreeMatchContext)
+  {
+    return ResolveStyleFor(aElement, aParentContext, aTreeMatchContext);
+  }
+
   // Get a style context (with the given parent) for the
   // sequence of style rules in the |aRules| array.
   already_AddRefed<nsStyleContext>
   ResolveStyleForRules(nsStyleContext* aParentContext,
                        const nsTArray< nsCOMPtr<nsIStyleRule> > &aRules);
 
   // Get a style context that represents aBaseContext, but as though
   // it additionally matched the rules in the aRules array (in that
--- a/layout/xul/nsListBoxBodyFrame.cpp
+++ b/layout/xul/nsListBoxBodyFrame.cpp
@@ -697,17 +697,19 @@ nsListBoxBodyFrame::ComputeIntrinsicISiz
   nsCOMPtr<nsIDOMElement> firstRowEl;
   GetItemAtIndex(index, getter_AddRefs(firstRowEl));
   nsCOMPtr<nsIContent> firstRowContent(do_QueryInterface(firstRowEl));
 
   if (firstRowContent) {
     RefPtr<nsStyleContext> styleContext;
     nsPresContext *presContext = aBoxLayoutState.PresContext();
     styleContext = presContext->StyleSet()->
-      ResolveStyleFor(firstRowContent->AsElement(), nullptr);
+      ResolveStyleFor(firstRowContent->AsElement(), nullptr,
+                      ConsumeStyleBehavior::DontConsume,
+                      LazyComputeBehavior::Allow);
 
     nscoord width = 0;
     nsMargin margin(0,0,0,0);
 
     if (styleContext->StylePadding()->GetPadding(margin))
       width += margin.LeftRight();
     width += styleContext->StyleBorder()->GetComputedBorder().LeftRight();
     if (styleContext->StyleMargin()->GetMargin(margin))
--- a/layout/xul/nsSplitterFrame.cpp
+++ b/layout/xul/nsSplitterFrame.cpp
@@ -281,17 +281,19 @@ nsSplitterFrame::Init(nsIContent*       
   if (aParent && aParent->IsXULBoxFrame()) {
     if (!aParent->IsXULHorizontal()) {
       if (!nsContentUtils::HasNonEmptyAttr(aContent, kNameSpaceID_None,
                                            nsGkAtoms::orient)) {
         aContent->SetAttr(kNameSpaceID_None, nsGkAtoms::orient,
                           NS_LITERAL_STRING("vertical"), false);
         nsStyleContext* parentStyleContext = StyleContext()->GetParent();
         RefPtr<nsStyleContext> newContext = PresContext()->StyleSet()->
-          ResolveStyleFor(aContent->AsElement(), parentStyleContext);
+          ResolveStyleFor(aContent->AsElement(), parentStyleContext,
+                          ConsumeStyleBehavior::Consume,
+                          LazyComputeBehavior::Allow);
         SetStyleContextWithoutNotification(newContext);
       }
     }
   }
 
   nsBoxFrame::Init(aContent, aParent, aPrevInFlow);
 
   mInner->mState = nsSplitterFrameInner::Open;