Bug 951310 - Make <input type=number correctly handle CSS min-height and max-height. r=dholbert
authorJonathan Watt <jwatt@jwatt.org>
Wed, 05 Feb 2014 00:42:57 +0000
changeset 169659 f55c1d834649aa4061e1d622e862f14f4e14a583
parent 169658 311053a3b3ade1eb8a37b9b6b0d47d994c4523e6
child 169660 7ac30a8de3d6cc4f3250114475956c6332064423
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersdholbert
bugs951310
milestone30.0a1
Bug 951310 - Make <input type=number correctly handle CSS min-height and max-height. r=dholbert
layout/forms/nsNumberControlFrame.cpp
layout/forms/nsNumberControlFrame.h
layout/reftests/forms/input/number/number-max-height-1-ref.html
layout/reftests/forms/input/number/number-max-height-1.html
layout/reftests/forms/input/number/number-max-height-2-ref.html
layout/reftests/forms/input/number/number-max-height-2.html
layout/reftests/forms/input/number/number-min-height-1-ref.html
layout/reftests/forms/input/number/number-min-height-1.html
layout/reftests/forms/input/number/number-min-height-2-ref.html
layout/reftests/forms/input/number/number-min-height-2.html
layout/reftests/forms/input/number/reftest.list
--- a/layout/forms/nsNumberControlFrame.cpp
+++ b/layout/forms/nsNumberControlFrame.cpp
@@ -78,88 +78,104 @@ nsNumberControlFrame::Reflow(nsPresConte
   NS_ASSERTION(!mFrames.FirstChild() ||
                !mFrames.FirstChild()->GetNextSibling(),
                "We expect at most one direct child frame");
 
   if (mState & NS_FRAME_FIRST_REFLOW) {
     nsFormControlFrame::RegUnRegAccessKey(this, true);
   }
 
-  nsHTMLReflowMetrics wrappersDesiredSize(aReflowState.GetWritingMode());
+  // The width of our content box, which is the available width
+  // for our anonymous content:
+  const nscoord contentBoxWidth = aReflowState.ComputedWidth();
+  nscoord contentBoxHeight = aReflowState.ComputedHeight();
+
   nsIFrame* outerWrapperFrame = mOuterWrapper->GetPrimaryFrame();
-  if (outerWrapperFrame) { // display:none?
+
+  if (!outerWrapperFrame) { // display:none?
+    if (contentBoxHeight == NS_INTRINSICSIZE) {
+      contentBoxHeight = 0;
+    }
+  } else {
     NS_ASSERTION(outerWrapperFrame == mFrames.FirstChild(), "huh?");
-    nsresult rv =
-      ReflowAnonymousContent(aPresContext, wrappersDesiredSize,
-                             aReflowState, outerWrapperFrame);
+
+    nsHTMLReflowMetrics wrappersDesiredSize(aReflowState.GetWritingMode());
+
+    nsHTMLReflowState wrapperReflowState(aPresContext, aReflowState,
+                                         outerWrapperFrame,
+                                         nsSize(contentBoxWidth,
+                                                NS_UNCONSTRAINEDSIZE));
+
+    // offsets of wrapper frame
+    nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left +
+                        wrapperReflowState.ComputedPhysicalMargin().left;
+    nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top +
+                        wrapperReflowState.ComputedPhysicalMargin().top;
+
+    nsReflowStatus childStatus;
+    nsresult rv = ReflowChild(outerWrapperFrame, aPresContext,
+                              wrappersDesiredSize, wrapperReflowState,
+                              xoffset, yoffset, 0, childStatus);
     NS_ENSURE_SUCCESS(rv, rv);
-    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
+    MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
+               "We gave our child unconstrained height, so it should be complete");
+
+    nscoord wrappersMarginBoxHeight = wrappersDesiredSize.Height() +
+      wrapperReflowState.ComputedPhysicalMargin().TopBottom();
+
+    if (contentBoxHeight == NS_INTRINSICSIZE) {
+      // We are intrinsically sized -- we should shrinkwrap the outer wrapper's
+      // height:
+      contentBoxHeight = wrappersMarginBoxHeight;
+
+      // Make sure we obey min/max-height in the case when we're doing intrinsic
+      // sizing (we get it for free when we have a non-intrinsic
+      // aReflowState.ComputedHeight()).  Note that we do this before
+      // adjusting for borderpadding, since mComputedMaxHeight and
+      // mComputedMinHeight are content heights.
+      contentBoxHeight =
+        NS_CSS_MINMAX(contentBoxHeight,
+                      aReflowState.ComputedMinHeight(),
+                      aReflowState.ComputedMaxHeight());
+    }
+
+    // Center child vertically
+    nscoord extraSpace = contentBoxHeight - wrappersMarginBoxHeight;
+    yoffset += std::max(0, extraSpace / 2);
+
+    // Place the child
+    rv = FinishReflowChild(outerWrapperFrame, aPresContext,
+                           wrappersDesiredSize, &wrapperReflowState,
+                           xoffset, yoffset, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    aDesiredSize.SetTopAscent(wrappersDesiredSize.TopAscent() +
+                              outerWrapperFrame->GetPosition().y);
   }
 
-  nscoord computedHeight = aReflowState.ComputedHeight();
-  if (computedHeight == NS_AUTOHEIGHT) {
-    computedHeight =
-      outerWrapperFrame ? outerWrapperFrame->GetSize().height : 0;
-  }
-  aDesiredSize.Width() = aReflowState.ComputedWidth() +
+  aDesiredSize.Width() = contentBoxWidth +
                          aReflowState.ComputedPhysicalBorderPadding().LeftRight();
-  aDesiredSize.Height() = computedHeight +
+  aDesiredSize.Height() = contentBoxHeight +
                           aReflowState.ComputedPhysicalBorderPadding().TopBottom();
 
+  aDesiredSize.SetOverflowAreasToDesiredBounds();
+
   if (outerWrapperFrame) {
-    aDesiredSize.SetTopAscent(wrappersDesiredSize.TopAscent() +
-                            outerWrapperFrame->GetPosition().y);
+    ConsiderChildOverflow(aDesiredSize.mOverflowAreas, outerWrapperFrame);
   }
 
-  aDesiredSize.SetOverflowAreasToDesiredBounds();
-
   FinishAndStoreOverflow(&aDesiredSize);
 
   aStatus = NS_FRAME_COMPLETE;
 
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
 
   return NS_OK;
 }
 
-nsresult
-nsNumberControlFrame::
-  ReflowAnonymousContent(nsPresContext* aPresContext,
-                         nsHTMLReflowMetrics& aWrappersDesiredSize,
-                         const nsHTMLReflowState& aParentReflowState,
-                         nsIFrame* aOuterWrapperFrame)
-{
-  MOZ_ASSERT(aOuterWrapperFrame);
-
-  // The width of our content box, which is the available width
-  // for our anonymous content:
-  nscoord inputFrameContentBoxWidth = aParentReflowState.ComputedWidth();
-
-  nsHTMLReflowState wrapperReflowState(aPresContext, aParentReflowState,
-                                       aOuterWrapperFrame,
-                                       nsSize(inputFrameContentBoxWidth,
-                                              NS_UNCONSTRAINEDSIZE));
-
-  nscoord xoffset = aParentReflowState.ComputedPhysicalBorderPadding().left +
-                      wrapperReflowState.ComputedPhysicalMargin().left;
-  nscoord yoffset = aParentReflowState.ComputedPhysicalBorderPadding().top +
-                      wrapperReflowState.ComputedPhysicalMargin().top;
-
-  nsReflowStatus childStatus;
-  nsresult rv = ReflowChild(aOuterWrapperFrame, aPresContext,
-                            aWrappersDesiredSize, wrapperReflowState,
-                            xoffset, yoffset, 0, childStatus);
-  NS_ENSURE_SUCCESS(rv, rv);
-  MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus),
-             "We gave our child unconstrained height, so it should be complete");
-  return FinishReflowChild(aOuterWrapperFrame, aPresContext,
-                           aWrappersDesiredSize, &wrapperReflowState,
-                           xoffset, yoffset, 0);
-}
-
 void
 nsNumberControlFrame::SyncDisabledState()
 {
   nsEventStates eventStates = mContent->AsElement()->State();
   if (eventStates.HasState(NS_EVENT_STATE_DISABLED)) {
     mTextField->SetAttr(kNameSpaceID_None, nsGkAtoms::disabled, EmptyString(),
                         true);
   } else {
--- a/layout/forms/nsNumberControlFrame.h
+++ b/layout/forms/nsNumberControlFrame.h
@@ -154,21 +154,16 @@ public:
 private:
 
   nsresult MakeAnonymousElement(Element** aResult,
                                 nsTArray<ContentInfo>& aElements,
                                 nsIAtom* aTagName,
                                 nsCSSPseudoElements::Type aPseudoType,
                                 nsStyleContext* aParentContext);
 
-  nsresult ReflowAnonymousContent(nsPresContext* aPresContext,
-                                  nsHTMLReflowMetrics& aWrappersDesiredSize,
-                                  const nsHTMLReflowState& aReflowState,
-                                  nsIFrame* aOuterWrapperFrame);
-
   class SyncDisabledStateEvent;
   friend class SyncDisabledStateEvent;
   class SyncDisabledStateEvent : public nsRunnable
   {
   public:
     SyncDisabledStateEvent(nsNumberControlFrame* aFrame)
     : mFrame(aFrame)
     {}
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-max-height-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  border: 3px solid black;
+  width: 308px;
+  height: 108px;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-max-height-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+input {
+  border: 3px solid black;
+  padding: 4px;
+  width: 300px;
+  height: 300px;
+  max-height: 100px;
+  box-sizing: content-box;
+  /* hide the spin buttons: */
+  -moz-appearance: textfield;
+}
+
+* > input[type=number] {
+  /* get rid of background gradient for Firefox OS */
+  background-color: transparent ! important;
+}
+
+    </style>
+  </head>
+  <body>
+    <input type="number">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-max-height-2-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  border: 3px solid black;
+  width: 294px;
+  height: 94px;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-max-height-2.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+input {
+  border: 3px solid black;
+  padding: 4px;
+  width: 300px;
+  height: 300px;
+  max-height: 100px;
+  box-sizing: border-box;
+  /* hide the spin buttons: */
+  -moz-appearance: textfield;
+}
+
+* > input[type=number] {
+  /* get rid of background gradient for Firefox OS */
+  background-color: transparent ! important;
+}
+
+    </style>
+  </head>
+  <body>
+    <input type="number">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-min-height-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  border: 3px solid black;
+  width: 308px;
+  height: 108px;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-min-height-1.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+input {
+  border: 3px solid black;
+  padding: 4px;
+  width: 300px;
+  min-height: 100px;
+  box-sizing: content-box;
+  /* hide the spin buttons: */
+  -moz-appearance: textfield;
+}
+
+* > input[type=number] {
+  /* get rid of background gradient for Firefox OS */
+  background-color: transparent ! important;
+}
+
+    </style>
+  </head>
+  <body>
+    <input type="number">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-min-height-2-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+div {
+  border: 3px solid black;
+  width: 294px;
+  height: 94px;
+}
+
+    </style>
+  </head>
+  <body>
+    <div></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/forms/input/number/number-min-height-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <style>
+
+input {
+  border: 3px solid black;
+  padding: 4px;
+  width: 300px;
+  min-height: 100px;
+  box-sizing: border-box;
+  /* hide the spin buttons: */
+  -moz-appearance: textfield;
+}
+
+* > input[type=number] {
+  /* get rid of background gradient for Firefox OS */
+  background-color: transparent ! important;
+}
+
+    </style>
+  </head>
+  <body>
+    <input type="number">
+  </body>
+</html>
--- a/layout/reftests/forms/input/number/reftest.list
+++ b/layout/reftests/forms/input/number/reftest.list
@@ -15,16 +15,22 @@ fuzzy-if(/^Windows\x20NT\x205\.1/.test(h
 == from-number-to-other-type-unthemed-1.html from-number-to-other-type-unthemed-1-ref.html
 
 # dynamic value changes:
 == show-value.html show-value-ref.html
 
 # disabled
 == number-disabled.html number-disabled-ref.html
 
+# min-height/max-height tests:
+skip-if(B2G) == number-min-height-1.html number-min-height-1-ref.html
+skip-if(B2G) == number-min-height-2.html number-min-height-2-ref.html
+skip-if(B2G) == number-max-height-1.html number-max-height-1-ref.html
+skip-if(B2G) == number-max-height-2.html number-max-height-2-ref.html
+
 # focus
 # autofocus is disabled on B2G
 # https://bugzilla.mozilla.org/show_bug.cgi?id=965763
 skip-if(B2G) needs-focus == focus-handling.html focus-handling-ref.html
 
 # select
 == number-selected.html number-selected-ref.html