Bug 1290335: stylo: Allow processing change hints generated from Servo. r=heycam
authorEmilio Cobos Álvarez <ecoal95@gmail.com>
Thu, 28 Jul 2016 19:20:45 -0700
changeset 333868 f1d25be43bde294949d5004c5ccb21e043bc3616
parent 333867 a3ec445491977faa3c803c5d82dc78100184f223
child 333869 f572c7858103dd68d0319722281d12d95d64f207
push id10033
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:50:26 +0000
treeherdermozilla-aurora@5dddbefdf759 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersheycam
bugs1290335
milestone51.0a1
Bug 1290335: stylo: Allow processing change hints generated from Servo. r=heycam MozReview-Commit-ID: Alc0wcXvHcD
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
layout/base/nsCSSFrameConstructor.h
layout/base/nsStyleChangeList.h
layout/style/ServoBindings.cpp
layout/style/ServoBindings.h
layout/style/nsStyleContext.cpp
layout/style/nsStyleContext.h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -62,55 +62,88 @@ ServoRestyleManager::RebuildAllStyleData
 
 void
 ServoRestyleManager::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint,
                                                   nsRestyleHint aRestyleHint)
 {
   MOZ_CRASH("stylo: ServoRestyleManager::PostRebuildAllStyleDataEvent not implemented");
 }
 
-/* static */ void
+void
 ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
                                            nsStyleContext* aParentContext,
