Implement correct intrinsic width calculation (for container) for elements with specified height properties and an intrinsic ratio. b=364066 r+sr=bzbarsky
authordbaron@dbaron.org
Tue, 12 Jun 2007 11:27:09 -0700 (2007-06-12)
changeset 2301 427bda154a0a003554a62c825fdd825c2b933943
parent 2300 e23d9b40e36efe1f80d7423baec1327d66461379
child 2302 7e89f709c6ff9878288d9b66e18c75250304f926
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000 (2008-03-20)
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs364066
milestone1.9a6pre
Implement correct intrinsic width calculation (for container) for elements with specified height properties and an intrinsic ratio. b=364066 r+sr=bzbarsky
layout/base/nsLayoutUtils.cpp
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsHTMLCanvasFrame.cpp
layout/generic/nsHTMLCanvasFrame.h
layout/generic/nsIFrame.h
layout/generic/nsImageFrame.cpp
layout/generic/nsImageFrame.h
layout/reftests/bugs/364066-1-ref.html
layout/reftests/bugs/364066-1.html
layout/reftests/bugs/reftest.list
layout/svg/base/src/nsSVGOuterSVGFrame.h
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1248,16 +1248,77 @@ nsLayoutUtils::GetAbsoluteCoord(const ns
   if (eStyleUnit_Chars == unit) {
     aResult = nsLayoutUtils::CharsToCoord(aStyle, aRenderingContext,
                                           aFrame->GetStyleContext());
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
+static PRBool
+GetPercentHeight(const nsStyleCoord& aStyle,
+                 nsIRenderingContext* aRenderingContext,
+                 nsIFrame* aFrame,
+                 nscoord& aResult)
+{
+  if (eStyleUnit_Percent != aStyle.GetUnit())
+    return PR_FALSE;
+
+  nsIFrame *f;
+  for (f = aFrame->GetParent(); f && !f->IsContainingBlock();
+       f = f->GetParent())
+    ;
+  if (!f) {
+    NS_NOTREACHED("top of frame tree not a containing block");
+    return PR_FALSE;
+  }
+
+  const nsStylePosition *pos = f->GetStylePosition();
+  nscoord h;
+  if (!nsLayoutUtils::
+        GetAbsoluteCoord(pos->mHeight, aRenderingContext, f, h) &&
+      !GetPercentHeight(pos->mHeight, aRenderingContext, f, h)) {
+    NS_ASSERTION(pos->mHeight.GetUnit() == eStyleUnit_Auto ||
+                 pos->mHeight.GetUnit() == eStyleUnit_Percent,
+                 "unknown height unit");
+    // There's no basis for the percentage height, so it acts like auto.
+    // Should we consider a max-height < min-height pair a basis for
+    // percentage heights?  The spec is somewhat unclear, and not doing
+    // so is simpler and avoids troubling discontinuities in behavior,
+    // so I'll choose not to. -LDB
+    return PR_FALSE;
+  }
+
+  nscoord maxh;
+  if (nsLayoutUtils::
+        GetAbsoluteCoord(pos->mMaxHeight, aRenderingContext, f, maxh) ||
+      GetPercentHeight(pos->mMaxHeight, aRenderingContext, f, maxh)) {
+    if (maxh < h)
+      h = maxh;
+  } else {
+    NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_None ||
+                 pos->mMaxHeight.GetUnit() == eStyleUnit_Percent,
+                 "unknown max-height unit");
+  }
+
+  nscoord minh;
+  if (nsLayoutUtils::
+        GetAbsoluteCoord(pos->mMinHeight, aRenderingContext, f, minh) ||
+      GetPercentHeight(pos->mMinHeight, aRenderingContext, f, minh)) {
+    if (minh > h)
+      h = minh;
+  } else {
+    NS_ASSERTION(pos->mMaxHeight.GetUnit() == eStyleUnit_Percent,
+                 "unknown min-height unit");
+  }
+
+  aResult = NSToCoordRound(aStyle.GetPercentValue() * h);
+  return PR_TRUE;
+}
+
 // Handles only -moz-intrinsic and -moz-min-intrinsic, and
 // -moz-shrink-wrap for min-width and max-width, since the others
 // (-moz-shrink-wrap for width, and -moz-fill) have no effect on
 // intrinsic widths.
 enum eWidthProperty { PROP_WIDTH, PROP_MAX_WIDTH, PROP_MIN_WIDTH };
 static PRBool
 GetIntrinsicCoord(const nsStyleCoord& aStyle,
                   nsIRenderingContext* aRenderingContext,
@@ -1364,16 +1425,53 @@ nsLayoutUtils::IntrinsicForContainer(nsI
       result = aFrame->GetPrefWidth(aRenderingContext);
 #ifdef DEBUG_INTRINSIC_WIDTH
     --gNoiseIndent;
     nsFrame::IndentBy(stdout, gNoiseIndent);
     NS_STATIC_CAST(nsFrame*, aFrame)->ListTag(stdout);
     printf(" %s intrinsic width from frame is %d.\n",
            aType == MIN_WIDTH ? "min" : "pref", result);
 #endif
+
+    // Handle elements with an intrinsic ratio (or size) and a specified
+    // height, min-height, or max-height.
+    const nsStyleCoord &styleHeight = stylePos->mHeight;
+    const nsStyleCoord &styleMinHeight = stylePos->mMinHeight;
+    const nsStyleCoord &styleMaxHeight = stylePos->mMaxHeight;
+    if (styleHeight.GetUnit() != eStyleUnit_Auto ||
+        !(styleMinHeight.GetUnit() == eStyleUnit_Coord &&
+          styleMinHeight.GetCoordValue() == 0) ||
+        styleMaxHeight.GetUnit() != eStyleUnit_None) {
+
+      nsSize ratio = aFrame->GetIntrinsicRatio();
+
+      if (ratio.height != 0) {
+
+        nscoord h;
+        if (GetAbsoluteCoord(styleHeight, aRenderingContext, aFrame, h) ||
+            GetPercentHeight(styleHeight, aRenderingContext, aFrame, h)) {
+          result =
+            NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
+        }
+
+        if (GetAbsoluteCoord(styleMaxHeight, aRenderingContext, aFrame, h) ||
+            GetPercentHeight(styleMaxHeight, aRenderingContext, aFrame, h)) {
+          h = NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
+          if (h < result)
+            result = h;
+        }
+
+        if (GetAbsoluteCoord(styleMinHeight, aRenderingContext, aFrame, h) ||
+            GetPercentHeight(styleMinHeight, aRenderingContext, aFrame, h)) {
+          h = NSToCoordRound(h * (float(ratio.width) / float(ratio.height)));
+          if (h > result)
+            result = h;
+        }
+      }
+    }
   }
       
   if (aFrame->GetType() == nsGkAtoms::tableFrame) {
     // Tables can't shrink smaller than their intrinsic minimum width,
     // no matter what.
     min = aFrame->GetMinWidth(aRenderingContext);
   }
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -3094,16 +3094,22 @@ nsFrame::IntrinsicWidthOffsets(nsIRender
       result.hPadding = presContext->DevPixelsToAppUnits(padding.LeftRight());
       result.hPctPadding = 0;
     }
   }
 
   return result;
 }
 
