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=bz draft
authorcam@mcc.id.au <cam@mcc.id.au>
Fri, 13 Jan 2017 03:21:11 +0000
changeset 485065 a905853cc85bf6d41a61c0163193b869a8b864dd
parent 485064 bc5a4ef8416f7fa6c9067bad1485bf025c268d5d
child 485462 7696fb9888f372f39ef971d9a64538cbee01b1a4
push id45616
push userbmo:bobbyholley@gmail.com
push dateThu, 16 Feb 2017 01:57:04 +0000
reviewersbz
bugs1331322
milestone54.0a1
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=bz MozReview-Commit-ID: LO0t92orjWZ
dom/base/Element.h
dom/base/crashtests/crashtests.list
dom/base/nsGkAtomList.h
layout/base/GeckoRestyleManager.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsCSSFrameConstructor.h
layout/forms/nsColorControlFrame.cpp
layout/forms/nsMeterFrame.cpp
layout/forms/nsNumberControlFrame.cpp
layout/forms/nsNumberControlFrame.h
layout/forms/nsProgressFrame.cpp
layout/forms/nsRangeFrame.cpp
layout/forms/nsTextControlFrame.cpp
layout/generic/nsFrame.cpp
layout/generic/nsIAnonymousContentCreator.h
layout/reftests/forms/input/number/number-style-inheritance-ref.html
layout/reftests/forms/input/number/number-style-inheritance.html
layout/reftests/forms/input/number/reftest.list
layout/style/res/forms.css
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -788,16 +788,35 @@ public:
     GetElementsByTagName(const nsAString& aQualifiedName);
   already_AddRefed<nsIHTMLCollection>
     GetElementsByTagNameNS(const nsAString& aNamespaceURI,
                            const nsAString& aLocalName,
                            ErrorResult& aError);
   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
    * https://dom.spec.whatwg.org/#insert-adjacent for both
    * |insertAdjacentElement()| and |insertAdjacentText()| APIs.
    */
   nsINode* InsertAdjacent(const nsAString& aWhere,
                           nsINode* aNode,
--- a/dom/base/crashtests/crashtests.list
+++ b/dom/base/crashtests/crashtests.list
@@ -188,17 +188,17 @@ load 852381.html
 load 863950.html
 load 864448.html
 load 886213.html
 load 898906.html
 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
 load 1154598.xhtml
 load 1157995.html
 load 1158412.html
 load 1181619.html
 load structured_clone_container_throws.html
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2147,16 +2147,17 @@ GK_ATOM(transitionsOfBeforeProperty, "Tr
 GK_ATOM(transitionsOfAfterProperty, "TransitionsOfAfterProperty") // FrameTransitions*
 GK_ATOM(genConInitializerProperty, "QuoteNodeProperty")
 GK_ATOM(labelMouseDownPtProperty, "LabelMouseDownPtProperty")
 GK_ATOM(baseURIProperty, "baseURIProperty")
 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")
 GK_ATOM(Chinese, "zh-CN")
 GK_ATOM(Taiwanese, "zh-TW")
 GK_ATOM(HongKongChinese, "zh-HK")
 GK_ATOM(Unicode, "x-unicode")
 
--- a/layout/base/GeckoRestyleManager.cpp
+++ b/layout/base/GeckoRestyleManager.cpp
@@ -2007,16 +2007,26 @@ ElementRestyler::ComputeRestyleResultFro
   // style contexts.
   if (aSelf->GetAdditionalStyleContext(0)) {
     LOG_RESTYLE_CONTINUE("there are additional style contexts");
     aRestyleResult = RestyleResult::eContinue;
     aCanStopWithStyleChange = false;
     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
   // that they get the correct style context parent.  Similarly for
   // nsLineFrames.
   nsIAtom* type = aSelf->GetType();
 
   if (type == nsGkAtoms::letterFrame) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -1862,16 +1862,17 @@ nsCSSFrameConstructor::CreateGeneratedCo
   nodeInfo = mDocument->NodeInfoManager()->GetNodeInfo(elemName, nullptr,
                                                        kNameSpaceID_None,
                                                        nsIDOMNode::ELEMENT_NODE);
   nsCOMPtr<Element> container;
   nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget());
   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
   // are not in document.
   nsIDocument* bindDocument =
     aParentContent->HasFlag(NODE_IS_IN_SHADOW_TREE) ? nullptr : mDocument;
   rv = container->BindToTree(bindDocument, aParentContent, aParentContent, true);
   if (NS_FAILED(rv)) {
@@ -4245,21 +4246,19 @@ nsCSSFrameConstructor::GetAnonymousConte
     }
     if (NS_FAILED(rv)) {
       content->UnbindFromTree();
       return rv;
     }
   }
 
   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());
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -5029,34 +5028,47 @@ nsCSSFrameConstructor::ResolveStyleConte
 {
   return ResolveStyleContext(aInsertion.mParentFrame, aInsertion.mContainer,
                              aChild, aState);
 }
 
 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,