-                                           ServoStyleSet* aStyleSet)
+                                           ServoStyleSet* aStyleSet,
+                                           nsStyleChangeList& aChangeListToProcess)
 {
   nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
-
-  // TODO: AFAIK this can happen when we have, let's say, display: none. Here we
-  // should trigger frame construction if the element is actually dirty (I
-  // guess), but we'd better do that once we have all the restyle hints thing
-  // figured out.
-  if (!primaryFrame) {
-    aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO | NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
+  if (!primaryFrame && !aContent->IsDirtyForServo()) {
+    NS_WARNING("Frame not found for non-dirty content");
     return;
   }
 
   if (aContent->IsDirtyForServo()) {
+    nsChangeHint changeHint;
+    if (primaryFrame) {
+      changeHint = primaryFrame->StyleContext()->ConsumeStoredChangeHint();
+    } else {
+      // TODO: Use the frame constructor's UndisplayedNodeMap to store the old
+      // style contexts, and thus the change hints. That way we can in most
+      // cases avoid generating ReconstructFrame and push it to the list just to
+      // notice at frame construction that it doesn't need a frame.
+      changeHint = nsChangeHint_ReconstructFrame;
+    }
+
+    // NB: The change list only expects elements.
+    if (changeHint && aContent->IsElement()) {
+      aChangeListToProcess.AppendChange(primaryFrame, aContent, changeHint);
+    }
+
+    if (!primaryFrame) {
+      // The frame reconstruction step will ask for the descendant's style
+      // correctly.
+      return;
+    }
+
+    // Even if we don't have a change hint, we still need to swap style contexts
+    // so our new style is updated properly.
     RefPtr<ServoComputedValues> computedValues =
       dont_AddRef(Servo_GetComputedValues(aContent));
 
     // TODO: Figure out what pseudos does this content have, and do the proper
     // thing with them.
-    RefPtr<nsStyleContext> context =
+    RefPtr<nsStyleContext> newContext =
       aStyleSet->GetContext(computedValues.forget(),
                             aParentContext,
                             nullptr,
                             CSSPseudoElementType::NotPseudo);
 
-    // TODO: Compare old and new styles to generate restyle change hints, and
-    // process them.
-    primaryFrame->SetStyleContext(context.get());
+    RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
+    MOZ_ASSERT(oldStyleContext);
 
+    // XXX This could not always work as expected there are kinds of content
+    // with the first split and the last sharing style, but others not. We
+    // should handle those properly.
+    for (nsIFrame* f = primaryFrame; f;
+         f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
+      f->SetStyleContext(newContext);
+    }
+
+    // TODO: There are other continuations we still haven't restyled, mostly
+    // pseudo-elements. We have to deal with those, and with anonymous boxes.
     aContent->UnsetFlags(NODE_IS_DIRTY_FOR_SERVO);
   }
 
   if (aContent->HasDirtyDescendantsForServo()) {
+    MOZ_ASSERT(primaryFrame,
+               "Frame construction should be scheduled, and it takes the "
+               "correct style for the children");
     FlattenedChildIterator it(aContent);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
-      RecreateStyleContexts(n, primaryFrame->StyleContext(), aStyleSet);
+      RecreateStyleContexts(n, primaryFrame->StyleContext(),
+                            aStyleSet, aChangeListToProcess);
     }
     aContent->UnsetFlags(NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO);
   }
 }
 
 static void
 MarkParentsAsHavingDirtyDescendants(Element* aElement)
 {
@@ -138,60 +171,66 @@ MarkChildrenAsDirtyForServo(nsIContent* 
   if (hadChildren) {
     aContent->SetHasDirtyDescendantsForServo();
   }
 }
 
 void
 ServoRestyleManager::NoteRestyleHint(Element* aElement, nsRestyleHint aHint)
 {
-  if (aHint & eRestyle_Self) {
+  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();
     MarkParentsAsHavingDirtyDescendants(aElement);
-    // 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.
-  } else if (aHint & eRestyle_Subtree) {
+  // 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);
     MarkParentsAsHavingDirtyDescendants(aElement);
   }
 
   if (aHint & eRestyle_LaterSiblings) {
     for (nsINode* cur = aElement->GetNextSibling(); cur;
          cur = cur->GetNextSibling()) {
-      if (cur->IsContent()) {
-        cur->SetIsDirtyForServo();
-      }
+      cur->SetIsDirtyForServo();
     }
   }
 
   // TODO: Handle all other nsRestyleHint values.
-  if (aHint & ~(eRestyle_Self | eRestyle_Subtree | eRestyle_LaterSiblings)) {
+  if (aHint & ~HANDLED_RESTYLE_HINTS) {
     NS_WARNING(nsPrintfCString("stylo: Unhandled restyle hint %s",
-                             RestyleManagerBase::RestyleHintToString(aHint).get()).get());
+                               RestyleManagerBase::RestyleHintToString(aHint).get()).get());
   }
 }
 
 void
 ServoRestyleManager::ProcessPendingRestyles()
 {
+  MOZ_ASSERT(PresContext()->Document(), "No document?  Pshaw!");
+  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");
   if (!HasPendingRestyles()) {
     return;
   }
+
   ServoStyleSet* styleSet = StyleSet();
-
   if (!styleSet->StylingStarted()) {
     // If something caused us to restyle, and we haven't started styling yet,
     // do nothing. Everything is dirty, and we'll style it all later.
     return;
   }
 
   nsIDocument* doc = PresContext()->Document();
-
   Element* root = doc->GetRootElement();
   if (root) {
     for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
       ServoElementSnapshot* snapshot = iter.UserData();
       Element* element = iter.Key();
 
       // TODO: avoid the ComputeRestyleHint call if we already have the highest
       // explicit restyle hint?
@@ -200,17 +239,31 @@ ServoRestyleManager::ProcessPendingResty
 
       if (hint) {
         NoteRestyleHint(element, hint);
       }
     }
 
     if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
       styleSet->RestyleSubtree(root);
-      RecreateStyleContexts(root, nullptr, styleSet);
+
+      // 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);
+      ProcessRestyledFrames(changeList);
     }
   }
 
   mModifiedElements.Clear();
 
   // NB: we restyle from the root element, but the document also gets the
   // HAS_DIRTY_DESCENDANTS flag as part of the loop on PostRestyleEvent, and we
   // use that to check we have pending restyles.
