Implement correct intrinsic width calculation (for container) for elements with specified height properties and an intrinsic ratio.
b=364066 r+sr=bzbarsky
--- 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);