From c0b99199c19c112a60472e84f22bba15239c2ad0 Mon Sep 17 00:00:00 2001 From: "cam@mcc.id.au" Date: Fri, 13 Jan 2017 03:21:11 +0000 Subject: [PATCH] Bug 1331322 - Allow tagging of pseudo-implementing native anonymous content with the pseudo type at creation time, and eliminate explicit style contexts in nsIAnonymousContentCreator::ContentInfo. r=bholley MozReview-Commit-ID: LO0t92orjWZ --- dom/base/Element.h | 19 +++ dom/base/crashtests/crashtests.list | 2 +- dom/base/nsGkAtomList.h | 1 + layout/base/GeckoRestyleManager.cpp | 10 ++ layout/base/nsCSSFrameConstructor.cpp | 143 ++++++++++++++---- 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 - .../number/number-style-inheritance-ref.html | 6 + .../number/number-style-inheritance.html | 6 + .../reftests/forms/input/number/reftest.list | 3 + layout/style/res/forms.css | 2 + 19 files changed, 213 insertions(+), 110 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 diff --git a/dom/base/Element.h b/dom/base/Element.h index 7ca7b8fdabff..15f903f5dfc4 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -793,6 +793,25 @@ public: already_AddRefed GetElementsByClassName(const nsAString& aClassNames); + CSSPseudoElementType GetPseudoElementType() const { + if (!HasProperties()) { + return CSSPseudoElementType::NotPseudo; + } + nsresult rv = NS_OK; + auto raw = GetProperty(nsGkAtoms::pseudoProperty, &rv); + if (rv == NS_PROPTABLE_PROP_NOT_THERE) { + return CSSPseudoElementType::NotPseudo; + } + return CSSPseudoElementType(reinterpret_cast(raw)); + } + + void SetPseudoElementType(CSSPseudoElementType aPseudo) { + static_assert(sizeof(CSSPseudoElementType) <= sizeof(uintptr_t), + "Need to be able to store this in a void*"); + MOZ_ASSERT(aPseudo != CSSPseudoElementType::NotPseudo); + SetProperty(nsGkAtoms::pseudoProperty, reinterpret_cast(aPseudo)); + } + private: /** * Implement the algorithm specified at diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index c3f085f365b8..3b43e5c4607f 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -193,7 +193,7 @@ load 930250.html load 942979.html load 973401.html load 978646.html -asserts-if(stylo,1-11) pref(dom.webcomponents.enabled,true) load 1024428-1.html # bug 1324671 +skip-if(stylo) pref(dom.webcomponents.enabled,true) load 1024428-1.html # bug 1340009 load 1026714.html pref(dom.webcomponents.enabled,true) load 1027461-1.html pref(dom.webcomponents.enabled,true) load 1029710.html diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h index 311e46b50074..b4ef54183386 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2152,6 +2152,7 @@ GK_ATOM(lockedStyleStates, "lockedStyleStates") GK_ATOM(apzCallbackTransform, "apzCallbackTransform") GK_ATOM(restylableAnonymousNode, "restylableAnonymousNode") GK_ATOM(paintRequestTime, "PaintRequestTime") +GK_ATOM(pseudoProperty, "PseudoProperty") // CSSPseudoElementType // Languages for lang-specific transforms GK_ATOM(Japanese, "ja") diff --git a/layout/base/GeckoRestyleManager.cpp b/layout/base/GeckoRestyleManager.cpp index 0a8edd95ce82..d360b6240632 100644 --- a/layout/base/GeckoRestyleManager.cpp +++ b/layout/base/GeckoRestyleManager.cpp @@ -2012,6 +2012,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 534fb498ef96..23ce5df542c9 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1867,6 +1867,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 @@ -4250,11 +4251,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 && info.mContent->IsElement()) { + if (info.mContent->IsElement()) { styleSet->StyleNewSubtree(info.mContent->AsElement()); } } @@ -5034,24 +5033,37 @@ 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, - LazyComputeBehavior::Assert, - aState->mTreeMatchContext); + auto pseudoType = aContent->AsElement()->GetPseudoElementType(); + if (pseudoType == CSSPseudoElementType::NotPseudo) { + MOZ_ASSERT(!aOriginatingElementOrNull); + if (aState) { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext, + LazyComputeBehavior::Assert, + aState->mTreeMatchContext); + } else { + result = styleSet->ResolveStyleFor(aContent->AsElement(), + aParentStyleContext, + LazyComputeBehavior::Assert); + } } else { - result = styleSet->ResolveStyleFor(aContent->AsElement(), - aParentStyleContext, - LazyComputeBehavior::Assert); + 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"); @@ -10725,25 +10737,96 @@ 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->IsElement() || !content->AsElement()->HasServoData()); - styleContext = aAnonymousItems[i].mStyleContext.forget(); - } else { - // If we don't have an explicit style context, that means we need the - // ordinary computed values. Make sure we eagerly cascaded them when the - // anonymous nodes were created. - MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(), - content->AsElement()->HasServoData()); - styleContext = ResolveStyleContext(aFrame, content, &aState); + + // 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