Bug 1351339: Compute text style changes when the parent is a display: contents element. r?heycam draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Mon, 08 May 2017 03:16:43 +0200
changeset 574184 4d0ac2b96771e6d375807157e1c8156986f1d407
parent 574009 56b72bcfa20c1c7d2222891317ecb4eed34bfb98
child 574185 d36e13a7c9528ec214b4cf08d2e171a17ad2acaa
push id57599
push userbmo:emilio+bugs@crisal.io
push dateMon, 08 May 2017 11:44:38 +0000
reviewersheycam
bugs1351339
milestone55.0a1
Bug 1351339: Compute text style changes when the parent is a display: contents element. r?heycam MozReview-Commit-ID: GWPNevfP4xc
layout/base/ServoRestyleManager.cpp
layout/base/ServoRestyleManager.h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -130,16 +130,72 @@ ServoRestyleManager::ClearRestyleStateFr
     }
   }
 
   Unused << Servo_TakeChangeHint(aElement);
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
 }
 
+/**
+ * This struct takes care of encapsulating some common state that text nodes may
+ * need to track during the post-traversal.
+ *
+ * This is currently used to properly compute change hints when the parent
+ * element of this node is a display: contents node.
+ */
+struct ServoRestyleManager::TextPostTraversalState
+{
+  bool mShouldPostHints;
+  bool mShouldComputeHints;
+  nsChangeHint mComputedHint;
+
+  explicit TextPostTraversalState(bool aDisplayContentsParentStyleChanged)
+    : mShouldPostHints(aDisplayContentsParentStyleChanged)
+    , mShouldComputeHints(aDisplayContentsParentStyleChanged)
+    , mComputedHint(nsChangeHint_Empty)
+  {}
+
+  void ComputeHintIfNeeded(nsIContent* aContent,
+                           nsIFrame* aTextFrame,
+                           nsStyleContext& aNewContext,
+                           nsStyleChangeList& aChangeList)
+  {
+    MOZ_ASSERT(aTextFrame);
+    MOZ_ASSERT(aNewContext.GetPseudo() == nsCSSAnonBoxes::mozText);
+
+    if (MOZ_LIKELY(!mShouldPostHints)) {
+      return;
+    }
+
+    nsStyleContext* oldContext = aTextFrame->StyleContext();
+    MOZ_ASSERT(oldContext->GetPseudo() == nsCSSAnonBoxes::mozText);
+
+    // We rely on the fact that all the text children for the same element share
+    // style to avoid recomputing style differences for all of them.
+    //
+    // TODO(emilio): The above may not be true for ::first-{line,letter}, but
+    // we'll cross that bridge when we support those in stylo.
+    //
+    // TODO(emilio): We could also use the same style context itself, can't we?
+    if (mShouldComputeHints) {
+      mShouldComputeHints = false;
+      uint32_t equalStructs, samePointerStructs;
+      mComputedHint =
+        oldContext->CalcStyleDifference(&aNewContext,
+                                        &equalStructs,
+                                        &samePointerStructs);
+    }
+
+    if (mComputedHint) {
+      aChangeList.AppendChange(aTextFrame, aContent, mComputedHint);
+    }
+  }
+};
+
 void
 ServoRestyleManager::ProcessPostTraversal(Element* aElement,
                                           nsStyleContext* aParentContext,
                                           ServoStyleSet* aStyleSet,
                                           nsStyleChangeList& aChangeList)
 {
   nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);
 
@@ -244,58 +300,66 @@ ServoRestyleManager::ProcessPostTraversa
       displayContentsNode->mStyle = newContext;
     }
 
     if (styleFrame) {
       styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint);
     }
   }
 
-  bool descendantsNeedFrames = aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
-  bool traverseElementChildren =
+  const bool descendantsNeedFrames =
+    aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
+  const bool traverseElementChildren =
     aElement->HasDirtyDescendantsForServo() || descendantsNeedFrames;
-  bool traverseTextChildren = recreateContext || descendantsNeedFrames;
+  const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
   if (traverseElementChildren || traverseTextChildren) {
     nsStyleContext* upToDateContext =
       recreateContext ? newContext : oldStyleContext;
 
     StyleChildrenIterator it(aElement);
+    TextPostTraversalState textState(displayContentsNode && recreateContext);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
         ProcessPostTraversal(n->AsElement(), upToDateContext,
                              aStyleSet, aChangeList);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
-        ProcessPostTraversalForText(n, upToDateContext, aStyleSet, aChangeList);
+        ProcessPostTraversalForText(
+            n, upToDateContext, aStyleSet, aChangeList, textState);
       }
     }
   }
 
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
 }
 
 void
-ServoRestyleManager::ProcessPostTraversalForText(nsIContent* aTextNode,
-                                                 nsStyleContext* aParentContext,
-                                                 ServoStyleSet* aStyleSet,
-                                                 nsStyleChangeList& aChangeList)
+ServoRestyleManager::ProcessPostTraversalForText(
+    nsIContent* aTextNode,
+    nsStyleContext* aParentContext,
+    ServoStyleSet* aStyleSet,
+    nsStyleChangeList& aChangeList,
+    TextPostTraversalState& aPostTraversalState)
 {
   // Handle lazy frame construction.
   if (aTextNode->HasFlag(NODE_NEEDS_FRAME)) {
     aChangeList.AppendChange(nullptr, aTextNode, nsChangeHint_ReconstructFrame);
     return;
   }
 
   // Handle restyle.
   nsIFrame* primaryFrame = aTextNode->GetPrimaryFrame();
   if (primaryFrame) {
     RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
     RefPtr<nsStyleContext> newContext =
       aStyleSet->ResolveStyleForText(aTextNode, aParentContext);
 
+    aPostTraversalState.ComputeHintIfNeeded(
+        aTextNode, primaryFrame, *newContext, aChangeList);
+
     for (nsIFrame* f = primaryFrame; f;
          f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
       f->SetStyleContext(newContext);
     }
   }
 }
 
 void
--- a/layout/base/ServoRestyleManager.h
+++ b/layout/base/ServoRestyleManager.h
@@ -116,20 +116,22 @@ private:
   /**
    * Performs post-Servo-traversal processing on this element and its descendants.
    */
   void ProcessPostTraversal(Element* aElement,
                             nsStyleContext* aParentContext,
                             ServoStyleSet* aStyleSet,
                             nsStyleChangeList& aChangeList);
 
+  struct TextPostTraversalState;
   void ProcessPostTraversalForText(nsIContent* aTextNode,
                                    nsStyleContext* aParentContext,
                                    ServoStyleSet* aStyleSet,
-                                   nsStyleChangeList& aChangeList);
+                                   nsStyleChangeList& aChangeList,
+                                   TextPostTraversalState& aState);
 
   inline ServoStyleSet* StyleSet() const
   {
     MOZ_ASSERT(PresContext()->StyleSet()->IsServo(),
                "ServoRestyleManager should only be used with a Servo-flavored "
                "style backend");
     return PresContext()->StyleSet()->AsServo();
   }