+/* virtual */ nsSize
+nsFrame::GetIntrinsicRatio()
+{
+  return nsSize(0, 0);
+}
+
 inline PRBool
 IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight)
 {
   nsStyleUnit unit = aCoord.GetUnit();
   return unit == eStyleUnit_Auto ||  // only for 'height'
          unit == eStyleUnit_None ||  // only for 'max-height'
          (unit == eStyleUnit_Percent && 
           aCBHeight == NS_AUTOHEIGHT);
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -274,16 +274,17 @@ public:
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
   virtual void AddInlineMinWidth(nsIRenderingContext *aRenderingContext,
                                  InlineMinWidthData *aData);
   virtual void AddInlinePrefWidth(nsIRenderingContext *aRenderingContext,
                                   InlinePrefWidthData *aData);
   virtual IntrinsicWidthOffsetData
     IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext);
+  virtual nsSize GetIntrinsicRatio();
 
   virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
                              nsSize aCBSize, nscoord aAvailableWidth,
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
                              PRBool aShrinkWrap);
 
   /**
    * A helper, used by |nsFrame::ComputeSize| (for frames that need to
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -95,16 +95,22 @@ nsHTMLCanvasFrame::GetPrefWidth(nsIRende
   // XXX The caller doesn't account for constraints of the height,
   // min-height, and max-height properties.
   nscoord result = nsPresContext::CSSPixelsToAppUnits(GetCanvasSize().width);
   DISPLAY_PREF_WIDTH(this, result);
   return result;
 }
 
 /* virtual */ nsSize
