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
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.
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 */
41 #include "nsStyleConsts.h"
42 #include "nsCSSAnonBoxes.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"
62 #include "nsBidiUtils.h"
66 #undef NOISY_VERTICAL_ALIGN
68 #undef NOISY_VERTICAL_ALIGN
71 // Prefs-driven control for |text-decoration: blink|
72 static PRPackedBool sPrefIsLoaded
= PR_FALSE
;
73 static PRPackedBool sBlinkIsAllowed
= PR_TRUE
;
75 enum eNormalLineHeightControl
{
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
,
88 nsIRenderingContext
* aRenderingContext
,
89 const nsSize
& aAvailableSpace
)
90 : nsCSSOffsetState(aFrame
, aRenderingContext
)
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
;
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
125 nsHTMLReflowState::nsHTMLReflowState(nsPresContext
* aPresContext
,
126 const nsHTMLReflowState
& aParentReflowState
,
128 const nsSize
& aAvailableSpace
,
129 nscoord aContainingBlockWidth
,
130 nscoord aContainingBlockHeight
,
132 : nsCSSOffsetState(aFrame
, aParentReflowState
.rendContext
)
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() &
157 availableWidth
= aAvailableSpace
.width
;
158 availableHeight
= aAvailableSpace
.height
;
160 mSpaceManager
= aParentReflowState
.mSpaceManager
;
161 if (frame
->IsFrameOfType(nsIFrame::eLineParticipant
))
162 mLineLayout
= aParentReflowState
.mLineLayout
;
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
;
177 Init(aPresContext
, aContainingBlockWidth
, aContainingBlockHeight
);
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
,
195 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth
,
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();
205 case NS_STYLE_BOX_SIZING_PADDING
:
206 inside
= mComputedPadding
.LeftRight();
211 return ComputeWidthValue(aContainingBlockWidth
, inside
,
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
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());
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
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());
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();
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
298 imgIRequest
*borderImage
= mStyleBorder
->GetBorderImage();
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
;
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
;
326 mCBReflowState
= parentReflowState
;
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
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()) {
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() &&
387 mFlags
.mVResize
= mCBReflowState
->mFlags
.mVResize
;
389 mFlags
.mVResize
= mFlags
.mHResize
|| NS_SUBTREE_DIRTY(frame
);
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
) &&
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
428 if (dependsOnCBHeight
&& mCBReflowState
) {
429 const nsHTMLReflowState
*rs
= this;
430 PRBool hitCBReflowState
= PR_FALSE
;
432 rs
= rs
->parentReflowState
;
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
460 frame
->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
466 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState
* aReflowState
)
468 const nsHTMLReflowState
* rs
= aReflowState
->mCBReflowState
;
471 return rs
->mComputedWidth
;
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).
485 while (container
&& !container
->IsContainingBlock()) {
486 container
= container
->GetParent();
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
;
513 NS_ASSERTION(disp
->mDisplay
== NS_STYLE_DISPLAY_POPUP
,
514 "unknown out of flow frame type");
515 frameType
= NS_CSS_FRAME_TYPE_UNKNOWN
;
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
;
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
;
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
;
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
;
553 case NS_STYLE_DISPLAY_NONE
:
555 frameType
= NS_CSS_FRAME_TYPE_UNKNOWN
;
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
;
571 nsPointDtor(void *aFrame
, nsIAtom
*aPropertyName
,
572 void *aPropertyValue
, void *aDtorData
)
574 nsPoint
*point
= static_cast<nsPoint
*>(aPropertyValue
);
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:
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
;
608 rightIsAuto
= PR_TRUE
;
614 // If both are 'auto' (their initial values), the computed values are 0
615 mComputedOffsets
.left
= mComputedOffsets
.right
= 0;
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
;
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
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()) {
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
;
662 // If both are 'auto' (their initial values), the computed values are 0
663 mComputedOffsets
.top
= mComputedOffsets
.bottom
= 0;
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
;
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
;
687 nsPropertyTable
* propTable
= aPresContext
->PropertyTable();
688 nsPoint
* offsets
= static_cast<nsPoint
*>
689 (propTable
->GetProperty(frame
, nsGkAtoms::computedOffsetProperty
));
691 offsets
->MoveTo(mComputedOffsets
.left
, mComputedOffsets
.top
);
693 offsets
= new nsPoint(mComputedOffsets
.left
, mComputedOffsets
.top
);
695 propTable
->SetProperty(frame
, nsGkAtoms::computedOffsetProperty
,
696 offsets
, nsPointDtor
, nsnull
);
701 nsHTMLReflowState::GetNearestContainingBlock(nsIFrame
* aFrame
, nscoord
& aCBLeftEdge
,
704 for (aFrame
= aFrame
->GetParent(); aFrame
&& !aFrame
->IsContainingBlock();
705 aFrame
= aFrame
->GetParent())
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
) {
726 aCBLeftEdge
= state
->mComputedBorderPadding
.left
;
727 aCBWidth
= state
->mComputedWidth
;
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();
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)
754 PRPackedBool mLeftIsExact
, mRightIsExact
;
757 nsHypotheticalBox() {
759 mLeftIsExact
= mRightIsExact
= PR_FALSE
;
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));
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.
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
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
823 margin
.right
= nsLayoutUtils::
824 ComputeWidthDependentValue(aContainingBlockWidth
,
825 mStyleMargin
->mMargin
.GetRight());
830 padding
.LeftRight() + border
.LeftRight() + margin
.LeftRight();
832 switch (mStylePosition
->mBoxSizing
) {
833 case NS_STYLE_BOX_SIZING_BORDER
:
834 inside
+= border
.LeftRight();
836 case NS_STYLE_BOX_SIZING_PADDING
:
837 inside
+= padding
.LeftRight();
840 *aInsideBoxSizing
= inside
;
841 *aOutsideBoxSizing
= outside
;
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
) {
855 if (!aFrame
->IsSelfEmpty()) {
859 for (nsIFrame
* f
= aFrame
->GetFirstChild(nsnull
); f
; f
= f
->GetNextSibling()) {
860 PRBool allEmpty
= AreAllEarlierInFlowFramesEmpty(f
, aDescendant
, aFound
);
861 if (*aFound
|| !allEmpty
) {
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
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
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
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
;
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
);
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
;
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
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
)
979 firstFrame
= firstFrame
->GetNextSibling();
981 NS_ASSERTION(firstFrame
, "Couldn't find placeholder!");
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
;
989 // The top of the hypothetical box is just below the line containing
991 aHypotheticalBox
.mTop
= lineBox
->mBounds
.YMost();
994 // Just use the placeholder's y-offset
995 aHypotheticalBox
.mTop
= placeholderOffset
.y
;
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
;
1014 aHypotheticalBox
.mLeft
= aBlockLeftContentEdge
;
1017 aHypotheticalBox
.mLeftIsExact
= PR_TRUE
;
1021 aHypotheticalBox
.mRight
= aHypotheticalBox
.mLeft
+ boxWidth
;
1023 aHypotheticalBox
.mRightIsExact
= PR_TRUE
;
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
;
1031 aHypotheticalBox
.mRightIsExact
= PR_FALSE
;
1036 // The placeholder represents the right edge of the hypothetical box
1037 if (NS_STYLE_DISPLAY_INLINE
== mStyleDisplay
->mOriginalDisplay
) {
1038 aHypotheticalBox
.mRight
= placeholderOffset
.x
;
1040 aHypotheticalBox
.mRight
= aBlockLeftContentEdge
+ aBlockContentWidth
;
1043 aHypotheticalBox
.mRightIsExact
= PR_TRUE
;
1047 aHypotheticalBox
.mLeft
= aHypotheticalBox
.mRight
- boxWidth
;
1049 aHypotheticalBox
.mLeftIsExact
= PR_TRUE
;
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
;
1057 aHypotheticalBox
.mLeftIsExact
= PR_FALSE
;
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;
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);
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
);
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
1092 nsMargin border
= cbrs
->mComputedBorderPadding
- cbrs
->mComputedPadding
;
1093 aHypotheticalBox
.mLeft
-= border
.left
;
1094 aHypotheticalBox
.mRight
-= border
.left
;
1095 aHypotheticalBox
.mTop
-= border
.top
;
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
,
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
;
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
;
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
;
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
;
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
;
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
;
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()),
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?
1225 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1227 // Solve for 'left'.
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
;
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
1242 // Solve for 'right'.
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
;
1249 mComputedOffsets
.right
= containingBlockWidth
- mComputedOffsets
.left
-
1250 mComputedMargin
.left
- mComputedBorderPadding
.left
- mComputedWidth
-
1251 mComputedBorderPadding
.right
- mComputedMargin
.right
;
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() -
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'.)
1277 NS_STYLE_DIRECTION_RTL
== cbrs
->mStyleVisibility
->mDirection
) {
1278 // Ignore the specified value for 'left'.
1279 mComputedOffsets
.left
+= availMarginSpace
;
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
1288 mComputedMargin
.left
= availMarginSpace
/ 2;
1289 mComputedMargin
.right
= availMarginSpace
- mComputedMargin
.left
;
1291 // Just 'margin-left' is 'auto'
1292 mComputedMargin
.left
= availMarginSpace
;
1295 // Just 'margin-right' is 'auto'
1296 mComputedMargin
.right
= availMarginSpace
;
1303 mComputedOffsets
.top
= NS_AUTOOFFSET
;
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'
1312 mComputedOffsets
.bottom
= NS_AUTOOFFSET
;
1314 mComputedOffsets
.bottom
= containingBlockHeight
- mComputedOffsets
.top
-
1315 mComputedMargin
.top
- mComputedBorderPadding
.top
- mComputedHeight
-
1316 mComputedBorderPadding
.bottom
- mComputedMargin
.bottom
;
1319 // Neither 'top' nor 'bottom' is 'auto'.
1320 nscoord autoHeight
= containingBlockHeight
-
1321 mComputedOffsets
.TopBottom() -
1322 mComputedMargin
.TopBottom() -
1323 mComputedBorderPadding
.TopBottom();
1324 if (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
1359 mComputedMargin
.top
= availMarginSpace
/ 2;
1360 mComputedMargin
.bottom
= availMarginSpace
- mComputedMargin
.top
;
1362 // Just 'margin-top' is 'auto'
1363 mComputedMargin
.top
= availMarginSpace
- mComputedMargin
.bottom
;
1366 // Just 'margin-bottom' is 'auto'
1367 mComputedMargin
.bottom
= availMarginSpace
- mComputedMargin
.top
;
1373 GetVerticalMarginBorderPadding(const nsHTMLReflowState
* aReflowState
)
1376 if (!aReflowState
) return result
;
1378 // zero auto margins
1379 nsMargin margin
= aReflowState
->mComputedMargin
;
1380 if (NS_AUTOMARGIN
== margin
.top
)
1382 if (NS_AUTOMARGIN
== margin
.bottom
)
1385 result
+= margin
.top
+ margin
.bottom
;
1386 result
+= aReflowState
->mComputedBorderPadding
.top
+
1387 aReflowState
->mComputedBorderPadding
.bottom
;
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.
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()) {
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
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
);
1466 // make sure the first ancestor is the HTML and the second is the BODY
1467 if (firstAncestorRS
) {
1468 nsIContent
* frameContent
= firstAncestorRS
->frame
->GetContent();
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();
1477 nsIAtom
*contentTag
= frameContent
->Tag();
1478 NS_ASSERTION(contentTag
== nsGkAtoms::body
, "Second ancestor is not BODY");
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
);
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
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!");
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
;
1545 aContainingBlockHeight
= rs
->mComputedHeight
;
1546 rs
= rs
->parentReflowState
;
1550 aContainingBlockHeight
+=
1551 aContainingBlockRS
->mComputedPadding
.TopBottom();
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
)
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
,
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
)
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
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)
1636 if (availableHeight
!= NS_UNCONSTRAINEDSIZE
) {
1637 mComputedHeight
= availableHeight
- mComputedBorderPadding
.TopBottom();
1638 if (mComputedHeight
< 0)
1639 mComputedHeight
= 0;
1641 mComputedHeight
= NS_UNCONSTRAINEDSIZE
;
1644 mComputedMinWidth
= mComputedMinHeight
= 0;
1645 mComputedMaxWidth
= mComputedMaxHeight
= NS_UNCONSTRAINEDSIZE
;
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
1653 if (aContainingBlockWidth
== -1) {
1654 ComputeContainingBlockRectangle(aPresContext
, cbrs
, aContainingBlockWidth
,
1655 aContainingBlockHeight
);
1659 nsFrame::ListTag(stdout
, frame
); printf(": cb=");
1660 nsFrame::ListTag(stdout
, cbrs
->frame
); printf(" size=%d,%d\n", aContainingBlockWidth
, aContainingBlockHeight
);
1663 // See if the containing block height is based on the size of its
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
;
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.
1712 if (NS_AUTOHEIGHT
!= cbrs
->mComputedHeight
)
1713 aContainingBlockHeight
= cbrs
->mComputedHeight
;
1715 heightUnit
= eStyleUnit_Auto
;
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
);
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)
1764 NS_ASSERTION(mComputedWidth
>= 0, "Bogus computed width");
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
;
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
);
1800 NS_CSS_FRAME_TYPE_BLOCK
== NS_FRAME_GET_TYPE(mFrameType
);
1802 frame
->ComputeSize(rendContext
,
1803 nsSize(aContainingBlockWidth
,
1804 aContainingBlockHeight
),
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()),
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();
1831 ((st
->mTextDecoration
& NS_STYLE_TEXT_DECORATION_BLINK
) != 0);
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();
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
;
1866 ComputePadding(aContainingBlockWidth
);
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
;
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
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
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
;
1938 mComputedMargin
.right
+= availMarginSpace
;
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
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
;
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
)) {
1967 prs
->mStyleText
->mTextAlign
!= NS_STYLE_TEXT_ALIGN_MOZ_LEFT
;
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
;
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
;
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.
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
;
2020 case eCompensateLeading
:
2021 if (!internalLeading
&& !externalLeading
)
2022 normalLineHeight
= NSToCoordRound(emHeight
* NORMAL_LINE_HEIGHT_FACTOR
);
2024 normalLineHeight
= emHeight
+ internalLeading
+ externalLeading
;
2027 //case eNoExternalLeading:
2028 normalLineHeight
= emHeight
+ internalLeading
;
2030 return normalLineHeight
;
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
);
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");
2070 nsCSSOffsetState::DestroyMarginFunc(void* aFrame
,
2071 nsIAtom
* aPropertyName
,
2072 void* aPropertyValue
,
2075 delete static_cast<nsMargin
*>(aPropertyValue
);
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();
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
),
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
),
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;
2168 nsHTMLReflowState::ApplyMinMaxConstraints(nscoord
* aFrameWidth
,
2169 nscoord
* aFrameHeight
) const
2172 if (NS_UNCONSTRAINEDSIZE
!= mComputedMaxWidth
) {
2173 *aFrameWidth
= PR_MIN(*aFrameWidth
, mComputedMaxWidth
);
2175 *aFrameWidth
= PR_MAX(*aFrameWidth
, mComputedMinWidth
);
2179 if (NS_UNCONSTRAINEDSIZE
!= mComputedMaxHeight
) {
2180 *aFrameHeight
= PR_MIN(*aFrameHeight
, mComputedMaxHeight
);
2182 *aFrameHeight
= PR_MAX(*aFrameHeight
, mComputedMinHeight
);
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
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;
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
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
;
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
;
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
;
2253 *aStatus
&= ~NS_FRAME_TRUNCATED
;