From 4375774c901bac4bd1ecaa35b40f55397044b7e9 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Thu, 16 Apr 2020 16:37:28 -0400 Subject: Bug 1331322 - Allow tagging of pseudo-implementing native anonymous content with the pseudo type at creation time * Stop using a node bit for HasExplicitBaseURI * Move MAY_HAVE_CLASS to mBoolFlags * Add a flag to indicate that a node is native anonymous content * Allow tagging of pseudo-implementing native anonymous content with the pseudo type at creation time, and eliminate explicit style contexts in nsIAnonymousContentCreator::ContentInfo Tag #1375 --- layout/base/RestyleManager.cpp | 10 ++ layout/base/nsCSSFrameConstructor.cpp | 148 +++++++++++++++++---- layout/base/nsCSSFrameConstructor.h | 3 +- layout/forms/nsColorControlFrame.cpp | 7 +- layout/forms/nsMeterFrame.cpp | 9 +- layout/forms/nsNumberControlFrame.cpp | 33 ++--- layout/forms/nsNumberControlFrame.h | 3 +- layout/forms/nsProgressFrame.cpp | 7 +- layout/forms/nsRangeFrame.cpp | 8 +- layout/forms/nsTextControlFrame.cpp | 26 +--- layout/generic/nsFrame.cpp | 30 +++++ layout/generic/nsIAnonymousContentCreator.h | 5 - .../input/number/number-style-inheritance-ref.html | 6 + .../input/number/number-style-inheritance.html | 6 + layout/reftests/forms/input/number/reftest.list | 3 + layout/style/res/forms.css | 2 + 16 files changed, 199 insertions(+), 107 deletions(-) create mode 100644 layout/reftests/forms/input/number/number-style-inheritance-ref.html create mode 100644 layout/reftests/forms/input/number/number-style-inheritance.html (limited to 'layout') diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 124b5535e..c3eeb8c71 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -2341,6 +2341,16 @@ ElementRestyler::ComputeRestyleResultFromFrame(nsIFrame* aSelf, return; } + // Each NAC element inherits from the first non-NAC ancestor, so child + // NAC may inherit from our parent instead of us. That means we can't + // cull traversal if our style context didn't change. + if (aSelf->GetContent() && aSelf->GetContent()->IsNativeAnonymous()) { + LOG_RESTYLE_CONTINUE("native anonymous content"); + aRestyleResult = RestyleResult::eContinue; + aCanStopWithStyleChange = false; + return; + } + // Style changes might have moved children between the two nsLetterFrames // (the one matching ::first-letter and the one containing the rest of the // content). Continue restyling to the children of the nsLetterFrame so diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 5a1036232..a2dec55e1 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1865,6 +1865,7 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat if (NS_FAILED(rv)) return; container->SetIsNativeAnonymousRoot(); + container->SetPseudoElementType(aPseudoElement); // If the parent is in a shadow tree, make sure we don't // bind with a document because shadow roots and its descendants @@ -4161,6 +4162,13 @@ ConnectAnonymousTreeDescendants(nsIContent* aParent, } } +void SetNativeAnonymousBitOnDescendants(nsIContent *aRoot) +{ + for (nsIContent* curr = aRoot; curr; curr = curr->GetNextNode(aRoot)) { + curr->SetFlags(NODE_IS_NATIVE_ANONYMOUS); + } +} + nsresult nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIFrame* aParentFrame, @@ -4182,16 +4190,17 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, nsIContent* content = aContent[i].mContent; NS_ASSERTION(content, "null anonymous content?"); + ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); + // least-surprise CSS binding until we do the SVG specified // cascading rules for - bug 265894 if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) { content->SetFlags(NODE_IS_ANONYMOUS_ROOT); } else { content->SetIsNativeAnonymousRoot(); + SetNativeAnonymousBitOnDescendants(content); } - ConnectAnonymousTreeDescendants(content, aContent[i].mChildren); - bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE); // If the parent is in a shadow tree, make sure we don't @@ -4216,11 +4225,9 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent, } if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) { - // Eagerly compute styles for the anonymous content tree, but only do so - // if the content doesn't have an explicit style context (if it does, we - // don't need the normal computed values). + // Eagerly compute styles for the anonymous content tree. for (auto& info : aContent) { - if (!info.mStyleContext) { + if (info.mContent->IsElement()) { styleSet->StyleNewSubtree(info.mContent); } } @@ -4999,22 +5006,35 @@ nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint& aInsertion, already_AddRefed nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState) + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull) { StyleSetHandle styleSet = mPresShell->StyleSet(); aContent->OwnerDoc()->FlushPendingLinkUpdates(); RefPtr result; if (aContent->IsElement()) { - if (aState) { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext, - aState->mTreeMatchContext); + auto pseudoType = aContent->AsElement()->GetPseudoElementType(); + if (pseudoType == CSSPseudoElementType::NotPseudo) { + MOZ_ASSERT(!aOriginatingElementOrNull); + if (aState) { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext, + aState->mTreeMatchContext); + } else { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext); + } } else { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext); + MOZ_ASSERT(aOriginatingElementOrNull); + MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree()); + 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"); @@ -10643,23 +10663,95 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent( RefPtr styleContext; TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper 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()); - styleContext = aAnonymousItems[i].mStyleContext.forget(); + + // Make sure we eagerly performed the servo cascade when the anonymous + // nodes were created. + MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), + content->AsElement()->HasServoData()); + + // Determine whether this NAC is pseudo-implementing. + nsIAtom* 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