-                                         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");
     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
@@ -10720,34 +10732,105 @@ nsCSSFrameConstructor::AddFCItemsForAnon
                "Should have no existing frame");
     MOZ_ASSERT(!content->IsNodeOfType(nsINode::eCOMMENT) &&
                !content->IsNodeOfType(nsINode::ePROCESSING_INSTRUCTION),
                "Why is someone creating garbage anonymous content");
 
     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->IsElement() || !content->AsElement()->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->AsElement()->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()) {
       anonChildren = &aAnonymousItems[i].mChildren;
     }
 
     uint32_t flags = ITEM_ALLOW_XBL_BASE | ITEM_ALLOW_PAGE_BREAK |
                      ITEM_IS_ANONYMOUSCONTENTCREATOR_CONTENT | aExtraFlags;
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -359,17 +359,18 @@ private:
                       nsFrameConstructorState* aState);
   already_AddRefed<nsStyleContext>
   ResolveStyleContext(const InsertionPoint&    aInsertion,
                       nsIContent*              aChild,
                       nsFrameConstructorState* aState);
   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.
   // 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/forms/nsColorControlFrame.cpp
+++ b/layout/forms/nsColorControlFrame.cpp
@@ -64,28 +64,25 @@ nsColorControlFrame::GetFrameName(nsAStr
 
 // Create the color area for the button.
 // The frame will be generated by the frame constructor.
 nsresult
 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();
 
   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;
   }
 
   return NS_OK;
 }
 
 void
 nsColorControlFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
--- a/layout/forms/nsMeterFrame.cpp
+++ b/layout/forms/nsMeterFrame.cpp
@@ -70,24 +70,19 @@ nsMeterFrame::CreateAnonymousContent(nsT
 {
   // Get the NodeInfoManager and tag necessary to create the meter bar div.
   nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
 
   // Create the div.
   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;
 }
 
 void
 nsMeterFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
                                        uint32_t aFilter)
 {
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -320,37 +320,25 @@ private:
   nsCOMPtr<nsIContent> mNumber;
   nsCOMPtr<nsIContent> mTextField;
 };
 
 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;
   }
 
   if (aPseudoType == CSSPseudoElementType::mozNumberSpinDown ||
       aPseudoType == CSSPseudoElementType::mozNumberSpinUp) {
     resultElement->SetAttr(kNameSpaceID_None, nsGkAtoms::role,
                            NS_LITERAL_STRING("button"), false);
   }
