Bug 1371450 - Recreate style context only if the element was restyled during the traversal. r?emilio draft
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Sat, 15 Jul 2017 13:16:11 +0900
changeset 609305 5a7c8b2b6f82639cbffea19c53a0917444234974
parent 609302 3e8118e3620f83f05df6c6ed63677ea0ddc630e6
child 609306 5735b1e3465063d79cf4c232e1e0a15a7391dff0
push id68545
push userhikezoe@mozilla.com
push dateSat, 15 Jul 2017 04:17:02 +0000
reviewersemilio
bugs1371450
milestone56.0a1
Bug 1371450 - Recreate style context only if the element was restyled during the traversal. r?emilio MozReview-Commit-ID: CBYTB5FeikP
layout/base/ServoRestyleManager.cpp
layout/style/ServoBindingList.h
--- a/layout/base/ServoRestyleManager.cpp
+++ b/layout/base/ServoRestyleManager.cpp
@@ -245,17 +245,20 @@ ServoRestyleManager::ClearRestyleStateFr
     StyleChildrenIterator it(aElement);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (n->IsElement()) {
         ClearRestyleStateFromSubtree(n->AsElement());
       }
     }
   }
 
-  Unused << Servo_TakeChangeHint(aElement, TraversalRestyleBehavior::Normal);
+  bool wasRestyled;
+  Unused << Servo_TakeChangeHint(aElement,
+                                 TraversalRestyleBehavior::Normal,
+                                 &wasRestyled);
   aElement->UnsetHasDirtyDescendantsForServo();
   aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
   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.
@@ -462,17 +465,20 @@ ServoRestyleManager::ProcessPostTraversa
   const bool isOutOfFlow =
     aElement->GetPrimaryFrame() &&
     aElement->GetPrimaryFrame()->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
 
   // Grab the change hint from Servo.
   // In case of flushing throttled animations, any restyle hints other than
   // animations are preserved since they are the hints which will be processed
   // in normal restyle later.
-  nsChangeHint changeHint = Servo_TakeChangeHint(aElement, aRestyleBehavior);
+  bool wasRestyled;
+  nsChangeHint changeHint = Servo_TakeChangeHint(aElement,
+                                                 aRestyleBehavior,
+                                                 &wasRestyled);
 
   // We should really fix the weird primary frame mapping for image maps
   // (bug 135040)...
   if (styleFrame && styleFrame->GetContent() != aElement) {
     MOZ_ASSERT(styleFrame->IsImageFrame());
     styleFrame = nullptr;
   }
 
@@ -521,49 +527,36 @@ ServoRestyleManager::ProcessPostTraversa
   if (!oldStyleContext) {
     displayContentsNode =
       PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
     if (displayContentsNode) {
       oldStyleContext = displayContentsNode->mStyle->AsServo();
     }
   }
 
-  RefPtr<ServoComputedValues> computedValues =
-    aRestyleState.StyleSet().ResolveServoStyle(aElement);
-
-  // Note that we rely in the fact that we don't cascade pseudo-element styles
-  // separately right now (that is, if a pseudo style changes, the normal style
-  // changes too).
-  //
-  // Otherwise we should probably encode that information somehow to avoid
-  // expensive checks in the common case.
-  //
-  // Also, we're going to need to check for pseudos of display: contents
-  // elements, though that is buggy right now even in non-stylo mode, see
-  // bug 1251799.
-  const bool recreateContext = oldStyleContext &&
-    oldStyleContext->ComputedValues() != computedValues;
-
   Maybe<ServoRestyleState> thisFrameRestyleState;
   if (styleFrame) {
     auto type = isOutOfFlow
       ? ServoRestyleState::Type::OutOfFlow
       : ServoRestyleState::Type::InFlow;
 
     thisFrameRestyleState.emplace(*styleFrame, aRestyleState, changeHint, type);
   }
 
   // We can't really assume as used changes from display: contents elements (or
   // other elements without frames).
   ServoRestyleState& childrenRestyleState =
     thisFrameRestyleState ? *thisFrameRestyleState : aRestyleState;
 
   RefPtr<ServoStyleContext> newContext = nullptr;