@@ -309,18 +362,20 @@ ServoRestyleManager::SnapshotForElement(
   // NB: aElement is the argument for the construction of the snapshot in the
   // not found case.
   return mModifiedElements.LookupOrAdd(aElement, aElement);
 }
 
 nsresult
 ServoRestyleManager::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
 {
-  MOZ_CRASH("stylo: ServoRestyleManager::ProcessRestyledFrames not implemented "
-            "for Servo-backed style system");
+  // XXX Hook the overflow tracker somewhere.
+  OverflowChangedTracker overflowChangedTracker;
+  return base_type::ProcessRestyledFrames(aChangeList, *PresContext(),
+                                          overflowChangedTracker);
 }
 
 void
 ServoRestyleManager::FlushOverflowChangedTracker()
 {
   MOZ_CRASH("stylo: ServoRestyleManager::FlushOverflowChangedTracker "
             "not implemented for Servo-backed style system");
 }
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -31,16 +31,18 @@ namespace mozilla {
 
 /**
  * Restyle manager for a Servo-backed style system.
  */
 class ServoRestyleManager : public RestyleManagerBase
 {
   friend class ServoStyleSet;
 public:
+  typedef RestyleManagerBase base_type;
+
   NS_INLINE_DECL_REFCOUNTING(ServoRestyleManager)
 
   explicit ServoRestyleManager(nsPresContext* aPresContext);
 
   void PostRestyleEvent(dom::Element* aElement,
                         nsRestyleHint aRestyleHint,
                         nsChangeHint aMinChangeHint);
   void PostRestyleEventForLazyConstruction();
@@ -85,24 +87,21 @@ private:
    * 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.
-   *
-   * This is just static so ServoStyleSet can mark this class as friend, so we
-   * can access to the GetContext method without making it available to everyone
-   * else.
    */
-  static void RecreateStyleContexts(nsIContent* aContent,
-                                    nsStyleContext* aParentContext,
-                                    ServoStyleSet* aStyleSet);
+  void RecreateStyleContexts(nsIContent* aContent,
+                             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);
 
   inline ServoStyleSet* StyleSet() const
   {
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -50,16 +50,17 @@ class FlattenedChildIterator;
 class nsCSSFrameConstructor : public nsFrameManager
 {
 public:
   typedef mozilla::CSSPseudoElementType CSSPseudoElementType;
   typedef mozilla::dom::Element Element;
 
   friend class mozilla::RestyleManager;
   friend class mozilla::RestyleManagerBase;
+  friend class mozilla::ServoRestyleManager;
 
   nsCSSFrameConstructor(nsIDocument* aDocument, nsIPresShell* aPresShell);
   ~nsCSSFrameConstructor(void) {
     NS_ASSERTION(mUpdateCount == 0, "Dying in the middle of our own update?");
   }
 
   // get the alternate text for a content node
   static void GetAlternateTextFor(nsIContent*    aContent,
--- a/layout/base/nsStyleChangeList.h
+++ b/layout/base/nsStyleChangeList.h
@@ -35,17 +35,17 @@ public:
   nsStyleChangeList();
   ~nsStyleChangeList();
 
   int32_t Count(void) const {
     return mCount;
   }
 
   /**
-   * Fills in pointers without reference counting.  
+   * Fills in pointers without reference counting.
    */
   nsresult ChangeAt(int32_t aIndex, nsIFrame*& aFrame, nsIContent*& aContent,
                     nsChangeHint& aHint) const;
 
   /**
    * Fills in a pointer to the list entry storage (no reference counting
    * involved).
    */
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -179,37 +179,72 @@ Gecko_SetNodeFlags(RawGeckoNode* aNode, 
 }
 
 void
 Gecko_UnsetNodeFlags(RawGeckoNode* aNode, uint32_t aFlags)
 {
   aNode->UnsetFlags(aFlags);
 }
 
+nsStyleContext*
+Gecko_GetStyleContext(RawGeckoNode* aNode)
+{
+  MOZ_ASSERT(aNode->IsContent());
+  nsIFrame* primaryFrame = aNode->AsContent()->GetPrimaryFrame();
+  if (!primaryFrame) {
+    return nullptr;
+  }
+
+  return primaryFrame->StyleContext();
+}
+
 nsChangeHint
-Gecko_CalcAndStoreStyleDifference(RawGeckoElement* aElement,
-                                  ServoComputedValues* aComputedValues)
+Gecko_CalcStyleDifference(nsStyleContext* aOldStyleContext,
+                          ServoComputedValues* aComputedValues)
 {
-#ifdef MOZ_STYLO
-  nsStyleContext* oldContext = aElement->GetPrimaryFrame()->StyleContext();
+  MOZ_ASSERT(aOldStyleContext);
+  MOZ_ASSERT(aComputedValues);
 
   // Pass the safe thing, which causes us to miss a potential optimization. See
   // bug 1289863.
   nsChangeHint forDescendants = nsChangeHint_Hints_NotHandledForDescendants;
 
   // Eventually, we should compute things out of these flags like
   // ElementRestyler::RestyleSelf does and pass the result to the caller to
   // potentially halt traversal. See bug 1289868.
   uint32_t equalStructs, samePointerStructs;
   nsChangeHint result =
-    oldContext->CalcStyleDifference(aComputedValues, forDescendants,
-                                    &equalStructs, &samePointerStructs);
+    aOldStyleContext->CalcStyleDifference(aComputedValues,
+                                          forDescendants,
+                                          &equalStructs,
+                                          &samePointerStructs);
+
   return result;
+}
+
+void
+Gecko_StoreStyleDifference(RawGeckoNode* aNode, nsChangeHint aChangeHintToStore)
+{
+#ifdef MOZ_STYLO
+  // XXXEmilio probably storing it in the nearest content parent is a sane thing
+  // to do if this case can ever happen?
+  MOZ_ASSERT(aNode->IsContent());
+
+  nsIContent* aContent = aNode->AsContent();
+  nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
+  if (!primaryFrame) {
+    // TODO: Pick the undisplayed content map from the frame-constructor, and
+    // stick it there. For now we're generating ReconstructFrame
+    // unconditionally, which is suboptimal.
+    return;
+  }
+
+  primaryFrame->StyleContext()->StoreChangeHint(aChangeHintToStore);
 #else
-  MOZ_CRASH("stylo: Shouldn't call Gecko_CalcAndStoreStyleDifference in "
+  MOZ_CRASH("stylo: Shouldn't call Gecko_StoreStyleDifference in "
             "non-stylo build");
 #endif
 }
 
 ServoDeclarationBlock*
 Gecko_GetServoDeclarationBlock(RawGeckoElement* aElement)
 {
   const nsAttrValue* attr = aElement->GetParsedAttr(nsGkAtoms::style);
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -182,18 +182,26 @@ void Gecko_SetMozBinding(nsStyleDisplay*
 void Gecko_CopyMozBindingFrom(nsStyleDisplay* des, const nsStyleDisplay* src);
 
 // Dirtiness tracking.
 uint32_t Gecko_GetNodeFlags(RawGeckoNode* node);
 void Gecko_SetNodeFlags(RawGeckoNode* node, uint32_t flags);
 void Gecko_UnsetNodeFlags(RawGeckoNode* node, uint32_t flags);
 
 // Incremental restyle.
-nsChangeHint Gecko_CalcAndStoreStyleDifference(RawGeckoElement* element,
-                                               ServoComputedValues* newstyle);
+// 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(RawGeckoNode* node);
+nsChangeHint Gecko_CalcStyleDifference(nsStyleContext* oldstyle,
+                                       ServoComputedValues* newstyle);
+void Gecko_StoreStyleDifference(RawGeckoNode* node, nsChangeHint change);
 
 // `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);
 
 
 void Gecko_EnsureImageLayersLength(nsStyleImageLayers* layers, size_t len);
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -80,17 +80,17 @@ nsStyleContext::nsStyleContext(nsStyleCo
   , mPresContext(nullptr)
 #endif
   , mCachedResetData(nullptr)
   , mBits(((uint64_t)aPseudoType) << NS_STYLE_CONTEXT_TYPE_SHIFT)
   , mRefCnt(0)
 #ifdef MOZ_STYLO
   , mStoredChangeHint(nsChangeHint(0))
 #ifdef DEBUG
-  , mHasStoredChangeHint(false)
+  , mConsumedChangeHint(false)
 #endif
 #endif
 #ifdef DEBUG
   , mFrameRefCnt(0)
   , mComputingStruct(nsStyleStructID_None)
 #endif
 {}
 
@@ -392,20 +392,16 @@ nsStyleContext::MoveTo(nsStyleContext* a
 }
 
 already_AddRefed<nsStyleContext>
 nsStyleContext::FindChildWithRules(const nsIAtom* aPseudoTag,
                                    NonOwningStyleContextSource aSource,
                                    NonOwningStyleContextSource aSourceIfVisited,
                                    bool aRelevantLinkVisited)
 {
-#ifdef MOZ_STYLO
-  MOZ_ASSERT(!mHasStoredChangeHint);
-#endif
-
   uint32_t threshold = 10; // The # of siblings we're willing to examine
                            // before just giving this whole thing up.
 
   RefPtr<nsStyleContext> result;
   nsStyleContext *list = aSource.MatchesNoRules() ? mEmptyChild : mChild;
 
   if (list) {
     nsStyleContext *child = list;
@@ -1229,18 +1225,16 @@ nsStyleContext::CalcStyleDifference(nsSt
                                     nsChangeHint aParentHintsNotHandledForDescendants,
                                     uint32_t* aEqualStructs,
                                     uint32_t* aSamePointerStructs)
 {
   return CalcStyleDifferenceInternal(aNewContext, aParentHintsNotHandledForDescendants,
                                      aEqualStructs, aSamePointerStructs);
 }
 
-#ifdef MOZ_STYLO
-
 class MOZ_STACK_CLASS FakeStyleContext
 {
 public:
   explicit FakeStyleContext(ServoComputedValues* aComputedValues)
     : mComputedValues(aComputedValues) {}
 
   mozilla::NonOwningStyleContextSource StyleSource() const {
     return mozilla::NonOwningStyleContextSource(mComputedValues);
@@ -1268,17 +1262,16 @@ nsStyleContext::CalcStyleDifference(Serv
                                     nsChangeHint aParentHintsNotHandledForDescendants,
                                     uint32_t* aEqualStructs,
                                     uint32_t* aSamePointerStructs)
 {
   FakeStyleContext newContext(aNewComputedValues);
   return CalcStyleDifferenceInternal(&newContext, aParentHintsNotHandledForDescendants,
                                      aEqualStructs, aSamePointerStructs);
 }
-#endif
 
 #ifdef DEBUG
 void nsStyleContext::List(FILE* out, int32_t aIndent, bool aListDescendants)
 {
   nsAutoCString str;
   // Indent
   int32_t ix;
   for (ix = aIndent; --ix >= 0; ) {
--- a/layout/style/nsStyleContext.h
+++ b/layout/style/nsStyleContext.h
@@ -393,35 +393,33 @@ public:
    * aEqualStructs must not be null.  Into it will be stored a bitfield
    * representing which structs were compared to be non-equal.
    */
   nsChangeHint CalcStyleDifference(nsStyleContext* aNewContext,
                                    nsChangeHint aParentHintsNotHandledForDescendants,
                                    uint32_t* aEqualStructs,
                                    uint32_t* aSamePointerStructs);
 
-#ifdef MOZ_STYLO
-  /*
-   * Like the above, but does not require the new style context to exist yet.
-   * Servo uses this to compute change hints during parallel traversal.
+  /**
+   * Like the above, but allows comparing ServoComputedValues instead of needing
+   * a full-fledged style context.
    */
   nsChangeHint CalcStyleDifference(ServoComputedValues* aNewComputedValues,
                                    nsChangeHint aParentHintsNotHandledForDescendants,
                                    uint32_t* aEqualStructs,
                                    uint32_t* aSamePointerStructs);
-#endif
 
 private:
   template<class StyleContextLike>
   nsChangeHint CalcStyleDifferenceInternal(StyleContextLike* aNewContext,
                                            nsChangeHint aParentHintsNotHandledForDescendants,
                                            uint32_t* aEqualStructs,
                                            uint32_t* aSamePointerStructs);
+
 public:
-
   /**
    * Get a color that depends on link-visitedness using this and
    * this->GetStyleIfVisited().
    *
    * aProperty must be a color-valued property that StyleAnimationValue
    * knows how to extract.  It must also be a property that we know to
    * do change handling for in nsStyleContext::CalcDifference.
    *
@@ -523,36 +521,52 @@ public:
       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(!mHasStoredChangeHint);
+    MOZ_ASSERT(!mConsumedChangeHint);
     MOZ_ASSERT(!IsShared());
     mStoredChangeHint = aHint;
 #ifdef DEBUG
-    mHasStoredChangeHint = true;
+    mConsumedChangeHint = false;
 #endif
   }
 
   nsChangeHint ConsumeStoredChangeHint()
   {
-    MOZ_ASSERT(mHasStoredChangeHint);
     nsChangeHint result = mStoredChangeHint;
     mStoredChangeHint = nsChangeHint(0);
 #ifdef DEBUG
-    mHasStoredChangeHint = false;
+    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,
@@ -760,17 +774,17 @@ private:
 
   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                    mHasStoredChangeHint;
+  bool                    mConsumedChangeHint;
 #endif
 #endif
 
 #ifdef DEBUG
   uint32_t                mFrameRefCnt; // number of frames that use this
                                         // as their style context
 
   nsStyleStructID         mComputingStruct;