@@ -377,28 +365,26 @@ nsNumberControlFrame::CreateAnonymousCon
   // If you change this, be careful to change the destruction order in
   // nsNumberControlFrame::DestroyFrom.
 
 
   // Create the anonymous outer wrapper:
   rv = MakeAnonymousElement(getter_AddRefs(mOuterWrapper),
                             aElements,
                             nsGkAtoms::div,
-                            CSSPseudoElementType::mozNumberWrapper,
-                            mStyleContext);
+                            CSSPseudoElementType::mozNumberWrapper);
   NS_ENSURE_SUCCESS(rv, rv);
 
   ContentInfo& outerWrapperCI = aElements.LastElement();
 
   // Create the ::-moz-number-text pseudo-element:
   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,
                       NS_LITERAL_STRING("text"), PR_FALSE);
 
   HTMLInputElement* content = HTMLInputElement::FromContent(mContent);
   HTMLInputElement* textField = HTMLInputElement::FromContent(mTextField);
 
@@ -435,36 +421,33 @@ nsNumberControlFrame::CreateAnonymousCon
     // -moz-appearance. We will reframe if it changes.
     return rv;
   }
 
   // Create the ::-moz-number-spin-box pseudo-element:
   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();
 
   // Create the ::-moz-number-spin-up pseudo-element:
   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);
 
   SyncDisabledState();
 
   return rv;
 }
 
 nsIAtom*
 nsNumberControlFrame::GetType() const
--- a/layout/forms/nsNumberControlFrame.h
+++ b/layout/forms/nsNumberControlFrame.h
@@ -165,18 +165,17 @@ public:
   bool ShouldUseNativeStyleForSpinner() const;
 
 private:
 
   nsITextControlFrame* GetTextFieldFrame();
   nsresult MakeAnonymousElement(Element** aResult,
                                 nsTArray<ContentInfo>& aElements,
                                 nsIAtom* aTagName,
-                                CSSPseudoElementType aPseudoType,
-                                nsStyleContext* aParentContext);
+                                CSSPseudoElementType aPseudoType);
 
   class SyncDisabledStateEvent;
   friend class SyncDisabledStateEvent;
   class SyncDisabledStateEvent : public mozilla::Runnable
   {
   public:
     explicit SyncDisabledStateEvent(nsNumberControlFrame* aFrame)
     : mFrame(aFrame)
--- a/layout/forms/nsProgressFrame.cpp
+++ b/layout/forms/nsProgressFrame.cpp
@@ -67,22 +67,19 @@ nsProgressFrame::GetType() const
 nsresult
 nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
   // Create the progress bar div.
   nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
   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;
   }
 
   return NS_OK;
 }
 
 void
 nsProgressFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements,
