diff options
25 files changed, 267 insertions, 128 deletions
diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index 5a92853ae..91a3b63d7 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -166,8 +166,7 @@ nsIContent::DoGetID() const const nsAttrValue* Element::DoGetClasses() const { - MOZ_ASSERT(HasFlag(NODE_MAY_HAVE_CLASS), "Unexpected call"); - + MOZ_ASSERT(MayHaveClass(), "Unexpected call"); if (IsSVGElement()) { const nsAttrValue* animClass = static_cast<const nsSVGElement*>(this)->GetAnimatedClassName(); @@ -2702,7 +2701,7 @@ Element::ParseAttribute(int32_t aNamespaceID, { if (aNamespaceID == kNameSpaceID_None) { if (aAttribute == nsGkAtoms::_class) { - SetFlags(NODE_MAY_HAVE_CLASS); + SetMayHaveClass(); // Result should have been preparsed above. return true; } diff --git a/dom/base/Element.h b/dom/base/Element.h index 1b29f0346..8139236aa 100644 --- a/dom/base/Element.h +++ b/dom/base/Element.h @@ -591,7 +591,7 @@ public: * guaranteed (e.g. we could have class=""). */ const nsAttrValue* GetClasses() const { - if (HasFlag(NODE_MAY_HAVE_CLASS)) { + if (MayHaveClass()) { return DoGetClasses(); } return nullptr; @@ -763,6 +763,25 @@ public: already_AddRefed<nsIHTMLCollection> 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<uintptr_t>(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<void*>(aPseudo)); + } + private: /** * Implement the algorithm specified at diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 0fb597b30..d8b5e9625 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 -pref(dom.webcomponents.enabled,true) load 1024428-1.html +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 73a3a02b1..c7c1197b1 100644 --- a/dom/base/nsGkAtomList.h +++ b/dom/base/nsGkAtomList.h @@ -2162,6 +2162,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/dom/base/nsIContent.h b/dom/base/nsIContent.h index 52f2ba5b2..ff0c57e8c 100644 --- a/dom/base/nsIContent.h +++ b/dom/base/nsIContent.h @@ -193,7 +193,7 @@ public: void SetIsNativeAnonymousRoot() { SetFlags(NODE_IS_ANONYMOUS_ROOT | NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | - NODE_IS_NATIVE_ANONYMOUS_ROOT); + NODE_IS_NATIVE_ANONYMOUS_ROOT | NODE_IS_NATIVE_ANONYMOUS); } /** diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 0b56f519d..560ded6a9 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -1515,7 +1515,6 @@ nsINode::SetExplicitBaseURI(nsIURI* aURI) { nsresult rv = SetProperty(nsGkAtoms::baseURIProperty, aURI, ReleaseURI); if (NS_SUCCEEDED(rv)) { - SetHasExplicitBaseURI(); NS_ADDREF(aURI); } return rv; diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index a0d972f80..0f882445f 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -126,9 +126,28 @@ enum { NODE_IS_EDITABLE = NODE_FLAG_BIT(7), - // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the - // node in fact has a class, but may be set even if it doesn't. - NODE_MAY_HAVE_CLASS = NODE_FLAG_BIT(8), + // This node was created by layout as native anonymous content. This + // generally corresponds to things created by nsIAnonymousContentCreator, + // though there are exceptions (svg:use content does not have this flag + // set, and any non-nsIAnonymousContentCreator callers of + // SetIsNativeAnonymousRoot also get this flag). + // + // One very important aspect here is that this node is not transitive over + // the subtree (if you want that, use NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE). + // If Gecko code somewhere attaches children to a node with this bit set, + // the children will not have the bit themselves unless the calling code sets + // it explicitly. This means that XBL content bound to NAC doesn't get this + // bit, nor do nodes inserted by editor. + // + // For now, this bit exists primarily to control style inheritance behavior, + // since the nodes for which we set it are often used to implement pseudo- + // elements, which need to inherit style from a script-visible element. + // + // A more general principle for this bit might be this: If the node is entirely + // a detail of layout, is not script-observable in any way, and other engines + // might accomplish the same task with a nodeless layout frame, then the node + // should have this bit set. + NODE_IS_NATIVE_ANONYMOUS = NODE_FLAG_BIT(8), // Whether the node participates in a shadow tree. NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(9), @@ -1212,6 +1231,15 @@ public: } /** + * Returns true if |this| is native anonymous (i.e. created by + * nsIAnonymousContentCreator); + */ + bool IsNativeAnonymous() const + { + return HasFlag(NODE_IS_NATIVE_ANONYMOUS); + } + + /** * Returns true if |this| or any of its ancestors is native anonymous. */ bool IsInNativeAnonymousSubtree() const @@ -1343,10 +1371,11 @@ public: protected: nsIURI* GetExplicitBaseURI() const { - if (HasExplicitBaseURI()) { - return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty)); + if (!HasProperties()) { + return nullptr; } - return nullptr; + + return static_cast<nsIURI*>(GetProperty(nsGkAtoms::baseURIProperty)); } public: @@ -1549,6 +1578,8 @@ private: // cases lie for nsXMLElement, such as when the node has been moved between // documents with different id mappings. ElementHasID, + // Set if the element might have a class. + ElementMayHaveClass, // Set if the element might have inline style. ElementMayHaveStyle, // Set if the element has a name attribute set. @@ -1567,8 +1598,6 @@ private: // Maybe set if the node is a root of a subtree // which needs to be kept in the purple buffer. NodeIsPurpleRoot, - // Set if the node has an explicit base URI stored - NodeHasExplicitBaseURI, // Set if the element has some style states locked ElementHasLockedStyleStates, // Set if element has pointer locked @@ -1645,6 +1674,8 @@ public: { SetBoolFlag(NodeHasRenderingObservers, aValue); } bool IsContent() const { return GetBoolFlag(NodeIsContent); } bool HasID() const { return GetBoolFlag(ElementHasID); } + bool MayHaveClass() const { return GetBoolFlag(ElementMayHaveClass); } + void SetMayHaveClass() { SetBoolFlag(ElementMayHaveClass); } bool MayHaveStyle() const { return GetBoolFlag(ElementMayHaveStyle); } bool HasName() const { return GetBoolFlag(ElementHasName); } bool MayHaveContentEditableAttr() const @@ -1774,8 +1805,6 @@ protected: void ClearHasName() { ClearBoolFlag(ElementHasName); } void SetMayHaveContentEditableAttr() { SetBoolFlag(ElementMayHaveContentEditableAttr); } - bool HasExplicitBaseURI() const { return GetBoolFlag(NodeHasExplicitBaseURI); } - void SetHasExplicitBaseURI() { SetBoolFlag(NodeHasExplicitBaseURI); } void SetHasLockedStyleStates() { SetBoolFlag(ElementHasLockedStyleStates); } void ClearHasLockedStyleStates() { ClearBoolFlag(ElementHasLockedStyleStates); } bool HasLockedStyleStates() const diff --git a/dom/svg/nsSVGClass.cpp b/dom/svg/nsSVGClass.cpp index 6b64c3be7..c5a825be4 100644 --- a/dom/svg/nsSVGClass.cpp +++ b/dom/svg/nsSVGClass.cpp @@ -70,7 +70,7 @@ nsSVGClass::SetBaseValue(const nsAString& aValue, { NS_ASSERTION(aSVGElement, "Null element passed to SetBaseValue"); - aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + aSVGElement->SetMayHaveClass(); if (aDoSetAttr) { aSVGElement->SetAttr(kNameSpaceID_None, nsGkAtoms::_class, aValue, true); } @@ -106,7 +106,7 @@ nsSVGClass::SetAnimValue(const nsAString& aValue, nsSVGElement *aSVGElement) mAnimVal = new nsString(); } *mAnimVal = aValue; - aSVGElement->SetFlags(NODE_MAY_HAVE_CLASS); + aSVGElement->SetMayHaveClass(); aSVGElement->DidAnimateClass(); } diff --git a/dom/xul/nsXULElement.cpp b/dom/xul/nsXULElement.cpp index bc9215086..c7a539370 100644 --- a/dom/xul/nsXULElement.cpp +++ b/dom/xul/nsXULElement.cpp @@ -204,7 +204,7 @@ nsXULElement::Create(nsXULPrototypeElement* aPrototype, mozilla::dom::NodeInfo * element->SetHasID(); } if (aPrototype->mHasClassAttribute) { - element->SetFlags(NODE_MAY_HAVE_CLASS); + element->SetMayHaveClass(); } if (aPrototype->mHasStyleAttribute) { element->SetMayHaveStyle(); @@ -374,7 +374,7 @@ nsXULElement::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const element->SetHasID(); } if (originalName->Equals(nsGkAtoms::_class)) { - element->SetFlags(NODE_MAY_HAVE_CLASS); + element->SetMayHaveClass(); } if (originalName->Equals(nsGkAtoms::style)) { element->SetMayHaveStyle(); 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 <svg:use> - 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<nsStyleContext> nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState) + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull) { StyleSetHandle styleSet = mPresShell->StyleSet(); aContent->OwnerDoc()->FlushPendingLinkUpdates(); RefPtr<nsStyleContext> 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<nsStyleContext> 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 <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). + // + // 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; + while (inheritFrame->GetContent()->IsNativeAnonymous()) { + inheritFrame = inheritFrame->GetParent(); + } + if (inheritFrame->GetType() == nsGkAtoms::canvasFrame) { + // CorrectStyleParentFrame returns nullptr if the prospective parent is + // the canvas frame, so avoid calling it in that situation. } 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->HasServoData()); - styleContext = ResolveStyleContext(aFrame, content, &aState); + inheritFrame = nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo); } + Element* originating = pseudo ? inheritFrame->GetContent()->AsElement() : nullptr; + + styleContext = + ResolveStyleContext(inheritFrame->StyleContext(), content, &aState, originating); nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr; if (!aAnonymousItems[i].mChildren.IsEmpty()) { diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h index 89e5594e4..db0d92566 100644 --- a/layout/base/nsCSSFrameConstructor.h +++ b/layout/base/nsCSSFrameConstructor.h @@ -364,7 +364,8 @@ private: already_AddRefed<nsStyleContext> ResolveStyleContext(nsStyleContext* aParentStyleContext, nsIContent* aContent, - nsFrameConstructorState* aState); + nsFrameConstructorState* aState, + Element* aOriginatingElementOrNull = nullptr); // 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. diff --git a/layout/forms/nsColorControlFrame.cpp b/layout/forms/nsColorControlFrame.cpp index e0bae43a9..63aee814a 100644 --- a/layout/forms/nsColorControlFrame.cpp +++ b/layout/forms/nsColorControlFrame.cpp @@ -67,6 +67,7 @@ nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) { nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc(); mColorContent = doc->CreateHTMLElement(nsGkAtoms::div); + mColorContent->SetPseudoElementType(CSSPseudoElementType::mozColorSwatch); // Mark the element to be native anonymous before setting any attributes. mColorContent->SetIsNativeAnonymousRoot(); @@ -74,11 +75,7 @@ nsColorControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) nsresult rv = UpdateColor(); NS_ENSURE_SUCCESS(rv, rv); - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozColorSwatch; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mColorContent->AsElement()); - if (!aElements.AppendElement(ContentInfo(mColorContent, newStyleContext))) { + if (!aElements.AppendElement(mColorContent)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsMeterFrame.cpp b/layout/forms/nsMeterFrame.cpp index 1f3bd8022..6ef362820 100644 --- a/layout/forms/nsMeterFrame.cpp +++ b/layout/forms/nsMeterFrame.cpp @@ -75,14 +75,9 @@ nsMeterFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); // Associate ::-moz-meter-bar pseudo-element to the anonymous child. - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozMeterBar; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mBarDiv->AsElement()); + mBarDiv->SetPseudoElementType(CSSPseudoElementType::mozMeterBar); - if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { - return NS_ERROR_OUT_OF_MEMORY; - } + aElements.AppendElement(mBarDiv); return NS_OK; } diff --git a/layout/forms/nsNumberControlFrame.cpp b/layout/forms/nsNumberControlFrame.cpp index 9724109cb..64e3df0fe 100644 --- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -325,27 +325,15 @@ nsresult nsNumberControlFrame::MakeAnonymousElement(Element** aResult, nsTArray<ContentInfo>& aElements, nsIAtom* aTagName, - CSSPseudoElementType aPseudoType, - nsStyleContext* aParentContext) + CSSPseudoElementType aPseudoType) { // Get the NodeInfoManager and tag necessary to create the anonymous divs. nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc(); RefPtr<Element> resultElement = doc->CreateHTMLElement(aTagName); + resultElement->SetPseudoElementType(aPseudoType); - // If we legitimately fail this assertion and need to allow - // non-pseudo-element anonymous children, then we'll need to add a branch - // that calls ResolveStyleFor((*aResult)->AsElement(), aParentContext)") to - // set newStyleContext. - NS_ASSERTION(aPseudoType != CSSPseudoElementType::NotPseudo, - "Expecting anonymous children to all be pseudo-elements"); // Associate the pseudo-element with the anonymous child - RefPtr<nsStyleContext> newStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(), - aPseudoType, - aParentContext, - resultElement); - - if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) { + if (!aElements.AppendElement(resultElement)) { return NS_ERROR_OUT_OF_MEMORY; } @@ -382,8 +370,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper), aElements, nsGkAtoms::div, - CSSPseudoElementType::mozNumberWrapper, - mStyleContext); + CSSPseudoElementType::mozNumberWrapper); NS_ENSURE_SUCCESS(rv, rv); ContentInfo& outerWrapperCI = aElements.LastElement(); @@ -392,8 +379,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mTextField), outerWrapperCI.mChildren, nsGkAtoms::input, - CSSPseudoElementType::mozNumberText, - outerWrapperCI.mStyleContext); + CSSPseudoElementType::mozNumberText); NS_ENSURE_SUCCESS(rv, rv); mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::type, @@ -442,8 +428,7 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mSpinBox), outerWrapperCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinBox, - outerWrapperCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinBox); NS_ENSURE_SUCCESS(rv, rv); ContentInfo& spinBoxCI = outerWrapperCI.mChildren.LastElement(); @@ -452,16 +437,14 @@ nsNumberControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) rv = MakeAnonymousElement(getter_AddRefs(mSpinUp), spinBoxCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinUp, - spinBoxCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinUp); NS_ENSURE_SUCCESS(rv, rv); // Create the ::-moz-number-spin-down pseudo-element: rv = MakeAnonymousElement(getter_AddRefs(mSpinDown), spinBoxCI.mChildren, nsGkAtoms::div, - CSSPseudoElementType::mozNumberSpinDown, - spinBoxCI.mStyleContext); + CSSPseudoElementType::mozNumberSpinDown); return rv; } diff --git a/layout/forms/nsNumberControlFrame.h b/layout/forms/nsNumberControlFrame.h index 47e32ad65..5396e7170 100644 --- a/layout/forms/nsNumberControlFrame.h +++ b/layout/forms/nsNumberControlFrame.h @@ -170,8 +170,7 @@ private: nsresult MakeAnonymousElement(Element** aResult, nsTArray<ContentInfo>& aElements, nsIAtom* aTagName, - CSSPseudoElementType aPseudoType, - nsStyleContext* aParentContext); + CSSPseudoElementType aPseudoType); class SyncDisabledStateEvent; friend class SyncDisabledStateEvent; diff --git a/layout/forms/nsProgressFrame.cpp b/layout/forms/nsProgressFrame.cpp index 2445defd3..6d6db7a1f 100644 --- a/layout/forms/nsProgressFrame.cpp +++ b/layout/forms/nsProgressFrame.cpp @@ -72,12 +72,9 @@ nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); // Associate ::-moz-progress-bar pseudo-element to the anonymous child. - CSSPseudoElementType pseudoType = CSSPseudoElementType::mozProgressBar; - RefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> - ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, - StyleContext(), mBarDiv->AsElement()); + mBarDiv->SetPseudoElementType(CSSPseudoElementType::mozProgressBar); - if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { + if (!aElements.AppendElement(mBarDiv)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsRangeFrame.cpp b/layout/forms/nsRangeFrame.cpp index 7590da066..99faa6043 100644 --- a/layout/forms/nsRangeFrame.cpp +++ b/layout/forms/nsRangeFrame.cpp @@ -119,13 +119,9 @@ nsRangeFrame::MakeAnonymousDiv(Element** aResult, RefPtr<Element> resultElement = doc->CreateHTMLElement(nsGkAtoms::div); // Associate the pseudo-element with the anonymous child. - RefPtr<nsStyleContext> newStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle(mContent->AsElement(), - aPseudoType, - StyleContext(), - resultElement); + resultElement->SetPseudoElementType(aPseudoType); - if (!aElements.AppendElement(ContentInfo(resultElement, newStyleContext))) { + if (!aElements.AppendElement(resultElement)) { return NS_ERROR_OUT_OF_MEMORY; } diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index f8fdf3420..aa3185d39 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -352,32 +352,12 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) // Create the placeholder anonymous content if needed. if (mUsePlaceholder) { - nsIContent* placeholderNode = txtCtrl->CreatePlaceholderNode(); + Element* placeholderNode = txtCtrl->CreatePlaceholderNode(); NS_ENSURE_TRUE(placeholderNode, NS_ERROR_OUT_OF_MEMORY); // Associate ::placeholder pseudo-element with the placeholder node. - CSSPseudoElementType pseudoType = CSSPseudoElementType::placeholder; - - // If this is a text input inside a number input then we want to use the - // main number input as the source of style for the placeholder frame. - nsIFrame* mainInputFrame = this; - if (StyleContext()->GetPseudoType() == CSSPseudoElementType::mozNumberText) { - do { - mainInputFrame = mainInputFrame->GetParent(); - } while (mainInputFrame && - mainInputFrame->GetType() != nsGkAtoms::numberControlFrame); - MOZ_ASSERT(mainInputFrame); - } - - RefPtr<nsStyleContext> placeholderStyleContext = - PresContext()->StyleSet()->ResolvePseudoElementStyle( - mainInputFrame->GetContent()->AsElement(), pseudoType, StyleContext(), - placeholderNode->AsElement()); - - if (!aElements.AppendElement(ContentInfo(placeholderNode, - placeholderStyleContext))) { - return NS_ERROR_OUT_OF_MEMORY; - } + placeholderNode->SetPseudoElementType(CSSPseudoElementType::placeholder); + aElements.AppendElement(placeholderNode); if (!IsSingleLineTextControl()) { // For textareas, UpdateValueDisplay doesn't initialize the visibility diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 0d0c7108c..ba1e9567a 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -8918,6 +8918,8 @@ GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) * * Also skip anonymous scrolled-content parents; inherit directly from the * outer scroll frame. + * + * Also skip NAC parents if the child frame is NAC. */ static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) @@ -8943,6 +8945,31 @@ GetCorrectedParent(const nsIFrame* aFrame) if (pseudo == nsCSSAnonBoxes::tableWrapper) { pseudo = aFrame->PrincipalChildList().FirstChild()->StyleContext()->GetPseudo(); } + + // Prevent NAC from inheriting NAC. This partially duplicates the logic + // implemented in nsCSSFrameConstructor::AddFCItemsForAnonymousContent, and is + // necessary so that restyle inherits style contexts in the same way as the + // initial styling performed in frame construction. + // + // It would be nice to put it in CorrectStyleParentFrame and therefore share + // it, but that would lose the information of whether the _child_ is NAC, + // since CorrectStyleParentFrame only knows about the prospective _parent_. + // This duplication and complexity will go away when we fully switch to the + // Servo style system, where all this can be handled much more naturally. + // + // We need to take special care not to disrupt the style inheritance of frames + // whose content is NAC but who implement a pseudo (like an anonymous + // box, or a non-NAC-backed pseudo like ::first-line) that does not match the + // one that the NAC implements, if any. + nsIContent* content = aFrame->GetContent(); + Element* element = content->IsElement() ? content->AsElement() : nullptr; + if (element && element->IsNativeAnonymous() && + element->GetPseudoElementType() == aFrame->StyleContext()->GetPseudoType()) { + while (parent->GetContent() && parent->GetContent()->IsNativeAnonymous()) { + parent = parent->GetParent(); + } + } + return nsFrame::CorrectStyleParentFrame(parent, pseudo); } @@ -9013,6 +9040,9 @@ nsFrame::DoGetParentStyleContext(nsIFrame** aProviderFrame) const { *aProviderFrame = nullptr; nsFrameManager* fm = PresContext()->FrameManager(); + + // Handle display:contents and the root frame, when there's no parent frame + // to inherit from. if (MOZ_LIKELY(mContent)) { nsIContent* parentContent = mContent->GetFlattenedTreeParent(); if (MOZ_LIKELY(parentContent)) { diff --git a/layout/generic/nsIAnonymousContentCreator.h b/layout/generic/nsIAnonymousContentCreator.h index 2728bb4bb..5167178b8 100644 --- a/layout/generic/nsIAnonymousContentCreator.h +++ b/layout/generic/nsIAnonymousContentCreator.h @@ -34,12 +34,7 @@ public: mContent(aContent) {} - ContentInfo(nsIContent* aContent, nsStyleContext* aStyleContext) : - mContent(aContent), mStyleContext(aStyleContext) - {} - nsIContent* mContent; - RefPtr<nsStyleContext> mStyleContext; nsTArray<ContentInfo> mChildren; }; diff --git a/layout/reftests/forms/input/number/number-style-inheritance-ref.html b/layout/reftests/forms/input/number/number-style-inheritance-ref.html new file mode 100644 index 000000000..00c8dba43 --- /dev/null +++ b/layout/reftests/forms/input/number/number-style-inheritance-ref.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <input type="text" style="width: 100px; text-decoration: underline;" value="1234"> + </body> +</html> diff --git a/layout/reftests/forms/input/number/number-style-inheritance.html b/layout/reftests/forms/input/number/number-style-inheritance.html new file mode 100644 index 000000000..b21ff1f1c --- /dev/null +++ b/layout/reftests/forms/input/number/number-style-inheritance.html @@ -0,0 +1,6 @@ +<!DOCTYPE html> +<html> + <body> + <input type="number" style="width: 100px; -moz-appearance: textfield; text-decoration: underline;" value="1234"> + </body> +</html> diff --git a/layout/reftests/forms/input/number/reftest.list b/layout/reftests/forms/input/number/reftest.list index ecf05ce15..bd07e274c 100644 --- a/layout/reftests/forms/input/number/reftest.list +++ b/layout/reftests/forms/input/number/reftest.list @@ -52,3 +52,6 @@ fuzzy-if(skiaContent,2,5) needs-focus == focus-handling.html focus-handling-ref. fuzzy(128,4) == number-reframe-anon-text-field.html number-reframe-anon-text-field-ref.html == pseudo-classes.html about:blank + +# Style inheritance: +== number-style-inheritance.html number-style-inheritance-ref.html diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index 95025221d..c91c2f739 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -1099,6 +1099,7 @@ input[type=number]::-moz-number-spin-box { } input[type=number]::-moz-number-spin-up { + writing-mode: horizontal-tb; -moz-appearance: spinner-upbutton; display: block; /* bug 926670 */ flex: none; @@ -1116,6 +1117,7 @@ input[type=number]::-moz-number-spin-up { } input[type=number]::-moz-number-spin-down { + writing-mode: horizontal-tb; -moz-appearance: spinner-downbutton; display: block; /* bug 926670 */ flex: none; |