Bug 575870 - Enable the firefox button on xp themed, classic, and aero basic. r=dao...
[mozilla-central.git] / layout / generic / nsHTMLReflowState.cpp
blobd8db30c329ec57b1c02116bb68edc897f925f82f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Communicator client code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 /* struct containing the input to nsIFrame::Reflow */
40 #include "nsCOMPtr.h"
41 #include "nsStyleConsts.h"
42 #include "nsCSSAnonBoxes.h"
43 #include "nsFrame.h"
44 #include "nsIContent.h"
45 #include "nsGkAtoms.h"
46 #include "nsPresContext.h"
47 #include "nsIPresShell.h"
48 #include "nsIDeviceContext.h"
49 #include "nsIRenderingContext.h"
50 #include "nsIFontMetrics.h"
51 #include "nsBlockFrame.h"
52 #include "nsLineBox.h"
53 #include "nsImageFrame.h"
54 #include "nsTableFrame.h"
55 #include "nsTableCellFrame.h"
56 #include "nsIServiceManager.h"
57 #include "nsIPercentHeightObserver.h"
58 #include "nsContentUtils.h"
59 #include "nsLayoutUtils.h"
60 #ifdef IBMBIDI
61 #include "nsBidiUtils.h"
62 #endif
64 #ifdef NS_DEBUG
65 #undef NOISY_VERTICAL_ALIGN
66 #else
67 #undef NOISY_VERTICAL_ALIGN
68 #endif
70 using namespace mozilla;
72 // Prefs-driven control for |text-decoration: blink|
73 static PRPackedBool sPrefIsLoaded = PR_FALSE;
74 static PRPackedBool sBlinkIsAllowed = PR_TRUE;
76 enum eNormalLineHeightControl {
77 eUninitialized = -1,
78 eNoExternalLeading = 0, // does not include external leading
79 eIncludeExternalLeading, // use whatever value font vendor provides
80 eCompensateLeading // compensate leading if leading provided by font vendor is not enough
83 static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
85 // Initialize a <b>root</b> reflow state with a rendering context to
86 // use for measuring things.
87 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
88 nsIFrame* aFrame,
89 nsIRenderingContext* aRenderingContext,
90 const nsSize& aAvailableSpace)
91 : nsCSSOffsetState(aFrame, aRenderingContext)
92 , mBlockDelta(0)
93 , mReflowDepth(0)
95 NS_PRECONDITION(aPresContext, "no pres context");
96 NS_PRECONDITION(aRenderingContext, "no rendering context");
97 NS_PRECONDITION(aFrame, "no frame");
98 parentReflowState = nsnull;
99 availableWidth = aAvailableSpace.width;
100 availableHeight = aAvailableSpace.height;
101 mFloatManager = nsnull;
102 mLineLayout = nsnull;
103 mFlags.mSpecialHeightReflow = PR_FALSE;
104 mFlags.mIsTopOfPage = PR_FALSE;
105 mFlags.mTableIsSplittable = PR_FALSE;
106 mFlags.mNextInFlowUntouched = PR_FALSE;
107 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
108 mFlags.mHasClearance = PR_FALSE;
109 mFlags.mHeightDependsOnAncestorCell = PR_FALSE;
110 mDiscoveredClearance = nsnull;
111 mPercentHeightObserver = nsnull;
112 Init(aPresContext);
115 static PRBool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
117 nsIFrame* frameNext = aFrame->GetNextInFlow();
118 nsIFrame* parentNext = aParent->GetNextInFlow();
119 return frameNext && parentNext && frameNext->GetParent() == parentNext;
122 // Initialize a reflow state for a child frames reflow. Some state
123 // is copied from the parent reflow state; the remaining state is
124 // computed.
125 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
126 const nsHTMLReflowState& aParentReflowState,
127 nsIFrame* aFrame,
128 const nsSize& aAvailableSpace,
129 nscoord aContainingBlockWidth,
130 nscoord aContainingBlockHeight,
131 PRBool aInit)
132 : nsCSSOffsetState(aFrame, aParentReflowState.rendContext)
133 , mBlockDelta(0)
134 , mReflowDepth(aParentReflowState.mReflowDepth + 1)
135 , mFlags(aParentReflowState.mFlags)
137 NS_PRECONDITION(aPresContext, "no pres context");
138 NS_PRECONDITION(aFrame, "no frame");
139 NS_PRECONDITION((aContainingBlockWidth == -1) ==
140 (aContainingBlockHeight == -1),
141 "cb width and height should only be non-default together");
142 NS_PRECONDITION(aInit == PR_TRUE || aInit == PR_FALSE,
143 "aInit out of range for PRBool");
144 NS_PRECONDITION(!mFlags.mSpecialHeightReflow ||
145 !NS_SUBTREE_DIRTY(aFrame),
146 "frame should be clean when getting special height reflow");
148 parentReflowState = &aParentReflowState;
150 // If the parent is dirty, then the child is as well.
151 // XXX Are the other cases where the parent reflows a child a second
152 // time, as a resize?
153 if (!mFlags.mSpecialHeightReflow)
154 frame->AddStateBits(parentReflowState->frame->GetStateBits() &
155 NS_FRAME_IS_DIRTY);
157 availableWidth = aAvailableSpace.width;
158 availableHeight = aAvailableSpace.height;
160 mFloatManager = aParentReflowState.mFloatManager;
161 if (frame->IsFrameOfType(nsIFrame::eLineParticipant))
162 mLineLayout = aParentReflowState.mLineLayout;
163 else
164 mLineLayout = nsnull;
165 mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
166 mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
167 CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
168 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
169 mFlags.mHasClearance = PR_FALSE;
170 mDiscoveredClearance = nsnull;
171 mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
172 aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
173 ? aParentReflowState.mPercentHeightObserver : nsnull;
175 if (aInit) {
176 Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
180 inline nscoord
181 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
182 nscoord aContentEdgeToBoxSizing,
183 nscoord aBoxSizingToMarginEdge,
184 const nsStyleCoord& aCoord)
186 return nsLayoutUtils::ComputeWidthValue(rendContext, frame,
187 aContainingBlockWidth,
188 aContentEdgeToBoxSizing,
189 aBoxSizingToMarginEdge,
190 aCoord);
193 nscoord
194 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
195 PRUint8 aBoxSizing,
196 const nsStyleCoord& aCoord)
198 nscoord inside = 0, outside = mComputedBorderPadding.LeftRight() +
199 mComputedMargin.LeftRight();
200 switch (aBoxSizing) {
201 case NS_STYLE_BOX_SIZING_BORDER:
202 inside = mComputedBorderPadding.LeftRight();
203 break;
204 case NS_STYLE_BOX_SIZING_PADDING:
205 inside = mComputedPadding.LeftRight();
206 break;
208 outside -= inside;
210 return ComputeWidthValue(aContainingBlockWidth, inside,
211 outside, aCoord);
214 void
215 nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth)
217 NS_ASSERTION(frame, "Must have a frame!");
218 // It'd be nice to assert that |frame| is not in reflow, but this fails for
219 // two reasons:
221 // 1) Viewport frames reset the computed width on a copy of their reflow
222 // state when reflowing fixed-pos kids. In that case we actually don't
223 // want to mess with the resize flags, because comparing the frame's rect
224 // to the munged computed width is pointless.
225 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
226 // state is not used to reflow the parent, but just as a parent for the
227 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
228 // (like a text control, for example), we'll end up creating a reflow
229 // state for the parent while the parent is reflowing.
231 NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width");
232 if (mComputedWidth != aComputedWidth) {
233 mComputedWidth = aComputedWidth;
234 if (frame->GetType() != nsGkAtoms::viewportFrame) { // Or check GetParent()?
235 InitResizeFlags(frame->PresContext());
240 void
241 nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight)
243 NS_ASSERTION(frame, "Must have a frame!");
244 // It'd be nice to assert that |frame| is not in reflow, but this fails
245 // because:
247 // nsFrame::BoxReflow creates a reflow state for its parent. This reflow
248 // state is not used to reflow the parent, but just as a parent for the
249 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
250 // (like a text control, for example), we'll end up creating a reflow
251 // state for the parent while the parent is reflowing.
253 NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height");
254 if (mComputedHeight != aComputedHeight) {
255 mComputedHeight = aComputedHeight;
256 InitResizeFlags(frame->PresContext());
260 void
261 nsHTMLReflowState::Init(nsPresContext* aPresContext,
262 nscoord aContainingBlockWidth,
263 nscoord aContainingBlockHeight,
264 const nsMargin* aBorder,
265 const nsMargin* aPadding)
267 NS_WARN_IF_FALSE(availableWidth != NS_UNCONSTRAINEDSIZE,
268 "have unconstrained width; this should only result from "
269 "very large sizes, not attempts at intrinsic width "
270 "calculation");
272 mStylePosition = frame->GetStylePosition();
273 mStyleDisplay = frame->GetStyleDisplay();
274 mStyleVisibility = frame->GetStyleVisibility();
275 mStyleBorder = frame->GetStyleBorder();
276 mStyleMargin = frame->GetStyleMargin();
277 mStylePadding = frame->GetStylePadding();
278 mStyleText = frame->GetStyleText();
280 InitFrameType();
281 InitCBReflowState();
283 InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding);
285 InitResizeFlags(aPresContext);
287 NS_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE &&
288 !frame->IsFrameOfType(nsIFrame::eReplaced)) ||
289 frame->GetType() == nsGkAtoms::textFrame ||
290 mComputedWidth != NS_UNCONSTRAINEDSIZE,
291 "have unconstrained width; this should only result from "
292 "very large sizes, not attempts at intrinsic width "
293 "calculation");
296 void nsHTMLReflowState::InitCBReflowState()
298 if (!parentReflowState) {
299 mCBReflowState = nsnull;
300 return;
303 // If outer tables ever become containing blocks, we need to make sure to use
304 // their mCBReflowState in the non-absolutely-positioned case for inner
305 // tables.
306 NS_ASSERTION(frame->GetType() != nsGkAtoms::tableFrame ||
307 !frame->GetParent()->IsContainingBlock(),
308 "Outer table should not be containing block");
310 if (parentReflowState->frame->IsContainingBlock() ||
311 // Absolutely positioned frames should always be kids of the frames that
312 // determine their containing block....
313 (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {
314 // an absolutely positioned inner table needs to use the parent of
315 // the outer table. So the above comment about absolutely
316 // positioned frames is sort of a lie.
317 if (parentReflowState->parentReflowState &&
318 frame->GetType() == nsGkAtoms::tableFrame) {
319 mCBReflowState = parentReflowState->parentReflowState;
320 } else {
321 mCBReflowState = parentReflowState;
324 return;
327 mCBReflowState = parentReflowState->mCBReflowState;
330 /* Check whether CalcQuirkContainingBlockHeight would stop on the
331 * given reflow state, using its block as a height. (essentially
332 * returns false for any case in which CalcQuirkContainingBlockHeight
333 * has a "continue" in its main loop.)
335 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
336 * this function as well
338 static PRBool
339 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs)
341 nsIAtom* frameType = rs->frame->GetType();
342 if (nsGkAtoms::blockFrame == frameType ||
343 #ifdef MOZ_XUL
344 nsGkAtoms::XULLabelFrame == frameType ||
345 #endif
346 nsGkAtoms::scrollFrame == frameType) {
347 // Note: This next condition could change due to a style change,
348 // but that would cause a style reflow anyway, which means we're ok.
349 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
350 if (!rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
351 return PR_FALSE;
355 return PR_TRUE;
359 void
360 nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext)
362 mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
363 frame->GetSize().width !=
364 mComputedWidth + mComputedBorderPadding.LeftRight();
366 // XXX Should we really need to null check mCBReflowState? (We do for
367 // at least nsBoxFrame).
368 if (IS_TABLE_CELL(frame->GetType()) &&
369 (mFlags.mSpecialHeightReflow ||
370 (frame->GetFirstInFlow()->GetStateBits() &
371 NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
372 (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
373 // Need to set the bit on the cell so that
374 // mCBReflowState->mFlags.mVResize is set correctly below when
375 // reflowing descendant.
376 mFlags.mVResize = PR_TRUE;
377 } else if (mCBReflowState && !frame->IsContainingBlock()) {
378 // XXX Is this problematic for relatively positioned inlines acting
379 // as containing block for absolutely positioned elements?
380 // Possibly; in that case we should at least be checking
381 // NS_SUBTREE_DIRTY, I'd think.
382 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
383 } else if (mComputedHeight == NS_AUTOHEIGHT) {
384 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
385 mCBReflowState) {
386 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
387 } else {
388 mFlags.mVResize = mFlags.mHResize;
390 mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame);
391 } else {
392 // not 'auto' height
393 mFlags.mVResize = frame->GetSize().height !=
394 mComputedHeight + mComputedBorderPadding.TopBottom();
397 PRBool dependsOnCBHeight =
398 mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent ||
399 mStylePosition->mMinHeight.GetUnit() == eStyleUnit_Percent ||
400 mStylePosition->mMaxHeight.GetUnit() == eStyleUnit_Percent ||
401 mStylePosition->mOffset.GetTopUnit() == eStyleUnit_Percent ||
402 mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto ||
403 frame->IsBoxFrame() ||
404 (mStylePosition->mHeight.GetUnit() == eStyleUnit_Auto &&
405 frame->GetIntrinsicSize().height.GetUnit() == eStyleUnit_Percent);
407 if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
408 NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
409 NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
410 "bad line-height value");
412 // line-height depends on block height
413 frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
414 // but only on containing blocks if this frame is not a suitable block
415 dependsOnCBHeight |= !frame->IsContainingBlock();
418 // If we're the descendant of a table cell that performs special height
419 // reflows and we could be the child that requires them, always set
420 // the vertical resize in case this is the first pass before the
421 // special height reflow. However, don't do this if it actually is
422 // the special height reflow, since in that case it will already be
423 // set correctly above if we need it set.
424 if (!mFlags.mVResize && mCBReflowState &&
425 (IS_TABLE_CELL(mCBReflowState->frame->GetType()) ||
426 mCBReflowState->mFlags.mHeightDependsOnAncestorCell) &&
427 !mCBReflowState->mFlags.mSpecialHeightReflow &&
428 dependsOnCBHeight) {
429 mFlags.mVResize = PR_TRUE;
430 mFlags.mHeightDependsOnAncestorCell = PR_TRUE;
433 // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed.
435 // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT|
436 // &&ed with the percentage height check. However, this doesn't get
437 // along with table special height reflows, since a special height
438 // reflow (a quirk that makes such percentage heights work on children
439 // of table cells) can cause not just a single percentage height to
440 // become fixed, but an entire descendant chain of percentage heights
441 // to become fixed.
442 if (dependsOnCBHeight && mCBReflowState) {
443 const nsHTMLReflowState *rs = this;
444 PRBool hitCBReflowState = PR_FALSE;
445 do {
446 rs = rs->parentReflowState;
447 if (!rs) {
448 break;
451 if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
452 break; // no need to go further
453 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
455 // Keep track of whether we've hit the containing block, because
456 // we need to go at least that far.
457 if (rs == mCBReflowState) {
458 hitCBReflowState = PR_TRUE;
461 } while (!hitCBReflowState ||
462 (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
463 !IsQuirkContainingBlockHeight(rs)));
464 // Note: We actually don't need to set the
465 // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases
466 // where we hit the early break statements in
467 // CalcQuirkContainingBlockHeight. But it doesn't hurt
468 // us to set the bit in these cases.
471 if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) {
472 // If we're reflowing everything, then we'll find out if we need
473 // to re-set this.
474 frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
478 /* static */
479 nscoord
480 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
482 const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
483 if (!rs)
484 return 0;
485 return rs->mComputedWidth;
488 /* static */
489 nsIFrame*
490 nsHTMLReflowState::GetContainingBlockFor(const nsIFrame* aFrame)
492 NS_PRECONDITION(aFrame, "Must have frame to work with");
493 nsIFrame* container = aFrame->GetParent();
494 if (aFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
495 // Absolutely positioned frames are just kids of their containing
496 // blocks (which may happen to be inlines).
497 return container;
499 while (container && !container->IsContainingBlock()) {
500 container = container->GetParent();
502 return container;
505 void
506 nsHTMLReflowState::InitFrameType()
508 const nsStyleDisplay *disp = mStyleDisplay;
509 nsCSSFrameType frameType;
511 // Section 9.7 of the CSS2 spec indicates that absolute position
512 // takes precedence over float which takes precedence over display.
513 // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
514 // Make sure the frame was actually moved out of the flow, and don't
516 // just assume what the style says, because we might not have had a
517 // useful float/absolute containing block
518 nsIFrame* frameToTest =
519 frame->GetType() == nsGkAtoms::tableFrame ? frame->GetParent() : frame;
521 DISPLAY_INIT_TYPE(frameToTest, this);
523 NS_ASSERTION(frameToTest->GetStyleDisplay()->IsAbsolutelyPositioned() ==
524 disp->IsAbsolutelyPositioned(),
525 "Unexpected position style");
526 NS_ASSERTION(frameToTest->GetStyleDisplay()->IsFloating() ==
527 disp->IsFloating(), "Unexpected float style");
528 if (frameToTest->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
529 if (disp->IsAbsolutelyPositioned()) {
530 frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
531 //XXXfr hack for making frames behave properly when in overflow container lists
532 // see bug 154892; need to revisit later
533 if (frameToTest->GetPrevInFlow())
534 frameType = NS_CSS_FRAME_TYPE_BLOCK;
536 else if (disp->IsFloating()) {
537 frameType = NS_CSS_FRAME_TYPE_FLOATING;
538 } else {
539 NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP,
540 "unknown out of flow frame type");
541 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
544 else {
545 switch (disp->mDisplay) {
546 case NS_STYLE_DISPLAY_BLOCK:
547 case NS_STYLE_DISPLAY_LIST_ITEM:
548 case NS_STYLE_DISPLAY_TABLE:
549 case NS_STYLE_DISPLAY_TABLE_CAPTION:
550 frameType = NS_CSS_FRAME_TYPE_BLOCK;
551 break;
553 case NS_STYLE_DISPLAY_INLINE:
554 case NS_STYLE_DISPLAY_INLINE_BLOCK:
555 case NS_STYLE_DISPLAY_MARKER:
556 case NS_STYLE_DISPLAY_INLINE_TABLE:
557 case NS_STYLE_DISPLAY_INLINE_BOX:
558 case NS_STYLE_DISPLAY_INLINE_GRID:
559 case NS_STYLE_DISPLAY_INLINE_STACK:
560 frameType = NS_CSS_FRAME_TYPE_INLINE;
561 break;
563 case NS_STYLE_DISPLAY_RUN_IN:
564 case NS_STYLE_DISPLAY_COMPACT:
565 // XXX need to look ahead at the frame's sibling
566 frameType = NS_CSS_FRAME_TYPE_BLOCK;
567 break;
569 case NS_STYLE_DISPLAY_TABLE_CELL:
570 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
571 case NS_STYLE_DISPLAY_TABLE_COLUMN:
572 case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
573 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
574 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
575 case NS_STYLE_DISPLAY_TABLE_ROW:
576 frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
577 break;
579 case NS_STYLE_DISPLAY_NONE:
580 default:
581 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
582 break;
586 // See if the frame is replaced
587 if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
588 frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType);
589 } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) {
590 frameType = NS_FRAME_REPLACED(frameType);
593 mFrameType = frameType;
596 void
597 nsHTMLReflowState::ComputeRelativeOffsets(const nsHTMLReflowState* cbrs,
598 nscoord aContainingBlockWidth,
599 nscoord aContainingBlockHeight,
600 nsPresContext* aPresContext)
602 // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
603 // and 'right' moves the boxes to the left. The computed values are always:
604 // left=-right
605 PRBool leftIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit();
606 PRBool rightIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit();
608 // Check for percentage based values and an unconstrained containing
609 // block width. Treat them like 'auto'
610 if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
611 if (eStyleUnit_Percent == mStylePosition->mOffset.GetLeftUnit()) {
612 leftIsAuto = PR_TRUE;
614 if (eStyleUnit_Percent == mStylePosition->mOffset.GetRightUnit()) {
615 rightIsAuto = PR_TRUE;
619 // If neither 'left' not 'right' are auto, then we're over-constrained and
620 // we ignore one of them
621 if (!leftIsAuto && !rightIsAuto) {
622 if (mCBReflowState &&
623 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) {
624 leftIsAuto = PR_TRUE;
625 } else {
626 rightIsAuto = PR_TRUE;
630 if (leftIsAuto) {
631 if (rightIsAuto) {
632 // If both are 'auto' (their initial values), the computed values are 0
633 mComputedOffsets.left = mComputedOffsets.right = 0;
634 } else {
635 // 'Right' isn't 'auto' so compute its value
636 mComputedOffsets.right = nsLayoutUtils::
637 ComputeWidthDependentValue(aContainingBlockWidth,
638 mStylePosition->mOffset.GetRight());
640 // Computed value for 'left' is minus the value of 'right'
641 mComputedOffsets.left = -mComputedOffsets.right;
644 } else {
645 NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
647 // 'Left' isn't 'auto' so compute its value
648 mComputedOffsets.left = nsLayoutUtils::
649 ComputeWidthDependentValue(aContainingBlockWidth,
650 mStylePosition->mOffset.GetLeft());
652 // Computed value for 'right' is minus the value of 'left'
653 mComputedOffsets.right = -mComputedOffsets.left;
656 // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
657 // move relatively positioned elements up and down. They also must be each
658 // other's negative
659 PRBool topIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit();
660 PRBool bottomIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit();
662 // Check for percentage based values and a containing block height that
663 // depends on the content height. Treat them like 'auto'
664 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
665 if (eStyleUnit_Percent == mStylePosition->mOffset.GetTopUnit()) {
666 topIsAuto = PR_TRUE;
668 if (eStyleUnit_Percent == mStylePosition->mOffset.GetBottomUnit()) {
669 bottomIsAuto = PR_TRUE;
673 // If neither is 'auto', 'bottom' is ignored
674 if (!topIsAuto && !bottomIsAuto) {
675 bottomIsAuto = PR_TRUE;
678 if (topIsAuto) {
679 if (bottomIsAuto) {
680 // If both are 'auto' (their initial values), the computed values are 0
681 mComputedOffsets.top = mComputedOffsets.bottom = 0;
682 } else {
683 // 'Bottom' isn't 'auto' so compute its value
684 mComputedOffsets.bottom = nsLayoutUtils::
685 ComputeHeightDependentValue(aContainingBlockHeight,
686 mStylePosition->mOffset.GetBottom());
688 // Computed value for 'top' is minus the value of 'bottom'
689 mComputedOffsets.top = -mComputedOffsets.bottom;
692 } else {
693 NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
695 // 'Top' isn't 'auto' so compute its value
696 mComputedOffsets.top = nsLayoutUtils::
697 ComputeHeightDependentValue(aContainingBlockHeight,
698 mStylePosition->mOffset.GetTop());
700 // Computed value for 'bottom' is minus the value of 'top'
701 mComputedOffsets.bottom = -mComputedOffsets.top;
704 // Store the offset
705 FrameProperties props(aPresContext->PropertyTable(), frame);
706 nsPoint* offsets = static_cast<nsPoint*>
707 (props.Get(nsIFrame::ComputedOffsetProperty()));
708 if (offsets) {
709 offsets->MoveTo(mComputedOffsets.left, mComputedOffsets.top);
710 } else {
711 props.Set(nsIFrame::ComputedOffsetProperty(),
712 new nsPoint(mComputedOffsets.left, mComputedOffsets.top));
716 static nsIFrame*
717 GetNearestContainingBlock(nsIFrame *aFrame)
719 nsIFrame *cb = aFrame;
720 do {
721 cb = cb->GetParent();
722 } while (!cb->IsContainingBlock());
723 return cb;
726 nsIFrame*
727 nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame,
728 nscoord& aCBLeftEdge,
729 nscoord& aCBWidth)
731 aFrame = GetNearestContainingBlock(aFrame);
732 NS_ASSERTION(aFrame != frame, "How did that happen?");
734 /* Now aFrame is the containing block we want */
736 /* Check whether the containing block is currently being reflowed.
737 If so, use the info from the reflow state. */
738 const nsHTMLReflowState* state;
739 if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
740 for (state = parentReflowState; state && state->frame != aFrame;
741 state = state->parentReflowState) {
742 /* do nothing */
744 } else {
745 state = nsnull;
748 if (state) {
749 aCBLeftEdge = state->mComputedBorderPadding.left;
750 aCBWidth = state->mComputedWidth;
751 } else {
752 /* Didn't find a reflow state for aFrame. Just compute the information we
753 want, on the assumption that aFrame already knows its size. This really
754 ought to be true by now. */
755 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
756 "aFrame shouldn't be in reflow; we'll lie if it is");
757 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
758 aCBLeftEdge = borderPadding.left;
759 aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight();
762 return aFrame;
765 // When determining the hypothetical box that would have been if the element
766 // had been in the flow we may not be able to exactly determine both the left
767 // and right edges. For example, if the element is a non-replaced inline-level
768 // element we would have to reflow it in order to determine it desired width.
769 // In that case depending on the progression direction either the left or
770 // right edge would be marked as not being exact
771 struct nsHypotheticalBox {
772 // offsets from left edge of containing block (which is a padding edge)
773 nscoord mLeft, mRight;
774 // offset from top edge of containing block (which is a padding edge)
775 nscoord mTop;
776 #ifdef DEBUG
777 PRPackedBool mLeftIsExact, mRightIsExact;
778 #endif
780 nsHypotheticalBox() {
781 #ifdef DEBUG
782 mLeftIsExact = mRightIsExact = PR_FALSE;
783 #endif
787 static PRBool
788 GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize)
790 // See if it is an image frame
791 PRBool result = PR_FALSE;
793 // Currently the only type of replaced frame that we can get the intrinsic
794 // size for is an image frame
795 // XXX We should add back the GetReflowMetrics() function and one of the
796 // things should be the intrinsic size...
797 if (aFrame->GetType() == nsGkAtoms::imageFrame) {
798 nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
800 imageFrame->GetIntrinsicImageSize(aIntrinsicSize);
801 result = (aIntrinsicSize != nsSize(0, 0));
803 return result;
807 * aInsideBoxSizing returns the part of the horizontal padding, border,
808 * and margin that goes inside the edge given by -moz-box-sizing;
809 * aOutsideBoxSizing returns the rest.
811 void
812 nsHTMLReflowState::CalculateHorizBorderPaddingMargin(
813 nscoord aContainingBlockWidth,
814 nscoord* aInsideBoxSizing,
815 nscoord* aOutsideBoxSizing)
817 const nsMargin& border = mStyleBorder->GetActualBorder();
818 nsMargin padding, margin;
820 // See if the style system can provide us the padding directly
821 if (!mStylePadding->GetPadding(padding)) {
822 // We have to compute the left and right values
823 padding.left = nsLayoutUtils::
824 ComputeWidthDependentValue(aContainingBlockWidth,
825 mStylePadding->mPadding.GetLeft());
826 padding.right = nsLayoutUtils::
827 ComputeWidthDependentValue(aContainingBlockWidth,
828 mStylePadding->mPadding.GetRight());
831 // See if the style system can provide us the margin directly
832 if (!mStyleMargin->GetMargin(margin)) {
833 // We have to compute the left and right values
834 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
835 // XXX FIXME (or does CalculateBlockSideMargins do this?)
836 margin.left = 0; // just ignore
837 } else {
838 margin.left = nsLayoutUtils::
839 ComputeWidthDependentValue(aContainingBlockWidth,
840 mStyleMargin->mMargin.GetLeft());
842 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
843 // XXX FIXME (or does CalculateBlockSideMargins do this?)
844 margin.right = 0; // just ignore
845 } else {
846 margin.right = nsLayoutUtils::
847 ComputeWidthDependentValue(aContainingBlockWidth,
848 mStyleMargin->mMargin.GetRight());
852 nscoord outside =
853 padding.LeftRight() + border.LeftRight() + margin.LeftRight();
854 nscoord inside = 0;
855 switch (mStylePosition->mBoxSizing) {
856 case NS_STYLE_BOX_SIZING_BORDER:
857 inside += border.LeftRight();
858 // fall through
859 case NS_STYLE_BOX_SIZING_PADDING:
860 inside += padding.LeftRight();
862 outside -= inside;
863 *aInsideBoxSizing = inside;
864 *aOutsideBoxSizing = outside;
865 return;
869 * Returns PR_TRUE iff a pre-order traversal of the normal child
870 * frames rooted at aFrame finds no non-empty frame before aDescendant.
872 static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
873 nsIFrame* aDescendant, PRBool* aFound) {
874 if (aFrame == aDescendant) {
875 *aFound = PR_TRUE;
876 return PR_TRUE;
878 if (!aFrame->IsSelfEmpty()) {
879 *aFound = PR_FALSE;
880 return PR_FALSE;
882 for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
883 PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
884 if (*aFound || !allEmpty) {
885 return allEmpty;
888 *aFound = PR_FALSE;
889 return PR_TRUE;
892 // Calculate the hypothetical box that the element would have if it were in
893 // the flow. The values returned are relative to the padding edge of the
894 // absolute containing block
895 // aContainingBlock is the placeholder's containing block (XXX rename it?)
896 // cbrs->frame is the actual containing block
897 void
898 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext,
899 nsIFrame* aPlaceholderFrame,
900 nsIFrame* aContainingBlock,
901 nscoord aBlockLeftContentEdge,
902 nscoord aBlockContentWidth,
903 const nsHTMLReflowState* cbrs,
904 nsHypotheticalBox& aHypotheticalBox)
906 NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
907 "mOriginalDisplay has not been properly initialized");
909 // If it's a replaced element and it has a 'auto' value for 'width', see if we
910 // can get the intrinsic size. This will allow us to exactly determine both the
911 // left and right edges
912 PRBool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto;
913 nsSize intrinsicSize;
914 PRBool knowIntrinsicSize = PR_FALSE;
915 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
916 // See if we can get the intrinsic size of the element
917 knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize);
920 // See if we can calculate what the box width would have been if the
921 // element had been in the flow
922 nscoord boxWidth;
923 PRBool knowBoxWidth = PR_FALSE;
924 if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) &&
925 !NS_FRAME_IS_REPLACED(mFrameType)) {
926 // For non-replaced inline-level elements the 'width' property doesn't apply,
927 // so we don't know what the width would have been without reflowing it
929 } else {
930 // It's either a replaced inline-level element or a block-level element
932 // Determine the total amount of horizontal border/padding/margin that
933 // the element would have had if it had been in the flow. Note that we
934 // ignore any 'auto' and 'inherit' values
935 nscoord insideBoxSizing, outsideBoxSizing;
936 CalculateHorizBorderPaddingMargin(aBlockContentWidth,
937 &insideBoxSizing, &outsideBoxSizing);
939 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
940 // It's a replaced element with an 'auto' width so the box width is
941 // its intrinsic size plus any border/padding/margin
942 if (knowIntrinsicSize) {
943 boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing;
944 knowBoxWidth = PR_TRUE;
947 } else if (isAutoWidth) {
948 // The box width is the containing block width
949 boxWidth = aBlockContentWidth;
950 knowBoxWidth = PR_TRUE;
952 } else {
953 // We need to compute it. It's important we do this, because if it's
954 // percentage based this computed value may be different from the computed
955 // value calculated using the absolute containing block width
956 boxWidth = ComputeWidthValue(aBlockContentWidth,
957 insideBoxSizing, outsideBoxSizing,
958 mStylePosition->mWidth) +
959 insideBoxSizing + outsideBoxSizing;
960 knowBoxWidth = PR_TRUE;
964 // Get the 'direction' of the block
965 const nsStyleVisibility* blockVis = aContainingBlock->GetStyleVisibility();
967 // Get the placeholder x-offset and y-offset in the coordinate
968 // space of its containing block
969 // XXXbz the placeholder is not fully reflowed yet if our containing block is
970 // relatively positioned...
971 nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock);
973 // First, determine the hypothetical box's mTop. We want to check the
974 // content insertion frame of aContainingBlock for block-ness, but make
975 // sure to compute all coordinates in the coordinate system of
976 // aContainingBlock.
977 nsBlockFrame* blockFrame =
978 nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame());
979 if (blockFrame) {
980 nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y;
981 PRBool isValid;
982 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
983 NS_ASSERTION(isValid, "Can't find placeholder!");
984 NS_ASSERTION(iter.GetContainer() == blockFrame, "Found placeholder in wrong block!");
985 nsBlockFrame::line_iterator lineBox = iter.GetLine();
987 // How we determine the hypothetical box depends on whether the element
988 // would have been inline-level or block-level
989 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
990 // Use the top of the inline box which the placeholder lives in as the
991 // hypothetical box's top.
992 aHypotheticalBox.mTop = lineBox->mBounds.y + blockYOffset;
993 } else {
994 // The element would have been block-level which means it would be below
995 // the line containing the placeholder frame, unless all the frames
996 // before it are empty. In that case, it would have been just before
997 // this line.
998 // XXXbz the line box is not fully reflowed yet if our containing block is
999 // relatively positioned...
1000 if (lineBox != iter.End()) {
1001 nsIFrame * firstFrame = lineBox->mFirstChild;
1002 PRBool found = PR_FALSE;
1003 PRBool allEmpty = PR_TRUE;
1004 while (firstFrame) { // See bug 223064
1005 allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
1006 aPlaceholderFrame, &found);
1007 if (found || !allEmpty)
1008 break;
1009 firstFrame = firstFrame->GetNextSibling();
1011 NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
1013 if (allEmpty) {
1014 // The top of the hypothetical box is the top of the line containing
1015 // the placeholder, since there is nothing in the line before our
1016 // placeholder except empty frames.
1017 aHypotheticalBox.mTop = lineBox->mBounds.y + blockYOffset;
1018 } else {
1019 // The top of the hypothetical box is just below the line containing
1020 // the placeholder.
1021 aHypotheticalBox.mTop = lineBox->mBounds.YMost() + blockYOffset;
1023 } else {
1024 // Just use the placeholder's y-offset wrt the containing block
1025 aHypotheticalBox.mTop = placeholderOffset.y;
1028 } else {
1029 // The containing block is not a block, so it's probably something
1030 // like a XUL box, etc.
1031 // Just use the placeholder's y-offset
1032 aHypotheticalBox.mTop = placeholderOffset.y;
1035 // Second, determine the hypothetical box's mLeft & mRight
1036 // To determine the left and right offsets we need to look at the block's 'direction'
1037 if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
1038 // How we determine the hypothetical box depends on whether the element
1039 // would have been inline-level or block-level
1040 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
1041 // The placeholder represents the left edge of the hypothetical box
1042 aHypotheticalBox.mLeft = placeholderOffset.x;
1043 } else {
1044 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1046 #ifdef DEBUG
1047 aHypotheticalBox.mLeftIsExact = PR_TRUE;
1048 #endif
1050 if (knowBoxWidth) {
1051 aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
1052 #ifdef DEBUG
1053 aHypotheticalBox.mRightIsExact = PR_TRUE;
1054 #endif
1055 } else {
1056 // We can't compute the right edge because we don't know the desired
1057 // width. So instead use the right content edge of the block parent,
1058 // but remember it's not exact
1059 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1060 #ifdef DEBUG
1061 aHypotheticalBox.mRightIsExact = PR_FALSE;
1062 #endif
1065 } else {
1066 // The placeholder represents the right edge of the hypothetical box
1067 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
1068 aHypotheticalBox.mRight = placeholderOffset.x;
1069 } else {
1070 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1072 #ifdef DEBUG
1073 aHypotheticalBox.mRightIsExact = PR_TRUE;
1074 #endif
1076 if (knowBoxWidth) {
1077 aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
1078 #ifdef DEBUG
1079 aHypotheticalBox.mLeftIsExact = PR_TRUE;
1080 #endif
1081 } else {
1082 // We can't compute the left edge because we don't know the desired
1083 // width. So instead use the left content edge of the block parent,
1084 // but remember it's not exact
1085 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1086 #ifdef DEBUG
1087 aHypotheticalBox.mLeftIsExact = PR_FALSE;
1088 #endif
1093 // The current coordinate space is that of the nearest block to the placeholder.
1094 // Convert to the coordinate space of the absolute containing block
1095 // One weird thing here is that for fixed-positioned elements we want to do
1096 // the conversion incorrectly; specifically we want to ignore any scrolling
1097 // that may have happened;
1098 nsPoint cbOffset;
1099 if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1100 // Exclude cases inside -moz-transform where fixed is like absolute.
1101 nsLayoutUtils::IsReallyFixedPos(frame)) {
1102 // In this case, cbrs->frame will always be an ancestor of
1103 // aContainingBlock, so can just walk our way up the frame tree.
1104 // Make sure to not add positions of frames whose parent is a
1105 // scrollFrame, since we're doing fixed positioning, which assumes
1106 // everything is scrolled to (0,0).
1107 cbOffset.MoveTo(0, 0);
1108 do {
1109 NS_ASSERTION(aContainingBlock,
1110 "Should hit cbrs->frame before we run off the frame tree!");
1111 cbOffset += aContainingBlock->GetPositionIgnoringScrolling();
1112 aContainingBlock = aContainingBlock->GetParent();
1113 } while (aContainingBlock != cbrs->frame);
1114 } else {
1115 // XXXldb We need to either ignore scrolling for the absolute
1116 // positioning case too (and take the incompatibility) or figure out
1117 // how to make these positioned elements actually *move* when we
1118 // scroll, and thus avoid the resulting incremental reflow bugs.
1119 cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame);
1121 aHypotheticalBox.mLeft += cbOffset.x;
1122 aHypotheticalBox.mTop += cbOffset.y;
1123 aHypotheticalBox.mRight += cbOffset.x;
1125 // The specified offsets are relative to the absolute containing block's
1126 // padding edge and our current values are relative to the border edge, so
1127 // translate.
1128 nsMargin border = cbrs->mComputedBorderPadding - cbrs->mComputedPadding;
1129 aHypotheticalBox.mLeft -= border.left;
1130 aHypotheticalBox.mRight -= border.left;
1131 aHypotheticalBox.mTop -= border.top;
1134 void
1135 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
1136 const nsHTMLReflowState* cbrs,
1137 nscoord containingBlockWidth,
1138 nscoord containingBlockHeight)
1140 NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
1141 "containing block height must be constrained");
1143 nsIFrame* outOfFlow =
1144 frame->GetType() == nsGkAtoms::tableFrame ? frame->GetParent() : frame;
1145 NS_ASSERTION(outOfFlow->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
1146 "Why are we here?");
1148 // Get the placeholder frame
1149 nsIFrame* placeholderFrame;
1151 placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(outOfFlow);
1152 NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");
1154 // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
1155 // 'auto', then compute the hypothetical box of where the element would
1156 // have been if it had been in the flow
1157 nsHypotheticalBox hypotheticalBox;
1158 if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
1159 (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
1160 ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
1161 (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {
1162 // Find the nearest containing block frame to the placeholder frame,
1163 // and return its left edge and width.
1164 nscoord cbLeftEdge, cbWidth;
1165 nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame,
1166 cbLeftEdge,
1167 cbWidth);
1169 CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame,
1170 cbLeftEdge, cbWidth, cbrs, hypotheticalBox);
1173 // Initialize the 'left' and 'right' computed offsets
1174 // XXX Handle new 'static-position' value...
1175 PRBool leftIsAuto = PR_FALSE, rightIsAuto = PR_FALSE;
1176 if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
1177 mComputedOffsets.left = 0;
1178 leftIsAuto = PR_TRUE;
1179 } else {
1180 mComputedOffsets.left = nsLayoutUtils::
1181 ComputeWidthDependentValue(containingBlockWidth,
1182 mStylePosition->mOffset.GetLeft());
1184 if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
1185 mComputedOffsets.right = 0;
1186 rightIsAuto = PR_TRUE;
1187 } else {
1188 mComputedOffsets.right = nsLayoutUtils::
1189 ComputeWidthDependentValue(containingBlockWidth,
1190 mStylePosition->mOffset.GetRight());
1193 // Use the horizontal component of the hypothetical box in the cases
1194 // where it's needed.
1195 if (leftIsAuto && rightIsAuto) {
1196 // Use the direction of the original ("static-position") containing block
1197 // to dictate whether 'left' or 'right' is treated like 'static-position'.
1198 if (NS_STYLE_DIRECTION_LTR == GetNearestContainingBlock(placeholderFrame)
1199 ->GetStyleVisibility()->mDirection) {
1200 NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have "
1201 "exact value on containing block's start side");
1202 mComputedOffsets.left = hypotheticalBox.mLeft;
1203 leftIsAuto = PR_FALSE;
1204 } else {
1205 NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have "
1206 "exact value on containing block's start side");
1207 mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
1208 rightIsAuto = PR_FALSE;
1212 // Initialize the 'top' and 'bottom' computed offsets
1213 PRBool topIsAuto = PR_FALSE, bottomIsAuto = PR_FALSE;
1214 if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
1215 mComputedOffsets.top = 0;
1216 topIsAuto = PR_TRUE;
1217 } else {
1218 mComputedOffsets.top = nsLayoutUtils::
1219 ComputeHeightDependentValue(containingBlockHeight,
1220 mStylePosition->mOffset.GetTop());
1222 if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
1223 mComputedOffsets.bottom = 0;
1224 bottomIsAuto = PR_TRUE;
1225 } else {
1226 mComputedOffsets.bottom = nsLayoutUtils::
1227 ComputeHeightDependentValue(containingBlockHeight,
1228 mStylePosition->mOffset.GetBottom());
1231 if (topIsAuto && bottomIsAuto) {
1232 // Treat 'top' like 'static-position'
1233 mComputedOffsets.top = hypotheticalBox.mTop;
1234 topIsAuto = PR_FALSE;
1237 PRBool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit();
1238 PRBool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit();
1240 PRBool shrinkWrap = leftIsAuto || rightIsAuto;
1241 nsSize size =
1242 frame->ComputeSize(rendContext,
1243 nsSize(containingBlockWidth,
1244 containingBlockHeight),
1245 containingBlockWidth, // XXX or availableWidth?
1246 nsSize(mComputedMargin.LeftRight() +
1247 mComputedOffsets.LeftRight(),
1248 mComputedMargin.TopBottom() +
1249 mComputedOffsets.TopBottom()),
1250 nsSize(mComputedBorderPadding.LeftRight() -
1251 mComputedPadding.LeftRight(),
1252 mComputedBorderPadding.TopBottom() -
1253 mComputedPadding.TopBottom()),
1254 nsSize(mComputedPadding.LeftRight(),
1255 mComputedPadding.TopBottom()),
1256 shrinkWrap);
1257 mComputedWidth = size.width;
1258 mComputedHeight = size.height;
1259 NS_ASSERTION(mComputedWidth >= 0, "Bogus width");
1260 NS_ASSERTION(mComputedHeight == NS_UNCONSTRAINEDSIZE ||
1261 mComputedHeight >= 0, "Bogus height");
1263 // XXX Now that we have ComputeSize, can we condense many of the
1264 // branches off of widthIsAuto?
1266 if (leftIsAuto) {
1267 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1268 // box code above.
1269 // Solve for 'left'.
1270 if (widthIsAuto) {
1271 // XXXldb This, and the corresponding code in
1272 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1273 // we always compute widths.
1274 mComputedOffsets.left = NS_AUTOOFFSET;
1275 } else {
1276 mComputedOffsets.left = containingBlockWidth - mComputedMargin.left -
1277 mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right -
1278 mComputedMargin.right - mComputedOffsets.right;
1281 } else if (rightIsAuto) {
1282 // We know 'left' is not 'auto' anymore thanks to the hypothetical
1283 // box code above.
1284 // Solve for 'right'.
1285 if (widthIsAuto) {
1286 // XXXldb This, and the corresponding code in
1287 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1288 // we always compute widths.
1289 mComputedOffsets.right = NS_AUTOOFFSET;
1290 } else {
1291 mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
1292 mComputedMargin.left - mComputedBorderPadding.left - mComputedWidth -
1293 mComputedBorderPadding.right - mComputedMargin.right;
1295 } else {
1296 // Neither 'left' nor 'right' is 'auto'. However, the width might
1297 // still not fill all the available space (even though we didn't
1298 // shrink-wrap) in case:
1299 // * width was specified
1300 // * we're dealing with a replaced element
1301 // * width was constrained by min-width or max-width.
1303 nscoord availMarginSpace = containingBlockWidth -
1304 mComputedOffsets.LeftRight() -
1305 mComputedMargin.LeftRight() -
1306 mComputedBorderPadding.LeftRight() -
1307 mComputedWidth;
1308 PRBool marginLeftIsAuto =
1309 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
1310 PRBool marginRightIsAuto =
1311 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
1313 if (availMarginSpace < 0 ||
1314 (!marginLeftIsAuto && !marginRightIsAuto)) {
1315 // We're over-constrained so use the direction of the containing block
1316 // to dictate which value to ignore. (And note that the spec says to ignore
1317 // 'left' or 'right' rather than 'margin-left' or 'margin-right'.)
1318 if (cbrs &&
1319 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1320 // Ignore the specified value for 'left'.
1321 mComputedOffsets.left += availMarginSpace;
1322 } else {
1323 // Ignore the specified value for 'right'.
1324 mComputedOffsets.right += availMarginSpace;
1326 } else if (marginLeftIsAuto) {
1327 if (marginRightIsAuto) {
1328 // Both 'margin-left' and 'margin-right' are 'auto', so they get
1329 // equal values
1330 mComputedMargin.left = availMarginSpace / 2;
1331 mComputedMargin.right = availMarginSpace - mComputedMargin.left;
1332 } else {
1333 // Just 'margin-left' is 'auto'
1334 mComputedMargin.left = availMarginSpace;
1336 } else {
1337 // Just 'margin-right' is 'auto'
1338 mComputedMargin.right = availMarginSpace;
1342 if (topIsAuto) {
1343 // solve for 'top'
1344 if (heightIsAuto) {
1345 mComputedOffsets.top = NS_AUTOOFFSET;
1346 } else {
1347 mComputedOffsets.top = containingBlockHeight - mComputedMargin.top -
1348 mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom -
1349 mComputedMargin.bottom - mComputedOffsets.bottom;
1351 } else if (bottomIsAuto) {
1352 // solve for 'bottom'
1353 if (heightIsAuto) {
1354 mComputedOffsets.bottom = NS_AUTOOFFSET;
1355 } else {
1356 mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
1357 mComputedMargin.top - mComputedBorderPadding.top - mComputedHeight -
1358 mComputedBorderPadding.bottom - mComputedMargin.bottom;
1360 } else {
1361 // Neither 'top' nor 'bottom' is 'auto'.
1362 nscoord autoHeight = containingBlockHeight -
1363 mComputedOffsets.TopBottom() -
1364 mComputedMargin.TopBottom() -
1365 mComputedBorderPadding.TopBottom();
1366 if (autoHeight < 0) {
1367 autoHeight = 0;
1370 if (mComputedHeight == NS_UNCONSTRAINEDSIZE) {
1371 // For non-replaced elements with 'height' auto, the 'height'
1372 // fills the remaining space.
1373 mComputedHeight = autoHeight;
1375 // XXX Do these need box-sizing adjustments?
1376 if (mComputedHeight > mComputedMaxHeight)
1377 mComputedHeight = mComputedMaxHeight;
1378 if (mComputedHeight < mComputedMinHeight)
1379 mComputedHeight = mComputedMinHeight;
1382 // The height might still not fill all the available space in case:
1383 // * height was specified
1384 // * we're dealing with a replaced element
1385 // * height was constrained by min-height or max-height.
1386 nscoord availMarginSpace = autoHeight - mComputedHeight;
1387 PRBool marginTopIsAuto =
1388 eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit();
1389 PRBool marginBottomIsAuto =
1390 eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit();
1392 if (availMarginSpace < 0 || (!marginTopIsAuto && !marginBottomIsAuto)) {
1393 // We're over-constrained so ignore the specified value for
1394 // 'bottom'. (And note that the spec says to ignore 'bottom'
1395 // rather than 'margin-bottom'.)
1396 mComputedOffsets.bottom += availMarginSpace;
1397 } else if (marginTopIsAuto) {
1398 if (marginBottomIsAuto) {
1399 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1400 // equal values
1401 mComputedMargin.top = availMarginSpace / 2;
1402 mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
1403 } else {
1404 // Just 'margin-top' is 'auto'
1405 mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
1407 } else {
1408 // Just 'margin-bottom' is 'auto'
1409 mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
1414 nscoord
1415 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
1417 nscoord result = 0;
1418 if (!aReflowState) return result;
1420 // zero auto margins
1421 nsMargin margin = aReflowState->mComputedMargin;
1422 if (NS_AUTOMARGIN == margin.top)
1423 margin.top = 0;
1424 if (NS_AUTOMARGIN == margin.bottom)
1425 margin.bottom = 0;
1427 result += margin.top + margin.bottom;
1428 result += aReflowState->mComputedBorderPadding.top +
1429 aReflowState->mComputedBorderPadding.bottom;
1431 return result;
1434 /* Get the height based on the viewport of the containing block specified
1435 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
1436 * This will walk up the chain of containing blocks looking for a computed height
1437 * until it finds the canvas frame, or it encounters a frame that is not a block,
1438 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
1440 * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
1442 * See also IsQuirkContainingBlockHeight.
1444 static nscoord
1445 CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState)
1447 nsHTMLReflowState* firstAncestorRS = nsnull; // a candidate for html frame
1448 nsHTMLReflowState* secondAncestorRS = nsnull; // a candidate for body frame
1450 // initialize the default to NS_AUTOHEIGHT as this is the containings block
1451 // computed height when this function is called. It is possible that we
1452 // don't alter this height especially if we are restricted to one level
1453 nscoord result = NS_AUTOHEIGHT;
1455 const nsHTMLReflowState* rs = aCBReflowState;
1456 for (; rs; rs = (nsHTMLReflowState *)(rs->parentReflowState)) {
1457 nsIAtom* frameType = rs->frame->GetType();
1458 // if the ancestor is auto height then skip it and continue up if it
1459 // is the first block frame and possibly the body/html
1460 if (nsGkAtoms::blockFrame == frameType ||
1461 #ifdef MOZ_XUL
1462 nsGkAtoms::XULLabelFrame == frameType ||
1463 #endif
1464 nsGkAtoms::scrollFrame == frameType) {
1466 secondAncestorRS = firstAncestorRS;
1467 firstAncestorRS = (nsHTMLReflowState*)rs;
1469 // If the current frame we're looking at is positioned, we don't want to
1470 // go any further (see bug 221784). The behavior we want here is: 1) If
1471 // not auto-height, use this as the percentage base. 2) If auto-height,
1472 // keep looking, unless the frame is positioned.
1473 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
1474 if (rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
1475 break;
1476 } else {
1477 continue;
1481 else if (nsGkAtoms::canvasFrame == frameType) {
1482 // Always continue on to the height calculation
1484 else if (nsGkAtoms::pageContentFrame == frameType) {
1485 nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
1486 // only use the page content frame for a height basis if it is the first in flow
1487 if (prevInFlow)
1488 break;
1490 else {
1491 break;
1494 // if the ancestor is the page content frame then the percent base is
1495 // the avail height, otherwise it is the computed height
1496 result = (nsGkAtoms::pageContentFrame == frameType)
1497 ? rs->availableHeight : rs->ComputedHeight();
1498 // if unconstrained - don't sutract borders - would result in huge height
1499 if (NS_AUTOHEIGHT == result) return result;
1501 // if we got to the canvas or page content frame, then subtract out
1502 // margin/border/padding for the BODY and HTML elements
1503 if ((nsGkAtoms::canvasFrame == frameType) ||
1504 (nsGkAtoms::pageContentFrame == frameType)) {
1506 result -= GetVerticalMarginBorderPadding(firstAncestorRS);
1507 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1509 #ifdef DEBUG
1510 // make sure the first ancestor is the HTML and the second is the BODY
1511 if (firstAncestorRS) {
1512 nsIContent* frameContent = firstAncestorRS->frame->GetContent();
1513 if (frameContent) {
1514 nsIAtom *contentTag = frameContent->Tag();
1515 NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML");
1518 if (secondAncestorRS) {
1519 nsIContent* frameContent = secondAncestorRS->frame->GetContent();
1520 if (frameContent) {
1521 nsIAtom *contentTag = frameContent->Tag();
1522 NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY");
1525 #endif
1528 // if we got to the html frame (a block child of the canvas) ...
1529 else if (nsGkAtoms::blockFrame == frameType &&
1530 nsGkAtoms::canvasFrame ==
1531 rs->parentReflowState->frame->GetType()) {
1532 // ... then subtract out margin/border/padding for the BODY element
1533 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1535 break;
1538 // Make sure not to return a negative height here!
1539 return NS_MAX(result, 0);
1541 // Called by InitConstraints() to compute the containing block rectangle for
1542 // the element. Handles the special logic for absolutely positioned elements
1543 void
1544 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext,
1545 const nsHTMLReflowState* aContainingBlockRS,
1546 nscoord& aContainingBlockWidth,
1547 nscoord& aContainingBlockHeight)
1549 // Unless the element is absolutely positioned, the containing block is
1550 // formed by the content edge of the nearest block-level ancestor
1551 aContainingBlockWidth = aContainingBlockRS->mComputedWidth;
1552 aContainingBlockHeight = aContainingBlockRS->mComputedHeight;
1554 if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
1555 // See if the ancestor is block-level or inline-level
1556 if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
1557 // Base our size on the actual size of the frame. In cases when this is
1558 // completely bogus (eg initial reflow), this code shouldn't even be
1559 // called, since the code in nsPositionedInlineFrame::Reflow will pass in
1560 // the containing block dimensions to our constructor.
1561 // XXXbz we should be taking the in-flows into account too, but
1562 // that's very hard.
1563 nsMargin computedBorder = aContainingBlockRS->mComputedBorderPadding -
1564 aContainingBlockRS->mComputedPadding;
1565 aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width -
1566 computedBorder.LeftRight();;
1567 NS_ASSERTION(aContainingBlockWidth >= 0,
1568 "Negative containing block width!");
1569 aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height -
1570 computedBorder.TopBottom();
1571 NS_ASSERTION(aContainingBlockHeight >= 0,
1572 "Negative containing block height!");
1573 } else {
1574 // If the ancestor is block-level, the containing block is formed by the
1575 // padding edge of the ancestor
1576 aContainingBlockWidth += aContainingBlockRS->mComputedPadding.LeftRight();
1577 aContainingBlockHeight += aContainingBlockRS->mComputedPadding.TopBottom();
1579 } else {
1580 // an element in quirks mode gets a containing block based on looking for a
1581 // parent with a non-auto height if the element has a percent height
1582 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1583 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
1584 mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
1585 aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS);
1591 // Prefs callback to pick up changes
1592 static int
1593 PrefsChanged(const char *aPrefName, void *instance)
1595 sBlinkIsAllowed =
1596 nsContentUtils::GetBoolPref("browser.blink_allowed", sBlinkIsAllowed);
1598 return 0; /* PREF_OK */
1601 // Check to see if |text-decoration: blink| is allowed. The first time
1602 // called, register the callback and then force-load the pref. After that,
1603 // just use the cached value.
1604 static PRBool BlinkIsAllowed(void)
1606 if (!sPrefIsLoaded) {
1607 // Set up a listener and check the initial value
1608 nsContentUtils::RegisterPrefCallback("browser.blink_allowed", PrefsChanged,
1609 nsnull);
1610 PrefsChanged(nsnull, nsnull);
1611 sPrefIsLoaded = PR_TRUE;
1613 return sBlinkIsAllowed;
1616 static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
1618 if (sNormalLineHeightControl == eUninitialized) {
1619 // browser.display.normal_lineheight_calc_control is not user
1620 // changeable, so no need to register callback for it.
1621 sNormalLineHeightControl =
1622 static_cast<eNormalLineHeightControl>
1623 (nsContentUtils::GetIntPref("browser.display.normal_lineheight_calc_control", eNoExternalLeading));
1625 return sNormalLineHeightControl;
1628 static inline PRBool
1629 IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay)
1631 if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION)
1632 return PR_FALSE;
1633 PRUint8 captionSide = aFrame->GetStyleTableBorder()->mCaptionSide;
1634 return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
1635 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
1638 // XXX refactor this code to have methods for each set of properties
1639 // we are computing: width,height,line-height; margin; offsets
1641 void
1642 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
1643 nscoord aContainingBlockWidth,
1644 nscoord aContainingBlockHeight,
1645 const nsMargin* aBorder,
1646 const nsMargin* aPadding)
1648 DISPLAY_INIT_CONSTRAINTS(frame, this,
1649 aContainingBlockWidth, aContainingBlockHeight,
1650 aBorder, aPadding);
1652 // Since we are in reflow, we don't need to store these properties anymore
1653 FrameProperties props(aPresContext->PropertyTable(), frame);
1654 props.Delete(nsIFrame::UsedBorderProperty());
1655 props.Delete(nsIFrame::UsedPaddingProperty());
1656 props.Delete(nsIFrame::UsedMarginProperty());
1658 // If this is the root frame, then set the computed width and
1659 // height equal to the available space
1660 if (nsnull == parentReflowState) {
1661 // XXXldb This doesn't mean what it used to!
1662 InitOffsets(aContainingBlockWidth, aBorder, aPadding);
1663 // Override mComputedMargin since reflow roots start from the
1664 // frame's boundary, which is inside the margin.
1665 mComputedMargin.SizeTo(0, 0, 0, 0);
1666 mComputedOffsets.SizeTo(0, 0, 0, 0);
1668 mComputedWidth = availableWidth - mComputedBorderPadding.LeftRight();
1669 if (mComputedWidth < 0)
1670 mComputedWidth = 0;
1671 if (availableHeight != NS_UNCONSTRAINEDSIZE) {
1672 mComputedHeight = availableHeight - mComputedBorderPadding.TopBottom();
1673 if (mComputedHeight < 0)
1674 mComputedHeight = 0;
1675 } else {
1676 mComputedHeight = NS_UNCONSTRAINEDSIZE;
1679 mComputedMinWidth = mComputedMinHeight = 0;
1680 mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
1681 } else {
1682 // Get the containing block reflow state
1683 const nsHTMLReflowState* cbrs = mCBReflowState;
1684 NS_ASSERTION(nsnull != cbrs, "no containing block");
1686 // If we weren't given a containing block width and height, then
1687 // compute one
1688 if (aContainingBlockWidth == -1) {
1689 ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth,
1690 aContainingBlockHeight);
1693 // See if the containing block height is based on the size of its
1694 // content
1695 nsIAtom* fType;
1696 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1697 // See if the containing block is a cell frame which needs
1698 // to use the mComputedHeight of the cell instead of what the cell block passed in.
1699 // XXX It seems like this could lead to bugs with min-height and friends
1700 if (cbrs->parentReflowState) {
1701 fType = cbrs->frame->GetType();
1702 if (IS_TABLE_CELL(fType)) {
1703 // use the cell's computed height
1704 aContainingBlockHeight = cbrs->mComputedHeight;
1709 InitOffsets(aContainingBlockWidth, aBorder, aPadding);
1711 nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
1713 // Check for a percentage based height and a containing block height
1714 // that depends on the content height
1715 // XXX twiddling heightUnit doesn't help anymore
1716 if (eStyleUnit_Percent == heightUnit) {
1717 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1718 // this if clause enables %-height on replaced inline frames,
1719 // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;"
1720 // used to be called exclusively.
1721 if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
1722 NS_FRAME_REPLACED_CONTAINS_BLOCK(
1723 NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
1724 // Get the containing block reflow state
1725 NS_ASSERTION(nsnull != cbrs, "no containing block");
1726 // in quirks mode, get the cb height using the special quirk method
1727 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
1728 if (!IS_TABLE_CELL(fType)) {
1729 aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs);
1730 if (aContainingBlockHeight == NS_AUTOHEIGHT) {
1731 heightUnit = eStyleUnit_Auto;
1734 else {
1735 heightUnit = eStyleUnit_Auto;
1738 // in standard mode, use the cb height. if it's "auto", as will be the case
1739 // by default in BODY, use auto height as per CSS2 spec.
1740 else
1742 if (NS_AUTOHEIGHT != cbrs->mComputedHeight)
1743 aContainingBlockHeight = cbrs->mComputedHeight;
1744 else
1745 heightUnit = eStyleUnit_Auto;
1748 else {
1749 // default to interpreting the height like 'auto'
1750 heightUnit = eStyleUnit_Auto;
1755 // Compute our offsets if the element is relatively positioned. We need
1756 // the correct containing block width and height here, which is why we need
1757 // to do it after all the quirks-n-such above.
1758 if (NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
1759 ComputeRelativeOffsets(cbrs, aContainingBlockWidth, aContainingBlockHeight, aPresContext);
1760 } else {
1761 // Initialize offsets to 0
1762 mComputedOffsets.SizeTo(0, 0, 0, 0);
1765 // Calculate the computed values for min and max properties. Note that
1766 // this MUST come after we've computed our border and padding.
1767 ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
1769 // Calculate the computed width and height. This varies by frame type
1771 if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
1772 // Internal table elements. The rules vary depending on the type.
1773 // Calculate the computed width
1774 PRBool rowOrRowGroup = PR_FALSE;
1775 nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
1776 if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
1777 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
1778 // 'width' property doesn't apply to table rows and row groups
1779 widthUnit = eStyleUnit_Auto;
1780 rowOrRowGroup = PR_TRUE;
1783 if (eStyleUnit_Auto == widthUnit) {
1784 mComputedWidth = availableWidth;
1786 if ((mComputedWidth != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
1787 // Internal table elements don't have margins. Only tables and
1788 // cells have border and padding
1789 mComputedWidth -= mComputedBorderPadding.left +
1790 mComputedBorderPadding.right;
1791 if (mComputedWidth < 0)
1792 mComputedWidth = 0;
1794 NS_ASSERTION(mComputedWidth >= 0, "Bogus computed width");
1796 } else {
1797 NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(),
1798 "unexpected width unit change");
1799 mComputedWidth = ComputeWidthValue(aContainingBlockWidth,
1800 mStylePosition->mBoxSizing,
1801 mStylePosition->mWidth);
1804 // Calculate the computed height
1805 if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
1806 (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
1807 // 'height' property doesn't apply to table columns and column groups
1808 heightUnit = eStyleUnit_Auto;
1810 if (eStyleUnit_Auto == heightUnit) {
1811 mComputedHeight = NS_AUTOHEIGHT;
1812 } else {
1813 NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(),
1814 "unexpected height unit change");
1815 mComputedHeight = nsLayoutUtils::
1816 ComputeHeightDependentValue(aContainingBlockHeight,
1817 mStylePosition->mHeight);
1820 // Doesn't apply to table elements
1821 mComputedMinWidth = mComputedMinHeight = 0;
1822 mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
1824 } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
1825 // XXX not sure if this belongs here or somewhere else - cwk
1826 InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
1827 aContainingBlockHeight);
1828 } else {
1829 PRBool isBlock =
1830 NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType);
1831 // make sure legend frames with display:block and width:auto still
1832 // shrink-wrap
1833 PRBool shrinkWrap = !isBlock || frame->GetType() == nsGkAtoms::legendFrame;
1834 nsSize size =
1835 frame->ComputeSize(rendContext,
1836 nsSize(aContainingBlockWidth,
1837 aContainingBlockHeight),
1838 availableWidth,
1839 nsSize(mComputedMargin.LeftRight(),
1840 mComputedMargin.TopBottom()),
1841 nsSize(mComputedBorderPadding.LeftRight() -
1842 mComputedPadding.LeftRight(),
1843 mComputedBorderPadding.TopBottom() -
1844 mComputedPadding.TopBottom()),
1845 nsSize(mComputedPadding.LeftRight(),
1846 mComputedPadding.TopBottom()),
1847 shrinkWrap);
1849 mComputedWidth = size.width;
1850 mComputedHeight = size.height;
1851 NS_ASSERTION(mComputedWidth >= 0, "Bogus width");
1852 NS_ASSERTION(mComputedHeight == NS_UNCONSTRAINEDSIZE ||
1853 mComputedHeight >= 0, "Bogus height");
1855 if (isBlock && !IsSideCaption(frame, mStyleDisplay))
1856 CalculateBlockSideMargins(availableWidth, mComputedWidth);
1859 // Check for blinking text and permission to display it
1860 mFlags.mBlinks = (parentReflowState && parentReflowState->mFlags.mBlinks);
1861 if (!mFlags.mBlinks && BlinkIsAllowed()) {
1862 const nsStyleTextReset* st = frame->GetStyleTextReset();
1863 mFlags.mBlinks =
1864 ((st->mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) != 0);
1868 void
1869 nsCSSOffsetState::InitOffsets(nscoord aContainingBlockWidth,
1870 const nsMargin *aBorder,
1871 const nsMargin *aPadding)
1873 DISPLAY_INIT_OFFSETS(frame, this, aContainingBlockWidth, aBorder, aPadding);
1875 // Compute margins from the specified margin style information. These
1876 // become the default computed values, and may be adjusted below
1877 // XXX fix to provide 0,0 for the top&bottom margins for
1878 // inline-non-replaced elements
1879 ComputeMargin(aContainingBlockWidth);
1881 const nsStyleDisplay *disp = frame->GetStyleDisplay();
1882 PRBool isThemed = frame->IsThemed(disp);
1883 nsPresContext *presContext = frame->PresContext();
1885 nsIntMargin widget;
1886 if (isThemed &&
1887 presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
1888 frame, disp->mAppearance,
1889 &widget)) {
1890 mComputedPadding.top = presContext->DevPixelsToAppUnits(widget.top);
1891 mComputedPadding.right = presContext->DevPixelsToAppUnits(widget.right);
1892 mComputedPadding.bottom = presContext->DevPixelsToAppUnits(widget.bottom);
1893 mComputedPadding.left = presContext->DevPixelsToAppUnits(widget.left);
1895 else if (aPadding) { // padding is an input arg
1896 mComputedPadding.top = aPadding->top;
1897 mComputedPadding.right = aPadding->right;
1898 mComputedPadding.bottom = aPadding->bottom;
1899 mComputedPadding.left = aPadding->left;
1901 else {
1902 ComputePadding(aContainingBlockWidth);
1905 if (isThemed) {
1906 nsIntMargin widget;
1907 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
1908 frame, disp->mAppearance,
1909 &widget);
1910 mComputedBorderPadding.top =
1911 presContext->DevPixelsToAppUnits(widget.top);
1912 mComputedBorderPadding.right =
1913 presContext->DevPixelsToAppUnits(widget.right);
1914 mComputedBorderPadding.bottom =
1915 presContext->DevPixelsToAppUnits(widget.bottom);
1916 mComputedBorderPadding.left =
1917 presContext->DevPixelsToAppUnits(widget.left);
1919 else if (aBorder) { // border is an input arg
1920 mComputedBorderPadding = *aBorder;
1922 else {
1923 mComputedBorderPadding = frame->GetStyleBorder()->GetActualBorder();
1925 mComputedBorderPadding += mComputedPadding;
1927 nsIAtom* frameType = frame->GetType();
1928 if (frameType == nsGkAtoms::tableFrame) {
1929 nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame);
1931 if (tableFrame->IsBorderCollapse()) {
1932 // border-collapsed tables don't use any of their padding, and
1933 // only part of their border. We need to do this here before we
1934 // try to do anything like handling 'auto' widths,
1935 // '-moz-box-sizing', or 'auto' margins.
1936 mComputedPadding.SizeTo(0,0,0,0);
1937 mComputedBorderPadding = tableFrame->GetIncludedOuterBCBorder();
1939 } else if (frameType == nsGkAtoms::scrollbarFrame) {
1940 // scrollbars may have had their width or height smashed to zero
1941 // by the associated scrollframe, in which case we must not report
1942 // any padding or border.
1943 nsSize size(frame->GetSize());
1944 if (size.width == 0 || size.height == 0) {
1945 mComputedPadding.left = 0;
1946 mComputedPadding.right = 0;
1947 mComputedBorderPadding.left = 0;
1948 mComputedBorderPadding.right = 0;
1949 mComputedPadding.top = 0;
1950 mComputedPadding.bottom = 0;
1951 mComputedBorderPadding.top = 0;
1952 mComputedBorderPadding.bottom = 0;
1957 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
1959 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
1960 // 'padding-right' + 'border-right-width' + 'margin-right'
1961 // = width of containing block
1963 // Note: the width unit is not auto when this is called
1964 void
1965 nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
1966 nscoord aComputedWidth)
1968 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aComputedWidth &&
1969 NS_UNCONSTRAINEDSIZE != aAvailWidth,
1970 "have unconstrained width; this should only result from "
1971 "very large sizes, not attempts at intrinsic width "
1972 "calculation");
1974 nscoord sum = mComputedMargin.left + mComputedBorderPadding.left +
1975 aComputedWidth + mComputedBorderPadding.right + mComputedMargin.right;
1976 if (sum == aAvailWidth)
1977 // The sum is already correct
1978 return;
1980 // Determine the left and right margin values. The width value
1981 // remains constant while we do this.
1983 // Calculate how much space is available for margins
1984 nscoord availMarginSpace = aAvailWidth - sum;
1986 // If the available margin space is negative, then don't follow the
1987 // usual overconstraint rules.
1988 if (availMarginSpace < 0) {
1989 if (mCBReflowState &&
1990 mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) {
1991 mComputedMargin.left += availMarginSpace;
1992 } else {
1993 mComputedMargin.right += availMarginSpace;
1995 return;
1998 // The css2 spec clearly defines how block elements should behave
1999 // in section 10.3.3.
2000 PRBool isAutoLeftMargin =
2001 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
2002 PRBool isAutoRightMargin =
2003 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
2004 if (!isAutoLeftMargin && !isAutoRightMargin) {
2005 // Neither margin is 'auto' so we're over constrained. Use the
2006 // 'direction' property of the parent to tell which margin to
2007 // ignore
2008 // First check if there is an HTML alignment that we should honor
2009 const nsHTMLReflowState* prs = parentReflowState;
2010 if (frame->GetType() == nsGkAtoms::tableFrame) {
2011 NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame,
2012 "table not inside outer table");
2013 // Center the table within the outer table based on the alignment
2014 // of the outer table's parent.
2015 prs = prs->parentReflowState;
2017 if (prs &&
2018 (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
2019 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
2020 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
2021 isAutoLeftMargin =
2022 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
2023 isAutoRightMargin =
2024 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2026 // Otherwise apply the CSS rules, and ignore one margin by forcing
2027 // it to 'auto', depending on 'direction'.
2028 else if (mCBReflowState &&
2029 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) {
2030 isAutoLeftMargin = PR_TRUE;
2032 else {
2033 isAutoRightMargin = PR_TRUE;
2037 // Logic which is common to blocks and tables
2038 // The computed margins need not be zero because the 'auto' could come from
2039 // overconstraint or from HTML alignment so values need to be accumulated
2041 if (isAutoLeftMargin) {
2042 if (isAutoRightMargin) {
2043 // Both margins are 'auto' so the computed addition should be equal
2044 nscoord forLeft = availMarginSpace / 2;
2045 mComputedMargin.left += forLeft;
2046 mComputedMargin.right += availMarginSpace - forLeft;
2047 } else {
2048 mComputedMargin.left += availMarginSpace;
2050 } else if (isAutoRightMargin) {
2051 mComputedMargin.right += availMarginSpace;
2055 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight
2056 // For "normal" we use the font's normal line height (em height + leading).
2057 // If both internal leading and external leading specified by font itself
2058 // are zeros, we should compensate this by creating extra (external) leading
2059 // in eCompensateLeading mode. This is necessary because without this
2060 // compensation, normal line height might looks too tight.
2062 // For risk management, we use preference to control the behavior, and
2063 // eNoExternalLeading is the old behavior.
2064 static nscoord
2065 GetNormalLineHeight(nsIFontMetrics* aFontMetrics)
2067 NS_PRECONDITION(nsnull != aFontMetrics, "no font metrics");
2069 nscoord normalLineHeight;
2071 nscoord externalLeading, internalLeading, emHeight;
2072 aFontMetrics->GetExternalLeading(externalLeading);
2073 aFontMetrics->GetInternalLeading(internalLeading);
2074 aFontMetrics->GetEmHeight(emHeight);
2075 switch (GetNormalLineHeightCalcControl()) {
2076 case eIncludeExternalLeading:
2077 normalLineHeight = emHeight+ internalLeading + externalLeading;
2078 break;
2079 case eCompensateLeading:
2080 if (!internalLeading && !externalLeading)
2081 normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
2082 else
2083 normalLineHeight = emHeight+ internalLeading + externalLeading;
2084 break;
2085 default:
2086 //case eNoExternalLeading:
2087 normalLineHeight = emHeight + internalLeading;
2089 return normalLineHeight;
2092 static nscoord
2093 ComputeLineHeight(nsStyleContext* aStyleContext,
2094 nscoord aBlockHeight)
2096 const nsStyleCoord& lhCoord = aStyleContext->GetStyleText()->mLineHeight;
2098 if (lhCoord.GetUnit() == eStyleUnit_Coord)
2099 return lhCoord.GetCoordValue();
2101 if (lhCoord.GetUnit() == eStyleUnit_Factor)
2102 // For factor units the computed value of the line-height property
2103 // is found by multiplying the factor by the font's computed size
2104 // (adjusted for min-size prefs and text zoom).
2105 return NSToCoordRound(lhCoord.GetFactorValue() *
2106 aStyleContext->GetStyleFont()->mFont.size);
2108 NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal ||
2109 lhCoord.GetUnit() == eStyleUnit_Enumerated,
2110 "bad line-height unit");
2112 if (lhCoord.GetUnit() == eStyleUnit_Enumerated) {
2113 NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
2114 "bad line-height value");
2115 if (aBlockHeight != NS_AUTOHEIGHT)
2116 return aBlockHeight;
2119 nsCOMPtr<nsIFontMetrics> fm;
2120 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
2121 getter_AddRefs(fm));
2122 return GetNormalLineHeight(fm);
2125 nscoord
2126 nsHTMLReflowState::CalcLineHeight() const
2128 nscoord blockHeight =
2129 frame->IsContainingBlock() ? mComputedHeight :
2130 (mCBReflowState ? mCBReflowState->mComputedHeight : NS_AUTOHEIGHT);
2132 return CalcLineHeight(frame->GetStyleContext(), blockHeight);
2135 /* static */ nscoord
2136 nsHTMLReflowState::CalcLineHeight(nsStyleContext* aStyleContext,
2137 nscoord aBlockHeight)
2139 NS_PRECONDITION(aStyleContext, "Must have a style context");
2141 nscoord lineHeight = ComputeLineHeight(aStyleContext, aBlockHeight);
2143 NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
2145 return lineHeight;
2148 void
2149 nsCSSOffsetState::ComputeMargin(nscoord aContainingBlockWidth)
2151 // If style style can provide us the margin directly, then use it.
2152 const nsStyleMargin *styleMargin = frame->GetStyleMargin();
2153 if (!styleMargin->GetMargin(mComputedMargin)) {
2154 // We have to compute the value
2155 if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
2156 mComputedMargin.left = 0;
2157 mComputedMargin.right = 0;
2159 if (eStyleUnit_Coord == styleMargin->mMargin.GetLeftUnit()) {
2160 mComputedMargin.left = styleMargin->mMargin.GetLeft().GetCoordValue();
2162 if (eStyleUnit_Coord == styleMargin->mMargin.GetRightUnit()) {
2163 mComputedMargin.right = styleMargin->mMargin.GetRight().GetCoordValue();
2166 } else {
2167 mComputedMargin.left = nsLayoutUtils::
2168 ComputeWidthDependentValue(aContainingBlockWidth,
2169 styleMargin->mMargin.GetLeft());
2170 mComputedMargin.right = nsLayoutUtils::
2171 ComputeWidthDependentValue(aContainingBlockWidth,
2172 styleMargin->mMargin.GetRight());
2175 // According to the CSS2 spec, margin percentages are
2176 // calculated with respect to the *width* of the containing
2177 // block, even for margin-top and margin-bottom.
2178 // XXX This isn't true for page boxes, if we implement them.
2179 mComputedMargin.top = nsLayoutUtils::
2180 ComputeWidthDependentValue(aContainingBlockWidth,
2181 styleMargin->mMargin.GetTop());
2182 mComputedMargin.bottom = nsLayoutUtils::
2183 ComputeWidthDependentValue(aContainingBlockWidth,
2184 styleMargin->mMargin.GetBottom());
2186 // XXX We need to include 'auto' horizontal margins in this too!
2187 // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
2188 // to use it even when the margins are all zero (since sometimes
2189 // they get treated as auto)
2190 frame->Properties().Set(nsIFrame::UsedMarginProperty(),
2191 new nsMargin(mComputedMargin));
2195 void
2196 nsCSSOffsetState::ComputePadding(nscoord aContainingBlockWidth)
2198 // If style can provide us the padding directly, then use it.
2199 const nsStylePadding *stylePadding = frame->GetStylePadding();
2200 if (!stylePadding->GetPadding(mComputedPadding)) {
2201 // We have to compute the value
2202 mComputedPadding.left = nsLayoutUtils::
2203 ComputeWidthDependentValue(aContainingBlockWidth,
2204 stylePadding->mPadding.GetLeft());
2205 mComputedPadding.right = nsLayoutUtils::
2206 ComputeWidthDependentValue(aContainingBlockWidth,
2207 stylePadding->mPadding.GetRight());
2209 // According to the CSS2 spec, percentages are calculated with respect to
2210 // containing block width for padding-top and padding-bottom
2211 mComputedPadding.top = nsLayoutUtils::
2212 ComputeWidthDependentValue(aContainingBlockWidth,
2213 stylePadding->mPadding.GetTop());
2214 mComputedPadding.bottom = nsLayoutUtils::
2215 ComputeWidthDependentValue(aContainingBlockWidth,
2216 stylePadding->mPadding.GetBottom());
2218 frame->Properties().Set(nsIFrame::UsedPaddingProperty(),
2219 new nsMargin(mComputedPadding));
2221 // a table row/col group, row/col doesn't have padding
2222 // XXXldb Neither do border-collapse tables.
2223 nsIAtom* frameType = frame->GetType();
2224 if (nsGkAtoms::tableRowGroupFrame == frameType ||
2225 nsGkAtoms::tableColGroupFrame == frameType ||
2226 nsGkAtoms::tableRowFrame == frameType ||
2227 nsGkAtoms::tableColFrame == frameType) {
2228 mComputedPadding.top = 0;
2229 mComputedPadding.right = 0;
2230 mComputedPadding.bottom = 0;
2231 mComputedPadding.left = 0;
2235 void
2236 nsHTMLReflowState::ApplyMinMaxConstraints(nscoord* aFrameWidth,
2237 nscoord* aFrameHeight) const
2239 if (aFrameWidth) {
2240 if (NS_UNCONSTRAINEDSIZE != mComputedMaxWidth) {
2241 *aFrameWidth = NS_MIN(*aFrameWidth, mComputedMaxWidth);
2243 *aFrameWidth = NS_MAX(*aFrameWidth, mComputedMinWidth);
2246 if (aFrameHeight) {
2247 if (NS_UNCONSTRAINEDSIZE != mComputedMaxHeight) {
2248 *aFrameHeight = NS_MIN(*aFrameHeight, mComputedMaxHeight);
2250 *aFrameHeight = NS_MAX(*aFrameHeight, mComputedMinHeight);
2254 void
2255 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
2256 nscoord aContainingBlockHeight,
2257 const nsHTMLReflowState* aContainingBlockRS)
2259 mComputedMinWidth = ComputeWidthValue(aContainingBlockWidth,
2260 mStylePosition->mBoxSizing,
2261 mStylePosition->mMinWidth);
2263 if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) {
2264 // Specified value of 'none'
2265 mComputedMaxWidth = NS_UNCONSTRAINEDSIZE; // no limit
2266 } else {
2267 mComputedMaxWidth = ComputeWidthValue(aContainingBlockWidth,
2268 mStylePosition->mBoxSizing,
2269 mStylePosition->mMaxWidth);
2272 // If the computed value of 'min-width' is greater than the value of
2273 // 'max-width', 'max-width' is set to the value of 'min-width'
2274 if (mComputedMinWidth > mComputedMaxWidth) {
2275 mComputedMaxWidth = mComputedMinWidth;
2278 // Check for percentage based values and a containing block height that
2279 // depends on the content height. Treat them like 'auto'
2280 if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
2281 (eStyleUnit_Percent == mStylePosition->mMinHeight.GetUnit())) {
2282 mComputedMinHeight = 0;
2283 } else {
2284 mComputedMinHeight = nsLayoutUtils::
2285 ComputeHeightDependentValue(aContainingBlockHeight,
2286 mStylePosition->mMinHeight);
2288 nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
2289 if (eStyleUnit_None == maxHeightUnit) {
2290 // Specified value of 'none'
2291 mComputedMaxHeight = NS_UNCONSTRAINEDSIZE; // no limit
2292 } else {
2293 // Check for percentage based values and a containing block height that
2294 // depends on the content height. Treat them like 'auto'
2295 if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
2296 (eStyleUnit_Percent == maxHeightUnit)) {
2297 mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
2298 } else {
2299 mComputedMaxHeight = nsLayoutUtils::
2300 ComputeHeightDependentValue(aContainingBlockHeight,
2301 mStylePosition->mMaxHeight);
2305 // If the computed value of 'min-height' is greater than the value of
2306 // 'max-height', 'max-height' is set to the value of 'min-height'
2307 if (mComputedMinHeight > mComputedMaxHeight) {
2308 mComputedMaxHeight = mComputedMinHeight;
2312 void
2313 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics,
2314 nsReflowStatus* aStatus) const
2316 if (availableHeight != NS_UNCONSTRAINEDSIZE &&
2317 availableHeight < aMetrics.height &&
2318 !mFlags.mIsTopOfPage) {
2319 *aStatus |= NS_FRAME_TRUNCATED;
2320 } else {
2321 *aStatus &= ~NS_FRAME_TRUNCATED;