Bug 1447476: Simplify ResolveStyleContext. r?xidorn draft
authorEmilio Cobos Álvarez <emilio@crisal.io>
Tue, 20 Mar 2018 20:25:50 +0100
changeset 770325 5b93c660daa6f27ca67457330c0e2409acbe244e
parent 770322 c43ba1ea661480cad0b2dc6d65796bc4acfbdb26
child 770326 2b2a684dc75e19ba890f6bd577ad7a68c8d7e5db
push id103378
push userbmo:emilio@crisal.io
push dateWed, 21 Mar 2018 00:05:51 +0000
reviewersxidorn
bugs1447476, 1447506
milestone61.0a1
Bug 1447476: Simplify ResolveStyleContext. r?xidorn We don't need the parent style context, nor the pseudo-element dance or anything like that. All end up in the same place, ResolveServoStyle. We cannot assert for text style resolution because of non-lazy frame construction, and we cannot remove non-lazy frame construction because of XBL. This is effectively the same code, since the old code passed the parent style around from the frame tree / display contents map, which didn't have a similar assertion either... Slow steps. I'll improve and cleanup LazyFC in bug 1447506. MozReview-Commit-ID: Ck4RCoFLGOi
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/style/ServoStyleSet.h
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2454,23 +2454,18 @@ nsCSSFrameConstructor::ConstructDocEleme
     if (!aDocElement->HasServoData()) {
       // NOTE(emilio): If the root has a non-null binding, we'll stop at the
       // document element and won't process any children, loading the bindings
       // (or failing to do so) will take care of the rest.
       set->StyleNewSubtree(aDocElement);
     }
   }
 
-  // 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 =
-    mPresShell->StyleSet()->ResolveStyleFor(aDocElement,
-                                            nullptr,
-                                            LazyComputeBehavior::Assert);
+    mPresShell->StyleSet()->AsServo()->ResolveServoStyle(aDocElement);
 
   const nsStyleDisplay* display = styleContext->StyleDisplay();
 
   // Ensure that our XBL bindings are installed.
   if (display->mBinding) {
     // Get the XBL loader.
     nsresult rv;
     bool resolveStyle;
@@ -2492,21 +2487,18 @@ nsCSSFrameConstructor::ConstructDocEleme
     if (binding) {
       // For backwards compat, keep firing the root's constructor
       // after all of its kids' constructors.  So tell the binding
       // manager about it right now.
       mDocument->BindingManager()->AddToAttachedQueue(binding);
     }
 
     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, LazyComputeBehavior::Assert);
+      styleContext =
+        mPresShell->StyleSet()->AsServo()->ResolveServoStyle(aDocElement);
       display = styleContext->StyleDisplay();
     }
   }
 
   // --------- IF SCROLLABLE WRAP IN SCROLLFRAME --------
 
   NS_ASSERTION(!display->IsScrollableOverflow() ||
                state.mPresContext->IsPaginated() ||
@@ -5024,108 +5016,42 @@ nsCSSFrameConstructor::InitAndRestoreFra
 
   if (aAllowCounters &&
       mCounterManager.AddCounterResetsAndIncrements(aNewFrame)) {
     CountersDirty();
   }
 }
 
 already_AddRefed<nsStyleContext>
-nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
-                                           nsIContent* aContainer,
-                                           nsIContent* aChild)
-{
-  MOZ_ASSERT(aContainer, "Must have parent here");
-  // XXX uncomment when bug 1089223 is fixed:
-  // MOZ_ASSERT(aContainer == aChild->GetFlattenedTreeParent());
-  nsStyleContext* parentStyleContext = GetDisplayContentsStyleFor(aContainer);
-  if (MOZ_LIKELY(!parentStyleContext)) {
-    aParentFrame = nsFrame::CorrectStyleParentFrame(aParentFrame, nullptr);
-    if (aParentFrame) {
-      MOZ_ASSERT(aParentFrame->GetContent() == aContainer);
-      // Resolve the style context based on the content object and the parent
-      // style context
-      parentStyleContext = aParentFrame->StyleContext();
-    } else {
-      // Perhaps aParentFrame is a canvasFrame and we're replicating
-      // fixed-pos frames.
-      // XXX should we create a way to tell ConstructFrame which style
-      // context to use, and pass it the style context for the
-      // previous page's fixed-pos frame?
-    }
-  }
-
-  return ResolveStyleContext(parentStyleContext, aChild);
-}
-
-already_AddRefed<nsStyleContext>
-nsCSSFrameConstructor::ResolveStyleContext(nsIFrame* aParentFrame,
-                                           nsIContent* aChild)
-{
-  return ResolveStyleContext(aParentFrame, aChild->GetFlattenedTreeParent(), aChild);
-}
-
-already_AddRefed<nsStyleContext>
-nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint& aInsertion,
-                                           nsIContent* aChild)
-{
-  return ResolveStyleContext(aInsertion.mParentFrame, aInsertion.mContainer,
-                             aChild);
-}
-
-already_AddRefed<nsStyleContext>
-nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
-                                           nsIContent* aContent,
-                                           Element* aOriginatingElementOrNull)
-{
-  StyleSetHandle styleSet = mPresShell->StyleSet();
-
-  RefPtr<nsStyleContext> result;
+nsCSSFrameConstructor::ResolveStyleContext(nsIContent* aContent)
+{
+  ServoStyleSet* styleSet = mPresShell->StyleSet()->AsServo();
+
   if (aContent->IsElement()) {
-    auto pseudoType = aContent->AsElement()->GetPseudoElementType();
-    if (pseudoType == CSSPseudoElementType::NotPseudo) {
-      MOZ_ASSERT(!aOriginatingElementOrNull);
-      result = styleSet->ResolveStyleFor(aContent->AsElement(),
-                                         aParentStyleContext,
-                                         LazyComputeBehavior::Assert);
-    } else {
-      MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree());
-      if (!aOriginatingElementOrNull) {
-        // For pseudo-implementing NAC created by JS using the ChromeOnly
-        // document.createElement(..., { pseudo: ... }) API, we find the
-        // originating element by lookup the tree until we find a non-NAC
-        // ancestor.  (These are the correct semantics for C++-generated pseudo-
-        // implementing NAC as well, but for those cases we already have a
-        // correct originating element passed in.)
-        MOZ_ASSERT(nsCSSPseudoElements::PseudoElementIsJSCreatedNAC(pseudoType));
-        aOriginatingElementOrNull =
-          nsContentUtils::GetClosestNonNativeAnonymousAncestor(aContent->AsElement());
-      }
-      MOZ_ASSERT(aOriginatingElementOrNull);
-      result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull,
-                                                   pseudoType,
-                                                   aParentStyleContext,
-                                                   aContent->AsElement());
-    }
-  } else {
-    MOZ_ASSERT(!aOriginatingElementOrNull);
-    NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
-                 "shouldn't waste time creating style contexts for "
-                 "comments and processing instructions");
-    result = styleSet->ResolveStyleForText(aContent, aParentStyleContext);
-  }
-
-  // ServoRestyleManager does not handle transitions yet, and when it does
-  // it probably won't need to track reframed style contexts to start
-  // transitions correctly.
-  if (RestyleManager()->IsGecko()) {
-    MOZ_CRASH("old style system disabled");
-  }
-
-  return result.forget();
+    return styleSet->ResolveServoStyle(aContent->AsElement());
+  }
+
+  MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eTEXT),
+             "shouldn't waste time creating style contexts for "
+             "comments and processing instructions");
+
+  Element* parent = aContent->GetFlattenedTreeParentElement();
+  MOZ_ASSERT(parent, "Text out of the flattened tree?");
+
+  // FIXME(emilio): We can't use ResolveServoStyle properly because this text
+  // node can come from non-lazy frame construction, in which case our style can
+  // indeed be out-of-date. After an eventual XBL removal, this can go.
+  //
+  // Also, this probably doesn't need to be a strong ref...
+  //
+  // Do NOT add new callers to this function in this file, ever, or I'll find
+  // out.
+  RefPtr<ServoStyleContext> parentStyle =
+    Servo_Element_GetPrimaryComputedValues(parent).Consume();
+  return styleSet->ResolveStyleForText(aContent, parentStyle);
 }
 
 // MathML Mod - RBS
 void
 nsCSSFrameConstructor::FlushAccumulatedBlock(nsFrameConstructorState& aState,
                                              nsIContent* aContent,
                                              nsContainerFrame* aParentFrame,
                                              nsFrameItems& aBlockItems,
@@ -5707,18 +5633,17 @@ nsCSSFrameConstructor::AddFrameConstruct
                                                  bool aSuppressWhiteSpaceOptimizations,
                                                  const InsertionPoint& aInsertion,
                                                  FrameConstructionItemList& aItems)
 {
   nsContainerFrame* parentFrame = aInsertion.mParentFrame;
   if (!ShouldCreateItemsForChild(aState, aContent, parentFrame)) {
     return;
   }
-  RefPtr<nsStyleContext> styleContext =
-    ResolveStyleContext(aInsertion, aContent);
+  RefPtr<nsStyleContext> styleContext = ResolveStyleContext(aContent);
   DoAddFrameConstructionItems(aState, aContent, styleContext,
                               aSuppressWhiteSpaceOptimizations, parentFrame,
                               nullptr, aItems);
 }
 
 void
 nsCSSFrameConstructor::SetAsUndisplayedContent(nsFrameConstructorState& aState,
                                                FrameConstructionItemList& aList,
@@ -5978,18 +5903,17 @@ nsCSSFrameConstructor::AddFrameConstruct
                                aItems);
 
     FlattenedChildIterator iter(aContent);
     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
       if (!ShouldCreateItemsForChild(aState, child, aParentFrame)) {
         continue;
       }
 