+nsHTMLCanvasFrame::GetIntrinsicRatio()
+{
+  return GetCanvasSize();
+}
+
+/* virtual */ nsSize
 nsHTMLCanvasFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
                                nsSize aCBSize, nscoord aAvailableWidth,
                                nsSize aMargin, nsSize aBorder, nsSize aPadding,
                                PRBool aShrinkWrap)
 {
   nsSize size = GetCanvasSize();
   nsSize canvasSize(nsPresContext::CSSPixelsToAppUnits(size.width),
                     nsPresContext::CSSPixelsToAppUnits(size.height));
--- a/layout/generic/nsHTMLCanvasFrame.h
+++ b/layout/generic/nsHTMLCanvasFrame.h
@@ -60,16 +60,17 @@ public:
   void PaintCanvas(nsIRenderingContext& aRenderingContext,
                    const nsRect& aDirtyRect, nsPoint aPt);
                               
   /* get the size of the canvas's image */
   nsSize GetCanvasSize();
 
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
+  virtual nsSize GetIntrinsicRatio();
 
   virtual nsSize ComputeSize(nsIRenderingContext *aRenderingContext,
                              nsSize aCBSize, nscoord aAvailableWidth,
                              nsSize aMargin, nsSize aBorder, nsSize aPadding,
                              PRBool aShrinkWrap);
 
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -95,20 +95,20 @@ struct nsPeekOffsetStruct;
 struct nsPoint;
 struct nsRect;
 struct nsSize;
 struct nsMargin;
 
 typedef class nsIFrame nsIBox;
 
 // IID for the nsIFrame interface 
-// 902aaa17-6433-4d96-86b3-fe1f4af41159
+// 4c0cfb5b-864d-46c5-ad78-b1b4de35a4c3
 #define NS_IFRAME_IID \
-{ 0x902aaa17, 0x6433, 0x4d96, \
-  { 0x86, 0xb3, 0xfe, 0x1f, 0x4a, 0xf4, 0x11, 0x59 } }
+{ 0x4c0cfb5b, 0x864d, 0x46c5, \
+  { 0xad, 0x78, 0xb1, 0xb4, 0xde, 0x35, 0xa4, 0xc3 } }
 
 /**
  * Indication of how the frame can be split. This is used when doing runaround
  * of floats, and when pulling up child frames from a next-in-flow.
  *
  * The choices are splittable, not splittable at all, and splittable in
  * a non-rectangular fashion. This last type only applies to block-level
  * elements, and indicates whether splitting can be used when doing runaround.
@@ -1181,16 +1181,27 @@ public:
     IntrinsicWidthOffsetData()
       : hPadding(0), hBorder(0), hMargin(0)
       , hPctPadding(0.0f), hPctMargin(0.0f)
     {}
   };
   virtual IntrinsicWidthOffsetData
     IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext) = 0;
 
+  /*
+   * Get the intrinsic ratio of this element, or nsSize(0,0) if it has
+   * no intrinsic ratio.  The intrinsic ratio is the ratio of the
+   * height/width of a box with an intrinsic size or the intrinsic
+   * aspect ratio of a scalable vector image without an intrinsic size.
+   *
+   * Either one of the sides may be zero, indicating a zero or infinite
+   * ratio.
+   */
+  virtual nsSize GetIntrinsicRatio() = 0;
+
   /**
    * Compute the size that a frame will occupy.  Called while
    * constructing the nsHTMLReflowState to be used to Reflow the frame,
    * in order to fill its mComputedWidth and mComputedHeight member
    * variables.
    *
    * The |height| member of the return value may be
    * NS_UNCONSTRAINEDSIZE, but the |width| member must not be.
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -764,16 +764,23 @@ nsImageFrame::GetPrefWidth(nsIRenderingC
   DISPLAY_PREF_WIDTH(this, result);
   nsPresContext *presContext = PresContext();
   EnsureIntrinsicSize(presContext);
   // convert from normal twips to scaled twips (printing...)
   result = mIntrinsicSize.width;
   return result;
 }
 
+/* virtual */ nsSize
+nsImageFrame::GetIntrinsicRatio()
+{
+  EnsureIntrinsicSize(PresContext());
+  return mIntrinsicSize;
+}
+
 NS_IMETHODIMP
 nsImageFrame::Reflow(nsPresContext*          aPresContext,
                      nsHTMLReflowMetrics&     aMetrics,
                      const nsHTMLReflowState& aReflowState,
                      nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsImageFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus);
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -103,16 +103,17 @@ public:
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
   NS_IMETHOD BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                               const nsRect&           aDirtyRect,
                               const nsDisplayListSet& aLists);
   virtual nscoord GetMinWidth(nsIRenderingContext *aRenderingContext);
   virtual nscoord GetPrefWidth(nsIRenderingContext *aRenderingContext);
+  virtual nsSize GetIntrinsicRatio();
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
   
   NS_IMETHOD  GetContentForEvent(nsPresContext* aPresContext,
                                  nsEvent* aEvent,
                                  nsIContent** aContent);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/364066-1-ref.html
