diff --git a/layout/html/base/src/nsFrameReflowState.cpp b/layout/html/base/src/nsFrameReflowState.cpp
index 5ea02977d2d3..4f7a7db1731e 100644
--- a/layout/html/base/src/nsFrameReflowState.cpp
+++ b/layout/html/base/src/nsFrameReflowState.cpp
@@ -168,6 +168,59 @@ nsHTMLReflowState::DetermineFrameType(nsIPresContext& aPresContext)
}
}
+// Helper function that re-calculates the left and right margin based on
+// the width of the containing block, the border/padding, and the computed
+// width.
+//
+// This function is called by InitConstraints() when the 'width' property
+// has a value other than 'auto'
+void
+nsHTMLReflowState::CalculateLeftRightMargin(const nsHTMLReflowState* aContainingBlockRS,
+ const nsStyleSpacing* aSpacing,
+ nscoord aComputedWidth,
+ const nsMargin& aBorderPadding,
+ nscoord& aComputedLeftMargin,
+ nscoord& aComputedRightMargin)
+{
+ PRBool isAutoLeftMargin = eStyleUnit_Auto == aSpacing->mMargin.GetLeftUnit();
+ PRBool isAutoRightMargin = eStyleUnit_Auto == aSpacing->mMargin.GetRightUnit();
+
+ // Calculate how much space is available for margins
+ nscoord availMarginSpace = aContainingBlockRS->computedWidth - aComputedWidth -
+ aBorderPadding.left - aBorderPadding.right;
+
+ // See whether we're over constrained
+ if (!isAutoLeftMargin && !isAutoRightMargin) {
+ // Neither margin is 'auto' so we're over constrained. Use the
+ // 'direction' property to tell which margin to ignore
+ const nsStyleDisplay* display;
+ frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)display);
+
+ if (NS_STYLE_DIRECTION_LTR == display->mDirection) {
+ isAutoRightMargin = PR_TRUE;
+ } else {
+ isAutoLeftMargin = PR_TRUE;
+ }
+ }
+
+ if (isAutoLeftMargin) {
+ if (isAutoRightMargin) {
+ // Both margins are 'auto' so their computed values are equal
+ if (availMarginSpace <= 0) {
+ aComputedLeftMargin = aComputedRightMargin = 0;
+ } else {
+ aComputedLeftMargin = (availMarginSpace + 1) / 2;
+ aComputedRightMargin = availMarginSpace - aComputedLeftMargin;
+ }
+ } else {
+ aComputedLeftMargin = PR_MAX(0, availMarginSpace - aComputedRightMargin);
+ }
+
+ } else if (isAutoRightMargin) {
+ aComputedRightMargin = PR_MAX(0, availMarginSpace - aComputedLeftMargin);
+ }
+}
+
void
nsHTMLReflowState::InitConstraints(nsIPresContext& aPresContext)
{
@@ -185,22 +238,18 @@ nsHTMLReflowState::InitConstraints(nsIPresContext& aPresContext)
computedTopMargin = margin.top;
computedBottomMargin = margin.bottom;
+ // Calculate the line height.
+ // XXX Do we need to do this for all elements or just inline non-replaced
+ // elements?
mLineHeight = CalcLineHeight(aPresContext, frame);
- // Some frame types are not constrained by width/height style
- // attributes. Return if the frame is one of those types.
- switch (frameType) {
- case eCSSFrameType_Unknown:
- return;
-
- case eCSSFrameType_Inline:
+ if (eCSSFrameType_Inline == frameType) {
+ // 'width' property doesn't apply to inline non-replaced elements. The
+ // 'height' is given by the element's 'line-height' value
if (mLineHeight >= 0) {
computedHeight = mLineHeight;
}
- return;
-
- default:
- break;
+ return; // nothing else to compute
}
// If this is the root frame then set the computed width and
@@ -210,8 +259,7 @@ nsHTMLReflowState::InitConstraints(nsIPresContext& aPresContext)
computedHeight = maxSize.height;
} else {
- // Get the containing block reflow state, because we'll need its
- // computed width
+ // Get the containing block reflow state
const nsHTMLReflowState* cbrs =
GetContainingBlockReflowState(parentReflowState);
NS_ASSERTION(nsnull != cbrs, "no containing block");
@@ -220,8 +268,8 @@ nsHTMLReflowState::InitConstraints(nsIPresContext& aPresContext)
// calculating the computed width and height
nscoord containingBlockWidth = cbrs->computedWidth;
nscoord containingBlockHeight = cbrs->computedHeight;
- PRIntn widthUnit = pos->mWidth.GetUnit();
- PRIntn heightUnit = pos->mHeight.GetUnit();
+ nsStyleUnit widthUnit = pos->mWidth.GetUnit();
+ nsStyleUnit heightUnit = pos->mHeight.GetUnit();
// Check for a percentage based height
if (eStyleUnit_Percent == heightUnit) {
@@ -232,86 +280,82 @@ nsHTMLReflowState::InitConstraints(nsIPresContext& aPresContext)
}
}
- // Compute border and padding
- nsMargin borderPadding;
- ComputeBorderPaddingFor(frame, parentReflowState, borderPadding);
-
- // Get the spacing style information
- const nsStyleSpacing* spacing;
- frame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)spacing);
-
- // Compute the content width
- PRBool isAutoLeftMargin = eStyleUnit_Auto == spacing->mMargin.GetLeftUnit();
- PRBool isAutoRightMargin = eStyleUnit_Auto == spacing->mMargin.GetRightUnit();
-
- if (eStyleUnit_Auto == widthUnit) {
- // 'auto' values for left or right margins become 0
- if (isAutoLeftMargin) {
- computedLeftMargin = 0;
+ // Calculate the computed width and height. This varies by frame type
+ if ((eCSSFrameType_InlineReplaced == frameType) ||
+ (eCSSFrameType_FloatingReplaced == frameType)) {
+ // Inline replaced element and floating replaced element are basically
+ // treated the same
+ if (eStyleUnit_Auto == widthUnit) {
+ // A specified value of 'auto' uses the element's intrinsic width
+ computedWidth = NS_INTRINSICSIZE;
+ } else {
+ ComputeHorizontalValue(*cbrs, widthUnit, pos->mWidth, computedWidth);
}
- if (isAutoRightMargin) {
- computedRightMargin = 0;
+ if (eStyleUnit_Auto == heightUnit) {
+ // A specified value of 'auto' uses the element's intrinsic height
+ computedHeight = NS_INTRINSICSIZE;
+ } else {
+ ComputeVerticalValue(*cbrs, heightUnit, pos->mHeight, computedHeight);
}
- computedWidth = containingBlockWidth - computedLeftMargin -
- computedRightMargin - borderPadding.left - borderPadding.right;
+ } else if (eCSSFrameType_Floating == frameType) {
+ // Floating non-replaced element
+ if (eStyleUnit_Auto == widthUnit) {
+ // A specified value of 'auto' becomes a computed width of 0
+ computedWidth = 0;
+ } else {
+ ComputeHorizontalValue(*cbrs, widthUnit, pos->mWidth, computedWidth);
+ }
+ if (eStyleUnit_Auto == heightUnit) {
+ computedHeight = NS_AUTOHEIGHT; // let it choose its height
+ } else {
+ ComputeVerticalValue(*cbrs, heightUnit, pos->mHeight, computedHeight);
+ }
} else {
- if (eStyleUnit_Coord == widthUnit) {
- computedWidth = pos->mWidth.GetCoordValue();
- } else if (eStyleUnit_Percent == widthUnit) {
- computedWidth = nscoord(pos->mWidth.GetPercentValue() * containingBlockWidth);
+ // Block-level elements and absolutely positioned elements
+ const nsStyleSpacing* spacing;
+ frame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)spacing);
+
+ // Compute border and padding
+ nsMargin borderPadding;
+ ComputeBorderPaddingFor(frame, parentReflowState, borderPadding);
+
+ // Compute the content width
+ if (eStyleUnit_Auto == widthUnit) {
+ if (eCSSFrameType_BlockReplaced == frameType) {
+ // Block-level replaced element in the flow. A specified value of 'auto'
+ // uses the element's intrinsic width
+ computedWidth = NS_INTRINSICSIZE;
+
+ } else {
+ // Block-level non-replaced element in the flow. 'auto' values for
+ // margin-left and margin-right become 0 and the sum of the areas must
+ // equal the width of the containing block
+ computedWidth = containingBlockWidth - computedLeftMargin - computedRightMargin -
+ borderPadding.left - borderPadding.right;
+ }
+
} else {
- NS_ASSERTION(PR_FALSE, "unexpected width constraint");
+ ComputeHorizontalValue(*cbrs, widthUnit, pos->mWidth, computedWidth);
+
+ // Calculate the computed left and right margin again taking into
+ // account the computed width, border/padding, and width of the
+ // containing block
+ CalculateLeftRightMargin(cbrs, spacing, computedWidth, borderPadding,
+ computedLeftMargin, computedRightMargin);
}
-
- // Calculate the computed left and right margin
- nscoord extra = containingBlockWidth - computedWidth - borderPadding.left -
- borderPadding.right;
-
- // See whether we're over constrained
- if (!isAutoLeftMargin && !isAutoRightMargin) {
- // Neither margin is 'auto' so we're over constrained. Use the
- // 'direction' property to tell which margin to ignore
- const nsStyleDisplay* display;
- frame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&)display);
-
- if (NS_STYLE_DIRECTION_LTR == display->mDirection) {
- isAutoRightMargin = PR_TRUE;
+
+ // Compute the content height
+ if (eStyleUnit_Auto == heightUnit) {
+ if (eCSSFrameType_BlockReplaced == frameType) {
+ computedHeight = NS_INTRINSICSIZE;
} else {
- isAutoLeftMargin = PR_TRUE;
+ computedHeight = NS_AUTOHEIGHT;
}
+ } else {
+ ComputeVerticalValue(*cbrs, heightUnit, pos->mHeight, computedHeight);
}
-
- if (isAutoLeftMargin) {
- if (isAutoRightMargin) {
- // Both margins are 'auto' so their computed values are equal
- if (extra <= 0) {
- computedLeftMargin = computedRightMargin = 0;
- } else {
- computedLeftMargin = (extra + 1) / 2;
- computedRightMargin = extra - computedLeftMargin;
- }
- } else {
- computedLeftMargin = PR_MAX(0, extra - computedRightMargin);
- }
-
- } else if (isAutoRightMargin) {
- computedRightMargin = PR_MAX(0, extra - computedLeftMargin);
- }
- }
-
- // Compute the content height
- switch (heightUnit) {
- case eStyleUnit_Coord:
- computedHeight = pos->mHeight.GetCoordValue();
- break;
- case eStyleUnit_Percent:
- computedHeight = nscoord(pos->mHeight.GetPercentValue() * containingBlockHeight);
- break;
- case eStyleUnit_Auto:
- computedHeight = NS_AUTOHEIGHT;
- break;
}
}
}
@@ -387,7 +431,7 @@ nsHTMLReflowState::CalcLineHeight(nsIPresContext& aPresContext,
void
nsHTMLReflowState::ComputeHorizontalValue(const nsHTMLReflowState& aRS,
nsStyleUnit aUnit,
- nsStyleCoord& aCoord,
+ const nsStyleCoord& aCoord,
nscoord& aResult)
{
aResult = 0;
@@ -395,21 +439,30 @@ nsHTMLReflowState::ComputeHorizontalValue(const nsHTMLReflowState& aRS,
nscoord width = aRS.computedWidth;
float pct = aCoord.GetPercentValue();
aResult = NSToCoordFloor(width * pct);
+
+ } else if (eStyleUnit_Coord == aUnit) {
+ aResult = aCoord.GetCoordValue();
}
}
void
nsHTMLReflowState::ComputeVerticalValue(const nsHTMLReflowState& aRS,
nsStyleUnit aUnit,
- nsStyleCoord& aCoord,
+ const nsStyleCoord& aCoord,
nscoord& aResult)
{
aResult = 0;
if (eStyleUnit_Percent == aUnit) {
- // XXX temporary!
- nscoord width = aRS.computedWidth;
+ // Verify no one is trying to calculate a percentage based height against
+ // a height that's shrink wrapping to its content. In that case they should
+ // treat the specified value like 'auto'
+ NS_ASSERTION(NS_AUTOHEIGHT != aRS.computedHeight, "unexpected containing block height");
+ nscoord height = aRS.computedHeight;
float pct = aCoord.GetPercentValue();
- aResult = NSToCoordFloor(width * pct);
+ aResult = NSToCoordFloor(height * pct);
+
+ } else if (eStyleUnit_Coord == aUnit) {
+ aResult = aCoord.GetCoordValue();
}
}
diff --git a/layout/html/base/src/nsIHTMLReflow.h b/layout/html/base/src/nsIHTMLReflow.h
index 9aadcf062944..a05fba6d7828 100644
--- a/layout/html/base/src/nsIHTMLReflow.h
+++ b/layout/html/base/src/nsIHTMLReflow.h
@@ -25,6 +25,7 @@
class nsISpaceManager;
class nsBlockFrame;
class nsLineLayout;
+struct nsStyleSpacing;
// IID for the nsIHTMLFrame interface
// a6cf9069-15b3-11d2-932e-00805f8add32
@@ -122,8 +123,15 @@ enum nsCSSFrameType {
//----------------------------------------------------------------------
+// XXX I think these should be NS_UNCONSTRAINEDSIZE instead, but that causes
+// problems for tables
+#if 0
+#define NS_INTRINSICSIZE NS_UNCONSTRAINEDSIZE
+#define NS_AUTOHEIGHT NS_UNCONSTRAINEDSIZE
+#else
#define NS_INTRINSICSIZE 0
#define NS_AUTOHEIGHT 0
+#endif
/**
* HTML version of the reflow state.
@@ -320,12 +328,21 @@ protected:
void InitConstraints(nsIPresContext& aPresContext);
+ void CalculateLeftRightMargin(const nsHTMLReflowState* aContainingBlockRS,
+ const nsStyleSpacing* aSpacing,
+ nscoord aComputedWidth,
+ const nsMargin& aBorderPadding,
+ nscoord& aComputedLeftMargin,
+ nscoord& aComputedRightMargin);
+
static void ComputeHorizontalValue(const nsHTMLReflowState& aReflowState,
- nsStyleUnit aUnit, nsStyleCoord& aCoord,
+ nsStyleUnit aUnit,
+ const nsStyleCoord& aCoord,
nscoord& aResult);
static void ComputeVerticalValue(const nsHTMLReflowState& aReflowState,
- nsStyleUnit aUnit, nsStyleCoord& aCoord,
+ nsStyleUnit aUnit,
+ const nsStyleCoord& aCoord,
nscoord& aResult);
};