-      RefPtr<nsStyleContext> childContext =
-        ResolveStyleContext(styleContext, child);
+      RefPtr<nsStyleContext> childContext = ResolveStyleContext(child);
       DoAddFrameConstructionItems(aState, child, childContext,
                                   aSuppressWhiteSpaceOptimizations,
                                   aParentFrame, aAnonChildren, aItems);
     }
     aItems.SetParentHasNoXBLChildren(!iter.XBLInvolved());
 
     CreateGeneratedContentItem(aState, aParentFrame, aContent->AsElement(),
                                styleContext, CSSPseudoElementType::after,
@@ -6538,18 +6462,17 @@ nsCSSFrameConstructor::IsValidSibling(ns
       }
       if (aContent->IsNodeOfType(nsINode::eCOMMENT) ||
           aContent->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION)) {
         // Comments and processing instructions never have frames, so we
         // should not try to generate style contexts for them.
         return false;
       }
       // FIXME(emilio): This is buggy some times, see bug 1424656.
-      RefPtr<nsStyleContext> styleContext =
-        ResolveStyleContext(styleParent, aContent);
+      RefPtr<nsStyleContext> styleContext = ResolveStyleContext(aContent);
       const nsStyleDisplay* display = styleContext->StyleDisplay();
       aDisplay = display->mDisplay;
     }
     if (LayoutFrameType::Menu == parentType) {
       return
         (StyleDisplay::MozPopup == aDisplay) ==
         (StyleDisplay::MozPopup == siblingDisplay);
     }
@@ -10400,113 +10323,17 @@ nsCSSFrameConstructor::AddFCItemsForAnon
                !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
                "Why is someone creating garbage anonymous content");
 
     // Make sure we eagerly performed the servo cascade when the anonymous
     // nodes were created.
     MOZ_ASSERT(!content->IsStyledByServo() || !content->IsElement() ||
                content->AsElement()->HasServoData());
 
