Bug 451040 ? Passwords Manager Empty after convert to MozStorage. r=gavin
[wine-gecko.git] / layout / generic / nsHTMLReflowState.cpp
blobc440a0a977a402aab42aa2a9b5f5a87bd4948935
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 #include "nsStyleStructInlines.h"
61 #ifdef IBMBIDI
62 #include "nsBidiUtils.h"
63 #endif
65 #ifdef NS_DEBUG
66 #undef NOISY_VERTICAL_ALIGN
67 #else
68 #undef NOISY_VERTICAL_ALIGN
69 #endif
71 // Prefs-driven control for |text-decoration: blink|
72 static PRPackedBool sPrefIsLoaded = PR_FALSE;
73 static PRPackedBool sBlinkIsAllowed = PR_TRUE;
75 enum eNormalLineHeightControl {
76 eUninitialized = -1,
77 eNoExternalLeading = 0, // does not include external leading
78 eIncludeExternalLeading, // use whatever value font vendor provides
79 eCompensateLeading // compensate leading if leading provided by font vendor is not enough
82 static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
84 // Initialize a <b>root</b> reflow state with a rendering context to
85 // use for measuring things.
86 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
87 nsIFrame* aFrame,
88 nsIRenderingContext* aRenderingContext,
89 const nsSize& aAvailableSpace)
90 : nsCSSOffsetState(aFrame, aRenderingContext)
91 , mBlockDelta(0)
92 , mReflowDepth(0)
94 NS_PRECONDITION(aPresContext, "no pres context");
95 NS_PRECONDITION(aRenderingContext, "no rendering context");
96 NS_PRECONDITION(aFrame, "no frame");
97 parentReflowState = nsnull;
98 availableWidth = aAvailableSpace.width;
99 availableHeight = aAvailableSpace.height;
100 mSpaceManager = nsnull;
101 mLineLayout = nsnull;
102 mFlags.mSpecialHeightReflow = PR_FALSE;
103 mFlags.mIsTopOfPage = PR_FALSE;
104 mFlags.mTableIsSplittable = PR_FALSE;
105 mFlags.mNextInFlowUntouched = PR_FALSE;
106 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = PR_FALSE;
107 mFlags.mHasClearance = PR_FALSE;
108 mFlags.mHeightDependsOnAncestorCell = PR_FALSE;
109 mDiscoveredClearance = nsnull;
110 mPercentHeightObserver = nsnull;
111 mPercentHeightReflowInitiator = 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 mSpaceManager = aParentReflowState.mSpaceManager;
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;
174 mPercentHeightReflowInitiator = aParentReflowState.mPercentHeightReflowInitiator;
176 if (aInit) {
177 Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
181 inline nscoord
182 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
183 nscoord aContentEdgeToBoxSizing,
184 nscoord aBoxSizingToMarginEdge,
185 const nsStyleCoord& aCoord)
187 return nsLayoutUtils::ComputeWidthValue(rendContext, frame,
188 aContainingBlockWidth,
189 aContentEdgeToBoxSizing,
190 aBoxSizingToMarginEdge,
191 aCoord);
194 nscoord
195 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
196 PRUint8 aBoxSizing,
197 const nsStyleCoord& aCoord)
199 nscoord inside = 0, outside = mComputedBorderPadding.LeftRight() +
200 mComputedMargin.LeftRight();
201 switch (aBoxSizing) {
202 case NS_STYLE_BOX_SIZING_BORDER:
203 inside = mComputedBorderPadding.LeftRight();
204 break;
205 case NS_STYLE_BOX_SIZING_PADDING:
206 inside = mComputedPadding.LeftRight();
207 break;
209 outside -= inside;
211 return ComputeWidthValue(aContainingBlockWidth, inside,
212 outside, aCoord);
215 void
216 nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth)
218 NS_ASSERTION(frame, "Must have a frame!");
219 // It'd be nice to assert that |frame| is not in reflow, but this fails for
220 // two reasons:
222 // 1) Viewport frames reset the computed width on a copy of their reflow
223 // state when reflowing fixed-pos kids. In that case we actually don't
224 // want to mess with the resize flags, because comparing the frame's rect
225 // to the munged computed width is pointless.
226 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
227 // state is not used to reflow the parent, but just as a parent for the
228 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
229 // (like a text control, for example), we'll end up creating a reflow
230 // state for the parent while the parent is reflowing.
232 NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width");
233 if (mComputedWidth != aComputedWidth) {
234 mComputedWidth = aComputedWidth;
235 if (frame->GetType() != nsGkAtoms::viewportFrame) { // Or check GetParent()?
236 InitResizeFlags(frame->PresContext());
241 void
242 nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight)
244 NS_ASSERTION(frame, "Must have a frame!");
245 // It'd be nice to assert that |frame| is not in reflow, but this fails for
246 // two reasons:
248 // 1) Viewport frames reset the computed height on a copy of their reflow
249 // state when reflowing fixed-pos kids. In that case we actually don't
250 // want to mess with the resize flags, because comparing the frame's rect
251 // to the munged computed width is pointless.
252 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
253 // state is not used to reflow the parent, but just as a parent for the
254 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
255 // (like a text control, for example), we'll end up creating a reflow
256 // state for the parent while the parent is reflowing.
258 NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height");
259 if (mComputedHeight != aComputedHeight) {
260 mComputedHeight = aComputedHeight;
261 if (frame->GetType() != nsGkAtoms::viewportFrame) { // Or check GetParent()?
262 InitResizeFlags(frame->PresContext());
267 void
268 nsHTMLReflowState::Init(nsPresContext* aPresContext,
269 nscoord aContainingBlockWidth,
270 nscoord aContainingBlockHeight,
271 const nsMargin* aBorder,
272 const nsMargin* aPadding)
274 NS_ASSERTION(availableWidth != NS_UNCONSTRAINEDSIZE,
275 "shouldn't use unconstrained widths anymore");
277 mStylePosition = frame->GetStylePosition();
278 mStyleDisplay = frame->GetStyleDisplay();
279 mStyleVisibility = frame->GetStyleVisibility();
280 mStyleBorder = frame->GetStyleBorder();
281 mStyleMargin = frame->GetStyleMargin();
282 mStylePadding = frame->GetStylePadding();
283 mStyleText = frame->GetStyleText();
285 InitFrameType();
286 InitCBReflowState();
288 InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding);
290 InitResizeFlags(aPresContext);
292 // We have to start loading the border image now, because the
293 // border-image's width overrides only apply once the image is loaded.
294 // Starting the load of the image means we'll get a reflow when the
295 // image loads. (If we didn't do it now, and the image loaded between
296 // reflow and paint, we'd never get the notification, and our size
297 // would be wrong.)
298 imgIRequest *borderImage = mStyleBorder->GetBorderImage();
299 if (borderImage) {
300 aPresContext->LoadBorderImage(borderImage, frame);
303 NS_ASSERTION((mFrameType == NS_CSS_FRAME_TYPE_INLINE &&
304 !frame->IsFrameOfType(nsIFrame::eReplaced)) ||
305 frame->GetType() == nsGkAtoms::textFrame ||
306 mComputedWidth != NS_UNCONSTRAINEDSIZE,
307 "shouldn't use unconstrained widths anymore");
310 void nsHTMLReflowState::InitCBReflowState()
312 if (!parentReflowState) {
313 mCBReflowState = nsnull;
314 return;
317 if (parentReflowState->frame->IsContainingBlock() ||
318 // Absolutely positioned frames should always be kids of the frames that
319 // determine their containing block
320 (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE)) {
321 // a block inside a table cell needs to use the table cell
322 if (parentReflowState->parentReflowState &&
323 IS_TABLE_CELL(parentReflowState->parentReflowState->frame->GetType())) {
324 mCBReflowState = parentReflowState->parentReflowState;
325 } else {
326 mCBReflowState = parentReflowState;
329 return;
332 mCBReflowState = parentReflowState->mCBReflowState;
335 /* Check whether CalcQuirkContainingBlockHeight would stop on the
336 * given reflow state, using its block as a height. (essentially
337 * returns false for any case in which CalcQuirkContainingBlockHeight
338 * has a "continue" in its main loop.)
340 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
341 * this function as well
343 static PRBool
344 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs)
346 nsIAtom* frameType = rs->frame->GetType();
347 if (nsGkAtoms::blockFrame == frameType ||
348 nsGkAtoms::areaFrame == frameType ||
349 nsGkAtoms::scrollFrame == frameType) {
350 // Note: This next condition could change due to a style change,
351 // but that would cause a style reflow anyway, which means we're ok.
352 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
353 if (!rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
354 return PR_FALSE;
358 return PR_TRUE;
362 void
363 nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext)
365 mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
366 frame->GetSize().width !=
367 mComputedWidth + mComputedBorderPadding.LeftRight();
369 // XXX Should we really need to null check mCBReflowState? (We do for
370 // at least nsBoxFrame).
371 if (IS_TABLE_CELL(frame->GetType()) &&
372 (mFlags.mSpecialHeightReflow ||
373 (frame->GetFirstInFlow()->GetStateBits() &
374 NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
375 (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
376 // Need to set the bit on the cell so that
377 // mCBReflowState->mFlags.mVResize is set correctly below when
378 // reflowing descendant.
379 mFlags.mVResize = PR_TRUE;
380 } else if (mCBReflowState && !frame->IsContainingBlock()) {
381 // XXX Is this problematic for relatively positioned inlines acting
382 // as containing block for absolutely positioned elements?
383 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
384 } else if (mComputedHeight == NS_AUTOHEIGHT) {
385 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
386 mCBReflowState) {
387 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
388 } else {
389 mFlags.mVResize = mFlags.mHResize || NS_SUBTREE_DIRTY(frame);
391 } else {
392 // not 'auto' height
393 mFlags.mVResize = frame->GetSize().height !=
394 mComputedHeight + mComputedBorderPadding.TopBottom();
397 const 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 we're the descendant of a table cell that performs special height
408 // reflows and we could be the child that requires them, always set
409 // the vertical resize in case this is the first pass before the
410 // special height reflow.
411 if (!mFlags.mVResize && mCBReflowState &&
412 (IS_TABLE_CELL(mCBReflowState->frame->GetType()) ||
413 mCBReflowState->mFlags.mHeightDependsOnAncestorCell) &&
414 dependsOnCBHeight) {
415 mFlags.mVResize = PR_TRUE;
416 mFlags.mHeightDependsOnAncestorCell = PR_TRUE;
419 // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed.
421 // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT|
422 // &&ed with the percentage height check. However, this doesn't get
423 // along with table special height reflows, since a special height
424 // reflow (a quirk that makes such percentage heights work on children
425 // of table cells) can cause not just a single percentage height to
426 // become fixed, but an entire descendant chain of percentage heights
427 // to become fixed.
428 if (dependsOnCBHeight && mCBReflowState) {
429 const nsHTMLReflowState *rs = this;
430 PRBool hitCBReflowState = PR_FALSE;
431 do {
432 rs = rs->parentReflowState;
433 if (!rs) {
434 break;
437 if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
438 break; // no need to go further
439 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
441 // Keep track of whether we've hit the containing block, because
442 // we need to go at least that far.
443 if (rs == mCBReflowState) {
444 hitCBReflowState = PR_TRUE;
447 } while (!hitCBReflowState ||
448 (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
449 !IsQuirkContainingBlockHeight(rs)));
450 // Note: We actually don't need to set the
451 // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases
452 // where we hit the early break statements in
453 // CalcQuirkContainingBlockHeight. But it doesn't hurt
454 // us to set the bit in these cases.
457 if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) {
458 // If we're reflowing everything, then we'll find out if we need
459 // to re-set this.
460 frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
464 /* static */
465 nscoord
466 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
468 const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
469 if (!rs)
470 return 0;
471 return rs->mComputedWidth;
474 /* static */
475 nsIFrame*
476 nsHTMLReflowState::GetContainingBlockFor(const nsIFrame* aFrame)
478 NS_PRECONDITION(aFrame, "Must have frame to work with");
479 nsIFrame* container = aFrame->GetParent();
480 if (aFrame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
481 // Absolutely positioned frames are just kids of their containing
482 // blocks (which may happen to be inlines).
483 return container;
485 while (container && !container->IsContainingBlock()) {
486 container = container->GetParent();
488 return container;
491 void
492 nsHTMLReflowState::InitFrameType()
494 const nsStyleDisplay *disp = mStyleDisplay;
495 nsCSSFrameType frameType;
497 // Section 9.7 of the CSS2 spec indicates that absolute position
498 // takes precedence over float which takes precedence over display.
499 // Make sure the frame was actually moved out of the flow, and don't
500 // just assume what the style says
501 // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
502 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
503 if (disp->IsAbsolutelyPositioned()) {
504 frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
505 //XXXfr hack for making frames behave properly when in overflow container lists
506 // see bug 154892; need to revisit later
507 if (frame->GetPrevInFlow())
508 frameType = NS_CSS_FRAME_TYPE_BLOCK;
510 else if (NS_STYLE_FLOAT_NONE != disp->mFloats) {
511 frameType = NS_CSS_FRAME_TYPE_FLOATING;
512 } else {
513 NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP,
514 "unknown out of flow frame type");
515 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
518 else {
519 switch (disp->mDisplay) {
520 case NS_STYLE_DISPLAY_BLOCK:
521 case NS_STYLE_DISPLAY_LIST_ITEM:
522 case NS_STYLE_DISPLAY_TABLE:
523 case NS_STYLE_DISPLAY_TABLE_CAPTION:
524 frameType = NS_CSS_FRAME_TYPE_BLOCK;
525 break;
527 case NS_STYLE_DISPLAY_INLINE:
528 case NS_STYLE_DISPLAY_INLINE_BLOCK:
529 case NS_STYLE_DISPLAY_MARKER:
530 case NS_STYLE_DISPLAY_INLINE_TABLE:
531 case NS_STYLE_DISPLAY_INLINE_BOX:
532 case NS_STYLE_DISPLAY_INLINE_GRID:
533 case NS_STYLE_DISPLAY_INLINE_STACK:
534 frameType = NS_CSS_FRAME_TYPE_INLINE;
535 break;
537 case NS_STYLE_DISPLAY_RUN_IN:
538 case NS_STYLE_DISPLAY_COMPACT:
539 // XXX need to look ahead at the frame's sibling
540 frameType = NS_CSS_FRAME_TYPE_BLOCK;
541 break;
543 case NS_STYLE_DISPLAY_TABLE_CELL:
544 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
545 case NS_STYLE_DISPLAY_TABLE_COLUMN:
546 case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
547 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
548 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
549 case NS_STYLE_DISPLAY_TABLE_ROW:
550 frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
551 break;
553 case NS_STYLE_DISPLAY_NONE:
554 default:
555 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
556 break;
560 // See if the frame is replaced
561 if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
562 frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType);
563 } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) {
564 frameType = NS_FRAME_REPLACED(frameType);
567 mFrameType = frameType;
570 static void
571 nsPointDtor(void *aFrame, nsIAtom *aPropertyName,
572 void *aPropertyValue, void *aDtorData)
574 nsPoint *point = static_cast<nsPoint*>(aPropertyValue);
575 delete point;
578 void
579 nsHTMLReflowState::ComputeRelativeOffsets(const nsHTMLReflowState* cbrs,
580 nscoord aContainingBlockWidth,
581 nscoord aContainingBlockHeight,
582 nsPresContext* aPresContext)
584 // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
585 // and 'right' moves the boxes to the left. The computed values are always:
586 // left=-right
587 PRBool leftIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit();
588 PRBool rightIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit();
590 // Check for percentage based values and an unconstrained containing
591 // block width. Treat them like 'auto'
592 if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
593 if (eStyleUnit_Percent == mStylePosition->mOffset.GetLeftUnit()) {
594 leftIsAuto = PR_TRUE;
596 if (eStyleUnit_Percent == mStylePosition->mOffset.GetRightUnit()) {
597 rightIsAuto = PR_TRUE;
601 // If neither 'left' not 'right' are auto, then we're over-constrained and
602 // we ignore one of them
603 if (!leftIsAuto && !rightIsAuto) {
604 if (mCBReflowState &&
605 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) {
606 leftIsAuto = PR_TRUE;
607 } else {
608 rightIsAuto = PR_TRUE;
612 if (leftIsAuto) {
613 if (rightIsAuto) {
614 // If both are 'auto' (their initial values), the computed values are 0
615 mComputedOffsets.left = mComputedOffsets.right = 0;
616 } else {
617 // 'Right' isn't 'auto' so compute its value
618 mComputedOffsets.right = nsLayoutUtils::
619 ComputeWidthDependentValue(aContainingBlockWidth,
620 mStylePosition->mOffset.GetRight());
622 // Computed value for 'left' is minus the value of 'right'
623 mComputedOffsets.left = -mComputedOffsets.right;
626 } else {
627 NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
629 // 'Left' isn't 'auto' so compute its value
630 mComputedOffsets.left = nsLayoutUtils::
631 ComputeWidthDependentValue(aContainingBlockWidth,
632 mStylePosition->mOffset.GetLeft());
634 // Computed value for 'right' is minus the value of 'left'
635 mComputedOffsets.right = -mComputedOffsets.left;
638 // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
639 // move relatively positioned elements up and down. They also must be each
640 // other's negative
641 PRBool topIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit();
642 PRBool bottomIsAuto = eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit();
644 // Check for percentage based values and a containing block height that
645 // depends on the content height. Treat them like 'auto'
646 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
647 if (eStyleUnit_Percent == mStylePosition->mOffset.GetTopUnit()) {
648 topIsAuto = PR_TRUE;
650 if (eStyleUnit_Percent == mStylePosition->mOffset.GetBottomUnit()) {
651 bottomIsAuto = PR_TRUE;
655 // If neither is 'auto', 'bottom' is ignored
656 if (!topIsAuto && !bottomIsAuto) {
657 bottomIsAuto = PR_TRUE;
660 if (topIsAuto) {
661 if (bottomIsAuto) {
662 // If both are 'auto' (their initial values), the computed values are 0
663 mComputedOffsets.top = mComputedOffsets.bottom = 0;
664 } else {
665 // 'Bottom' isn't 'auto' so compute its value
666 mComputedOffsets.bottom = nsLayoutUtils::
667 ComputeHeightDependentValue(aContainingBlockHeight,
668 mStylePosition->mOffset.GetBottom());
670 // Computed value for 'top' is minus the value of 'bottom'
671 mComputedOffsets.top = -mComputedOffsets.bottom;
674 } else {
675 NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
677 // 'Top' isn't 'auto' so compute its value
678 mComputedOffsets.top = nsLayoutUtils::
679 ComputeHeightDependentValue(aContainingBlockHeight,
680 mStylePosition->mOffset.GetTop());
682 // Computed value for 'bottom' is minus the value of 'top'
683 mComputedOffsets.bottom = -mComputedOffsets.top;
686 // Store the offset
687 nsPropertyTable* propTable = aPresContext->PropertyTable();
688 nsPoint* offsets = static_cast<nsPoint*>
689 (propTable->GetProperty(frame, nsGkAtoms::computedOffsetProperty));
690 if (offsets)
691 offsets->MoveTo(mComputedOffsets.left, mComputedOffsets.top);
692 else {
693 offsets = new nsPoint(mComputedOffsets.left, mComputedOffsets.top);
694 if (offsets)
695 propTable->SetProperty(frame, nsGkAtoms::computedOffsetProperty,
696 offsets, nsPointDtor, nsnull);
700 nsIFrame*
701 nsHTMLReflowState::GetNearestContainingBlock(nsIFrame* aFrame, nscoord& aCBLeftEdge,
702 nscoord& aCBWidth)
704 for (aFrame = aFrame->GetParent(); aFrame && !aFrame->IsContainingBlock();
705 aFrame = aFrame->GetParent())
706 /* do nothing */;
708 NS_ASSERTION(aFrame, "Must find containing block somewhere");
709 NS_ASSERTION(aFrame != frame, "How did that happen?");
711 /* Now aFrame is the containing block we want */
713 /* Check whether the containing block is currently being reflown.
714 If so, use the info from the reflow state. */
715 const nsHTMLReflowState* state;
716 if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
717 for (state = parentReflowState; state && state->frame != aFrame;
718 state = state->parentReflowState) {
719 /* do nothing */
721 } else {
722 state = nsnull;
725 if (state) {
726 aCBLeftEdge = state->mComputedBorderPadding.left;
727 aCBWidth = state->mComputedWidth;
728 } else {
729 /* Didn't find a reflow state for aFrame. Just compute the information we
730 want, on the assumption that aFrame already knows its size. This really
731 ought to be true by now. */
732 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
733 "aFrame shouldn't be in reflow; we'll lie if it is");
734 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
735 aCBLeftEdge = borderPadding.left;
736 aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight();
739 return aFrame;
742 // When determining the hypothetical box that would have been if the element
743 // had been in the flow we may not be able to exactly determine both the left
744 // and right edges. For example, if the element is a non-replaced inline-level
745 // element we would have to reflow it in order to determine it desired width.
746 // In that case depending on the progression direction either the left or
747 // right edge would be marked as not being exact
748 struct nsHypotheticalBox {
749 // offsets from left edge of containing block (which is a padding edge)
750 nscoord mLeft, mRight;
751 // offset from top edge of containing block (which is a padding edge)
752 nscoord mTop;
753 #ifdef DEBUG
754 PRPackedBool mLeftIsExact, mRightIsExact;
755 #endif
757 nsHypotheticalBox() {
758 #ifdef DEBUG
759 mLeftIsExact = mRightIsExact = PR_FALSE;
760 #endif
764 static PRBool
765 GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize)
767 // See if it is an image frame
768 PRBool result = PR_FALSE;
770 // Currently the only type of replaced frame that we can get the intrinsic
771 // size for is an image frame
772 // XXX We should add back the GetReflowMetrics() function and one of the
773 // things should be the intrinsic size...
774 if (aFrame->GetType() == nsGkAtoms::imageFrame) {
775 nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
777 imageFrame->GetIntrinsicImageSize(aIntrinsicSize);
778 result = (aIntrinsicSize != nsSize(0, 0));
780 return result;
784 * aInsideBoxSizing returns the part of the horizontal padding, border,
785 * and margin that goes inside the edge given by -moz-box-sizing;
786 * aOutsideBoxSizing returns the rest.
788 void
789 nsHTMLReflowState::CalculateHorizBorderPaddingMargin(
790 nscoord aContainingBlockWidth,
791 nscoord* aInsideBoxSizing,
792 nscoord* aOutsideBoxSizing)
794 const nsMargin& border = mStyleBorder->GetActualBorder();
795 nsMargin padding, margin;
797 // See if the style system can provide us the padding directly
798 if (!mStylePadding->GetPadding(padding)) {
799 // We have to compute the left and right values
800 padding.left = nsLayoutUtils::
801 ComputeWidthDependentValue(aContainingBlockWidth,
802 mStylePadding->mPadding.GetLeft());
803 padding.right = nsLayoutUtils::
804 ComputeWidthDependentValue(aContainingBlockWidth,
805 mStylePadding->mPadding.GetRight());
808 // See if the style system can provide us the margin directly
809 if (!mStyleMargin->GetMargin(margin)) {
810 // We have to compute the left and right values
811 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
812 // XXX FIXME (or does CalculateBlockSideMargins do this?)
813 margin.left = 0; // just ignore
814 } else {
815 margin.left = nsLayoutUtils::
816 ComputeWidthDependentValue(aContainingBlockWidth,
817 mStyleMargin->mMargin.GetLeft());
819 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
820 // XXX FIXME (or does CalculateBlockSideMargins do this?)
821 margin.right = 0; // just ignore
822 } else {
823 margin.right = nsLayoutUtils::
824 ComputeWidthDependentValue(aContainingBlockWidth,
825 mStyleMargin->mMargin.GetRight());
829 nscoord outside =
830 padding.LeftRight() + border.LeftRight() + margin.LeftRight();
831 nscoord inside = 0;
832 switch (mStylePosition->mBoxSizing) {
833 case NS_STYLE_BOX_SIZING_BORDER:
834 inside += border.LeftRight();
835 // fall through
836 case NS_STYLE_BOX_SIZING_PADDING:
837 inside += padding.LeftRight();
839 outside -= inside;
840 *aInsideBoxSizing = inside;
841 *aOutsideBoxSizing = outside;
842 return;
846 * Returns PR_TRUE iff a pre-order traversal of the normal child
847 * frames rooted at aFrame finds no non-empty frame before aDescendant.
849 static PRBool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
850 nsIFrame* aDescendant, PRBool* aFound) {
851 if (aFrame == aDescendant) {
852 *aFound = PR_TRUE;
853 return PR_TRUE;
855 if (!aFrame->IsSelfEmpty()) {
856 *aFound = PR_FALSE;
857 return PR_FALSE;
859 for (nsIFrame* f = aFrame->GetFirstChild(nsnull); f; f = f->GetNextSibling()) {
860 PRBool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
861 if (*aFound || !allEmpty) {
862 return allEmpty;
865 *aFound = PR_FALSE;
866 return PR_TRUE;
869 // Calculate the hypothetical box that the element would have if it were in
870 // the flow. The values returned are relative to the padding edge of the
871 // absolute containing block
872 void
873 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext,
874 nsIFrame* aPlaceholderFrame,
875 nsIFrame* aContainingBlock,
876 nscoord aBlockLeftContentEdge,
877 nscoord aBlockContentWidth,
878 const nsHTMLReflowState* cbrs,
879 nsHypotheticalBox& aHypotheticalBox)
881 NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
882 "mOriginalDisplay has not been properly initialized");
884 // If it's a replaced element and it has a 'auto' value for 'width', see if we
885 // can get the intrinsic size. This will allow us to exactly determine both the
886 // left and right edges
887 PRBool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto;
888 nsSize intrinsicSize;
889 PRBool knowIntrinsicSize = PR_FALSE;
890 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
891 // See if we can get the intrinsic size of the element
892 knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize);
895 // See if we can calculate what the box width would have been if the
896 // element had been in the flow
897 nscoord boxWidth;
898 PRBool knowBoxWidth = PR_FALSE;
899 if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) &&
900 !NS_FRAME_IS_REPLACED(mFrameType)) {
901 // For non-replaced inline-level elements the 'width' property doesn't apply,
902 // so we don't know what the width would have been without reflowing it
904 } else {
905 // It's either a replaced inline-level element or a block-level element
907 // Determine the total amount of horizontal border/padding/margin that
908 // the element would have had if it had been in the flow. Note that we
909 // ignore any 'auto' and 'inherit' values
910 nscoord insideBoxSizing, outsideBoxSizing;
911 CalculateHorizBorderPaddingMargin(aBlockContentWidth,
912 &insideBoxSizing, &outsideBoxSizing);
914 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
915 // It's a replaced element with an 'auto' width so the box width is
916 // its intrinsic size plus any border/padding/margin
917 if (knowIntrinsicSize) {
918 boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing;
919 knowBoxWidth = PR_TRUE;
922 } else if (isAutoWidth) {
923 // The box width is the containing block width
924 boxWidth = aBlockContentWidth;
925 knowBoxWidth = PR_TRUE;
927 } else {
928 // We need to compute it. It's important we do this, because if it's
929 // percentage based this computed value may be different from the computed
930 // value calculated using the absolute containing block width
931 boxWidth = ComputeWidthValue(aBlockContentWidth,
932 insideBoxSizing, outsideBoxSizing,
933 mStylePosition->mWidth) +
934 insideBoxSizing + outsideBoxSizing;
935 knowBoxWidth = PR_TRUE;
939 // Get the 'direction' of the block
940 const nsStyleVisibility* blockVis = aContainingBlock->GetStyleVisibility();
942 // Get the placeholder x-offset and y-offset in the coordinate
943 // space of the block frame that contains it
944 // XXXbz the placeholder is not fully reflown yet if our containing block is
945 // relatively positioned...
946 nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock);
948 // First, determine the hypothetical box's mTop
949 nsBlockFrame* blockFrame = nsLayoutUtils::GetAsBlock(aContainingBlock);
950 if (blockFrame) {
951 PRBool isValid;
952 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
953 NS_ASSERTION(isValid, "Can't find placeholder!");
954 NS_ASSERTION(iter.GetContainer() == blockFrame, "Found placeholder in wrong block!");
955 nsBlockFrame::line_iterator lineBox = iter.GetLine();
957 // How we determine the hypothetical box depends on whether the element
958 // would have been inline-level or block-level
959 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
960 // Use the top of the inline box which the placeholder lives in as the
961 // hypothetical box's top.
962 aHypotheticalBox.mTop = lineBox->mBounds.y;
963 } else {
964 // The element would have been block-level which means it would be below
965 // the line containing the placeholder frame, unless all the frames
966 // before it are empty. In that case, it would have been just before
967 // this line.
968 // XXXbz the line box is not fully reflown yet if our containing block is
969 // relatively positioned...
970 if (lineBox != iter.End()) {
971 nsIFrame * firstFrame = lineBox->mFirstChild;
972 PRBool found = PR_FALSE;
973 PRBool allEmpty = PR_TRUE;
974 while (firstFrame) { // See bug 223064
975 allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
976 aPlaceholderFrame, &found);
977 if (found || !allEmpty)
978 break;
979 firstFrame = firstFrame->GetNextSibling();
981 NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
983 if (allEmpty) {
984 // The top of the hypothetical box is the top of the line containing
985 // the placeholder, since there is nothing in the line before our
986 // placeholder except empty frames.
987 aHypotheticalBox.mTop = lineBox->mBounds.y;
988 } else {
989 // The top of the hypothetical box is just below the line containing
990 // the placeholder.
991 aHypotheticalBox.mTop = lineBox->mBounds.YMost();
993 } else {
994 // Just use the placeholder's y-offset
995 aHypotheticalBox.mTop = placeholderOffset.y;
998 } else {
999 // The containing block is not a block, so it's probably something
1000 // like a XUL box, etc.
1001 // Just use the placeholder's y-offset
1002 aHypotheticalBox.mTop = placeholderOffset.y;
1005 // Second, determine the hypothetical box's mLeft & mRight
1006 // To determine the left and right offsets we need to look at the block's 'direction'
1007 if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
1008 // How we determine the hypothetical box depends on whether the element
1009 // would have been inline-level or block-level
1010 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
1011 // The placeholder represents the left edge of the hypothetical box
1012 aHypotheticalBox.mLeft = placeholderOffset.x;
1013 } else {
1014 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1016 #ifdef DEBUG
1017 aHypotheticalBox.mLeftIsExact = PR_TRUE;
1018 #endif
1020 if (knowBoxWidth) {
1021 aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
1022 #ifdef DEBUG
1023 aHypotheticalBox.mRightIsExact = PR_TRUE;
1024 #endif
1025 } else {
1026 // We can't compute the right edge because we don't know the desired
1027 // width. So instead use the right content edge of the block parent,
1028 // but remember it's not exact
1029 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1030 #ifdef DEBUG
1031 aHypotheticalBox.mRightIsExact = PR_FALSE;
1032 #endif
1035 } else {
1036 // The placeholder represents the right edge of the hypothetical box
1037 if (NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) {
1038 aHypotheticalBox.mRight = placeholderOffset.x;
1039 } else {
1040 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1042 #ifdef DEBUG
1043 aHypotheticalBox.mRightIsExact = PR_TRUE;
1044 #endif
1046 if (knowBoxWidth) {
1047 aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
1048 #ifdef DEBUG
1049 aHypotheticalBox.mLeftIsExact = PR_TRUE;
1050 #endif
1051 } else {
1052 // We can't compute the left edge because we don't know the desired
1053 // width. So instead use the left content edge of the block parent,
1054 // but remember it's not exact
1055 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1056 #ifdef DEBUG
1057 aHypotheticalBox.mLeftIsExact = PR_FALSE;
1058 #endif
1063 // The current coordinate space is that of the nearest block to the placeholder.
1064 // Convert to the coordinate space of the absolute containing block
1065 // One weird thing here is that for fixed-positioned elements we want to do
1066 // the conversion incorrectly; specifically we want to ignore any scrolling
1067 // that may have happened;
1068 nsPoint cbOffset;
1069 if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED) {
1070 // In this case, cbrs->frame will always be an ancestor of
1071 // aContainingBlock, so can just walk our way up the frame tree.
1072 // Make sure to not add positions of frames whose parent is a
1073 // scrollFrame, since we're doing fixed positioning, which assumes
1074 // everything is scrolled to (0,0).
1075 cbOffset.MoveTo(0, 0);
1076 do {
1077 NS_ASSERTION(aContainingBlock,
1078 "Should hit cbrs->frame before we run off the frame tree!");
1079 cbOffset += aContainingBlock->GetPositionIgnoringScrolling();
1080 aContainingBlock = aContainingBlock->GetParent();
1081 } while (aContainingBlock != cbrs->frame);
1082 } else {
1083 cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame);
1085 aHypotheticalBox.mLeft += cbOffset.x;
1086 aHypotheticalBox.mTop += cbOffset.y;
1087 aHypotheticalBox.mRight += cbOffset.x;
1089 // The specified offsets are relative to the absolute containing block's
1090 // padding edge and our current values are relative to the border edge, so
1091 // translate.
1092 nsMargin border = cbrs->mComputedBorderPadding - cbrs->mComputedPadding;
1093 aHypotheticalBox.mLeft -= border.left;
1094 aHypotheticalBox.mRight -= border.left;
1095 aHypotheticalBox.mTop -= border.top;
1098 void
1099 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
1100 const nsHTMLReflowState* cbrs,
1101 nscoord containingBlockWidth,
1102 nscoord containingBlockHeight)
1104 NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
1105 "containing block height must be constrained");
1107 // Get the placeholder frame
1108 nsIFrame* placeholderFrame;
1110 aPresContext->PresShell()->GetPlaceholderFrameFor(frame, &placeholderFrame);
1111 NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");
1113 // Find the nearest containing block frame to the placeholder frame,
1114 // and return its left edge and width.
1115 nscoord cbLeftEdge, cbWidth;
1116 nsIFrame* cbFrame = GetNearestContainingBlock(placeholderFrame, cbLeftEdge,
1117 cbWidth);
1119 // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
1120 // 'auto', then compute the hypothetical box of where the element would
1121 // have been if it had been in the flow
1122 nsHypotheticalBox hypotheticalBox;
1123 if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
1124 (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
1125 ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
1126 (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {
1128 CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame,
1129 cbLeftEdge, cbWidth, cbrs, hypotheticalBox);
1132 // Initialize the 'left' and 'right' computed offsets
1133 // XXX Handle new 'static-position' value...
1134 PRBool leftIsAuto = PR_FALSE, rightIsAuto = PR_FALSE;
1135 if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
1136 mComputedOffsets.left = 0;
1137 leftIsAuto = PR_TRUE;
1138 } else {
1139 mComputedOffsets.left = nsLayoutUtils::
1140 ComputeWidthDependentValue(containingBlockWidth,
1141 mStylePosition->mOffset.GetLeft());
1143 if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
1144 mComputedOffsets.right = 0;
1145 rightIsAuto = PR_TRUE;
1146 } else {
1147 mComputedOffsets.right = nsLayoutUtils::
1148 ComputeWidthDependentValue(containingBlockWidth,
1149 mStylePosition->mOffset.GetRight());
1152 // Use the horizontal component of the hypothetical box in the cases
1153 // where it's needed.
1154 if (leftIsAuto && rightIsAuto) {
1155 // Use the direction of the original ("static-position") containing block
1156 // to dictate whether 'left' or 'right' is treated like 'static-position'.
1157 if (NS_STYLE_DIRECTION_LTR == cbFrame->GetStyleVisibility()->mDirection) {
1158 NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have "
1159 "exact value on containing block's start side");
1160 mComputedOffsets.left = hypotheticalBox.mLeft;
1161 leftIsAuto = PR_FALSE;
1162 } else {
1163 NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have "
1164 "exact value on containing block's start side");
1165 mComputedOffsets.right = containingBlockWidth - hypotheticalBox.mRight;
1166 rightIsAuto = PR_FALSE;
1170 // Initialize the 'top' and 'bottom' computed offsets
1171 PRBool topIsAuto = PR_FALSE, bottomIsAuto = PR_FALSE;
1172 if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
1173 mComputedOffsets.top = 0;
1174 topIsAuto = PR_TRUE;
1175 } else {
1176 mComputedOffsets.top = nsLayoutUtils::
1177 ComputeHeightDependentValue(containingBlockHeight,
1178 mStylePosition->mOffset.GetTop());
1180 if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
1181 mComputedOffsets.bottom = 0;
1182 bottomIsAuto = PR_TRUE;
1183 } else {
1184 mComputedOffsets.bottom = nsLayoutUtils::
1185 ComputeHeightDependentValue(containingBlockHeight,
1186 mStylePosition->mOffset.GetBottom());
1189 if (topIsAuto && bottomIsAuto) {
1190 // Treat 'top' like 'static-position'
1191 mComputedOffsets.top = hypotheticalBox.mTop;
1192 topIsAuto = PR_FALSE;
1195 PRBool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit();
1196 PRBool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit();
1198 PRBool shrinkWrap = leftIsAuto || rightIsAuto;
1199 nsSize size =
1200 frame->ComputeSize(rendContext,
1201 nsSize(containingBlockWidth,
1202 containingBlockHeight),
1203 containingBlockWidth, // XXX or availableWidth?
1204 nsSize(mComputedMargin.LeftRight() +
1205 mComputedOffsets.LeftRight(),
1206 mComputedMargin.TopBottom() +
1207 mComputedOffsets.TopBottom()),
1208 nsSize(mComputedBorderPadding.LeftRight() -
1209 mComputedPadding.LeftRight(),
1210 mComputedBorderPadding.TopBottom() -
1211 mComputedPadding.TopBottom()),
1212 nsSize(mComputedPadding.LeftRight(),
1213 mComputedPadding.TopBottom()),
1214 shrinkWrap);
1215 mComputedWidth = size.width;
1216 mComputedHeight = size.height;
1217 NS_ASSERTION(mComputedWidth >= 0, "Bogus width");
1218 NS_ASSERTION(mComputedHeight == NS_UNCONSTRAINEDSIZE ||
1219 mComputedHeight >= 0, "Bogus height");
1221 // XXX Now that we have ComputeSize, can we condense many of the
1222 // branches off of widthIsAuto?
1224 if (leftIsAuto) {
1225 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1226 // box code above.
1227 // Solve for 'left'.
1228 if (widthIsAuto) {
1229 // XXXldb This, and the corresponding code in
1230 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1231 // we always compute widths.
1232 mComputedOffsets.left = NS_AUTOOFFSET;
1233 } else {
1234 mComputedOffsets.left = containingBlockWidth - mComputedMargin.left -
1235 mComputedBorderPadding.left - mComputedWidth - mComputedBorderPadding.right -
1236 mComputedMargin.right - mComputedOffsets.right;
1239 } else if (rightIsAuto) {
1240 // We know 'left' is not 'auto' anymore thanks to the hypothetical
1241 // box code above.
1242 // Solve for 'right'.
1243 if (widthIsAuto) {
1244 // XXXldb This, and the corresponding code in
1245 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1246 // we always compute widths.
1247 mComputedOffsets.right = NS_AUTOOFFSET;
1248 } else {
1249 mComputedOffsets.right = containingBlockWidth - mComputedOffsets.left -
1250 mComputedMargin.left - mComputedBorderPadding.left - mComputedWidth -
1251 mComputedBorderPadding.right - mComputedMargin.right;
1253 } else {
1254 // Neither 'left' nor 'right' is 'auto'. However, the width might
1255 // still not fill all the available space (even though we didn't
1256 // shrink-wrap) in case:
1257 // * width was specified
1258 // * we're dealing with a replaced element
1259 // * width was constrained by min-width or max-width.
1261 nscoord availMarginSpace = containingBlockWidth -
1262 mComputedOffsets.LeftRight() -
1263 mComputedMargin.LeftRight() -
1264 mComputedBorderPadding.LeftRight() -
1265 mComputedWidth;
1266 PRBool marginLeftIsAuto =
1267 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
1268 PRBool marginRightIsAuto =
1269 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
1271 if (availMarginSpace < 0 ||
1272 (!marginLeftIsAuto && !marginRightIsAuto)) {
1273 // We're over-constrained so use the direction of the containing block
1274 // to dictate which value to ignore. (And note that the spec says to ignore
1275 // 'left' or 'right' rather than 'margin-left' or 'margin-right'.)
1276 if (cbrs &&
1277 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1278 // Ignore the specified value for 'left'.
1279 mComputedOffsets.left += availMarginSpace;
1280 } else {
1281 // Ignore the specified value for 'right'.
1282 mComputedOffsets.right += availMarginSpace;
1284 } else if (marginLeftIsAuto) {
1285 if (marginRightIsAuto) {
1286 // Both 'margin-left' and 'margin-right' are 'auto', so they get
1287 // equal values
1288 mComputedMargin.left = availMarginSpace / 2;
1289 mComputedMargin.right = availMarginSpace - mComputedMargin.left;
1290 } else {
1291 // Just 'margin-left' is 'auto'
1292 mComputedMargin.left = availMarginSpace;
1294 } else {
1295 // Just 'margin-right' is 'auto'
1296 mComputedMargin.right = availMarginSpace;
1300 if (topIsAuto) {
1301 // solve for 'top'
1302 if (heightIsAuto) {
1303 mComputedOffsets.top = NS_AUTOOFFSET;
1304 } else {
1305 mComputedOffsets.top = containingBlockHeight - mComputedMargin.top -
1306 mComputedBorderPadding.top - mComputedHeight - mComputedBorderPadding.bottom -
1307 mComputedMargin.bottom - mComputedOffsets.bottom;
1309 } else if (bottomIsAuto) {
1310 // solve for 'bottom'
1311 if (heightIsAuto) {
1312 mComputedOffsets.bottom = NS_AUTOOFFSET;
1313 } else {
1314 mComputedOffsets.bottom = containingBlockHeight - mComputedOffsets.top -
1315 mComputedMargin.top - mComputedBorderPadding.top - mComputedHeight -
1316 mComputedBorderPadding.bottom - mComputedMargin.bottom;
1318 } else {
1319 // Neither 'top' nor 'bottom' is 'auto'.
1320 nscoord autoHeight = containingBlockHeight -
1321 mComputedOffsets.TopBottom() -
1322 mComputedMargin.TopBottom() -
1323 mComputedBorderPadding.TopBottom();
1324 if (autoHeight < 0) {
1325 autoHeight = 0;
1328 if (mComputedHeight == NS_UNCONSTRAINEDSIZE) {
1329 // For non-replaced elements with 'height' auto, the 'height'
1330 // fills the remaining space.
1331 mComputedHeight = autoHeight;
1333 // XXX Do these need box-sizing adjustments?
1334 if (mComputedHeight > mComputedMaxHeight)
1335 mComputedHeight = mComputedMaxHeight;
1336 if (mComputedHeight < mComputedMinHeight)
1337 mComputedHeight = mComputedMinHeight;
1340 // The height might still not fill all the available space in case:
1341 // * height was specified
1342 // * we're dealing with a replaced element
1343 // * height was constrained by min-height or max-height.
1344 nscoord availMarginSpace = autoHeight - mComputedHeight;
1345 PRBool marginTopIsAuto =
1346 eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit();
1347 PRBool marginBottomIsAuto =
1348 eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit();
1350 if (availMarginSpace < 0 || (!marginTopIsAuto && !marginBottomIsAuto)) {
1351 // We're over-constrained so ignore the specified value for
1352 // 'bottom'. (And note that the spec says to ignore 'bottom'
1353 // rather than 'margin-bottom'.)
1354 mComputedOffsets.bottom += availMarginSpace;
1355 } else if (marginTopIsAuto) {
1356 if (marginBottomIsAuto) {
1357 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1358 // equal values
1359 mComputedMargin.top = availMarginSpace / 2;
1360 mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
1361 } else {
1362 // Just 'margin-top' is 'auto'
1363 mComputedMargin.top = availMarginSpace - mComputedMargin.bottom;
1365 } else {
1366 // Just 'margin-bottom' is 'auto'
1367 mComputedMargin.bottom = availMarginSpace - mComputedMargin.top;
1372 nscoord
1373 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
1375 nscoord result = 0;
1376 if (!aReflowState) return result;
1378 // zero auto margins
1379 nsMargin margin = aReflowState->mComputedMargin;
1380 if (NS_AUTOMARGIN == margin.top)
1381 margin.top = 0;
1382 if (NS_AUTOMARGIN == margin.bottom)
1383 margin.bottom = 0;
1385 result += margin.top + margin.bottom;
1386 result += aReflowState->mComputedBorderPadding.top +
1387 aReflowState->mComputedBorderPadding.bottom;
1389 return result;
1392 /* Get the height based on the viewport of the containing block specified
1393 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
1394 * This will walk up the chain of containing blocks looking for a computed height
1395 * until it finds the canvas frame, or it encounters a frame that is not a block,
1396 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
1398 * When we encounter scrolledContent area frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
1400 * See also IsQuirkContainingBlockHeight.
1402 static nscoord
1403 CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState)
1405 nsHTMLReflowState* firstAncestorRS = nsnull; // a candidate for html frame
1406 nsHTMLReflowState* secondAncestorRS = nsnull; // a candidate for body frame
1408 // initialize the default to NS_AUTOHEIGHT as this is the containings block
1409 // computed height when this function is called. It is possible that we
1410 // don't alter this height especially if we are restricted to one level
1411 nscoord result = NS_AUTOHEIGHT;
1413 const nsHTMLReflowState* rs = aCBReflowState;
1414 for (; rs; rs = (nsHTMLReflowState *)(rs->parentReflowState)) {
1415 nsIAtom* frameType = rs->frame->GetType();
1416 // if the ancestor is auto height then skip it and continue up if it
1417 // is the first block/area frame and possibly the body/html
1418 if (nsGkAtoms::blockFrame == frameType ||
1419 nsGkAtoms::areaFrame == frameType ||
1420 nsGkAtoms::scrollFrame == frameType) {
1422 secondAncestorRS = firstAncestorRS;
1423 firstAncestorRS = (nsHTMLReflowState*)rs;
1425 // If the current frame we're looking at is positioned, we don't want to
1426 // go any further (see bug 221784). The behavior we want here is: 1) If
1427 // not auto-height, use this as the percentage base. 2) If auto-height,
1428 // keep looking, unless the frame is positioned.
1429 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
1430 if (rs->frame->GetStyleDisplay()->IsAbsolutelyPositioned()) {
1431 break;
1432 } else {
1433 continue;
1437 else if (nsGkAtoms::canvasFrame == frameType) {
1438 // Always continue on to the height calculation
1440 else if (nsGkAtoms::pageContentFrame == frameType) {
1441 nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
1442 // only use the page content frame for a height basis if it is the first in flow
1443 if (prevInFlow)
1444 break;
1446 else {
1447 break;
1450 // if the ancestor is the page content frame then the percent base is
1451 // the avail height, otherwise it is the computed height
1452 result = (nsGkAtoms::pageContentFrame == frameType)
1453 ? rs->availableHeight : rs->ComputedHeight();
1454 // if unconstrained - don't sutract borders - would result in huge height
1455 if (NS_AUTOHEIGHT == result) return result;
1457 // if we got to the canvas or page content frame, then subtract out
1458 // margin/border/padding for the BODY and HTML elements
1459 if ((nsGkAtoms::canvasFrame == frameType) ||
1460 (nsGkAtoms::pageContentFrame == frameType)) {
1462 result -= GetVerticalMarginBorderPadding(firstAncestorRS);
1463 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1465 #ifdef DEBUG
1466 // make sure the first ancestor is the HTML and the second is the BODY
1467 if (firstAncestorRS) {
1468 nsIContent* frameContent = firstAncestorRS->frame->GetContent();
1469 if (frameContent) {
1470 nsIAtom *contentTag = frameContent->Tag();
1471 NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML");
1474 if (secondAncestorRS) {
1475 nsIContent* frameContent = secondAncestorRS->frame->GetContent();
1476 if (frameContent) {
1477 nsIAtom *contentTag = frameContent->Tag();
1478 NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY");
1481 #endif
1484 // if we got to the html frame, then subtract out
1485 // margin/border/padding for the BODY element
1486 else if (nsGkAtoms::areaFrame == frameType) {
1487 // make sure it is the body
1488 if (nsGkAtoms::canvasFrame == rs->parentReflowState->frame->GetType()) {
1489 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1492 break;
1495 // Make sure not to return a negative height here!
1496 return PR_MAX(result, 0);
1498 // Called by InitConstraints() to compute the containing block rectangle for
1499 // the element. Handles the special logic for absolutely positioned elements
1500 void
1501 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext,
1502 const nsHTMLReflowState* aContainingBlockRS,
1503 nscoord& aContainingBlockWidth,
1504 nscoord& aContainingBlockHeight)
1506 // Unless the element is absolutely positioned, the containing block is
1507 // formed by the content edge of the nearest block-level ancestor
1508 aContainingBlockWidth = aContainingBlockRS->mComputedWidth;
1509 aContainingBlockHeight = aContainingBlockRS->mComputedHeight;
1511 if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
1512 // See if the ancestor is block-level or inline-level
1513 if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
1514 // Base our size on the actual size of the frame. In cases when this is
1515 // completely bogus (eg initial reflow), this code shouldn't even be
1516 // called, since the code in nsPositionedInlineFrame::Reflow will pass in
1517 // the containing block dimensions to our constructor.
1518 // XXXbz we should be taking the in-flows into account too, but
1519 // that's very hard.
1520 nsMargin computedBorder = aContainingBlockRS->mComputedBorderPadding -
1521 aContainingBlockRS->mComputedPadding;
1522 aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width -
1523 computedBorder.LeftRight();;
1524 NS_ASSERTION(aContainingBlockWidth >= 0,
1525 "Negative containing block width!");
1526 aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height -
1527 computedBorder.TopBottom();
1528 NS_ASSERTION(aContainingBlockHeight >= 0,
1529 "Negative containing block height!");
1530 } else {
1531 // If the ancestor is block-level, the containing block is formed by the
1532 // padding edge of the ancestor
1533 aContainingBlockWidth += aContainingBlockRS->mComputedPadding.LeftRight();
1535 // If the containing block is the initial containing block and it has a
1536 // height that depends on its content, then use the viewport height instead.
1537 // This gives us a reasonable value against which to compute percentage
1538 // based heights and to do bottom relative positioning
1539 if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
1540 nsLayoutUtils::IsInitialContainingBlock(aContainingBlockRS->frame)) {
1542 // Use the viewport height as the containing block height
1543 const nsHTMLReflowState* rs = aContainingBlockRS->parentReflowState;
1544 while (rs) {
1545 aContainingBlockHeight = rs->mComputedHeight;
1546 rs = rs->parentReflowState;
1549 } else {
1550 aContainingBlockHeight +=
1551 aContainingBlockRS->mComputedPadding.TopBottom();
1554 } else {
1555 // an element in quirks mode gets a containing block based on looking for a
1556 // parent with a non-auto height if the element has a percent height
1557 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1558 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
1559 mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
1560 aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS);
1566 // Prefs callback to pick up changes
1567 PR_STATIC_CALLBACK(int)
1568 PrefsChanged(const char *aPrefName, void *instance)
1570 sBlinkIsAllowed =
1571 nsContentUtils::GetBoolPref("browser.blink_allowed", sBlinkIsAllowed);
1573 return 0; /* PREF_OK */
1576 // Check to see if |text-decoration: blink| is allowed. The first time
1577 // called, register the callback and then force-load the pref. After that,
1578 // just use the cached value.
1579 static PRBool BlinkIsAllowed(void)
1581 if (!sPrefIsLoaded) {
1582 // Set up a listener and check the initial value
1583 nsContentUtils::RegisterPrefCallback("browser.blink_allowed", PrefsChanged,
1584 nsnull);
1585 PrefsChanged(nsnull, nsnull);
1586 sPrefIsLoaded = PR_TRUE;
1588 return sBlinkIsAllowed;
1591 static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
1593 if (sNormalLineHeightControl == eUninitialized) {
1594 // browser.display.normal_lineheight_calc_control is not user
1595 // changeable, so no need to register callback for it.
1596 sNormalLineHeightControl =
1597 static_cast<eNormalLineHeightControl>
1598 (nsContentUtils::GetIntPref("browser.display.normal_lineheight_calc_control", eNoExternalLeading));
1600 return sNormalLineHeightControl;
1603 static inline PRBool
1604 IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay)
1606 if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION)
1607 return PR_FALSE;
1608 PRUint8 captionSide = aFrame->GetStyleTableBorder()->mCaptionSide;
1609 return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
1610 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
1613 // XXX refactor this code to have methods for each set of properties
1614 // we are computing: width,height,line-height; margin; offsets
1616 void
1617 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
1618 nscoord aContainingBlockWidth,
1619 nscoord aContainingBlockHeight,
1620 const nsMargin* aBorder,
1621 const nsMargin* aPadding)
1623 // If this is the root frame, then set the computed width and
1624 // height equal to the available space
1625 if (nsnull == parentReflowState) {
1626 // XXXldb This doesn't mean what it used to!
1627 InitOffsets(aContainingBlockWidth, aBorder, aPadding);
1628 // Override mComputedMargin since reflow roots start from the
1629 // frame's boundary, which is inside the margin.
1630 mComputedMargin.SizeTo(0, 0, 0, 0);
1631 mComputedOffsets.SizeTo(0, 0, 0, 0);
1633 mComputedWidth = availableWidth - mComputedBorderPadding.LeftRight();
1634 if (mComputedWidth < 0)
1635 mComputedWidth = 0;
1636 if (availableHeight != NS_UNCONSTRAINEDSIZE) {
1637 mComputedHeight = availableHeight - mComputedBorderPadding.TopBottom();
1638 if (mComputedHeight < 0)
1639 mComputedHeight = 0;
1640 } else {
1641 mComputedHeight = NS_UNCONSTRAINEDSIZE;
1644 mComputedMinWidth = mComputedMinHeight = 0;
1645 mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
1646 } else {
1647 // Get the containing block reflow state
1648 const nsHTMLReflowState* cbrs = mCBReflowState;
1649 NS_ASSERTION(nsnull != cbrs, "no containing block");
1651 // If we weren't given a containing block width and height, then
1652 // compute one
1653 if (aContainingBlockWidth == -1) {
1654 ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth,
1655 aContainingBlockHeight);
1658 #if 0
1659 nsFrame::ListTag(stdout, frame); printf(": cb=");
1660 nsFrame::ListTag(stdout, cbrs->frame); printf(" size=%d,%d\n", aContainingBlockWidth, aContainingBlockHeight);
1661 #endif
1663 // See if the containing block height is based on the size of its
1664 // content
1665 nsIAtom* fType;
1666 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1667 // See if the containing block is a cell frame which needs
1668 // to use the mComputedHeight of the cell instead of what the cell block passed in.
1669 // XXX It seems like this could lead to bugs with min-height and friends
1670 if (cbrs->parentReflowState) {
1671 fType = cbrs->frame->GetType();
1672 if (IS_TABLE_CELL(fType)) {
1673 // use the cell's computed height
1674 aContainingBlockHeight = cbrs->mComputedHeight;
1679 InitOffsets(aContainingBlockWidth, aBorder, aPadding);
1681 nsStyleUnit heightUnit = mStylePosition->mHeight.GetUnit();
1683 // Check for a percentage based height and a containing block height
1684 // that depends on the content height
1685 // XXX twiddling heightUnit doesn't help anymore
1686 if (eStyleUnit_Percent == heightUnit) {
1687 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1688 // this if clause enables %-height on replaced inline frames,
1689 // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;"
1690 // used to be called exclusively.
1691 if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
1692 NS_FRAME_REPLACED_CONTAINS_BLOCK(
1693 NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
1694 // Get the containing block reflow state
1695 NS_ASSERTION(nsnull != cbrs, "no containing block");
1696 // in quirks mode, get the cb height using the special quirk method
1697 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
1698 if (!IS_TABLE_CELL(fType)) {
1699 aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs);
1700 if (aContainingBlockHeight == NS_AUTOHEIGHT) {
1701 heightUnit = eStyleUnit_Auto;
1704 else {
1705 heightUnit = eStyleUnit_Auto;
1708 // in standard mode, use the cb height. if it's "auto", as will be the case
1709 // by default in BODY, use auto height as per CSS2 spec.
1710 else
1712 if (NS_AUTOHEIGHT != cbrs->mComputedHeight)
1713 aContainingBlockHeight = cbrs->mComputedHeight;
1714 else
1715 heightUnit = eStyleUnit_Auto;
1718 else {
1719 // default to interpreting the height like 'auto'
1720 heightUnit = eStyleUnit_Auto;
1725 // Compute our offsets if the element is relatively positioned. We need
1726 // the correct containing block width and height here, which is why we need
1727 // to do it after all the quirks-n-such above.
1728 if (NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
1729 ComputeRelativeOffsets(cbrs, aContainingBlockWidth, aContainingBlockHeight, aPresContext);
1730 } else {
1731 // Initialize offsets to 0
1732 mComputedOffsets.SizeTo(0, 0, 0, 0);
1735 // Calculate the computed values for min and max properties. Note that
1736 // this MUST come after we've computed our border and padding.
1737 ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
1739 // Calculate the computed width and height. This varies by frame type
1741 if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
1742 // Internal table elements. The rules vary depending on the type.
1743 // Calculate the computed width
1744 PRBool rowOrRowGroup = PR_FALSE;
1745 nsStyleUnit widthUnit = mStylePosition->mWidth.GetUnit();
1746 if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
1747 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
1748 // 'width' property doesn't apply to table rows and row groups
1749 widthUnit = eStyleUnit_Auto;
1750 rowOrRowGroup = PR_TRUE;
1753 if (eStyleUnit_Auto == widthUnit) {
1754 mComputedWidth = availableWidth;
1756 if ((mComputedWidth != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
1757 // Internal table elements don't have margins. Only tables and
1758 // cells have border and padding
1759 mComputedWidth -= mComputedBorderPadding.left +
1760 mComputedBorderPadding.right;
1761 if (mComputedWidth < 0)
1762 mComputedWidth = 0;
1764 NS_ASSERTION(mComputedWidth >= 0, "Bogus computed width");
1766 } else {
1767 NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(),
1768 "unexpected width unit change");
1769 mComputedWidth = ComputeWidthValue(aContainingBlockWidth,
1770 mStylePosition->mBoxSizing,
1771 mStylePosition->mWidth);
1774 // Calculate the computed height
1775 if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
1776 (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
1777 // 'height' property doesn't apply to table columns and column groups
1778 heightUnit = eStyleUnit_Auto;
1780 if (eStyleUnit_Auto == heightUnit) {
1781 mComputedHeight = NS_AUTOHEIGHT;
1782 } else {
1783 NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(),
1784 "unexpected height unit change");
1785 mComputedHeight = nsLayoutUtils::
1786 ComputeHeightDependentValue(aContainingBlockHeight,
1787 mStylePosition->mHeight);
1790 // Doesn't apply to table elements
1791 mComputedMinWidth = mComputedMinHeight = 0;
1792 mComputedMaxWidth = mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
1794 } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
1795 // XXX not sure if this belongs here or somewhere else - cwk
1796 InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
1797 aContainingBlockHeight);
1798 } else {
1799 PRBool isBlock =
1800 NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType);
1801 nsSize size =
1802 frame->ComputeSize(rendContext,
1803 nsSize(aContainingBlockWidth,
1804 aContainingBlockHeight),
1805 availableWidth,
1806 nsSize(mComputedMargin.LeftRight(),
1807 mComputedMargin.TopBottom()),
1808 nsSize(mComputedBorderPadding.LeftRight() -
1809 mComputedPadding.LeftRight(),
1810 mComputedBorderPadding.TopBottom() -
1811 mComputedPadding.TopBottom()),
1812 nsSize(mComputedPadding.LeftRight(),
1813 mComputedPadding.TopBottom()),
1814 !isBlock);
1816 mComputedWidth = size.width;
1817 mComputedHeight = size.height;
1818 NS_ASSERTION(mComputedWidth >= 0, "Bogus width");
1819 NS_ASSERTION(mComputedHeight == NS_UNCONSTRAINEDSIZE ||
1820 mComputedHeight >= 0, "Bogus height");
1822 if (isBlock && !IsSideCaption(frame, mStyleDisplay))
1823 CalculateBlockSideMargins(availableWidth, mComputedWidth);
1826 // Check for blinking text and permission to display it
1827 mFlags.mBlinks = (parentReflowState && parentReflowState->mFlags.mBlinks);
1828 if (!mFlags.mBlinks && BlinkIsAllowed()) {
1829 const nsStyleTextReset* st = frame->GetStyleTextReset();
1830 mFlags.mBlinks =
1831 ((st->mTextDecoration & NS_STYLE_TEXT_DECORATION_BLINK) != 0);
1835 void
1836 nsCSSOffsetState::InitOffsets(nscoord aContainingBlockWidth,
1837 const nsMargin *aBorder,
1838 const nsMargin *aPadding)
1840 // Compute margins from the specified margin style information. These
1841 // become the default computed values, and may be adjusted below
1842 // XXX fix to provide 0,0 for the top&bottom margins for
1843 // inline-non-replaced elements
1844 ComputeMargin(aContainingBlockWidth);
1846 const nsStyleDisplay *disp = frame->GetStyleDisplay();
1847 PRBool isThemed = frame->IsThemed(disp);
1848 nsPresContext *presContext = frame->PresContext();
1850 if (isThemed &&
1851 presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
1852 frame, disp->mAppearance,
1853 &mComputedPadding)) {
1854 mComputedPadding.top = presContext->DevPixelsToAppUnits(mComputedPadding.top);
1855 mComputedPadding.right = presContext->DevPixelsToAppUnits(mComputedPadding.right);
1856 mComputedPadding.bottom = presContext->DevPixelsToAppUnits(mComputedPadding.bottom);
1857 mComputedPadding.left = presContext->DevPixelsToAppUnits(mComputedPadding.left);
1859 else if (aPadding) { // padding is an input arg
1860 mComputedPadding.top = aPadding->top;
1861 mComputedPadding.right = aPadding->right;
1862 mComputedPadding.bottom = aPadding->bottom;
1863 mComputedPadding.left = aPadding->left;
1865 else {
1866 ComputePadding(aContainingBlockWidth);
1869 if (isThemed) {
1870 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
1871 frame, disp->mAppearance,
1872 &mComputedBorderPadding);
1873 mComputedBorderPadding.top =
1874 presContext->DevPixelsToAppUnits(mComputedBorderPadding.top);
1875 mComputedBorderPadding.right =
1876 presContext->DevPixelsToAppUnits(mComputedBorderPadding.right);
1877 mComputedBorderPadding.bottom =
1878 presContext->DevPixelsToAppUnits(mComputedBorderPadding.bottom);
1879 mComputedBorderPadding.left =
1880 presContext->DevPixelsToAppUnits(mComputedBorderPadding.left);
1882 else if (aBorder) { // border is an input arg
1883 mComputedBorderPadding = *aBorder;
1885 else {
1886 mComputedBorderPadding = frame->GetStyleBorder()->GetActualBorder();
1888 mComputedBorderPadding += mComputedPadding;
1890 if (frame->GetType() == nsGkAtoms::tableFrame) {
1891 nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame);
1893 if (tableFrame->IsBorderCollapse()) {
1894 // border-collapsed tables don't use any of their padding, and
1895 // only part of their border. We need to do this here before we
1896 // try to do anything like handling 'auto' widths,
1897 // '-moz-box-sizing', or 'auto' margins.
1898 mComputedPadding.SizeTo(0,0,0,0);
1899 mComputedBorderPadding = tableFrame->GetIncludedOuterBCBorder();
1904 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
1906 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
1907 // 'padding-right' + 'border-right-width' + 'margin-right'
1908 // = width of containing block
1910 // Note: the width unit is not auto when this is called
1911 void
1912 nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
1913 nscoord aComputedWidth)
1915 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aComputedWidth &&
1916 NS_UNCONSTRAINEDSIZE != aAvailWidth,
1917 "this shouldn't happen anymore");
1919 nscoord sum = mComputedMargin.left + mComputedBorderPadding.left +
1920 aComputedWidth + mComputedBorderPadding.right + mComputedMargin.right;
1921 if (sum == aAvailWidth)
1922 // The sum is already correct
1923 return;
1925 // Determine the left and right margin values. The width value
1926 // remains constant while we do this.
1928 // Calculate how much space is available for margins
1929 nscoord availMarginSpace = aAvailWidth - sum;
1931 // If the available margin space is negative, then don't follow the
1932 // usual overconstraint rules.
1933 if (availMarginSpace < 0) {
1934 if (mCBReflowState &&
1935 mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) {
1936 mComputedMargin.left += availMarginSpace;
1937 } else {
1938 mComputedMargin.right += availMarginSpace;
1940 return;
1943 // The css2 spec clearly defines how block elements should behave
1944 // in section 10.3.3.
1945 PRBool isAutoLeftMargin =
1946 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
1947 PRBool isAutoRightMargin =
1948 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
1949 if (!isAutoLeftMargin && !isAutoRightMargin) {
1950 // Neither margin is 'auto' so we're over constrained. Use the
1951 // 'direction' property of the parent to tell which margin to
1952 // ignore
1953 // First check if there is an HTML alignment that we should honor
1954 const nsHTMLReflowState* prs = parentReflowState;
1955 if (frame->GetType() == nsGkAtoms::tableFrame) {
1956 NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame,
1957 "table not inside outer table");
1958 // Center the table within the outer table based on the alignment
1959 // of the outer table's parent.
1960 prs = prs->parentReflowState;
1962 if (prs &&
1963 (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
1964 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
1965 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
1966 isAutoLeftMargin =
1967 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
1968 isAutoRightMargin =
1969 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
1971 // Otherwise apply the CSS rules, and ignore one margin by forcing
1972 // it to 'auto', depending on 'direction'.
1973 else if (mCBReflowState &&
1974 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) {
1975 isAutoLeftMargin = PR_TRUE;
1977 else {
1978 isAutoRightMargin = PR_TRUE;
1982 // Logic which is common to blocks and tables
1983 if (isAutoLeftMargin) {
1984 if (isAutoRightMargin) {
1985 // Both margins are 'auto' so their computed values are equal
1986 mComputedMargin.left = availMarginSpace / 2;
1987 mComputedMargin.right = availMarginSpace - mComputedMargin.left;
1988 } else {
1989 mComputedMargin.left += availMarginSpace;
1991 } else if (isAutoRightMargin) {
1992 mComputedMargin.right += availMarginSpace;
1996 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight
1997 // For "normal" we use the font's normal line height (em height + leading).
1998 // If both internal leading and external leading specified by font itself
1999 // are zeros, we should compensate this by creating extra (external) leading
2000 // in eCompensateLeading mode. This is necessary because without this
2001 // compensation, normal line height might looks too tight.
2003 // For risk management, we use preference to control the behavior, and
2004 // eNoExternalLeading is the old behavior.
2005 static nscoord
2006 GetNormalLineHeight(nsIFontMetrics* aFontMetrics)
2008 NS_PRECONDITION(nsnull != aFontMetrics, "no font metrics");
2010 nscoord normalLineHeight;
2012 nscoord externalLeading, internalLeading, emHeight;
2013 aFontMetrics->GetExternalLeading(externalLeading);
2014 aFontMetrics->GetInternalLeading(internalLeading);
2015 aFontMetrics->GetEmHeight(emHeight);
2016 switch (GetNormalLineHeightCalcControl()) {
2017 case eIncludeExternalLeading:
2018 normalLineHeight = emHeight+ internalLeading + externalLeading;
2019 break;
2020 case eCompensateLeading:
2021 if (!internalLeading && !externalLeading)
2022 normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
2023 else
2024 normalLineHeight = emHeight+ internalLeading + externalLeading;
2025 break;
2026 default:
2027 //case eNoExternalLeading:
2028 normalLineHeight = emHeight + internalLeading;
2030 return normalLineHeight;
2033 static nscoord
2034 ComputeLineHeight(nsStyleContext* aStyleContext)
2036 const nsStyleCoord& lhCoord = aStyleContext->GetStyleText()->mLineHeight;
2038 if (lhCoord.GetUnit() == eStyleUnit_Coord)
2039 return lhCoord.GetCoordValue();
2041 if (lhCoord.GetUnit() == eStyleUnit_Factor)
2042 // For factor units the computed value of the line-height property
2043 // is found by multiplying the factor by the font's computed size
2044 // (adjusted for min-size prefs and text zoom).
2045 return NSToCoordRound(lhCoord.GetFactorValue() *
2046 aStyleContext->GetStyleFont()->mFont.size);
2049 NS_ASSERTION(eStyleUnit_Normal == lhCoord.GetUnit(), "bad unit");
2050 nsCOMPtr<nsIFontMetrics> fm;
2051 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
2052 getter_AddRefs(fm));
2053 return GetNormalLineHeight(fm);
2056 nscoord
2057 nsHTMLReflowState::CalcLineHeight(nsStyleContext* aStyleContext)
2059 NS_PRECONDITION(aStyleContext, "Must have a style context");
2061 nscoord lineHeight = ComputeLineHeight(aStyleContext);
2063 NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
2065 return lineHeight;
2068 /* static */
2069 void
2070 nsCSSOffsetState::DestroyMarginFunc(void* aFrame,
2071 nsIAtom* aPropertyName,
2072 void* aPropertyValue,
2073 void* aDtorData)
2075 delete static_cast<nsMargin*>(aPropertyValue);
2078 void
2079 nsCSSOffsetState::ComputeMargin(nscoord aContainingBlockWidth)
2081 // If style style can provide us the margin directly, then use it.
2082 const nsStyleMargin *styleMargin = frame->GetStyleMargin();
2083 if (!styleMargin->GetMargin(mComputedMargin)) {
2084 // We have to compute the value
2085 if (NS_UNCONSTRAINEDSIZE == aContainingBlockWidth) {
2086 mComputedMargin.left = 0;
2087 mComputedMargin.right = 0;
2089 if (eStyleUnit_Coord == styleMargin->mMargin.GetLeftUnit()) {
2090 mComputedMargin.left = styleMargin->mMargin.GetLeft().GetCoordValue();
2092 if (eStyleUnit_Coord == styleMargin->mMargin.GetRightUnit()) {
2093 mComputedMargin.right = styleMargin->mMargin.GetRight().GetCoordValue();
2096 } else {
2097 mComputedMargin.left = nsLayoutUtils::
2098 ComputeWidthDependentValue(aContainingBlockWidth,
2099 styleMargin->mMargin.GetLeft());
2100 mComputedMargin.right = nsLayoutUtils::
2101 ComputeWidthDependentValue(aContainingBlockWidth,
2102 styleMargin->mMargin.GetRight());
2105 // According to the CSS2 spec, margin percentages are
2106 // calculated with respect to the *width* of the containing
2107 // block, even for margin-top and margin-bottom.
2108 // XXX This isn't true for page boxes, if we implement them.
2109 mComputedMargin.top = nsLayoutUtils::
2110 ComputeWidthDependentValue(aContainingBlockWidth,
2111 styleMargin->mMargin.GetTop());
2112 mComputedMargin.bottom = nsLayoutUtils::
2113 ComputeWidthDependentValue(aContainingBlockWidth,
2114 styleMargin->mMargin.GetBottom());
2116 // XXX We need to include 'auto' horizontal margins in this too!
2117 // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
2118 // to use it even when the margins are all zero (since sometimes
2119 // they get treated as auto)
2120 frame->SetProperty(nsGkAtoms::usedMarginProperty,
2121 new nsMargin(mComputedMargin),
2122 DestroyMarginFunc);
2126 void
2127 nsCSSOffsetState::ComputePadding(nscoord aContainingBlockWidth)
2129 // If style can provide us the padding directly, then use it.
2130 const nsStylePadding *stylePadding = frame->GetStylePadding();
2131 if (!stylePadding->GetPadding(mComputedPadding)) {
2132 // We have to compute the value
2133 mComputedPadding.left = nsLayoutUtils::
2134 ComputeWidthDependentValue(aContainingBlockWidth,
2135 stylePadding->mPadding.GetLeft());
2136 mComputedPadding.right = nsLayoutUtils::
2137 ComputeWidthDependentValue(aContainingBlockWidth,
2138 stylePadding->mPadding.GetRight());
2140 // According to the CSS2 spec, percentages are calculated with respect to
2141 // containing block width for padding-top and padding-bottom
2142 mComputedPadding.top = nsLayoutUtils::
2143 ComputeWidthDependentValue(aContainingBlockWidth,
2144 stylePadding->mPadding.GetTop());
2145 mComputedPadding.bottom = nsLayoutUtils::
2146 ComputeWidthDependentValue(aContainingBlockWidth,
2147 stylePadding->mPadding.GetBottom());
2149 frame->SetProperty(nsGkAtoms::usedPaddingProperty,
2150 new nsMargin(mComputedPadding),
2151 DestroyMarginFunc);
2153 // a table row/col group, row/col doesn't have padding
2154 // XXXldb Neither do border-collapse tables.
2155 nsIAtom* frameType = frame->GetType();
2156 if (nsGkAtoms::tableRowGroupFrame == frameType ||
2157 nsGkAtoms::tableColGroupFrame == frameType ||
2158 nsGkAtoms::tableRowFrame == frameType ||
2159 nsGkAtoms::tableColFrame == frameType) {
2160 mComputedPadding.top = 0;
2161 mComputedPadding.right = 0;
2162 mComputedPadding.bottom = 0;
2163 mComputedPadding.left = 0;
2167 void
2168 nsHTMLReflowState::ApplyMinMaxConstraints(nscoord* aFrameWidth,
2169 nscoord* aFrameHeight) const
2171 if (aFrameWidth) {
2172 if (NS_UNCONSTRAINEDSIZE != mComputedMaxWidth) {
2173 *aFrameWidth = PR_MIN(*aFrameWidth, mComputedMaxWidth);
2175 *aFrameWidth = PR_MAX(*aFrameWidth, mComputedMinWidth);
2178 if (aFrameHeight) {
2179 if (NS_UNCONSTRAINEDSIZE != mComputedMaxHeight) {
2180 *aFrameHeight = PR_MIN(*aFrameHeight, mComputedMaxHeight);
2182 *aFrameHeight = PR_MAX(*aFrameHeight, mComputedMinHeight);
2186 void
2187 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
2188 nscoord aContainingBlockHeight,
2189 const nsHTMLReflowState* aContainingBlockRS)
2191 mComputedMinWidth = ComputeWidthValue(aContainingBlockWidth,
2192 mStylePosition->mBoxSizing,
2193 mStylePosition->mMinWidth);
2195 if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) {
2196 // Specified value of 'none'
2197 mComputedMaxWidth = NS_UNCONSTRAINEDSIZE; // no limit
2198 } else {
2199 mComputedMaxWidth = ComputeWidthValue(aContainingBlockWidth,
2200 mStylePosition->mBoxSizing,
2201 mStylePosition->mMaxWidth);
2204 // If the computed value of 'min-width' is greater than the value of
2205 // 'max-width', 'max-width' is set to the value of 'min-width'
2206 if (mComputedMinWidth > mComputedMaxWidth) {
2207 mComputedMaxWidth = mComputedMinWidth;
2210 // Check for percentage based values and a containing block height that
2211 // depends on the content height. Treat them like 'auto'
2212 if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
2213 (eStyleUnit_Percent == mStylePosition->mMinHeight.GetUnit())) {
2214 mComputedMinHeight = 0;
2215 } else {
2216 mComputedMinHeight = nsLayoutUtils::
2217 ComputeHeightDependentValue(aContainingBlockHeight,
2218 mStylePosition->mMinHeight);
2220 nsStyleUnit maxHeightUnit = mStylePosition->mMaxHeight.GetUnit();
2221 if (eStyleUnit_None == maxHeightUnit) {
2222 // Specified value of 'none'
2223 mComputedMaxHeight = NS_UNCONSTRAINEDSIZE; // no limit
2224 } else {
2225 // Check for percentage based values and a containing block height that
2226 // depends on the content height. Treat them like 'auto'
2227 if ((NS_AUTOHEIGHT == aContainingBlockHeight) &&
2228 (eStyleUnit_Percent == maxHeightUnit)) {
2229 mComputedMaxHeight = NS_UNCONSTRAINEDSIZE;
2230 } else {
2231 mComputedMaxHeight = nsLayoutUtils::
2232 ComputeHeightDependentValue(aContainingBlockHeight,
2233 mStylePosition->mMaxHeight);
2237 // If the computed value of 'min-height' is greater than the value of
2238 // 'max-height', 'max-height' is set to the value of 'min-height'
2239 if (mComputedMinHeight > mComputedMaxHeight) {
2240 mComputedMaxHeight = mComputedMinHeight;
2244 void
2245 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics,
2246 nsReflowStatus* aStatus) const
2248 if (availableHeight != NS_UNCONSTRAINEDSIZE &&
2249 availableHeight < aMetrics.height &&
2250 !mFlags.mIsTopOfPage) {
2251 *aStatus |= NS_FRAME_TRUNCATED;
2252 } else {
2253 *aStatus &= ~NS_FRAME_TRUNCATED;