--- a/layout/forms/nsRangeFrame.cpp
+++ b/layout/forms/nsRangeFrame.cpp
@@ -114,23 +114,19 @@ nsresult
 nsRangeFrame::MakeAnonymousDiv(Element** aResult,
                                CSSPseudoElementType aPseudoType,
                                nsTArray<ContentInfo>& aElements)
 {
   nsCOMPtr<nsIDocument> doc = mContent->GetComposedDoc();
   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;
   }
 
   resultElement.forget(aResult);
   return NS_OK;
 }
 
 nsresult
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -342,42 +342,22 @@ nsTextControlFrame::CreateAnonymousConte
   nsAutoString placeholderTxt;
   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
                     placeholderTxt);
   nsContentUtils::RemoveNewlines(placeholderTxt);
   mUsePlaceholder = !placeholderTxt.IsEmpty();
 
   // 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
       // status of the placeholder because it returns early, so we have to
       // do that manually here.
       txtCtrl->UpdatePlaceholderVisibility(true);
     }
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9060,16 +9060,18 @@ GetIBSplitSiblingForAnonymousBlock(const
  * Get the parent, corrected for the mangled frame tree resulting from
  * having a block within an inline.  The result only differs from the
  * result of |GetParent| when |GetParent| returns an anonymous block
  * that was created for an element that was 'display: inline' because
  * that element contained a block.
  *
  * 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)
 {
   nsIFrame* parent = aFrame->GetParent();
   if (!parent) {
     return nullptr;
   }
@@ -9085,16 +9087,41 @@ GetCorrectedParent(const nsIFrame* aFram
 
   // Table wrappers are always anon boxes; if we're in here for an outer
   // table, that actually means its the _inner_ table that wants to
   // know its parent. So get the pseudo of the inner in that case.
   nsIAtom* pseudo = aFrame->StyleContext()->GetPseudo();
   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);
 }
 
 /* static */
 nsIFrame*
 nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
                                  nsIAtom* aChildPseudo)
 {
@@ -9155,16 +9182,19 @@ nsFrame::CorrectStyleParentFrame(nsIFram
   return nullptr;
 }
 
 nsStyleContext*
 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)) {
       nsIAtom* pseudo = StyleContext()->GetPseudo();
       if (!pseudo || !mContent->IsElement() ||
           (!nsCSSAnonBoxes::IsAnonBox(pseudo) &&
            // Ensure that we don't return the display:contents style
            // of the parent content for pseudos that have the same content
--- a/layout/generic/nsIAnonymousContentCreator.h
+++ b/layout/generic/nsIAnonymousContentCreator.h
@@ -29,22 +29,17 @@ class nsIAnonymousContentCreator
 public:
   NS_DECL_QUERYFRAME_TARGET(nsIAnonymousContentCreator)
 
   struct ContentInfo {
     explicit ContentInfo(nsIContent* aContent) :
       mContent(aContent)
     {}
 
-    ContentInfo(nsIContent* aContent, nsStyleContext* aStyleContext) :
-      mContent(aContent), mStyleContext(aStyleContext)
-    {}
-
     nsIContent* mContent;
-    RefPtr<nsStyleContext> mStyleContext;
     nsTArray<ContentInfo> mChildren;
   };
 
   /**
    * Creates "native" anonymous content and adds the created content to
    * the aElements array. None of the returned elements can be nullptr.
    *
    * If the anonymous content creator sets the editable flag on some
new file mode 100644
--- /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>
new file mode 100644
--- /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>
--- a/layout/reftests/forms/input/number/reftest.list
+++ b/layout/reftests/forms/input/number/reftest.list
@@ -47,8 +47,11 @@ fuzzy-if(skiaContent,2,5) needs-focus ==
 == number-placeholder.html number-placeholder-ref.html
 
 # check that if the anonymous text control is reframed, we reframe the whole
 # number control (the fuzzy is for the top-right and bottom-left of the border
 # bevel which gets slightly different antialiasing after invalidation):
 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
--- a/layout/style/res/forms.css
+++ b/layout/style/res/forms.css
@@ -1167,16 +1167,17 @@ input[type=number]::-moz-number-spin-box
    * if the font-size is made substantially larger or smaller. (Bug 1175074.)
    */
   max-height: 1em;
   align-self: center;
   justify-content: center;
 }
 
 input[type=number]::-moz-number-spin-up {
+  writing-mode: horizontal-tb;
   -moz-appearance: spinner-upbutton;
   display: block; /* bug 926670 */
   flex: none;
   cursor: default;
   /* Style for when native theming is off: */
   background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="5"><path d="M1,4 L3,0 5,4" fill="dimgrey"/></svg>');
   background-repeat: no-repeat;
   background-position: center bottom;
@@ -1184,16 +1185,17 @@ input[type=number]::-moz-number-spin-up 
   border-bottom: none;
   /* [JK] I think the border-*-*-radius properties here can remain physical,
      as we probably don't want to turn the spinner sideways in vertical writing mode */
   border-top-left-radius: 4px;
   border-top-right-radius: 4px;
 }
 
 input[type=number]::-moz-number-spin-down {
+  writing-mode: horizontal-tb;
   -moz-appearance: spinner-downbutton;
   display: block; /* bug 926670 */
   flex: none;
   cursor: default;
   /* Style for when native theming is off: */
   background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="6" height="5"><path d="M1,1 L3,5 5,1" fill="dimgrey"/></svg>');
   background-repeat: no-repeat;
   background-position: center top;