@@ -0,0 +1,73 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Testcase, bug 364066</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css">
+
+table { margin: 1px 0; border-spacing: 0; }
+td { padding: 0; border: 1px solid; }
+
+</style>
+</head>
+<body>
+
+<!-- solidblue.png is a 16x16 image -->
+
+<table border><tr>
+
+<td width="200">
+
+<table><tr><td>
+  <div style="height: 16px; width: 16px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 32px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 32px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 8px; width: 8px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 8px; width: 8px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 8px; width: 8px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 8px; width: 8px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+
+</td><td width="200">
+
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+<table><tr><td>
+  <div style="height: 40px; width: 32px"></div>
+</td></tr></table>
+
+</td>
+
+</tr></table>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/364066-1.html
@@ -0,0 +1,72 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Testcase, bug 364066</title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta http-equiv="Content-Style-Type" content="text/css">
+<style type="text/css">
+
+table { margin: 1px 0; border-spacing: 0; }
+td { padding: 0; border: 1px solid; line-height: 1px; }
+img { visibility: hidden; vertical-align: bottom; }
+
+</style>
+</head>
+<body>
+
+<!-- solidblue.png is a 16x16 image -->
+
+<table border><tr>
+
+<td width="200">
+
+<table><tr><td><img src="solidblue.png"></td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="height: 32px">
+</td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="min-height: 32px">
+</td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="max-height: 8px">
+</td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="height: 4px;min-height:8px">
+</td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="max-height: 4px;min-height:8px">
+</td></tr></table>
+<table><tr><td>
+  <img src="solidblue.png" style="height: 12px;max-height:8px">
+</td></tr></table>
+<table><tr><td><div style="height: 40px">
+  <img src="solidblue.png" style="height: 80%">
+</div></td></tr></table>
+<table><tr><td><div style="height: 40px">
+  <img src="solidblue.png" style="min-height: 80%">
+</div></td></tr></table>
+<table><tr><td><div style="height: 40px">
+  <img src="solidblue.png" style="height: 60px; max-height: 80%">
+</div></td></tr></table>
+
+</td><td width="200">
+
+<table><tr><td><div style="height: 40px"><div style="height:100%"><div style="height:100%">
+  <img src="solidblue.png" style="height: 80%">
+</div></div></div></td></tr></table>
+<table><tr><td><div style="height: 64px; max-height: 40px">
+  <img src="solidblue.png" style="height: 80%">
+</div></td></tr></table>
+<table><tr><td><div style="height: 20px;min-height: 40px">
+  <img src="solidblue.png" style="height: 80%">
+</div></td></tr></table>
+<table><tr><td><div style="height: 40px"><div style="height:20px; min-height:100%"><div style="height:100px;max-height:100%">
+  <img src="solidblue.png" style="height: 80%">
+</div></div></div></td></tr></table>
+
+</td>
+
+</tr></table>
+
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -169,16 +169,17 @@ fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") ==
 == 362594-2b.html 362594-2-standards-ref.html
 == 362594-2c.html 362594-2-standards-ref.html
 fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == 362901-1.html 362901-1-ref.html # mac bug 382399
 == 363329-1.html 363329-1-ref.html
 == 363329-2.html 363329-2-ref.html
 == 363637-1.html 363637-1-ref.html
 == 363874.html 363874-ref.html
 == 363874-max-width.html 363874-max-width-ref.html
+== 364066-1.html 364066-1-ref.html
 == 364079-1.html 364079-1-ref.html
 == 364861-1.html 364861-1-ref.html
 == 364862-1.html 364862-1-ref.html
 == 365173-1.html 365173-1-ref.html
 == 366616-1.xul 366616-1-ref.xul
 == 367220-1.html 367220-1-ref.html
 == 367247-s-visible.html 367247-s-hidden.html
 == 367247-s-hidden.html 367247-s-auto.html
--- a/layout/svg/base/src/nsSVGOuterSVGFrame.h
+++ b/layout/svg/base/src/nsSVGOuterSVGFrame.h
@@ -62,16 +62,19 @@ protected:
    // nsISupports interface:
   NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
 private:
   NS_IMETHOD_(nsrefcnt) AddRef() { return 1; }
   NS_IMETHOD_(nsrefcnt) Release() { return 1; }
 
 public:
   // nsIFrame:
+  // XXX Should this implement intrinsic width methods (esp.
+  // GetIntrinsicRatio)?
+
   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
                     nsHTMLReflowMetrics&     aDesiredSize,
                     const nsHTMLReflowState& aReflowState,
                     nsReflowStatus&          aStatus);
 
   NS_IMETHOD  DidReflow(nsPresContext*   aPresContext,
                         const nsHTMLReflowState*  aReflowState,
                         nsDidReflowStatus aStatus);