-  if (recreateContext) {
+  if (wasRestyled && oldStyleContext) {
     MOZ_ASSERT(styleFrame || displayContentsNode);
+    RefPtr<ServoComputedValues> computedValues =
+      aRestyleState.StyleSet().ResolveServoStyle(aElement);
+    MOZ_ASSERT(oldStyleContext->ComputedValues() != computedValues);
 
     auto pseudo = aElement->GetPseudoElementType();
     nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
       ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);
 
     newContext = aRestyleState.StyleSet().GetContext(
       computedValues.forget(), aParentContext, pseudoTag, pseudo, aElement);
 
@@ -617,25 +610,25 @@ ServoRestyleManager::ProcessPostTraversa
 
   const bool traverseElementChildren =
     NeedsToTraverseElementChildren(*aElement, aRestyleBehavior);
   const bool descendantsNeedFrames =
     aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
   const bool forThrottledAnimationFlush =
     aRestyleBehavior == TraversalRestyleBehavior::ForThrottledAnimationFlush;
   const bool traverseTextChildren =
-    recreateContext || (!forThrottledAnimationFlush && descendantsNeedFrames);
-  bool recreatedAnyContext = recreateContext;
+    wasRestyled || (!forThrottledAnimationFlush && descendantsNeedFrames);
+  bool recreatedAnyContext = wasRestyled;
   if (traverseElementChildren || traverseTextChildren) {
     nsStyleContext* upToDateContext =
-      recreateContext ? newContext : oldStyleContext;
+      wasRestyled ? newContext : oldStyleContext;
 
     StyleChildrenIterator it(aElement);
     TextPostTraversalState textState(*upToDateContext,
-                                     displayContentsNode && recreateContext,
+                                     displayContentsNode && wasRestyled,
                                      childrenRestyleState);
     for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
       if (traverseElementChildren && n->IsElement()) {
         recreatedAnyContext |= ProcessPostTraversal(n->AsElement(),
                                                     upToDateContext,
                                                     childrenRestyleState,
                                                     aRestyleBehavior);
       } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
@@ -643,17 +636,17 @@ ServoRestyleManager::ProcessPostTraversa
       }
     }
   }
 
   // We want to update frame pseudo-element styles after we've traversed our
   // kids, because some of those updates (::first-line/::first-letter) need to
   // modify the styles of the kids, and the child traversal above would just
   // clobber those modifications.
-  if (recreateContext && styleFrame) {
+  if (wasRestyled && styleFrame) {
     UpdateFramePseudoElementStyles(styleFrame, childrenRestyleState);
   }
 
   if (!forThrottledAnimationFlush) {
     aElement->UnsetHasDirtyDescendantsForServo();
     aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
   }
   aElement->UnsetHasAnimationOnlyDirtyDescendantsForServo();
--- a/layout/style/ServoBindingList.h
+++ b/layout/style/ServoBindingList.h
@@ -489,17 +489,18 @@ SERVO_BINDING_FUNC(Servo_Initialize, voi
 SERVO_BINDING_FUNC(Servo_Shutdown, void)
 
 // Restyle and change hints.
 SERVO_BINDING_FUNC(Servo_NoteExplicitHints, void, RawGeckoElementBorrowed element,
                    nsRestyleHint restyle_hint, nsChangeHint change_hint)
 SERVO_BINDING_FUNC(Servo_TakeChangeHint,
                    nsChangeHint,
                    RawGeckoElementBorrowed element,
-                   mozilla::TraversalRestyleBehavior restyle_behavior)
+                   mozilla::TraversalRestyleBehavior restyle_behavior,
+                   bool* was_restyled)
 SERVO_BINDING_FUNC(Servo_ResolveStyle, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element,
                    RawServoStyleSetBorrowed set)
 SERVO_BINDING_FUNC(Servo_ResolvePseudoStyle, ServoComputedValuesStrong,
                    RawGeckoElementBorrowed element,
                    mozilla::CSSPseudoElementType pseudo_type,
                    bool is_probe,
                    ServoComputedValuesBorrowedOrNull inherited_style,