-    // Determine whether this NAC is pseudo-implementing.
-    nsAtom* pseudo = nullptr;
-    if (content->IsElement()) {
-      auto pseudoType = content->AsElement()->GetPseudoElementType();
-      if (pseudoType != CSSPseudoElementType::NotPseudo) {
-        pseudo = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
-      }
-    }
-
-    // Determine the appropriate parent style for this NAC, and if the NAC
-    // implements a pseudo-element, the appropriate originating element
-    // (that is to say, the element to the left of the ::pseudo-element in
-    // the selector). This is all rather tricky, and merits some discussion.
-    //
-    // First, it's important to note that author stylesheets generally do not
-    // apply to elements in native-anonymous subtrees. The exceptions to
-    // this are web-exposed pseudo-elements, where authors can style the
-    // pseudo-implementing NAC if the originating element is not itself in a NAC
-    // subtree.
-    //
-    // For this reason, it's very important that we avoid using a style parent
-    // that is inside a NAC subtree together with an originating element that
-    // is not inside a NAC subtree, since that would allow authors to
-    // explicitly inherit styles from internal elements, potentially making
-    // the NAC hierarchy observable. To ensure this, and generally simplify
-    // things, we always set the originating element to the style parent.
-    //
-    // As a consequence of the above, all web-exposed pseudo-elements (which,
-    // by definition, must have a content-accessible originating element) must
-    // also inherit style from that same content-accessible element. To avoid
-    // unintuitive behavior differences between NAC elements that do and don't
-    // correspond to web-exposed pseudo-elements, we follow this protocol for
-    // all NAC, pseudo-implementing or not.
-    //
-    // However, things get tricky with the <video> element, where we have a
-    // bunch of XBL-generated anonymous content descending from a native-
-    // anonymous XULElement. The XBL elements inherit style from their
-    // flattened tree parent, because that's how XBL works. But then we need
-    // to figure out what to do when one of those anonymous XBL elements
-    // (like an <input> element) generates its own (possibly pseudo-element-
-    // implementing) NAC.
-    //
-    // In this case, we inherit style from the XBL-generated NAC-creating
-    // element, rather than the <video> element. There are a number of good
-    // reasons for this. First, inheriting from the great-grandparent while
-    // the parent inherits from the grandparent would be bizarre at best.
-    // Second, exposing pseudo-elements from elements within our particular
-    // XBL implementation would allow content styles to (un)intentionally
-    // alter the video controls, which would be very bad. Third, our UA
-    // stylesheets have selectors like:
-    //
-    // input[type=range][orient=horizontal]::-moz-range-track
-    //
-    // and we need to make sure that the originating element is the <input>,
-    // not the <video>, because that's where the |orient| attribute lives.
-    //
-    // The upshot of all of this is that, to find the style parent (and
-    // originating element, if applicable), we walk up our parent chain to the
-    // first element that is not itself NAC (distinct from whether it happens
-    // to be in a NAC subtree).
-    //
-    // The one exception to all of this is scrollbar content, which we parent
-    // directly to the scrollframe. This is because the special-snowflake
-    // construction of scroll frames doesn't result in the placeholder frame
-    // being constructed until later, which means that GetInFlowParent() doesn't
-    // work right in the case of out-of-flow scrollframes.
-    //
-    // To implement all this, we need to pass the correct parent style context
-    // here because SetPrimaryFrame() may not have been called on the content
-    // yet and thus ResolveStyleContext can't find it otherwise.
-    //
-    // We don't need to worry about display:contents here, because such
-    // elements don't get a frame and thus can't generate NAC. But we do need
-    // to worry about anonymous boxes, which CorrectStyleParentFrame handles
-    // for us.
-    nsIFrame* inheritFrame = aFrame;
-    if (!content->IsNativeScrollbarContent()) {
-      while (inheritFrame->GetContent()->IsNativeAnonymous()) {
-        inheritFrame = inheritFrame->GetInFlowParent();
-      }
-    }
-
-    nsIFrame* styleParentFrame =
-      nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo);
-    // The only way we can not have a style parent now is if inheritFrame is the
-    // canvas frame and we're the NAC parent for all the things added via
-    // nsIDocument::InsertAnonymousContent.
-    MOZ_ASSERT(styleParentFrame || inheritFrame->IsCanvasFrame());
-    // And that anonymous div has no pseudo.
-    MOZ_ASSERT(styleParentFrame || !pseudo);
-
-    Element* originating =
-      pseudo ? styleParentFrame->GetContent()->AsElement() : nullptr;
-    nsStyleContext* parentStyle =
-      styleParentFrame ? styleParentFrame->StyleContext() : nullptr;
-    RefPtr<nsStyleContext> styleContext =
-      ResolveStyleContext(parentStyle, content, originating);
+    RefPtr<nsStyleContext> styleContext = ResolveStyleContext(content);
 
     nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
     if (!aAnonymousItems[i].mChildren.IsEmpty()) {
       anonChildren = &aAnonymousItems[i].mChildren;
     }
 
     uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
                      ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
@@ -11472,21 +11299,17 @@ nsCSSFrameConstructor::CreateListBoxCont
   if (aParentFrame) {
     nsFrameItems            frameItems;
     nsFrameConstructorState state(mPresShell,
                                   GetAbsoluteContainingBlock(aParentFrame, FIXED_POS),
                                   GetAbsoluteContainingBlock(aParentFrame, ABS_POS),
                                   GetFloatContainingBlock(aParentFrame),
                                   do_AddRef(mTempFrameTreeState));
 
-    // If we ever initialize the ancestor filter on |state|, make sure
-    // to push the right parent!
-
-    RefPtr<nsStyleContext> styleContext =
-      ResolveStyleContext(aParentFrame, aChild);
+    RefPtr<nsStyleContext> styleContext = ResolveStyleContext(aChild);
 
     // Pre-check for display "none" - only if we find that, do we create
     // any frame at all
     const nsStyleDisplay* display = styleContext->StyleDisplay();
 
     if (StyleDisplay::None == display->mDisplay) {
       *aNewFrame = nullptr;
       return;
@@ -11853,18 +11676,17 @@ nsCSSFrameConstructor::BuildInlineChildI
       }
 
       // See comment explaining why we need to remove the "is possible
       // restyle root" flags in AddFrameConstructionItems.  But note
       // that we can remove all restyle flags, just like in
       // ProcessChildren and for the same reason.
       content->UnsetRestyleFlagsIfGecko();
 
-      RefPtr<nsStyleContext> childContext =
-        ResolveStyleContext(parentStyleContext, content);
+      RefPtr<nsStyleContext> childContext = ResolveStyleContext(content);
 
       AddFrameConstructionItemsInternal(aState, content, nullptr,
                                         content->NodeInfo()->NameAtom(),
                                         content->GetNameSpaceID(),
                                         iter.XBLInvolved(), childContext,
                                         flags, nullptr,
                                         aParentItem.mChildItems);
     }
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -384,31 +384,17 @@ private:
                                        nsContainerFrame*& aCanvasFrame);
 
   void InitAndRestoreFrame (const nsFrameConstructorState& aState,
                             nsIContent*                    aContent,
                             nsContainerFrame*              aParentFrame,
                             nsIFrame*                      aNewFrame,
                             bool                           aAllowCounters = true);
 
-  already_AddRefed<nsStyleContext>
-  ResolveStyleContext(nsIFrame* aParentFrame,
-                      nsIContent* aContainer,
-                      nsIContent* aChild);
-
-  already_AddRefed<nsStyleContext>
-  ResolveStyleContext(nsIFrame* aParentFrame, nsIContent* aChild);
-
-  already_AddRefed<nsStyleContext>
-  ResolveStyleContext(const InsertionPoint& aInsertion, nsIContent* aChild);
-
-  already_AddRefed<nsStyleContext>
-  ResolveStyleContext(nsStyleContext* aParentStyleContext,
-                      nsIContent* aContent,
-                      Element* aOriginatingElementOrNull = nullptr);
+  already_AddRefed<nsStyleContext> ResolveStyleContext(nsIContent* aContent);
 
   // Add the frame construction items for the given aContent and aParentFrame
   // to the list.  This might add more than one item in some rare cases.
   // If aSuppressWhiteSpaceOptimizations is true, optimizations that
   // may suppress the construction of white-space-only text frames
   // must be skipped for these items and items around them.
   void AddFrameConstructionItems(nsFrameConstructorState& aState,
                                  nsIContent*              aContent,
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -163,18 +163,17 @@ public:
   // Get a style context for a text node (which no rules will match).
   //
   // The returned style context will have nsCSSAnonBoxes::mozText as its pseudo.
   //
   // (Perhaps mozText should go away and we shouldn't even create style
   // contexts for such content nodes, when text-combine-upright is not
   // present.  However, not doing any rule matching for them is a first step.)
   already_AddRefed<ServoStyleContext>
-  ResolveStyleForText(nsIContent* aTextNode,
-                      ServoStyleContext* aParentContext);
+  ResolveStyleForText(nsIContent* aTextNode, ServoStyleContext* aParentContext);
 
   // Get a style context for a first-letter continuation (which no rules will
   // match).
   //
   // The returned style context will have
   // nsCSSAnonBoxes::firstLetterContinuation as its pseudo.
   //
   // (Perhaps nsCSSAnonBoxes::firstLetterContinuation should go away and we