1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* struct containing the input to nsIFrame::Reflow */
8 #include "nsHTMLReflowState.h"
10 #include "nsStyleConsts.h"
11 #include "nsCSSAnonBoxes.h"
13 #include "nsIContent.h"
14 #include "nsGkAtoms.h"
15 #include "nsPresContext.h"
16 #include "nsIPresShell.h"
17 #include "nsFontMetrics.h"
18 #include "nsBlockFrame.h"
19 #include "nsLineBox.h"
20 #include "nsFlexContainerFrame.h"
21 #include "nsImageFrame.h"
22 #include "nsTableFrame.h"
23 #include "nsTableCellFrame.h"
24 #include "nsIPercentHeightObserver.h"
25 #include "nsLayoutUtils.h"
26 #include "mozilla/Preferences.h"
27 #include "nsFontInflationData.h"
28 #include "StickyScrollContainer.h"
29 #include "nsIFrameInlines.h"
30 #include "CounterStyleManager.h"
32 #include "mozilla/dom/HTMLInputElement.h"
35 #undef NOISY_VERTICAL_ALIGN
37 #undef NOISY_VERTICAL_ALIGN
40 using namespace mozilla
;
41 using namespace mozilla::css
;
42 using namespace mozilla::dom
;
43 using namespace mozilla::layout
;
45 enum eNormalLineHeightControl
{
47 eNoExternalLeading
= 0, // does not include external leading
48 eIncludeExternalLeading
, // use whatever value font vendor provides
49 eCompensateLeading
// compensate leading if leading provided by font vendor is not enough
52 static eNormalLineHeightControl sNormalLineHeightControl
= eUninitialized
;
54 // Initialize a <b>root</b> reflow state with a rendering context to
55 // use for measuring things.
56 nsHTMLReflowState::nsHTMLReflowState(nsPresContext
* aPresContext
,
58 nsRenderingContext
* aRenderingContext
,
59 const LogicalSize
& aAvailableSpace
,
61 : nsCSSOffsetState(aFrame
, aRenderingContext
)
63 , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE
)
66 NS_PRECONDITION(aRenderingContext
, "no rendering context");
67 MOZ_ASSERT(aPresContext
, "no pres context");
68 MOZ_ASSERT(aFrame
, "no frame");
69 MOZ_ASSERT(aPresContext
== aFrame
->PresContext(), "wrong pres context");
70 parentReflowState
= nullptr;
71 AvailableISize() = aAvailableSpace
.ISize(mWritingMode
);
72 AvailableBSize() = aAvailableSpace
.BSize(mWritingMode
);
73 mFloatManager
= nullptr;
74 mLineLayout
= nullptr;
75 mRubyReflowState
= nullptr;
76 memset(&mFlags
, 0, sizeof(mFlags
));
77 mDiscoveredClearance
= nullptr;
78 mPercentHeightObserver
= nullptr;
80 if (aFlags
& DUMMY_PARENT_REFLOW_STATE
) {
81 mFlags
.mDummyParentReflowState
= true;
84 if (!(aFlags
& CALLER_WILL_INIT
)) {
89 static bool CheckNextInFlowParenthood(nsIFrame
* aFrame
, nsIFrame
* aParent
)
91 nsIFrame
* frameNext
= aFrame
->GetNextInFlow();
92 nsIFrame
* parentNext
= aParent
->GetNextInFlow();
93 return frameNext
&& parentNext
&& frameNext
->GetParent() == parentNext
;
97 * Adjusts the margin for a list (ol, ul), if necessary, depending on
98 * font inflation settings. Unfortunately, because bullets from a list are
99 * placed in the margin area, we only have ~40px in which to place the
100 * bullets. When they are inflated, however, this causes problems, since
101 * the text takes up more space than is available in the margin.
103 * This method will return a small amount (in app units) by which the
104 * margin can be adjusted, so that the space is available for list
105 * bullets to be rendered with font inflation enabled.
108 FontSizeInflationListMarginAdjustment(const nsIFrame
* aFrame
)
110 float inflation
= nsLayoutUtils::FontSizeInflationFor(aFrame
);
111 if (aFrame
->IsFrameOfType(nsIFrame::eBlockFrame
)) {
112 const nsBlockFrame
* blockFrame
= static_cast<const nsBlockFrame
*>(aFrame
);
114 // We only want to adjust the margins if we're dealing with an ordered
116 if (inflation
> 1.0f
&&
117 blockFrame
->HasBullet() &&
120 auto listStyleType
= aFrame
->StyleList()->GetCounterStyle()->GetStyle();
121 if (listStyleType
!= NS_STYLE_LIST_STYLE_NONE
&&
122 listStyleType
!= NS_STYLE_LIST_STYLE_DISC
&&
123 listStyleType
!= NS_STYLE_LIST_STYLE_CIRCLE
&&
124 listStyleType
!= NS_STYLE_LIST_STYLE_SQUARE
&&
125 listStyleType
!= NS_STYLE_LIST_STYLE_DISCLOSURE_CLOSED
&&
126 listStyleType
!= NS_STYLE_LIST_STYLE_DISCLOSURE_OPEN
) {
127 // The HTML spec states that the default padding for ordered lists
128 // begins at 40px, indicating that we have 40px of space to place a
129 // bullet. When performing font inflation calculations, we add space
130 // equivalent to this, but simply inflated at the same amount as the
131 // text, in app units.
132 return nsPresContext::CSSPixelsToAppUnits(40) * (inflation
- 1);
141 // NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid
142 // item, we need to make it take the containing-block height as well as the
143 // width, since flex items and grid items resolve vertical percent margins
144 // and padding against the containing-block height, rather than its width.
145 nsCSSOffsetState::nsCSSOffsetState(nsIFrame
*aFrame
,
146 nsRenderingContext
*aRenderingContext
,
147 nscoord aContainingBlockWidth
)
149 , rendContext(aRenderingContext
)
150 , mWritingMode(aFrame
->GetWritingMode())
152 MOZ_ASSERT(!aFrame
->IsFlexOrGridItem(),
153 "We're about to resolve vertical percent margin & padding "
154 "values against CB width, which is incorrect for flex/grid items");
155 InitOffsets(aContainingBlockWidth
, aContainingBlockWidth
, frame
->GetType());
158 // Initialize a reflow state for a child frame's reflow. Some state
159 // is copied from the parent reflow state; the remaining state is
161 nsHTMLReflowState::nsHTMLReflowState(nsPresContext
* aPresContext
,
162 const nsHTMLReflowState
& aParentReflowState
,
164 const LogicalSize
& aAvailableSpace
,
165 nscoord aContainingBlockWidth
,
166 nscoord aContainingBlockHeight
,
168 : nsCSSOffsetState(aFrame
, aParentReflowState
.rendContext
)
170 , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE
)
171 , mReflowDepth(aParentReflowState
.mReflowDepth
+ 1)
172 , mFlags(aParentReflowState
.mFlags
)
174 MOZ_ASSERT(aPresContext
, "no pres context");
175 MOZ_ASSERT(aFrame
, "no frame");
176 MOZ_ASSERT(aPresContext
== aFrame
->PresContext(), "wrong pres context");
177 NS_PRECONDITION((aContainingBlockWidth
== -1) ==
178 (aContainingBlockHeight
== -1),
179 "cb width and height should only be non-default together");
180 NS_PRECONDITION(!mFlags
.mSpecialHeightReflow
||
181 !NS_SUBTREE_DIRTY(aFrame
),
182 "frame should be clean when getting special height reflow");
184 parentReflowState
= &aParentReflowState
;
186 // If the parent is dirty, then the child is as well.
187 // XXX Are the other cases where the parent reflows a child a second
188 // time, as a resize?
189 if (!mFlags
.mSpecialHeightReflow
)
190 frame
->AddStateBits(parentReflowState
->frame
->GetStateBits() &
193 AvailableISize() = aAvailableSpace
.ISize(mWritingMode
);
194 AvailableBSize() = aAvailableSpace
.BSize(mWritingMode
);
196 if (mWritingMode
.IsOrthogonalTo(aParentReflowState
.GetWritingMode())) {
197 // If we're setting up for an orthogonal flow, and the parent reflow state
198 // had a constrained ComputedBSize, we can use that as our AvailableISize
199 // in preference to leaving it unconstrained.
200 if (AvailableISize() == NS_UNCONSTRAINEDSIZE
&&
201 aParentReflowState
.ComputedBSize() != NS_UNCONSTRAINEDSIZE
) {
202 AvailableISize() = aParentReflowState
.ComputedBSize();
206 mFloatManager
= aParentReflowState
.mFloatManager
;
207 if (frame
->IsFrameOfType(nsIFrame::eLineParticipant
))
208 mLineLayout
= aParentReflowState
.mLineLayout
;
210 mLineLayout
= nullptr;
211 mRubyReflowState
= nullptr;
213 // Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in
214 // this constructor's init list, so the only flags that we need to explicitly
215 // initialize here are those that may need a value other than our parent's.
216 mFlags
.mNextInFlowUntouched
= aParentReflowState
.mFlags
.mNextInFlowUntouched
&&
217 CheckNextInFlowParenthood(aFrame
, aParentReflowState
.frame
);
218 mFlags
.mAssumingHScrollbar
= mFlags
.mAssumingVScrollbar
= false;
219 mFlags
.mHasClearance
= false;
220 mFlags
.mIsColumnBalancing
= false;
221 mFlags
.mIsFlexContainerMeasuringHeight
= false;
222 mFlags
.mDummyParentReflowState
= false;
224 mDiscoveredClearance
= nullptr;
225 mPercentHeightObserver
= (aParentReflowState
.mPercentHeightObserver
&&
226 aParentReflowState
.mPercentHeightObserver
->NeedsToObserve(*this))
227 ? aParentReflowState
.mPercentHeightObserver
: nullptr;
229 if ((aFlags
& DUMMY_PARENT_REFLOW_STATE
) ||
230 (parentReflowState
->mFlags
.mDummyParentReflowState
&&
231 frame
->GetType() == nsGkAtoms::tableFrame
)) {
232 mFlags
.mDummyParentReflowState
= true;
235 if (!(aFlags
& CALLER_WILL_INIT
)) {
236 Init(aPresContext
, aContainingBlockWidth
, aContainingBlockHeight
);
241 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth
,
242 nscoord aContentEdgeToBoxSizing
,
243 nscoord aBoxSizingToMarginEdge
,
244 const nsStyleCoord
& aCoord
)
246 return nsLayoutUtils::ComputeWidthValue(rendContext
, frame
,
247 aContainingBlockWidth
,
248 aContentEdgeToBoxSizing
,
249 aBoxSizingToMarginEdge
,
254 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth
,
256 const nsStyleCoord
& aCoord
)
258 nscoord inside
= 0, outside
= ComputedPhysicalBorderPadding().LeftRight() +
259 ComputedPhysicalMargin().LeftRight();
260 switch (aBoxSizing
) {
261 case NS_STYLE_BOX_SIZING_BORDER
:
262 inside
= ComputedPhysicalBorderPadding().LeftRight();
264 case NS_STYLE_BOX_SIZING_PADDING
:
265 inside
= ComputedPhysicalPadding().LeftRight();
270 return ComputeWidthValue(aContainingBlockWidth
, inside
,
275 nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight
,
277 const nsStyleCoord
& aCoord
)
280 switch (aBoxSizing
) {
281 case NS_STYLE_BOX_SIZING_BORDER
:
282 inside
= ComputedPhysicalBorderPadding().TopBottom();
284 case NS_STYLE_BOX_SIZING_PADDING
:
285 inside
= ComputedPhysicalPadding().TopBottom();
288 return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight
,
293 nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth
)
295 NS_ASSERTION(frame
, "Must have a frame!");
296 // It'd be nice to assert that |frame| is not in reflow, but this fails for
299 // 1) Viewport frames reset the computed width on a copy of their reflow
300 // state when reflowing fixed-pos kids. In that case we actually don't
301 // want to mess with the resize flags, because comparing the frame's rect
302 // to the munged computed width is pointless.
303 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
304 // state is not used to reflow the parent, but just as a parent for the
305 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
306 // (like a text control, for example), we'll end up creating a reflow
307 // state for the parent while the parent is reflowing.
309 NS_PRECONDITION(aComputedWidth
>= 0, "Invalid computed width");
310 if (ComputedWidth() != aComputedWidth
) {
311 ComputedWidth() = aComputedWidth
;
312 nsIAtom
* frameType
= frame
->GetType();
313 if (frameType
!= nsGkAtoms::viewportFrame
) { // Or check GetParent()?
314 InitResizeFlags(frame
->PresContext(), frameType
);
320 nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight
)
322 NS_ASSERTION(frame
, "Must have a frame!");
323 // It'd be nice to assert that |frame| is not in reflow, but this fails
326 // nsFrame::BoxReflow creates a reflow state for its parent. This reflow
327 // state is not used to reflow the parent, but just as a parent for the
328 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
329 // (like a text control, for example), we'll end up creating a reflow
330 // state for the parent while the parent is reflowing.
332 NS_PRECONDITION(aComputedHeight
>= 0, "Invalid computed height");
333 if (ComputedHeight() != aComputedHeight
) {
334 ComputedHeight() = aComputedHeight
;
335 InitResizeFlags(frame
->PresContext(), frame
->GetType());
340 nsHTMLReflowState::Init(nsPresContext
* aPresContext
,
341 nscoord aContainingBlockWidth
,
342 nscoord aContainingBlockHeight
,
343 const nsMargin
* aBorder
,
344 const nsMargin
* aPadding
)
346 if (AvailableISize() == NS_UNCONSTRAINEDSIZE
) {
347 // Look up the parent chain for an orthogonal inline limit,
348 // and reset AvailableISize() if found.
349 for (const nsHTMLReflowState
*parent
= parentReflowState
;
350 parent
!= nullptr; parent
= parent
->parentReflowState
) {
351 if (parent
->GetWritingMode().IsOrthogonalTo(mWritingMode
) &&
352 parent
->mOrthogonalLimit
!= NS_UNCONSTRAINEDSIZE
) {
353 AvailableISize() = parent
->mOrthogonalLimit
;
359 NS_WARN_IF_FALSE(AvailableISize() != NS_UNCONSTRAINEDSIZE
,
360 "have unconstrained inline-size; this should only result from "
361 "very large sizes, not attempts at intrinsic inline-size "
364 mStylePosition
= frame
->StylePosition();
365 mStyleDisplay
= frame
->StyleDisplay();
366 mStyleVisibility
= frame
->StyleVisibility();
367 mStyleBorder
= frame
->StyleBorder();
368 mStyleMargin
= frame
->StyleMargin();
369 mStylePadding
= frame
->StylePadding();
370 mStyleText
= frame
->StyleText();
372 nsIAtom
* type
= frame
->GetType();
377 InitConstraints(aPresContext
, aContainingBlockWidth
, aContainingBlockHeight
,
378 aBorder
, aPadding
, type
);
380 InitResizeFlags(aPresContext
, type
);
382 nsIFrame
*parent
= frame
->GetParent();
384 (parent
->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT
) &&
385 !(parent
->GetType() == nsGkAtoms::scrollFrame
&&
386 parent
->StyleDisplay()->mOverflowY
!= NS_STYLE_OVERFLOW_HIDDEN
)) {
387 frame
->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
388 } else if (type
== nsGkAtoms::svgForeignObjectFrame
) {
389 // An SVG foreignObject frame is inherently constrained height.
390 frame
->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
391 } else if ((mStylePosition
->mHeight
.GetUnit() != eStyleUnit_Auto
||
392 mStylePosition
->mMaxHeight
.GetUnit() != eStyleUnit_None
) &&
393 // Don't set NS_FRAME_IN_CONSTRAINED_HEIGHT on body or html
395 (frame
->GetContent() &&
396 !(frame
->GetContent()->IsHTML(nsGkAtoms::body
) ||
397 frame
->GetContent()->IsHTML(nsGkAtoms::html
)))) {
399 // If our height was specified as a percentage, then this could
400 // actually resolve to 'auto', based on:
401 // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
402 nsIFrame
* containingBlk
= frame
;
403 while (containingBlk
) {
404 const nsStylePosition
* stylePos
= containingBlk
->StylePosition();
405 if ((stylePos
->mHeight
.IsCoordPercentCalcUnit() &&
406 !stylePos
->mHeight
.HasPercent()) ||
407 (stylePos
->mMaxHeight
.IsCoordPercentCalcUnit() &&
408 !stylePos
->mMaxHeight
.HasPercent())) {
409 frame
->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
411 } else if ((stylePos
->mHeight
.IsCoordPercentCalcUnit() &&
412 stylePos
->mHeight
.HasPercent()) ||
413 (stylePos
->mMaxHeight
.IsCoordPercentCalcUnit() &&
414 stylePos
->mMaxHeight
.HasPercent())) {
415 if (!(containingBlk
= containingBlk
->GetContainingBlock())) {
416 // If we've reached the top of the tree, then we don't have
417 // a constrained height.
418 frame
->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
424 frame
->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
429 frame
->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT
);
432 NS_WARN_IF_FALSE((mFrameType
== NS_CSS_FRAME_TYPE_INLINE
&&
433 !frame
->IsFrameOfType(nsIFrame::eReplaced
)) ||
434 type
== nsGkAtoms::textFrame
||
435 ComputedISize() != NS_UNCONSTRAINEDSIZE
,
436 "have unconstrained inline-size; this should only result from "
437 "very large sizes, not attempts at intrinsic inline-size "
441 void nsHTMLReflowState::InitCBReflowState()
443 if (!parentReflowState
) {
444 mCBReflowState
= nullptr;
448 if (parentReflowState
->frame
== frame
->GetContainingBlock()) {
449 // Inner table frames need to use the containing block of the outer
451 if (frame
->GetType() == nsGkAtoms::tableFrame
) {
452 mCBReflowState
= parentReflowState
->mCBReflowState
;
454 mCBReflowState
= parentReflowState
;
457 mCBReflowState
= parentReflowState
->mCBReflowState
;
461 /* Check whether CalcQuirkContainingBlockHeight would stop on the
462 * given reflow state, using its block as a height. (essentially
463 * returns false for any case in which CalcQuirkContainingBlockHeight
464 * has a "continue" in its main loop.)
466 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
467 * this function as well
470 IsQuirkContainingBlockHeight(const nsHTMLReflowState
* rs
, nsIAtom
* aFrameType
)
472 if (nsGkAtoms::blockFrame
== aFrameType
||
474 nsGkAtoms::XULLabelFrame
== aFrameType
||
476 nsGkAtoms::scrollFrame
== aFrameType
) {
477 // Note: This next condition could change due to a style change,
478 // but that would cause a style reflow anyway, which means we're ok.
479 if (NS_AUTOHEIGHT
== rs
->ComputedHeight()) {
480 if (!rs
->frame
->IsAbsolutelyPositioned()) {
490 nsHTMLReflowState::InitResizeFlags(nsPresContext
* aPresContext
, nsIAtom
* aFrameType
)
492 bool isHResize
= (frame
->GetSize().width
!=
493 ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) ||
494 aPresContext
->PresShell()->IsReflowOnZoomPending();
496 if ((frame
->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT
) &&
497 nsLayoutUtils::FontSizeInflationEnabled(aPresContext
)) {
498 // Create our font inflation data if we don't have it already, and
499 // give it our current width information.
500 bool dirty
= nsFontInflationData::UpdateFontInflationDataWidthFor(*this) &&
501 // Avoid running this at the box-to-block interface
502 // (where we shouldn't be inflating anyway, and where
503 // reflow state construction is probably to construct a
504 // dummy parent reflow state anyway).
505 !mFlags
.mDummyParentReflowState
;
507 if (dirty
|| (!frame
->GetParent() && isHResize
)) {
508 // When font size inflation is enabled, a change in either:
509 // * the effective width of a font inflation flow root
510 // * the width of the frame
511 // needs to cause a dirty reflow since they change the font size
512 // inflation calculations, which in turn change the size of text,
513 // line-heights, etc. This is relatively similar to a classic
514 // case of style change reflow, except that because inflation
515 // doesn't affect the intrinsic sizing codepath, there's no need
516 // to invalidate intrinsic sizes.
518 // Note that this makes horizontal resizing a good bit more
519 // expensive. However, font size inflation is targeted at a set of
520 // devices (zoom-and-pan devices) where the main use case for
521 // horizontal resizing needing to be efficient (window resizing) is
522 // not present. It does still increase the cost of dynamic changes
523 // caused by script where a style or content change in one place
524 // causes a resize in another (e.g., rebalancing a table).
526 // FIXME: This isn't so great for the cases where
527 // nsHTMLReflowState::SetComputedWidth is called, if the first time
528 // we go through InitResizeFlags we set IsHResize() to true, and then
529 // the second time we'd set it to false even without the
530 // NS_FRAME_IS_DIRTY bit already set.
531 if (frame
->GetType() == nsGkAtoms::svgForeignObjectFrame
) {
532 // Foreign object frames use dirty bits in a special way.
533 frame
->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN
);
534 nsIFrame
*kid
= frame
->GetFirstPrincipalChild();
536 kid
->AddStateBits(NS_FRAME_IS_DIRTY
);
539 frame
->AddStateBits(NS_FRAME_IS_DIRTY
);
542 // Mark intrinsic widths on all descendants dirty. We need to do
543 // this (1) since we're changing the size of text and need to
544 // clear text runs on text frames and (2) since we actually are
545 // changing some intrinsic widths, but only those that live inside
548 // It makes sense to do this for descendants but not ancestors
549 // (which is unusual) because we're only changing the unusual
550 // inflation-dependent intrinsic widths (i.e., ones computed with
551 // nsPresContext::mInflationDisabledForShrinkWrap set to false),
552 // which should never affect anything outside of their inflation
553 // flow root (or, for that matter, even their inflation
556 // This is also different from what PresShell::FrameNeedsReflow
557 // does because it doesn't go through placeholders. It doesn't
558 // need to because we're actually doing something that cares about
559 // frame tree geometry (the width on an ancestor) rather than
562 nsAutoTArray
<nsIFrame
*, 32> stack
;
563 stack
.AppendElement(frame
);
566 nsIFrame
*f
= stack
.ElementAt(stack
.Length() - 1);
567 stack
.RemoveElementAt(stack
.Length() - 1);
569 nsIFrame::ChildListIterator
lists(f
);
570 for (; !lists
.IsDone(); lists
.Next()) {
571 nsFrameList::Enumerator
childFrames(lists
.CurrentList());
572 for (; !childFrames
.AtEnd(); childFrames
.Next()) {
573 nsIFrame
* kid
= childFrames
.get();
574 kid
->MarkIntrinsicISizesDirty();
575 stack
.AppendElement(kid
);
578 } while (stack
.Length() != 0);
582 SetHResize(!(frame
->GetStateBits() & NS_FRAME_IS_DIRTY
) &&
585 // XXX Should we really need to null check mCBReflowState? (We do for
586 // at least nsBoxFrame).
587 if (IS_TABLE_CELL(aFrameType
) &&
588 (mFlags
.mSpecialHeightReflow
||
589 (frame
->FirstInFlow()->GetStateBits() &
590 NS_TABLE_CELL_HAD_SPECIAL_REFLOW
)) &&
591 (frame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
)) {
592 // Need to set the bit on the cell so that
593 // mCBReflowState->IsVResize() is set correctly below when
594 // reflowing descendant.
596 } else if (mCBReflowState
&& !nsLayoutUtils::IsNonWrapperBlock(frame
)) {
597 // XXX Is this problematic for relatively positioned inlines acting
598 // as containing block for absolutely positioned elements?
599 // Possibly; in that case we should at least be checking
600 // NS_SUBTREE_DIRTY, I'd think.
601 SetVResize(mCBReflowState
->IsVResize());
602 } else if (ComputedHeight() == NS_AUTOHEIGHT
) {
603 if (eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode() &&
605 SetVResize(mCBReflowState
->IsVResize());
607 SetVResize(IsHResize());
609 SetVResize(IsVResize() || NS_SUBTREE_DIRTY(frame
));
612 SetVResize(frame
->GetSize().height
!=
613 ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom());
616 bool dependsOnCBHeight
=
617 (mStylePosition
->HeightDependsOnContainer() &&
618 // FIXME: condition this on not-abspos?
619 mStylePosition
->mHeight
.GetUnit() != eStyleUnit_Auto
) ||
620 mStylePosition
->MinHeightDependsOnContainer() ||
621 mStylePosition
->MaxHeightDependsOnContainer() ||
622 mStylePosition
->OffsetHasPercent(NS_SIDE_TOP
) ||
623 mStylePosition
->mOffset
.GetBottomUnit() != eStyleUnit_Auto
||
626 if (mStyleText
->mLineHeight
.GetUnit() == eStyleUnit_Enumerated
) {
627 NS_ASSERTION(mStyleText
->mLineHeight
.GetIntValue() ==
628 NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT
,
629 "bad line-height value");
631 // line-height depends on block height
632 frame
->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
633 // but only on containing blocks if this frame is not a suitable block
634 dependsOnCBHeight
|= !nsLayoutUtils::IsNonWrapperBlock(frame
);
637 // If we're the descendant of a table cell that performs special height
638 // reflows and we could be the child that requires them, always set
639 // the vertical resize in case this is the first pass before the
640 // special height reflow. However, don't do this if it actually is
641 // the special height reflow, since in that case it will already be
642 // set correctly above if we need it set.
643 if (!IsVResize() && mCBReflowState
&&
644 (IS_TABLE_CELL(mCBReflowState
->frame
->GetType()) ||
645 mCBReflowState
->mFlags
.mHeightDependsOnAncestorCell
) &&
646 !mCBReflowState
->mFlags
.mSpecialHeightReflow
&&
649 mFlags
.mHeightDependsOnAncestorCell
= true;
652 // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed.
654 // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT|
655 // &&ed with the percentage height check. However, this doesn't get
656 // along with table special height reflows, since a special height
657 // reflow (a quirk that makes such percentage heights work on children
658 // of table cells) can cause not just a single percentage height to
659 // become fixed, but an entire descendant chain of percentage heights
661 if (dependsOnCBHeight
&& mCBReflowState
) {
662 const nsHTMLReflowState
*rs
= this;
663 bool hitCBReflowState
= false;
665 rs
= rs
->parentReflowState
;
670 if (rs
->frame
->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT
)
671 break; // no need to go further
672 rs
->frame
->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
674 // Keep track of whether we've hit the containing block, because
675 // we need to go at least that far.
676 if (rs
== mCBReflowState
) {
677 hitCBReflowState
= true;
680 } while (!hitCBReflowState
||
681 (eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode() &&
682 !IsQuirkContainingBlockHeight(rs
, rs
->frame
->GetType())));
683 // Note: We actually don't need to set the
684 // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases
685 // where we hit the early break statements in
686 // CalcQuirkContainingBlockHeight. But it doesn't hurt
687 // us to set the bit in these cases.
690 if (frame
->GetStateBits() & NS_FRAME_IS_DIRTY
) {
691 // If we're reflowing everything, then we'll find out if we need
693 frame
->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT
);
699 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState
* aReflowState
)
701 const nsHTMLReflowState
* rs
= aReflowState
->mCBReflowState
;
704 return rs
->ComputedWidth();
708 nsHTMLReflowState::InitFrameType(nsIAtom
* aFrameType
)
710 const nsStyleDisplay
*disp
= mStyleDisplay
;
711 nsCSSFrameType frameType
;
713 // Section 9.7 of the CSS2 spec indicates that absolute position
714 // takes precedence over float which takes precedence over display.
715 // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
716 // Make sure the frame was actually moved out of the flow, and don't
717 // just assume what the style says, because we might not have had a
718 // useful float/absolute containing block
720 DISPLAY_INIT_TYPE(frame
, this);
722 if (aFrameType
== nsGkAtoms::tableFrame
) {
723 mFrameType
= NS_CSS_FRAME_TYPE_BLOCK
;
727 NS_ASSERTION(frame
->StyleDisplay()->IsAbsolutelyPositionedStyle() ==
728 disp
->IsAbsolutelyPositionedStyle(),
729 "Unexpected position style");
730 NS_ASSERTION(frame
->StyleDisplay()->IsFloatingStyle() ==
731 disp
->IsFloatingStyle(), "Unexpected float style");
732 if (frame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
) {
733 if (disp
->IsAbsolutelyPositioned(frame
)) {
734 frameType
= NS_CSS_FRAME_TYPE_ABSOLUTE
;
735 //XXXfr hack for making frames behave properly when in overflow container lists
736 // see bug 154892; need to revisit later
737 if (frame
->GetPrevInFlow())
738 frameType
= NS_CSS_FRAME_TYPE_BLOCK
;
740 else if (disp
->IsFloating(frame
)) {
741 frameType
= NS_CSS_FRAME_TYPE_FLOATING
;
743 NS_ASSERTION(disp
->mDisplay
== NS_STYLE_DISPLAY_POPUP
,
744 "unknown out of flow frame type");
745 frameType
= NS_CSS_FRAME_TYPE_UNKNOWN
;
749 switch (GetDisplay()) {
750 case NS_STYLE_DISPLAY_BLOCK
:
751 case NS_STYLE_DISPLAY_LIST_ITEM
:
752 case NS_STYLE_DISPLAY_TABLE
:
753 case NS_STYLE_DISPLAY_TABLE_CAPTION
:
754 case NS_STYLE_DISPLAY_FLEX
:
755 case NS_STYLE_DISPLAY_GRID
:
756 case NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER
:
757 frameType
= NS_CSS_FRAME_TYPE_BLOCK
;
760 case NS_STYLE_DISPLAY_INLINE
:
761 case NS_STYLE_DISPLAY_INLINE_BLOCK
:
762 case NS_STYLE_DISPLAY_INLINE_TABLE
:
763 case NS_STYLE_DISPLAY_INLINE_BOX
:
764 case NS_STYLE_DISPLAY_INLINE_XUL_GRID
:
765 case NS_STYLE_DISPLAY_INLINE_STACK
:
766 case NS_STYLE_DISPLAY_INLINE_FLEX
:
767 case NS_STYLE_DISPLAY_INLINE_GRID
:
768 case NS_STYLE_DISPLAY_RUBY
:
769 case NS_STYLE_DISPLAY_RUBY_BASE
:
770 case NS_STYLE_DISPLAY_RUBY_TEXT
:
771 case NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER
:
772 frameType
= NS_CSS_FRAME_TYPE_INLINE
;
775 case NS_STYLE_DISPLAY_TABLE_CELL
:
776 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP
:
777 case NS_STYLE_DISPLAY_TABLE_COLUMN
:
778 case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP
:
779 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP
:
780 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP
:
781 case NS_STYLE_DISPLAY_TABLE_ROW
:
782 frameType
= NS_CSS_FRAME_TYPE_INTERNAL_TABLE
;
785 case NS_STYLE_DISPLAY_NONE
:
787 frameType
= NS_CSS_FRAME_TYPE_UNKNOWN
;
792 // See if the frame is replaced
793 if (frame
->IsFrameOfType(nsIFrame::eReplacedContainsBlock
)) {
794 frameType
= NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType
);
795 } else if (frame
->IsFrameOfType(nsIFrame::eReplaced
)) {
796 frameType
= NS_FRAME_REPLACED(frameType
);
799 mFrameType
= frameType
;
803 nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection
,
805 nscoord aContainingBlockWidth
,
806 nscoord aContainingBlockHeight
,
807 nsMargin
& aComputedOffsets
)
809 const nsStylePosition
* position
= aFrame
->StylePosition();
811 // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
812 // and 'right' moves the boxes to the left. The computed values are always:
814 bool leftIsAuto
= eStyleUnit_Auto
== position
->mOffset
.GetLeftUnit();
815 bool rightIsAuto
= eStyleUnit_Auto
== position
->mOffset
.GetRightUnit();
817 // If neither 'left' not 'right' are auto, then we're over-constrained and
818 // we ignore one of them
819 if (!leftIsAuto
&& !rightIsAuto
) {
820 if (aCBDirection
== NS_STYLE_DIRECTION_RTL
) {
829 // If both are 'auto' (their initial values), the computed values are 0
830 aComputedOffsets
.left
= aComputedOffsets
.right
= 0;
832 // 'Right' isn't 'auto' so compute its value
833 aComputedOffsets
.right
= nsLayoutUtils::
834 ComputeCBDependentValue(aContainingBlockWidth
,
835 position
->mOffset
.GetRight());
837 // Computed value for 'left' is minus the value of 'right'
838 aComputedOffsets
.left
= -aComputedOffsets
.right
;
842 NS_ASSERTION(rightIsAuto
, "unexpected specified constraint");
844 // 'Left' isn't 'auto' so compute its value
845 aComputedOffsets
.left
= nsLayoutUtils::
846 ComputeCBDependentValue(aContainingBlockWidth
,
847 position
->mOffset
.GetLeft());
849 // Computed value for 'right' is minus the value of 'left'
850 aComputedOffsets
.right
= -aComputedOffsets
.left
;
853 // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
854 // move relatively positioned elements up and down. They also must be each
856 bool topIsAuto
= eStyleUnit_Auto
== position
->mOffset
.GetTopUnit();
857 bool bottomIsAuto
= eStyleUnit_Auto
== position
->mOffset
.GetBottomUnit();
859 // Check for percentage based values and a containing block height that
860 // depends on the content height. Treat them like 'auto'
861 if (NS_AUTOHEIGHT
== aContainingBlockHeight
) {
862 if (position
->OffsetHasPercent(NS_SIDE_TOP
)) {
865 if (position
->OffsetHasPercent(NS_SIDE_BOTTOM
)) {
870 // If neither is 'auto', 'bottom' is ignored
871 if (!topIsAuto
&& !bottomIsAuto
) {
877 // If both are 'auto' (their initial values), the computed values are 0
878 aComputedOffsets
.top
= aComputedOffsets
.bottom
= 0;
880 // 'Bottom' isn't 'auto' so compute its value
881 aComputedOffsets
.bottom
= nsLayoutUtils::
882 ComputeHeightDependentValue(aContainingBlockHeight
,
883 position
->mOffset
.GetBottom());
885 // Computed value for 'top' is minus the value of 'bottom'
886 aComputedOffsets
.top
= -aComputedOffsets
.bottom
;
890 NS_ASSERTION(bottomIsAuto
, "unexpected specified constraint");
892 // 'Top' isn't 'auto' so compute its value
893 aComputedOffsets
.top
= nsLayoutUtils::
894 ComputeHeightDependentValue(aContainingBlockHeight
,
895 position
->mOffset
.GetTop());
897 // Computed value for 'bottom' is minus the value of 'top'
898 aComputedOffsets
.bottom
= -aComputedOffsets
.top
;
902 FrameProperties props
= aFrame
->Properties();
903 nsMargin
* offsets
= static_cast<nsMargin
*>
904 (props
.Get(nsIFrame::ComputedOffsetProperty()));
906 *offsets
= aComputedOffsets
;
908 props
.Set(nsIFrame::ComputedOffsetProperty(),
909 new nsMargin(aComputedOffsets
));
914 nsHTMLReflowState::ApplyRelativePositioning(nsIFrame
* aFrame
,
915 const nsMargin
& aComputedOffsets
,
918 if (!aFrame
->IsRelativelyPositioned()) {
919 NS_ASSERTION(!aFrame
->Properties().Get(nsIFrame::NormalPositionProperty()),
920 "We assume that changing the 'position' property causes "
921 "frame reconstruction. If that ever changes, this code "
923 "props.Delete(nsIFrame::NormalPositionProperty())");
927 // Store the normal position
928 FrameProperties props
= aFrame
->Properties();
929 nsPoint
* normalPosition
= static_cast<nsPoint
*>
930 (props
.Get(nsIFrame::NormalPositionProperty()));
931 if (normalPosition
) {
932 *normalPosition
= *aPosition
;
934 props
.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition
));
937 const nsStyleDisplay
* display
= aFrame
->StyleDisplay();
938 if (NS_STYLE_POSITION_RELATIVE
== display
->mPosition
) {
939 *aPosition
+= nsPoint(aComputedOffsets
.left
, aComputedOffsets
.top
);
940 } else if (NS_STYLE_POSITION_STICKY
== display
->mPosition
&&
941 !aFrame
->GetNextContinuation() &&
942 !aFrame
->GetPrevContinuation() &&
943 !(aFrame
->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT
)) {
944 // Sticky positioning for elements with multiple frames needs to be
945 // computed all at once. We can't safely do that here because we might be
946 // partway through (re)positioning the frames, so leave it until the scroll
947 // container reflows and calls StickyScrollContainer::UpdatePositions.
948 // For single-frame sticky positioned elements, though, go ahead and apply
949 // it now to avoid unnecessary overflow updates later.
950 StickyScrollContainer
* ssc
=
951 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame
);
953 *aPosition
= ssc
->ComputePosition(aFrame
);
959 nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame
* aFrame
,
960 nscoord
& aCBLeftEdge
,
963 aFrame
= aFrame
->GetContainingBlock();
964 NS_ASSERTION(aFrame
!= frame
, "How did that happen?");
966 /* Now aFrame is the containing block we want */
968 /* Check whether the containing block is currently being reflowed.
969 If so, use the info from the reflow state. */
970 const nsHTMLReflowState
* state
;
971 if (aFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
) {
972 for (state
= parentReflowState
; state
&& state
->frame
!= aFrame
;
973 state
= state
->parentReflowState
) {
981 aCBLeftEdge
= state
->ComputedPhysicalBorderPadding().left
;
982 aCBWidth
= state
->ComputedWidth();
984 /* Didn't find a reflow state for aFrame. Just compute the information we
985 want, on the assumption that aFrame already knows its size. This really
986 ought to be true by now. */
987 NS_ASSERTION(!(aFrame
->GetStateBits() & NS_FRAME_IN_REFLOW
),
988 "aFrame shouldn't be in reflow; we'll lie if it is");
989 nsMargin borderPadding
= aFrame
->GetUsedBorderAndPadding();
990 aCBLeftEdge
= borderPadding
.left
;
991 aCBWidth
= aFrame
->GetSize().width
- borderPadding
.LeftRight();
997 // When determining the hypothetical box that would have been if the element
998 // had been in the flow we may not be able to exactly determine both the left
999 // and right edges. For example, if the element is a non-replaced inline-level
1000 // element we would have to reflow it in order to determine it desired width.
1001 // In that case depending on the progression direction either the left or
1002 // right edge would be marked as not being exact
1003 struct nsHypotheticalBox
{
1004 // offsets from left edge of containing block (which is a padding edge)
1005 nscoord mLeft
, mRight
;
1006 // offset from top edge of containing block (which is a padding edge)
1009 bool mLeftIsExact
, mRightIsExact
;
1012 nsHypotheticalBox() {
1014 mLeftIsExact
= mRightIsExact
= false;
1020 GetIntrinsicSizeFor(nsIFrame
* aFrame
, nsSize
& aIntrinsicSize
, nsIAtom
* aFrameType
)
1022 // See if it is an image frame
1023 bool success
= false;
1025 // Currently the only type of replaced frame that we can get the intrinsic
1026 // size for is an image frame
1027 // XXX We should add back the GetReflowMetrics() function and one of the
1028 // things should be the intrinsic size...
1029 if (aFrameType
== nsGkAtoms::imageFrame
) {
1030 nsImageFrame
* imageFrame
= (nsImageFrame
*)aFrame
;
1032 if (NS_SUCCEEDED(imageFrame
->GetIntrinsicImageSize(aIntrinsicSize
))) {
1033 success
= (aIntrinsicSize
!= nsSize(0, 0));
1040 * aInsideBoxSizing returns the part of the horizontal padding, border,
1041 * and margin that goes inside the edge given by box-sizing;
1042 * aOutsideBoxSizing returns the rest.
1045 nsHTMLReflowState::CalculateHorizBorderPaddingMargin(
1046 nscoord aContainingBlockWidth
,
1047 nscoord
* aInsideBoxSizing
,
1048 nscoord
* aOutsideBoxSizing
)
1050 const nsMargin
& border
= mStyleBorder
->GetComputedBorder();
1051 nsMargin padding
, margin
;
1053 // See if the style system can provide us the padding directly
1054 if (!mStylePadding
->GetPadding(padding
)) {
1055 // We have to compute the left and right values
1056 padding
.left
= nsLayoutUtils::
1057 ComputeCBDependentValue(aContainingBlockWidth
,
1058 mStylePadding
->mPadding
.GetLeft());
1059 padding
.right
= nsLayoutUtils::
1060 ComputeCBDependentValue(aContainingBlockWidth
,
1061 mStylePadding
->mPadding
.GetRight());
1064 // See if the style system can provide us the margin directly
1065 if (!mStyleMargin
->GetMargin(margin
)) {
1066 // We have to compute the left and right values
1067 if (eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetLeftUnit()) {
1068 // XXX FIXME (or does CalculateBlockSideMargins do this?)
1069 margin
.left
= 0; // just ignore
1071 margin
.left
= nsLayoutUtils::
1072 ComputeCBDependentValue(aContainingBlockWidth
,
1073 mStyleMargin
->mMargin
.GetLeft());
1075 if (eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetRightUnit()) {
1076 // XXX FIXME (or does CalculateBlockSideMargins do this?)
1077 margin
.right
= 0; // just ignore
1079 margin
.right
= nsLayoutUtils::
1080 ComputeCBDependentValue(aContainingBlockWidth
,
1081 mStyleMargin
->mMargin
.GetRight());
1086 padding
.LeftRight() + border
.LeftRight() + margin
.LeftRight();
1088 switch (mStylePosition
->mBoxSizing
) {
1089 case NS_STYLE_BOX_SIZING_BORDER
:
1090 inside
+= border
.LeftRight();
1092 case NS_STYLE_BOX_SIZING_PADDING
:
1093 inside
+= padding
.LeftRight();
1096 *aInsideBoxSizing
= inside
;
1097 *aOutsideBoxSizing
= outside
;
1102 * Returns true iff a pre-order traversal of the normal child
1103 * frames rooted at aFrame finds no non-empty frame before aDescendant.
1105 static bool AreAllEarlierInFlowFramesEmpty(nsIFrame
* aFrame
,
1106 nsIFrame
* aDescendant
, bool* aFound
) {
1107 if (aFrame
== aDescendant
) {
1111 if (!aFrame
->IsSelfEmpty()) {
1115 for (nsIFrame
* f
= aFrame
->GetFirstPrincipalChild(); f
; f
= f
->GetNextSibling()) {
1116 bool allEmpty
= AreAllEarlierInFlowFramesEmpty(f
, aDescendant
, aFound
);
1117 if (*aFound
|| !allEmpty
) {
1125 // Calculate the hypothetical box that the element would have if it were in
1126 // the flow. The values returned are relative to the padding edge of the
1127 // absolute containing block
1128 // aContainingBlock is the placeholder's containing block (XXX rename it?)
1129 // cbrs->frame is the actual containing block
1131 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext
* aPresContext
,
1132 nsIFrame
* aPlaceholderFrame
,
1133 nsIFrame
* aContainingBlock
,
1134 nscoord aBlockLeftContentEdge
,
1135 nscoord aBlockContentWidth
,
1136 const nsHTMLReflowState
* cbrs
,
1137 nsHypotheticalBox
& aHypotheticalBox
,
1138 nsIAtom
* aFrameType
)
1140 NS_ASSERTION(mStyleDisplay
->mOriginalDisplay
!= NS_STYLE_DISPLAY_NONE
,
1141 "mOriginalDisplay has not been properly initialized");
1143 // If it's a replaced element and it has a 'auto' value for 'width', see if we
1144 // can get the intrinsic size. This will allow us to exactly determine both the
1145 // left and right edges
1146 bool isAutoWidth
= mStylePosition
->mWidth
.GetUnit() == eStyleUnit_Auto
;
1147 nsSize intrinsicSize
;
1148 bool knowIntrinsicSize
= false;
1149 if (NS_FRAME_IS_REPLACED(mFrameType
) && isAutoWidth
) {
1150 // See if we can get the intrinsic size of the element
1151 knowIntrinsicSize
= GetIntrinsicSizeFor(frame
, intrinsicSize
, aFrameType
);
1154 // See if we can calculate what the box width would have been if the
1155 // element had been in the flow
1157 bool knowBoxWidth
= false;
1158 if ((NS_STYLE_DISPLAY_INLINE
== mStyleDisplay
->mOriginalDisplay
) &&
1159 !NS_FRAME_IS_REPLACED(mFrameType
)) {
1160 // For non-replaced inline-level elements the 'width' property doesn't apply,
1161 // so we don't know what the width would have been without reflowing it
1164 // It's either a replaced inline-level element or a block-level element
1166 // Determine the total amount of horizontal border/padding/margin that
1167 // the element would have had if it had been in the flow. Note that we
1168 // ignore any 'auto' and 'inherit' values
1169 nscoord insideBoxSizing
, outsideBoxSizing
;
1170 CalculateHorizBorderPaddingMargin(aBlockContentWidth
,
1171 &insideBoxSizing
, &outsideBoxSizing
);
1173 if (NS_FRAME_IS_REPLACED(mFrameType
) && isAutoWidth
) {
1174 // It's a replaced element with an 'auto' width so the box width is
1175 // its intrinsic size plus any border/padding/margin
1176 if (knowIntrinsicSize
) {
1177 boxWidth
= intrinsicSize
.width
+ outsideBoxSizing
+ insideBoxSizing
;
1178 knowBoxWidth
= true;
1181 } else if (isAutoWidth
) {
1182 // The box width is the containing block width
1183 boxWidth
= aBlockContentWidth
;
1184 knowBoxWidth
= true;
1187 // We need to compute it. It's important we do this, because if it's
1188 // percentage based this computed value may be different from the computed
1189 // value calculated using the absolute containing block width
1190 boxWidth
= ComputeWidthValue(aBlockContentWidth
,
1191 insideBoxSizing
, outsideBoxSizing
,
1192 mStylePosition
->mWidth
) +
1193 insideBoxSizing
+ outsideBoxSizing
;
1194 knowBoxWidth
= true;
1198 // Get the 'direction' of the block
1199 const nsStyleVisibility
* blockVis
= aContainingBlock
->StyleVisibility();
1201 // Get the placeholder x-offset and y-offset in the coordinate
1202 // space of its containing block
1203 // XXXbz the placeholder is not fully reflowed yet if our containing block is
1204 // relatively positioned...
1205 nsPoint placeholderOffset
= aPlaceholderFrame
->GetOffsetTo(aContainingBlock
);
1207 // First, determine the hypothetical box's mTop. We want to check the
1208 // content insertion frame of aContainingBlock for block-ness, but make
1209 // sure to compute all coordinates in the coordinate system of
1210 // aContainingBlock.
1211 nsBlockFrame
* blockFrame
=
1212 nsLayoutUtils::GetAsBlock(aContainingBlock
->GetContentInsertionFrame());
1214 nscoord blockYOffset
= blockFrame
->GetOffsetTo(aContainingBlock
).y
;
1216 nsBlockInFlowLineIterator
iter(blockFrame
, aPlaceholderFrame
, &isValid
);
1218 // Give up. We're probably dealing with somebody using
1219 // position:absolute inside native-anonymous content anyway.
1220 aHypotheticalBox
.mTop
= placeholderOffset
.y
;
1222 NS_ASSERTION(iter
.GetContainer() == blockFrame
,
1223 "Found placeholder in wrong block!");
1224 nsBlockFrame::line_iterator lineBox
= iter
.GetLine();
1226 // How we determine the hypothetical box depends on whether the element
1227 // would have been inline-level or block-level
1228 if (mStyleDisplay
->IsOriginalDisplayInlineOutsideStyle()) {
1229 // Use the top of the inline box which the placeholder lives in
1230 // as the hypothetical box's top.
1231 aHypotheticalBox
.mTop
= lineBox
->GetPhysicalBounds().y
+ blockYOffset
;
1233 // The element would have been block-level which means it would
1234 // be below the line containing the placeholder frame, unless
1235 // all the frames before it are empty. In that case, it would
1236 // have been just before this line.
1237 // XXXbz the line box is not fully reflowed yet if our
1238 // containing block is relatively positioned...
1239 if (lineBox
!= iter
.End()) {
1240 nsIFrame
* firstFrame
= lineBox
->mFirstChild
;
1242 bool allEmpty
= true;
1243 while (firstFrame
) { // See bug 223064
1244 allEmpty
= AreAllEarlierInFlowFramesEmpty(firstFrame
,
1245 aPlaceholderFrame
, &found
);
1246 if (found
|| !allEmpty
)
1248 firstFrame
= firstFrame
->GetNextSibling();
1250 NS_ASSERTION(firstFrame
, "Couldn't find placeholder!");
1253 // The top of the hypothetical box is the top of the line
1254 // containing the placeholder, since there is nothing in the
1255 // line before our placeholder except empty frames.
1256 aHypotheticalBox
.mTop
= lineBox
->GetPhysicalBounds().y
+ blockYOffset
;
1258 // The top of the hypothetical box is just below the line
1259 // containing the placeholder.
1260 aHypotheticalBox
.mTop
= lineBox
->GetPhysicalBounds().YMost() + blockYOffset
;
1263 // Just use the placeholder's y-offset wrt the containing block
1264 aHypotheticalBox
.mTop
= placeholderOffset
.y
;
1269 // The containing block is not a block, so it's probably something
1270 // like a XUL box, etc.
1271 // Just use the placeholder's y-offset
1272 aHypotheticalBox
.mTop
= placeholderOffset
.y
;
1275 // Second, determine the hypothetical box's mLeft & mRight
1276 // To determine the left and right offsets we need to look at the block's 'direction'
1277 if (NS_STYLE_DIRECTION_LTR
== blockVis
->mDirection
) {
1278 // How we determine the hypothetical box depends on whether the element
1279 // would have been inline-level or block-level
1280 if (mStyleDisplay
->IsOriginalDisplayInlineOutsideStyle()) {
1281 // The placeholder represents the left edge of the hypothetical box
1282 aHypotheticalBox
.mLeft
= placeholderOffset
.x
;
1284 aHypotheticalBox
.mLeft
= aBlockLeftContentEdge
;
1287 aHypotheticalBox
.mLeftIsExact
= true;
1291 aHypotheticalBox
.mRight
= aHypotheticalBox
.mLeft
+ boxWidth
;
1293 aHypotheticalBox
.mRightIsExact
= true;
1296 // We can't compute the right edge because we don't know the desired
1297 // width. So instead use the right content edge of the block parent,
1298 // but remember it's not exact
1299 aHypotheticalBox
.mRight
= aBlockLeftContentEdge
+ aBlockContentWidth
;
1301 aHypotheticalBox
.mRightIsExact
= false;
1306 // The placeholder represents the right edge of the hypothetical box
1307 if (mStyleDisplay
->IsOriginalDisplayInlineOutsideStyle()) {
1308 aHypotheticalBox
.mRight
= placeholderOffset
.x
;
1310 aHypotheticalBox
.mRight
= aBlockLeftContentEdge
+ aBlockContentWidth
;
1313 aHypotheticalBox
.mRightIsExact
= true;
1317 aHypotheticalBox
.mLeft
= aHypotheticalBox
.mRight
- boxWidth
;
1319 aHypotheticalBox
.mLeftIsExact
= true;
1322 // We can't compute the left edge because we don't know the desired
1323 // width. So instead use the left content edge of the block parent,
1324 // but remember it's not exact
1325 aHypotheticalBox
.mLeft
= aBlockLeftContentEdge
;
1327 aHypotheticalBox
.mLeftIsExact
= false;
1333 // The current coordinate space is that of the nearest block to the placeholder.
1334 // Convert to the coordinate space of the absolute containing block
1335 // One weird thing here is that for fixed-positioned elements we want to do
1336 // the conversion incorrectly; specifically we want to ignore any scrolling
1337 // that may have happened;
1339 if (mStyleDisplay
->mPosition
== NS_STYLE_POSITION_FIXED
&&
1340 // Exclude cases inside -moz-transform where fixed is like absolute.
1341 nsLayoutUtils::IsReallyFixedPos(frame
)) {
1342 // In this case, cbrs->frame will likely be an ancestor of
1343 // aContainingBlock, so can just walk our way up the frame tree.
1344 // Make sure to not add positions of frames whose parent is a
1345 // scrollFrame, since we're doing fixed positioning, which assumes
1346 // everything is scrolled to (0,0).
1347 cbOffset
.MoveTo(0, 0);
1349 cbOffset
+= aContainingBlock
->GetPositionIgnoringScrolling();
1350 nsContainerFrame
* parent
= aContainingBlock
->GetParent();
1352 // Oops, our absolute containing block isn't an ancestor of the
1353 // placeholder's containing block. This can happen if the placeholder
1354 // is pushed to a different page in a printing context. 'cbOffset' is
1355 // currently relative to the root frame (aContainingBlock) - so just
1356 // subtract the offset to the absolute containing block to make it
1357 // relative to that.
1358 cbOffset
-= aContainingBlock
->GetOffsetTo(cbrs
->frame
);
1361 aContainingBlock
= parent
;
1362 } while (aContainingBlock
!= cbrs
->frame
);
1364 // XXXldb We need to either ignore scrolling for the absolute
1365 // positioning case too (and take the incompatibility) or figure out
1366 // how to make these positioned elements actually *move* when we
1367 // scroll, and thus avoid the resulting incremental reflow bugs.
1368 cbOffset
= aContainingBlock
->GetOffsetTo(cbrs
->frame
);
1370 aHypotheticalBox
.mLeft
+= cbOffset
.x
;
1371 aHypotheticalBox
.mTop
+= cbOffset
.y
;
1372 aHypotheticalBox
.mRight
+= cbOffset
.x
;
1374 // The specified offsets are relative to the absolute containing block's
1375 // padding edge and our current values are relative to the border edge, so
1377 nsMargin border
= cbrs
->ComputedPhysicalBorderPadding() - cbrs
->ComputedPhysicalPadding();
1378 aHypotheticalBox
.mLeft
-= border
.left
;
1379 aHypotheticalBox
.mRight
-= border
.left
;
1380 aHypotheticalBox
.mTop
-= border
.top
;
1384 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext
* aPresContext
,
1385 const nsHTMLReflowState
* cbrs
,
1386 nscoord containingBlockWidth
,
1387 nscoord containingBlockHeight
,
1388 nsIAtom
* aFrameType
)
1390 NS_PRECONDITION(containingBlockHeight
!= NS_AUTOHEIGHT
,
1391 "containing block height must be constrained");
1393 NS_ASSERTION(aFrameType
!= nsGkAtoms::tableFrame
,
1394 "InitAbsoluteConstraints should not be called on table frames");
1395 NS_ASSERTION(frame
->GetStateBits() & NS_FRAME_OUT_OF_FLOW
,
1396 "Why are we here?");
1398 // Get the placeholder frame
1399 nsIFrame
* placeholderFrame
;
1401 placeholderFrame
= aPresContext
->PresShell()->GetPlaceholderFrameFor(frame
);
1402 NS_ASSERTION(nullptr != placeholderFrame
, "no placeholder frame");
1404 // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
1405 // 'auto', then compute the hypothetical box of where the element would
1406 // have been if it had been in the flow
1407 nsHypotheticalBox hypotheticalBox
;
1408 if (((eStyleUnit_Auto
== mStylePosition
->mOffset
.GetLeftUnit()) &&
1409 (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetRightUnit())) ||
1410 ((eStyleUnit_Auto
== mStylePosition
->mOffset
.GetTopUnit()) &&
1411 (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetBottomUnit()))) {
1412 // Find the nearest containing block frame to the placeholder frame,
1413 // and return its left edge and width.
1414 nscoord cbLeftEdge
, cbWidth
;
1415 nsIFrame
* cbFrame
= GetHypotheticalBoxContainer(placeholderFrame
,
1419 CalculateHypotheticalBox(aPresContext
, placeholderFrame
, cbFrame
,
1420 cbLeftEdge
, cbWidth
, cbrs
, hypotheticalBox
, aFrameType
);
1423 // Initialize the 'left' and 'right' computed offsets
1424 // XXX Handle new 'static-position' value...
1425 bool leftIsAuto
= false, rightIsAuto
= false;
1426 if (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetLeftUnit()) {
1427 ComputedPhysicalOffsets().left
= 0;
1430 ComputedPhysicalOffsets().left
= nsLayoutUtils::
1431 ComputeCBDependentValue(containingBlockWidth
,
1432 mStylePosition
->mOffset
.GetLeft());
1434 if (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetRightUnit()) {
1435 ComputedPhysicalOffsets().right
= 0;
1438 ComputedPhysicalOffsets().right
= nsLayoutUtils::
1439 ComputeCBDependentValue(containingBlockWidth
,
1440 mStylePosition
->mOffset
.GetRight());
1443 // Use the horizontal component of the hypothetical box in the cases
1444 // where it's needed.
1445 if (leftIsAuto
&& rightIsAuto
) {
1446 // Use the direction of the original ("static-position") containing block
1447 // to dictate whether 'left' or 'right' is treated like 'static-position'.
1448 if (NS_STYLE_DIRECTION_LTR
== placeholderFrame
->GetContainingBlock()
1449 ->StyleVisibility()->mDirection
) {
1450 NS_ASSERTION(hypotheticalBox
.mLeftIsExact
, "should always have "
1451 "exact value on containing block's start side");
1452 ComputedPhysicalOffsets().left
= hypotheticalBox
.mLeft
;
1455 NS_ASSERTION(hypotheticalBox
.mRightIsExact
, "should always have "
1456 "exact value on containing block's start side");
1457 ComputedPhysicalOffsets().right
= containingBlockWidth
- hypotheticalBox
.mRight
;
1458 rightIsAuto
= false;
1462 // Initialize the 'top' and 'bottom' computed offsets
1463 bool topIsAuto
= false, bottomIsAuto
= false;
1464 if (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetTopUnit()) {
1465 ComputedPhysicalOffsets().top
= 0;
1468 ComputedPhysicalOffsets().top
= nsLayoutUtils::
1469 ComputeHeightDependentValue(containingBlockHeight
,
1470 mStylePosition
->mOffset
.GetTop());
1472 if (eStyleUnit_Auto
== mStylePosition
->mOffset
.GetBottomUnit()) {
1473 ComputedPhysicalOffsets().bottom
= 0;
1474 bottomIsAuto
= true;
1476 ComputedPhysicalOffsets().bottom
= nsLayoutUtils::
1477 ComputeHeightDependentValue(containingBlockHeight
,
1478 mStylePosition
->mOffset
.GetBottom());
1481 if (topIsAuto
&& bottomIsAuto
) {
1482 // Treat 'top' like 'static-position'
1483 ComputedPhysicalOffsets().top
= hypotheticalBox
.mTop
;
1487 bool widthIsAuto
= eStyleUnit_Auto
== mStylePosition
->mWidth
.GetUnit();
1488 bool heightIsAuto
= eStyleUnit_Auto
== mStylePosition
->mHeight
.GetUnit();
1490 typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags
;
1491 ComputeSizeFlags computeSizeFlags
= ComputeSizeFlags::eDefault
;
1492 if (leftIsAuto
|| rightIsAuto
) {
1494 ComputeSizeFlags(computeSizeFlags
| ComputeSizeFlags::eShrinkWrap
);
1498 AutoMaybeDisableFontInflation
an(frame
);
1500 WritingMode wm
= GetWritingMode();
1501 // Size of the containing block in our writing mode
1502 LogicalSize
cbSize(wm
, nsSize(containingBlockWidth
, containingBlockHeight
));
1504 frame
->ComputeSize(rendContext
, wm
, cbSize
,
1505 cbSize
.ISize(wm
), // XXX or AvailableISize()?
1506 ComputedLogicalMargin().Size(wm
) +
1507 ComputedLogicalOffsets().Size(wm
),
1508 ComputedLogicalBorderPadding().Size(wm
) -
1509 ComputedLogicalPadding().Size(wm
),
1510 ComputedLogicalPadding().Size(wm
),
1512 ComputedISize() = size
.ISize(wm
);
1513 ComputedBSize() = size
.BSize(wm
);
1515 NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
1516 NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE
||
1517 ComputedBSize() >= 0, "Bogus block-size");
1519 // XXX Now that we have ComputeSize, can we condense many of the
1520 // branches off of widthIsAuto?
1523 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1525 // Solve for 'left'.
1527 // XXXldb This, and the corresponding code in
1528 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1529 // we always compute widths.
1530 ComputedPhysicalOffsets().left
= NS_AUTOOFFSET
;
1532 ComputedPhysicalOffsets().left
= containingBlockWidth
- ComputedPhysicalMargin().left
-
1533 ComputedPhysicalBorderPadding().left
- ComputedWidth() - ComputedPhysicalBorderPadding().right
-
1534 ComputedPhysicalMargin().right
- ComputedPhysicalOffsets().right
;
1537 } else if (rightIsAuto
) {
1538 // We know 'left' is not 'auto' anymore thanks to the hypothetical
1540 // Solve for 'right'.
1542 // XXXldb This, and the corresponding code in
1543 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1544 // we always compute widths.
1545 ComputedPhysicalOffsets().right
= NS_AUTOOFFSET
;
1547 ComputedPhysicalOffsets().right
= containingBlockWidth
- ComputedPhysicalOffsets().left
-
1548 ComputedPhysicalMargin().left
- ComputedPhysicalBorderPadding().left
- ComputedWidth() -
1549 ComputedPhysicalBorderPadding().right
- ComputedPhysicalMargin().right
;
1552 // Neither 'left' nor 'right' is 'auto'. However, the width might
1553 // still not fill all the available space (even though we didn't
1554 // shrink-wrap) in case:
1555 // * width was specified
1556 // * we're dealing with a replaced element
1557 // * width was constrained by min-width or max-width.
1559 nscoord availMarginSpace
= containingBlockWidth
-
1560 ComputedPhysicalOffsets().LeftRight() -
1561 ComputedPhysicalMargin().LeftRight() -
1562 ComputedPhysicalBorderPadding().LeftRight() -
1564 bool marginLeftIsAuto
=
1565 eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetLeftUnit();
1566 bool marginRightIsAuto
=
1567 eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetRightUnit();
1569 if (marginLeftIsAuto
) {
1570 if (marginRightIsAuto
) {
1571 if (availMarginSpace
< 0) {
1572 // Note that this case is different from the neither-'auto'
1573 // case below, where the spec says to ignore 'left'/'right'.
1575 NS_STYLE_DIRECTION_RTL
== cbrs
->mStyleVisibility
->mDirection
) {
1576 // Ignore the specified value for 'margin-left'.
1577 ComputedPhysicalMargin().left
= availMarginSpace
;
1579 // Ignore the specified value for 'margin-right'.
1580 ComputedPhysicalMargin().right
= availMarginSpace
;
1583 // Both 'margin-left' and 'margin-right' are 'auto', so they get
1585 ComputedPhysicalMargin().left
= availMarginSpace
/ 2;
1586 ComputedPhysicalMargin().right
= availMarginSpace
- ComputedPhysicalMargin().left
;
1589 // Just 'margin-left' is 'auto'
1590 ComputedPhysicalMargin().left
= availMarginSpace
;
1593 if (marginRightIsAuto
) {
1594 // Just 'margin-right' is 'auto'
1595 ComputedPhysicalMargin().right
= availMarginSpace
;
1597 // We're over-constrained so use the direction of the containing
1598 // block to dictate which value to ignore. (And note that the
1599 // spec says to ignore 'left' or 'right' rather than
1600 // 'margin-left' or 'margin-right'.)
1601 // Note that this case is different from the both-'auto' case
1602 // above, where the spec says to ignore
1603 // 'margin-left'/'margin-right'.
1605 NS_STYLE_DIRECTION_RTL
== cbrs
->mStyleVisibility
->mDirection
) {
1606 // Ignore the specified value for 'left'.
1607 ComputedPhysicalOffsets().left
+= availMarginSpace
;
1609 // Ignore the specified value for 'right'.
1610 ComputedPhysicalOffsets().right
+= availMarginSpace
;
1619 ComputedPhysicalOffsets().top
= NS_AUTOOFFSET
;
1621 ComputedPhysicalOffsets().top
= containingBlockHeight
- ComputedPhysicalMargin().top
-
1622 ComputedPhysicalBorderPadding().top
- ComputedHeight() - ComputedPhysicalBorderPadding().bottom
-
1623 ComputedPhysicalMargin().bottom
- ComputedPhysicalOffsets().bottom
;
1625 } else if (bottomIsAuto
) {
1626 // solve for 'bottom'
1628 ComputedPhysicalOffsets().bottom
= NS_AUTOOFFSET
;
1630 ComputedPhysicalOffsets().bottom
= containingBlockHeight
- ComputedPhysicalOffsets().top
-
1631 ComputedPhysicalMargin().top
- ComputedPhysicalBorderPadding().top
- ComputedHeight() -
1632 ComputedPhysicalBorderPadding().bottom
- ComputedPhysicalMargin().bottom
;
1635 // Neither 'top' nor 'bottom' is 'auto'.
1636 nscoord autoHeight
= containingBlockHeight
-
1637 ComputedPhysicalOffsets().TopBottom() -
1638 ComputedPhysicalMargin().TopBottom() -
1639 ComputedPhysicalBorderPadding().TopBottom();
1640 if (autoHeight
< 0) {
1644 if (ComputedHeight() == NS_UNCONSTRAINEDSIZE
) {
1645 // For non-replaced elements with 'height' auto, the 'height'
1646 // fills the remaining space.
1647 ComputedHeight() = autoHeight
;
1649 // XXX Do these need box-sizing adjustments?
1650 if (ComputedHeight() > ComputedMaxHeight())
1651 ComputedHeight() = ComputedMaxHeight();
1652 if (ComputedHeight() < ComputedMinHeight())
1653 ComputedHeight() = ComputedMinHeight();
1656 // The height might still not fill all the available space in case:
1657 // * height was specified
1658 // * we're dealing with a replaced element
1659 // * height was constrained by min-height or max-height.
1660 nscoord availMarginSpace
= autoHeight
- ComputedHeight();
1661 bool marginTopIsAuto
=
1662 eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetTopUnit();
1663 bool marginBottomIsAuto
=
1664 eStyleUnit_Auto
== mStyleMargin
->mMargin
.GetBottomUnit();
1666 if (marginTopIsAuto
) {
1667 if (marginBottomIsAuto
) {
1668 if (availMarginSpace
< 0) {
1669 // FIXME: Note that the spec doesn't actually say we should do this!
1670 ComputedPhysicalMargin().bottom
= availMarginSpace
;
1672 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1674 ComputedPhysicalMargin().top
= availMarginSpace
/ 2;
1675 ComputedPhysicalMargin().bottom
= availMarginSpace
- ComputedPhysicalMargin().top
;
1678 // Just 'margin-top' is 'auto'
1679 ComputedPhysicalMargin().top
= availMarginSpace
;
1682 if (marginBottomIsAuto
) {
1683 // Just 'margin-bottom' is 'auto'
1684 ComputedPhysicalMargin().bottom
= availMarginSpace
;
1686 // We're over-constrained so ignore the specified value for
1687 // 'bottom'. (And note that the spec says to ignore 'bottom'
1688 // rather than 'margin-bottom'.)
1689 ComputedPhysicalOffsets().bottom
+= availMarginSpace
;
1696 GetVerticalMarginBorderPadding(const nsHTMLReflowState
* aReflowState
)
1699 if (!aReflowState
) return result
;
1701 // zero auto margins
1702 nsMargin margin
= aReflowState
->ComputedPhysicalMargin();
1703 if (NS_AUTOMARGIN
== margin
.top
)
1705 if (NS_AUTOMARGIN
== margin
.bottom
)
1708 result
+= margin
.top
+ margin
.bottom
;
1709 result
+= aReflowState
->ComputedPhysicalBorderPadding().top
+
1710 aReflowState
->ComputedPhysicalBorderPadding().bottom
;
1715 /* Get the height based on the viewport of the containing block specified
1716 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
1717 * This will walk up the chain of containing blocks looking for a computed height
1718 * until it finds the canvas frame, or it encounters a frame that is not a block,
1719 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
1721 * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
1723 * See also IsQuirkContainingBlockHeight.
1726 CalcQuirkContainingBlockHeight(const nsHTMLReflowState
* aCBReflowState
)
1728 const nsHTMLReflowState
* firstAncestorRS
= nullptr; // a candidate for html frame
1729 const nsHTMLReflowState
* secondAncestorRS
= nullptr; // a candidate for body frame
1731 // initialize the default to NS_AUTOHEIGHT as this is the containings block
1732 // computed height when this function is called. It is possible that we
1733 // don't alter this height especially if we are restricted to one level
1734 nscoord result
= NS_AUTOHEIGHT
;
1736 const nsHTMLReflowState
* rs
= aCBReflowState
;
1737 for (; rs
; rs
= rs
->parentReflowState
) {
1738 nsIAtom
* frameType
= rs
->frame
->GetType();
1739 // if the ancestor is auto height then skip it and continue up if it
1740 // is the first block frame and possibly the body/html
1741 if (nsGkAtoms::blockFrame
== frameType
||
1743 nsGkAtoms::XULLabelFrame
== frameType
||
1745 nsGkAtoms::scrollFrame
== frameType
) {
1747 secondAncestorRS
= firstAncestorRS
;
1748 firstAncestorRS
= rs
;
1750 // If the current frame we're looking at is positioned, we don't want to
1751 // go any further (see bug 221784). The behavior we want here is: 1) If
1752 // not auto-height, use this as the percentage base. 2) If auto-height,
1753 // keep looking, unless the frame is positioned.
1754 if (NS_AUTOHEIGHT
== rs
->ComputedHeight()) {
1755 if (rs
->frame
->IsAbsolutelyPositioned()) {
1762 else if (nsGkAtoms::canvasFrame
== frameType
) {
1763 // Always continue on to the height calculation
1765 else if (nsGkAtoms::pageContentFrame
== frameType
) {
1766 nsIFrame
* prevInFlow
= rs
->frame
->GetPrevInFlow();
1767 // only use the page content frame for a height basis if it is the first in flow
1775 // if the ancestor is the page content frame then the percent base is
1776 // the avail height, otherwise it is the computed height
1777 result
= (nsGkAtoms::pageContentFrame
== frameType
)
1778 ? rs
->AvailableHeight() : rs
->ComputedHeight();
1779 // if unconstrained - don't sutract borders - would result in huge height
1780 if (NS_AUTOHEIGHT
== result
) return result
;
1782 // if we got to the canvas or page content frame, then subtract out
1783 // margin/border/padding for the BODY and HTML elements
1784 if ((nsGkAtoms::canvasFrame
== frameType
) ||
1785 (nsGkAtoms::pageContentFrame
== frameType
)) {
1787 result
-= GetVerticalMarginBorderPadding(firstAncestorRS
);
1788 result
-= GetVerticalMarginBorderPadding(secondAncestorRS
);
1791 // make sure the first ancestor is the HTML and the second is the BODY
1792 if (firstAncestorRS
) {
1793 nsIContent
* frameContent
= firstAncestorRS
->frame
->GetContent();
1795 nsIAtom
*contentTag
= frameContent
->Tag();
1796 NS_ASSERTION(contentTag
== nsGkAtoms::html
, "First ancestor is not HTML");
1799 if (secondAncestorRS
) {
1800 nsIContent
* frameContent
= secondAncestorRS
->frame
->GetContent();
1802 nsIAtom
*contentTag
= frameContent
->Tag();
1803 NS_ASSERTION(contentTag
== nsGkAtoms::body
, "Second ancestor is not BODY");
1809 // if we got to the html frame (a block child of the canvas) ...
1810 else if (nsGkAtoms::blockFrame
== frameType
&&
1811 rs
->parentReflowState
&&
1812 nsGkAtoms::canvasFrame
==
1813 rs
->parentReflowState
->frame
->GetType()) {
1814 // ... then subtract out margin/border/padding for the BODY element
1815 result
-= GetVerticalMarginBorderPadding(secondAncestorRS
);
1820 // Make sure not to return a negative height here!
1821 return std::max(result
, 0);
1824 // Called by InitConstraints() to compute the containing block rectangle for
1825 // the element. Handles the special logic for absolutely positioned elements
1827 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext
* aPresContext
,
1828 const nsHTMLReflowState
* aContainingBlockRS
,
1829 nscoord
& aContainingBlockWidth
,
1830 nscoord
& aContainingBlockHeight
)
1832 // Unless the element is absolutely positioned, the containing block is
1833 // formed by the content edge of the nearest block-level ancestor
1834 aContainingBlockWidth
= aContainingBlockRS
->ComputedWidth();
1835 aContainingBlockHeight
= aContainingBlockRS
->ComputedHeight();
1837 // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to
1838 // special case them here.
1839 if (NS_FRAME_GET_TYPE(mFrameType
) == NS_CSS_FRAME_TYPE_ABSOLUTE
||
1840 (frame
->GetType() == nsGkAtoms::tableFrame
&&
1841 frame
->IsAbsolutelyPositioned() &&
1842 (frame
->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW
))) {
1843 // See if the ancestor is block-level or inline-level
1844 if (NS_FRAME_GET_TYPE(aContainingBlockRS
->mFrameType
) == NS_CSS_FRAME_TYPE_INLINE
) {
1845 // Base our size on the actual size of the frame. In cases when this is
1846 // completely bogus (eg initial reflow), this code shouldn't even be
1847 // called, since the code in nsInlineFrame::Reflow will pass in
1848 // the containing block dimensions to our constructor.
1849 // XXXbz we should be taking the in-flows into account too, but
1850 // that's very hard.
1851 nsMargin computedBorder
= aContainingBlockRS
->ComputedPhysicalBorderPadding() -
1852 aContainingBlockRS
->ComputedPhysicalPadding();
1853 aContainingBlockWidth
= aContainingBlockRS
->frame
->GetRect().width
-
1854 computedBorder
.LeftRight();
1855 NS_ASSERTION(aContainingBlockWidth
>= 0,
1856 "Negative containing block width!");
1857 aContainingBlockHeight
= aContainingBlockRS
->frame
->GetRect().height
-
1858 computedBorder
.TopBottom();
1859 NS_ASSERTION(aContainingBlockHeight
>= 0,
1860 "Negative containing block height!");
1862 // If the ancestor is block-level, the containing block is formed by the
1863 // padding edge of the ancestor
1864 aContainingBlockWidth
+= aContainingBlockRS
->ComputedPhysicalPadding().LeftRight();
1865 aContainingBlockHeight
+= aContainingBlockRS
->ComputedPhysicalPadding().TopBottom();
1868 // an element in quirks mode gets a containing block based on looking for a
1869 // parent with a non-auto height if the element has a percent height
1870 // Note: We don't emulate this quirk for percents in calc().
1871 if (NS_AUTOHEIGHT
== aContainingBlockHeight
) {
1872 if (eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode() &&
1873 mStylePosition
->mHeight
.GetUnit() == eStyleUnit_Percent
) {
1874 aContainingBlockHeight
= CalcQuirkContainingBlockHeight(aContainingBlockRS
);
1880 static eNormalLineHeightControl
GetNormalLineHeightCalcControl(void)
1882 if (sNormalLineHeightControl
== eUninitialized
) {
1883 // browser.display.normal_lineheight_calc_control is not user
1884 // changeable, so no need to register callback for it.
1886 Preferences::GetInt("browser.display.normal_lineheight_calc_control",
1887 eNoExternalLeading
);
1888 sNormalLineHeightControl
= static_cast<eNormalLineHeightControl
>(val
);
1890 return sNormalLineHeightControl
;
1894 IsSideCaption(nsIFrame
* aFrame
, const nsStyleDisplay
* aStyleDisplay
)
1896 if (aStyleDisplay
->mDisplay
!= NS_STYLE_DISPLAY_TABLE_CAPTION
)
1898 uint8_t captionSide
= aFrame
->StyleTableBorder()->mCaptionSide
;
1899 return captionSide
== NS_STYLE_CAPTION_SIDE_LEFT
||
1900 captionSide
== NS_STYLE_CAPTION_SIDE_RIGHT
;
1903 static nsFlexContainerFrame
*
1904 GetFlexContainer(nsIFrame
* aFrame
)
1906 nsIFrame
* parent
= aFrame
->GetParent();
1908 parent
->GetType() != nsGkAtoms::flexContainerFrame
) {
1912 return static_cast<nsFlexContainerFrame
*>(parent
);
1915 // Flex items resolve block-axis percentage margin & padding against the flex
1916 // container's block-size (which is the containing block block-size).
1917 // For everything else: the CSS21 spec requires that margin and padding
1918 // percentage values are calculated with respect to the inline-size of the
1919 // containing block, even for margin & padding in the block axis.
1921 BlockDirOffsetPercentBasis(const nsIFrame
* aFrame
,
1922 nscoord aContainingBlockISize
,
1923 nscoord aContainingBlockBSize
)
1925 if (!aFrame
->IsFlexOrGridItem()) {
1926 return aContainingBlockISize
;
1929 if (aContainingBlockBSize
== NS_AUTOHEIGHT
) {
1933 return aContainingBlockBSize
;
1936 // XXX refactor this code to have methods for each set of properties
1937 // we are computing: width,height,line-height; margin; offsets
1940 nsHTMLReflowState::InitConstraints(nsPresContext
* aPresContext
,
1941 nscoord aContainingBlockWidth
,
1942 nscoord aContainingBlockHeight
,
1943 const nsMargin
* aBorder
,
1944 const nsMargin
* aPadding
,
1945 nsIAtom
* aFrameType
)
1947 DISPLAY_INIT_CONSTRAINTS(frame
, this,
1948 aContainingBlockWidth
, aContainingBlockHeight
,
1951 WritingMode wm
= GetWritingMode();
1953 // If this is a reflow root, then set the computed width and
1954 // height equal to the available space
1955 if (nullptr == parentReflowState
|| mFlags
.mDummyParentReflowState
) {
1956 // XXXldb This doesn't mean what it used to!
1957 LogicalSize
cbSize(wm
, nsSize(aContainingBlockWidth
,
1958 aContainingBlockHeight
));
1959 InitOffsets(cbSize
.ISize(wm
),
1960 BlockDirOffsetPercentBasis(frame
, cbSize
.ISize(wm
),
1962 aFrameType
, aBorder
, aPadding
);
1963 // Override mComputedMargin since reflow roots start from the
1964 // frame's boundary, which is inside the margin.
1965 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
1966 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
1968 ComputedWidth() = AvailableWidth() - ComputedPhysicalBorderPadding().LeftRight();
1969 if (ComputedWidth() < 0)
1970 ComputedWidth() = 0;
1971 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE
) {
1972 ComputedHeight() = AvailableHeight() - ComputedPhysicalBorderPadding().TopBottom();
1973 if (ComputedHeight() < 0)
1974 ComputedHeight() = 0;
1976 ComputedHeight() = NS_UNCONSTRAINEDSIZE
;
1979 ComputedMinWidth() = ComputedMinHeight() = 0;
1980 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE
;
1982 // Get the containing block reflow state
1983 const nsHTMLReflowState
* cbrs
= mCBReflowState
;
1984 NS_ASSERTION(nullptr != cbrs
, "no containing block");
1986 // If we weren't given a containing block width and height, then
1988 if (aContainingBlockWidth
== -1) {
1989 ComputeContainingBlockRectangle(aPresContext
, cbrs
, aContainingBlockWidth
,
1990 aContainingBlockHeight
);
1993 // See if the containing block height is based on the size of its
1996 if (NS_AUTOHEIGHT
== aContainingBlockHeight
) {
1997 // See if the containing block is a cell frame which needs
1998 // to use the mComputedHeight of the cell instead of what the cell block passed in.
1999 // XXX It seems like this could lead to bugs with min-height and friends
2000 if (cbrs
->parentReflowState
) {
2001 fType
= cbrs
->frame
->GetType();
2002 if (IS_TABLE_CELL(fType
)) {
2003 // use the cell's computed height
2004 aContainingBlockHeight
= cbrs
->ComputedHeight();
2009 // XXX Might need to also pass the CB height (not width) for page boxes,
2010 // too, if we implement them.
2011 WritingMode cbwm
= mCBReflowState
->GetWritingMode();
2012 LogicalSize
cbSize(cbwm
, nsSize(aContainingBlockWidth
,
2013 aContainingBlockHeight
));
2014 InitOffsets(cbSize
.ISize(cbwm
),
2015 BlockDirOffsetPercentBasis(frame
, cbSize
.ISize(cbwm
),
2016 cbSize
.BSize(cbwm
)),
2017 aFrameType
, aBorder
, aPadding
);
2019 const nsStyleCoord
&height
= mStylePosition
->mHeight
;
2020 nsStyleUnit heightUnit
= height
.GetUnit();
2022 // Check for a percentage based height and a containing block height
2023 // that depends on the content height
2024 // XXX twiddling heightUnit doesn't help anymore
2025 // FIXME Shouldn't we fix that?
2026 if (height
.HasPercent()) {
2027 if (NS_AUTOHEIGHT
== aContainingBlockHeight
) {
2028 // this if clause enables %-height on replaced inline frames,
2029 // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;"
2030 // used to be called exclusively.
2031 if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE
) == mFrameType
||
2032 NS_FRAME_REPLACED_CONTAINS_BLOCK(
2033 NS_CSS_FRAME_TYPE_INLINE
) == mFrameType
) {
2034 // Get the containing block reflow state
2035 NS_ASSERTION(nullptr != cbrs
, "no containing block");
2036 // in quirks mode, get the cb height using the special quirk method
2037 if (eCompatibility_NavQuirks
== aPresContext
->CompatibilityMode()) {
2038 if (!IS_TABLE_CELL(fType
)) {
2039 aContainingBlockHeight
= CalcQuirkContainingBlockHeight(cbrs
);
2040 if (aContainingBlockHeight
== NS_AUTOHEIGHT
) {
2041 heightUnit
= eStyleUnit_Auto
;
2045 heightUnit
= eStyleUnit_Auto
;
2048 // in standard mode, use the cb height. if it's "auto", as will be the case
2049 // by default in BODY, use auto height as per CSS2 spec.
2052 if (NS_AUTOHEIGHT
!= cbrs
->ComputedHeight())
2053 aContainingBlockHeight
= cbrs
->ComputedHeight();
2055 heightUnit
= eStyleUnit_Auto
;
2059 // default to interpreting the height like 'auto'
2060 heightUnit
= eStyleUnit_Auto
;
2065 // Compute our offsets if the element is relatively positioned. We need
2066 // the correct containing block width and height here, which is why we need
2067 // to do it after all the quirks-n-such above. (If the element is sticky
2068 // positioned, we need to wait until the scroll container knows its size,
2069 // so we compute offsets from StickyScrollContainer::UpdatePositions.)
2070 if (mStyleDisplay
->IsRelativelyPositioned(frame
) &&
2071 NS_STYLE_POSITION_RELATIVE
== mStyleDisplay
->mPosition
) {
2072 uint8_t direction
= NS_STYLE_DIRECTION_LTR
;
2073 if (cbrs
&& NS_STYLE_DIRECTION_RTL
== cbrs
->mStyleVisibility
->mDirection
) {
2074 direction
= NS_STYLE_DIRECTION_RTL
;
2076 ComputeRelativeOffsets(direction
, frame
, aContainingBlockWidth
,
2077 aContainingBlockHeight
, ComputedPhysicalOffsets());
2079 // Initialize offsets to 0
2080 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
2083 // Calculate the computed values for min and max properties. Note that
2084 // this MUST come after we've computed our border and padding.
2085 ComputeMinMaxValues(aContainingBlockWidth
, aContainingBlockHeight
, cbrs
);
2087 // Calculate the computed width and height. This varies by frame type
2089 if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE
== mFrameType
) {
2090 // Internal table elements. The rules vary depending on the type.
2091 // Calculate the computed width
2092 bool rowOrRowGroup
= false;
2093 const nsStyleCoord
&width
= mStylePosition
->mWidth
;
2094 nsStyleUnit widthUnit
= width
.GetUnit();
2095 if ((NS_STYLE_DISPLAY_TABLE_ROW
== mStyleDisplay
->mDisplay
) ||
2096 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP
== mStyleDisplay
->mDisplay
)) {
2097 // 'width' property doesn't apply to table rows and row groups
2098 widthUnit
= eStyleUnit_Auto
;
2099 rowOrRowGroup
= true;
2102 // calc() with percentages acts like auto on internal table elements
2103 if (eStyleUnit_Auto
== widthUnit
||
2104 (width
.IsCalcUnit() && width
.CalcHasPercent())) {
2105 ComputedWidth() = AvailableWidth();
2107 if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE
) && !rowOrRowGroup
){
2108 // Internal table elements don't have margins. Only tables and
2109 // cells have border and padding
2110 ComputedWidth() -= ComputedPhysicalBorderPadding().left
+
2111 ComputedPhysicalBorderPadding().right
;
2112 if (ComputedWidth() < 0)
2113 ComputedWidth() = 0;
2115 NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width");
2118 NS_ASSERTION(widthUnit
== mStylePosition
->mWidth
.GetUnit(),
2119 "unexpected width unit change");
2120 ComputedWidth() = ComputeWidthValue(aContainingBlockWidth
,
2121 mStylePosition
->mBoxSizing
,
2122 mStylePosition
->mWidth
);
2125 // Calculate the computed height
2126 if ((NS_STYLE_DISPLAY_TABLE_COLUMN
== mStyleDisplay
->mDisplay
) ||
2127 (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP
== mStyleDisplay
->mDisplay
)) {
2128 // 'height' property doesn't apply to table columns and column groups
2129 heightUnit
= eStyleUnit_Auto
;
2131 // calc() with percentages acts like 'auto' on internal table elements
2132 if (eStyleUnit_Auto
== heightUnit
||
2133 (height
.IsCalcUnit() && height
.CalcHasPercent())) {
2134 ComputedHeight() = NS_AUTOHEIGHT
;
2136 NS_ASSERTION(heightUnit
== mStylePosition
->mHeight
.GetUnit(),
2137 "unexpected height unit change");
2138 ComputedHeight() = ComputeHeightValue(aContainingBlockHeight
,
2139 mStylePosition
->mBoxSizing
,
2140 mStylePosition
->mHeight
);
2143 // Doesn't apply to table elements
2144 ComputedMinWidth() = ComputedMinHeight() = 0;
2145 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE
;
2147 } else if (NS_FRAME_GET_TYPE(mFrameType
) == NS_CSS_FRAME_TYPE_ABSOLUTE
) {
2148 // XXX not sure if this belongs here or somewhere else - cwk
2149 InitAbsoluteConstraints(aPresContext
, cbrs
, aContainingBlockWidth
,
2150 aContainingBlockHeight
, aFrameType
);
2152 AutoMaybeDisableFontInflation
an(frame
);
2154 bool isBlock
= NS_CSS_FRAME_TYPE_BLOCK
== NS_FRAME_GET_TYPE(mFrameType
);
2155 typedef nsIFrame::ComputeSizeFlags ComputeSizeFlags
;
2156 ComputeSizeFlags computeSizeFlags
=
2157 isBlock
? ComputeSizeFlags::eDefault
: ComputeSizeFlags::eShrinkWrap
;
2159 // Make sure legend frames with display:block and width:auto still
2162 ((aFrameType
== nsGkAtoms::legendFrame
&&
2163 frame
->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent
) ||
2164 (aFrameType
== nsGkAtoms::scrollFrame
&&
2165 frame
->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame
))) {
2167 ComputeSizeFlags(computeSizeFlags
| ComputeSizeFlags::eShrinkWrap
);
2170 const nsFlexContainerFrame
* flexContainerFrame
= GetFlexContainer(frame
);
2171 if (flexContainerFrame
) {
2173 ComputeSizeFlags(computeSizeFlags
| ComputeSizeFlags::eShrinkWrap
);
2175 // If we're inside of a flex container that needs to measure our
2176 // auto height, pass that information along to ComputeSize().
2177 if (mFlags
.mIsFlexContainerMeasuringHeight
) {
2179 ComputeSizeFlags(computeSizeFlags
| ComputeSizeFlags::eUseAutoHeight
);
2182 MOZ_ASSERT(!mFlags
.mIsFlexContainerMeasuringHeight
,
2183 "We're not in a flex container, so the flag "
2184 "'mIsFlexContainerMeasuringHeight' shouldn't be set");
2187 LogicalSize
cbSize(wm
, nsSize(aContainingBlockWidth
,
2188 aContainingBlockHeight
));
2189 if (cbSize
.ISize(wm
) == NS_UNCONSTRAINEDSIZE
) {
2190 // For orthogonal flows, where we found a parent orthogonal-limit
2191 // for AvailableISize() in Init(), we'll use the same here as well.
2192 cbSize
.ISize(wm
) = AvailableISize();
2196 frame
->ComputeSize(rendContext
, wm
, cbSize
,
2198 ComputedLogicalMargin().Size(wm
),
2199 ComputedLogicalBorderPadding().Size(wm
) -
2200 ComputedLogicalPadding().Size(wm
),
2201 ComputedLogicalPadding().Size(wm
),
2204 ComputedISize() = size
.ISize(wm
);
2205 ComputedBSize() = size
.BSize(wm
);
2206 NS_ASSERTION(ComputedISize() >= 0, "Bogus inline-size");
2207 NS_ASSERTION(ComputedBSize() == NS_UNCONSTRAINEDSIZE
||
2208 ComputedBSize() >= 0, "Bogus block-size");
2210 // Exclude inline tables and flex items from the block margin calculations
2212 !IsSideCaption(frame
, mStyleDisplay
) &&
2213 mStyleDisplay
->mDisplay
!= NS_STYLE_DISPLAY_INLINE_TABLE
&&
2214 !flexContainerFrame
) {
2215 CalculateBlockSideMargins(aFrameType
);
2222 UpdateProp(FrameProperties
& aProps
,
2223 const FramePropertyDescriptor
* aProperty
,
2225 nsMargin
& aNewValue
)
2228 nsMargin
* propValue
= static_cast<nsMargin
*>(aProps
.Get(aProperty
));
2230 *propValue
= aNewValue
;
2232 aProps
.Set(aProperty
, new nsMargin(aNewValue
));
2235 aProps
.Delete(aProperty
);
2240 nsCSSOffsetState::InitOffsets(nscoord aInlineDirPercentBasis
,
2241 nscoord aBlockDirPercentBasis
,
2242 nsIAtom
* aFrameType
,
2243 const nsMargin
*aBorder
,
2244 const nsMargin
*aPadding
)
2246 DISPLAY_INIT_OFFSETS(frame
, this,
2247 aInlineDirPercentBasis
,
2248 aBlockDirPercentBasis
,
2251 // Since we are in reflow, we don't need to store these properties anymore
2252 // unless they are dependent on width, in which case we store the new value.
2253 nsPresContext
*presContext
= frame
->PresContext();
2254 FrameProperties
props(presContext
->PropertyTable(), frame
);
2255 props
.Delete(nsIFrame::UsedBorderProperty());
2257 // Compute margins from the specified margin style information. These
2258 // become the default computed values, and may be adjusted below
2259 // XXX fix to provide 0,0 for the top&bottom margins for
2260 // inline-non-replaced elements
2261 bool needMarginProp
= ComputeMargin(aInlineDirPercentBasis
,
2262 aBlockDirPercentBasis
);
2263 // XXX We need to include 'auto' horizontal margins in this too!
2264 // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
2265 // to use it even when the margins are all zero (since sometimes
2266 // they get treated as auto)
2267 ::UpdateProp(props
, nsIFrame::UsedMarginProperty(), needMarginProp
,
2268 ComputedPhysicalMargin());
2271 const nsStyleDisplay
*disp
= frame
->StyleDisplay();
2272 bool isThemed
= frame
->IsThemed(disp
);
2273 bool needPaddingProp
;
2276 presContext
->GetTheme()->GetWidgetPadding(presContext
->DeviceContext(),
2277 frame
, disp
->mAppearance
,
2279 ComputedPhysicalPadding().top
= presContext
->DevPixelsToAppUnits(widget
.top
);
2280 ComputedPhysicalPadding().right
= presContext
->DevPixelsToAppUnits(widget
.right
);
2281 ComputedPhysicalPadding().bottom
= presContext
->DevPixelsToAppUnits(widget
.bottom
);
2282 ComputedPhysicalPadding().left
= presContext
->DevPixelsToAppUnits(widget
.left
);
2283 needPaddingProp
= false;
2285 else if (frame
->IsSVGText()) {
2286 ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
2287 needPaddingProp
= false;
2289 else if (aPadding
) { // padding is an input arg
2290 ComputedPhysicalPadding() = *aPadding
;
2291 needPaddingProp
= frame
->StylePadding()->IsWidthDependent() ||
2292 (frame
->GetStateBits() & NS_FRAME_REFLOW_ROOT
);
2295 needPaddingProp
= ComputePadding(aInlineDirPercentBasis
,
2296 aBlockDirPercentBasis
, aFrameType
);
2301 presContext
->GetTheme()->GetWidgetBorder(presContext
->DeviceContext(),
2302 frame
, disp
->mAppearance
,
2304 ComputedPhysicalBorderPadding().top
=
2305 presContext
->DevPixelsToAppUnits(widget
.top
);
2306 ComputedPhysicalBorderPadding().right
=
2307 presContext
->DevPixelsToAppUnits(widget
.right
);
2308 ComputedPhysicalBorderPadding().bottom
=
2309 presContext
->DevPixelsToAppUnits(widget
.bottom
);
2310 ComputedPhysicalBorderPadding().left
=
2311 presContext
->DevPixelsToAppUnits(widget
.left
);
2313 else if (frame
->IsSVGText()) {
2314 ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0);
2316 else if (aBorder
) { // border is an input arg
2317 ComputedPhysicalBorderPadding() = *aBorder
;
2320 ComputedPhysicalBorderPadding() = frame
->StyleBorder()->GetComputedBorder();
2322 ComputedPhysicalBorderPadding() += ComputedPhysicalPadding();
2324 if (aFrameType
== nsGkAtoms::tableFrame
) {
2325 nsTableFrame
*tableFrame
= static_cast<nsTableFrame
*>(frame
);
2327 if (tableFrame
->IsBorderCollapse()) {
2328 // border-collapsed tables don't use any of their padding, and
2329 // only part of their border. We need to do this here before we
2330 // try to do anything like handling 'auto' widths,
2331 // 'box-sizing', or 'auto' margins.
2332 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2333 ComputedPhysicalBorderPadding() = tableFrame
->GetIncludedOuterBCBorder();
2336 // The margin is inherited to the outer table frame via
2337 // the ::-moz-table-outer rule in ua.css.
2338 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
2339 } else if (aFrameType
== nsGkAtoms::scrollbarFrame
) {
2340 // scrollbars may have had their width or height smashed to zero
2341 // by the associated scrollframe, in which case we must not report
2342 // any padding or border.
2343 nsSize
size(frame
->GetSize());
2344 if (size
.width
== 0 || size
.height
== 0) {
2345 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2346 ComputedPhysicalBorderPadding().SizeTo(0,0,0,0);
2349 ::UpdateProp(props
, nsIFrame::UsedPaddingProperty(), needPaddingProp
,
2350 ComputedPhysicalPadding());
2353 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
2355 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
2356 // 'padding-right' + 'border-right-width' + 'margin-right'
2357 // = width of containing block
2359 // Note: the width unit is not auto when this is called
2361 nsHTMLReflowState::CalculateBlockSideMargins(nsIAtom
* aFrameType
)
2363 // Calculations here are done in the containing block's writing mode,
2364 // which is where margins will eventually be applied: we're calculating
2365 // margins that will be used by the container in its inline direction,
2366 // which in the case of an orthogonal contained block will correspond to
2367 // the block direction of this reflow state. So in the orthogonal-flow
2368 // case, "CalculateBlock*Side*Margins" will actually end up adjusting
2369 // the BStart/BEnd margins; those are the "sides" of the block from its
2370 // container's point of view.
2372 mCBReflowState
? mCBReflowState
->GetWritingMode(): GetWritingMode();
2374 nscoord availISizeCBWM
= AvailableSize(cbWM
).ISize(cbWM
);
2375 nscoord computedISizeCBWM
= ComputedSize(cbWM
).ISize(cbWM
);
2376 if (computedISizeCBWM
== NS_UNCONSTRAINEDSIZE
) {
2377 // For orthogonal flows, where we found a parent orthogonal-limit
2378 // for AvailableISize() in Init(), we'll use the same here as well.
2379 computedISizeCBWM
= availISizeCBWM
;
2382 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE
!= computedISizeCBWM
&&
2383 NS_UNCONSTRAINEDSIZE
!= availISizeCBWM
,
2384 "have unconstrained inline-size; this should only result from "
2385 "very large sizes, not attempts at intrinsic inline-size "
2388 LogicalMargin margin
=
2389 ComputedLogicalMargin().ConvertTo(cbWM
, mWritingMode
);
2390 LogicalMargin borderPadding
=
2391 ComputedLogicalBorderPadding().ConvertTo(cbWM
, mWritingMode
);
2392 nscoord sum
= margin
.IStartEnd(cbWM
) +
2393 borderPadding
.IStartEnd(cbWM
) + computedISizeCBWM
;
2394 if (sum
== availISizeCBWM
) {
2395 // The sum is already correct
2399 // Determine the start and end margin values. The isize value
2400 // remains constant while we do this.
2402 // Calculate how much space is available for margins
2403 nscoord availMarginSpace
= availISizeCBWM
- sum
;
2405 // If the available margin space is negative, then don't follow the
2406 // usual overconstraint rules.
2407 if (availMarginSpace
< 0) {
2408 margin
.IEnd(cbWM
) += availMarginSpace
;
2409 SetComputedLogicalMargin(margin
.ConvertTo(mWritingMode
, cbWM
));
2413 // The css2 spec clearly defines how block elements should behave
2414 // in section 10.3.3.
2415 bool isAutoStartMargin
, isAutoEndMargin
;
2416 const nsStyleSides
& styleSides
= mStyleMargin
->mMargin
;
2417 if (cbWM
.IsVertical()) {
2418 if (cbWM
.IsBidiLTR()) {
2419 isAutoStartMargin
= eStyleUnit_Auto
== styleSides
.GetTopUnit();
2420 isAutoEndMargin
= eStyleUnit_Auto
== styleSides
.GetBottomUnit();
2422 isAutoStartMargin
= eStyleUnit_Auto
== styleSides
.GetBottomUnit();
2423 isAutoEndMargin
= eStyleUnit_Auto
== styleSides
.GetTopUnit();
2426 if (cbWM
.IsBidiLTR()) {
2427 isAutoStartMargin
= eStyleUnit_Auto
== styleSides
.GetLeftUnit();
2428 isAutoEndMargin
= eStyleUnit_Auto
== styleSides
.GetRightUnit();
2430 isAutoStartMargin
= eStyleUnit_Auto
== styleSides
.GetRightUnit();
2431 isAutoEndMargin
= eStyleUnit_Auto
== styleSides
.GetLeftUnit();
2434 if (!isAutoStartMargin
&& !isAutoEndMargin
) {
2435 // Neither margin is 'auto' so we're over constrained. Use the
2436 // 'direction' property of the parent to tell which margin to
2438 // First check if there is an HTML alignment that we should honor
2439 const nsHTMLReflowState
* prs
= parentReflowState
;
2440 if (aFrameType
== nsGkAtoms::tableFrame
) {
2441 NS_ASSERTION(prs
->frame
->GetType() == nsGkAtoms::tableOuterFrame
,
2442 "table not inside outer table");
2443 // Center the table within the outer table based on the alignment
2444 // of the outer table's parent.
2445 prs
= prs
->parentReflowState
;
2448 (prs
->mStyleText
->mTextAlign
== NS_STYLE_TEXT_ALIGN_MOZ_LEFT
||
2449 prs
->mStyleText
->mTextAlign
== NS_STYLE_TEXT_ALIGN_MOZ_CENTER
||
2450 prs
->mStyleText
->mTextAlign
== NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
)) {
2451 if (prs
->mWritingMode
.IsBidiLTR()) {
2453 prs
->mStyleText
->mTextAlign
!= NS_STYLE_TEXT_ALIGN_MOZ_LEFT
;
2455 prs
->mStyleText
->mTextAlign
!= NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
;
2458 prs
->mStyleText
->mTextAlign
!= NS_STYLE_TEXT_ALIGN_MOZ_RIGHT
;
2460 prs
->mStyleText
->mTextAlign
!= NS_STYLE_TEXT_ALIGN_MOZ_LEFT
;
2463 // Otherwise apply the CSS rules, and ignore one margin by forcing
2464 // it to 'auto', depending on 'direction'.
2466 isAutoEndMargin
= true;
2470 // Logic which is common to blocks and tables
2471 // The computed margins need not be zero because the 'auto' could come from
2472 // overconstraint or from HTML alignment so values need to be accumulated
2474 if (isAutoStartMargin
) {
2475 if (isAutoEndMargin
) {
2476 // Both margins are 'auto' so the computed addition should be equal
2477 nscoord forStart
= availMarginSpace
/ 2;
2478 margin
.IStart(cbWM
) += forStart
;
2479 margin
.IEnd(cbWM
) += availMarginSpace
- forStart
;
2481 margin
.IStart(cbWM
) += availMarginSpace
;
2483 } else if (isAutoEndMargin
) {
2484 margin
.IEnd(cbWM
) += availMarginSpace
;
2486 SetComputedLogicalMargin(margin
.ConvertTo(mWritingMode
, cbWM
));
2489 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight
2490 // For "normal" we use the font's normal line height (em height + leading).
2491 // If both internal leading and external leading specified by font itself
2492 // are zeros, we should compensate this by creating extra (external) leading
2493 // in eCompensateLeading mode. This is necessary because without this
2494 // compensation, normal line height might looks too tight.
2496 // For risk management, we use preference to control the behavior, and
2497 // eNoExternalLeading is the old behavior.
2499 GetNormalLineHeight(nsFontMetrics
* aFontMetrics
)
2501 NS_PRECONDITION(nullptr != aFontMetrics
, "no font metrics");
2503 nscoord normalLineHeight
;
2505 nscoord externalLeading
= aFontMetrics
->ExternalLeading();
2506 nscoord internalLeading
= aFontMetrics
->InternalLeading();
2507 nscoord emHeight
= aFontMetrics
->EmHeight();
2508 switch (GetNormalLineHeightCalcControl()) {
2509 case eIncludeExternalLeading
:
2510 normalLineHeight
= emHeight
+ internalLeading
+ externalLeading
;
2512 case eCompensateLeading
:
2513 if (!internalLeading
&& !externalLeading
)
2514 normalLineHeight
= NSToCoordRound(emHeight
* NORMAL_LINE_HEIGHT_FACTOR
);
2516 normalLineHeight
= emHeight
+ internalLeading
+ externalLeading
;
2519 //case eNoExternalLeading:
2520 normalLineHeight
= emHeight
+ internalLeading
;
2522 return normalLineHeight
;
2525 static inline nscoord
2526 ComputeLineHeight(nsStyleContext
* aStyleContext
,
2527 nscoord aBlockBSize
,
2528 float aFontSizeInflation
)
2530 const nsStyleCoord
& lhCoord
= aStyleContext
->StyleText()->mLineHeight
;
2532 if (lhCoord
.GetUnit() == eStyleUnit_Coord
) {
2533 nscoord result
= lhCoord
.GetCoordValue();
2534 if (aFontSizeInflation
!= 1.0f
) {
2535 result
= NSToCoordRound(result
* aFontSizeInflation
);
2540 if (lhCoord
.GetUnit() == eStyleUnit_Factor
)
2541 // For factor units the computed value of the line-height property
2542 // is found by multiplying the factor by the font's computed size
2543 // (adjusted for min-size prefs and text zoom).
2544 return NSToCoordRound(lhCoord
.GetFactorValue() * aFontSizeInflation
*
2545 aStyleContext
->StyleFont()->mFont
.size
);
2547 NS_ASSERTION(lhCoord
.GetUnit() == eStyleUnit_Normal
||
2548 lhCoord
.GetUnit() == eStyleUnit_Enumerated
,
2549 "bad line-height unit");
2551 if (lhCoord
.GetUnit() == eStyleUnit_Enumerated
) {
2552 NS_ASSERTION(lhCoord
.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT
,
2553 "bad line-height value");
2554 if (aBlockBSize
!= NS_AUTOHEIGHT
) {
2559 nsRefPtr
<nsFontMetrics
> fm
;
2560 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext
,
2562 aFontSizeInflation
);
2563 return GetNormalLineHeight(fm
);
2567 nsHTMLReflowState::CalcLineHeight() const
2569 nscoord blockBSize
=
2570 nsLayoutUtils::IsNonWrapperBlock(frame
) ? ComputedBSize() :
2571 (mCBReflowState
? mCBReflowState
->ComputedBSize() : NS_AUTOHEIGHT
);
2573 return CalcLineHeight(frame
->GetContent(), frame
->StyleContext(), blockBSize
,
2574 nsLayoutUtils::FontSizeInflationFor(frame
));
2577 /* static */ nscoord
2578 nsHTMLReflowState::CalcLineHeight(nsIContent
* aContent
,
2579 nsStyleContext
* aStyleContext
,
2580 nscoord aBlockBSize
,
2581 float aFontSizeInflation
)
2583 NS_PRECONDITION(aStyleContext
, "Must have a style context");
2585 nscoord lineHeight
=
2586 ComputeLineHeight(aStyleContext
, aBlockBSize
, aFontSizeInflation
);
2588 NS_ASSERTION(lineHeight
>= 0, "ComputeLineHeight screwed up");
2590 HTMLInputElement
* input
= HTMLInputElement::FromContentOrNull(aContent
);
2591 if (input
&& input
->IsSingleLineTextControl()) {
2592 // For Web-compatibility, single-line text input elements cannot
2593 // have a line-height smaller than one.
2594 nscoord lineHeightOne
=
2595 aFontSizeInflation
* aStyleContext
->StyleFont()->mFont
.size
;
2596 if (lineHeight
< lineHeightOne
) {
2597 lineHeight
= lineHeightOne
;
2605 nsCSSOffsetState::ComputeMargin(nscoord aInlineDirPercentBasis
,
2606 nscoord aBlockDirPercentBasis
)
2608 // SVG text frames have no margin.
2609 if (frame
->IsSVGText()) {
2613 // If style style can provide us the margin directly, then use it.
2614 const nsStyleMargin
*styleMargin
= frame
->StyleMargin();
2615 bool isCBDependent
= !styleMargin
->GetMargin(ComputedPhysicalMargin());
2616 if (isCBDependent
) {
2617 // We have to compute the value
2618 LogicalMargin
m(mWritingMode
);
2619 nscoord horizontalPercentBasis
=
2620 mWritingMode
.IsVertical() ? aBlockDirPercentBasis
2621 : aInlineDirPercentBasis
;
2622 m
.Left(mWritingMode
) = nsLayoutUtils::
2623 ComputeCBDependentValue(horizontalPercentBasis
,
2624 styleMargin
->mMargin
.GetLeft());
2625 m
.Right(mWritingMode
) = nsLayoutUtils::
2626 ComputeCBDependentValue(horizontalPercentBasis
,
2627 styleMargin
->mMargin
.GetRight());
2629 nscoord verticalPercentBasis
=
2630 mWritingMode
.IsVertical() ? aInlineDirPercentBasis
2631 : aBlockDirPercentBasis
;
2632 m
.Top(mWritingMode
) = nsLayoutUtils::
2633 ComputeCBDependentValue(verticalPercentBasis
,
2634 styleMargin
->mMargin
.GetTop());
2635 m
.Bottom(mWritingMode
) = nsLayoutUtils::
2636 ComputeCBDependentValue(verticalPercentBasis
,
2637 styleMargin
->mMargin
.GetBottom());
2639 SetComputedLogicalMargin(m
);
2642 nscoord marginAdjustment
= FontSizeInflationListMarginAdjustment(frame
);
2644 if (marginAdjustment
> 0) {
2645 LogicalMargin m
= ComputedLogicalMargin();
2646 m
.IStart(mWritingMode
) += marginAdjustment
;
2647 SetComputedLogicalMargin(m
);
2650 return isCBDependent
;
2654 nsCSSOffsetState::ComputePadding(nscoord aInlineDirPercentBasis
,
2655 nscoord aBlockDirPercentBasis
,
2656 nsIAtom
* aFrameType
)
2658 // If style can provide us the padding directly, then use it.
2659 const nsStylePadding
*stylePadding
= frame
->StylePadding();
2660 bool isCBDependent
= !stylePadding
->GetPadding(ComputedPhysicalPadding());
2661 // a table row/col group, row/col doesn't have padding
2662 // XXXldb Neither do border-collapse tables.
2663 if (nsGkAtoms::tableRowGroupFrame
== aFrameType
||
2664 nsGkAtoms::tableColGroupFrame
== aFrameType
||
2665 nsGkAtoms::tableRowFrame
== aFrameType
||
2666 nsGkAtoms::tableColFrame
== aFrameType
) {
2667 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2669 else if (isCBDependent
) {
2670 // We have to compute the value
2671 // clamp negative calc() results to 0
2672 LogicalMargin
p(mWritingMode
);
2673 nscoord horizontalPercentBasis
=
2674 mWritingMode
.IsVertical() ? aBlockDirPercentBasis
2675 : aInlineDirPercentBasis
;
2676 p
.Left(mWritingMode
) = std::max(0, nsLayoutUtils::
2677 ComputeCBDependentValue(horizontalPercentBasis
,
2678 stylePadding
->mPadding
.GetLeft()));
2679 p
.Right(mWritingMode
) = std::max(0, nsLayoutUtils::
2680 ComputeCBDependentValue(horizontalPercentBasis
,
2681 stylePadding
->mPadding
.GetRight()));
2683 nscoord verticalPercentBasis
=
2684 mWritingMode
.IsVertical() ? aInlineDirPercentBasis
2685 : aBlockDirPercentBasis
;
2686 p
.Top(mWritingMode
) = std::max(0, nsLayoutUtils::
2687 ComputeCBDependentValue(verticalPercentBasis
,
2688 stylePadding
->mPadding
.GetTop()));
2689 p
.Bottom(mWritingMode
) = std::max(0, nsLayoutUtils::
2690 ComputeCBDependentValue(verticalPercentBasis
,
2691 stylePadding
->mPadding
.GetBottom()));
2693 SetComputedLogicalPadding(p
);
2695 return isCBDependent
;
2699 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth
,
2700 nscoord aContainingBlockHeight
,
2701 const nsHTMLReflowState
* aContainingBlockRS
)
2703 // NOTE: min-width:auto resolves to 0, except on a flex item. (But
2704 // even there, it's supposed to be ignored (i.e. treated as 0) until
2705 // the flex container explicitly resolves & considers it.)
2706 if (eStyleUnit_Auto
== mStylePosition
->mMinWidth
.GetUnit()) {
2707 ComputedMinWidth() = 0;
2709 ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth
,
2710 mStylePosition
->mBoxSizing
,
2711 mStylePosition
->mMinWidth
);
2714 if (eStyleUnit_None
== mStylePosition
->mMaxWidth
.GetUnit()) {
2715 // Specified value of 'none'
2716 ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE
; // no limit
2718 ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth
,
2719 mStylePosition
->mBoxSizing
,
2720 mStylePosition
->mMaxWidth
);
2723 // If the computed value of 'min-width' is greater than the value of
2724 // 'max-width', 'max-width' is set to the value of 'min-width'
2725 if (ComputedMinWidth() > ComputedMaxWidth()) {
2726 ComputedMaxWidth() = ComputedMinWidth();
2729 // Check for percentage based values and a containing block height that
2730 // depends on the content height. Treat them like 'auto'
2731 // Likewise, check for calc() with percentages on internal table elements;
2732 // that's treated as 'auto' too.
2733 // Likewise, if we're a child of a flex container who's measuring our
2734 // intrinsic height, then we want to disregard our min-height.
2736 // NOTE: min-height:auto resolves to 0, except on a flex item. (But
2737 // even there, it's supposed to be ignored (i.e. treated as 0) until
2738 // the flex container explicitly resolves & considers it.)
2739 const nsStyleCoord
&minHeight
= mStylePosition
->mMinHeight
;
2740 if (eStyleUnit_Auto
== minHeight
.GetUnit() ||
2741 (NS_AUTOHEIGHT
== aContainingBlockHeight
&&
2742 minHeight
.HasPercent()) ||
2743 (mFrameType
== NS_CSS_FRAME_TYPE_INTERNAL_TABLE
&&
2744 minHeight
.IsCalcUnit() && minHeight
.CalcHasPercent()) ||
2745 mFlags
.mIsFlexContainerMeasuringHeight
) {
2746 ComputedMinHeight() = 0;
2748 ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight
,
2749 mStylePosition
->mBoxSizing
,
2752 const nsStyleCoord
&maxHeight
= mStylePosition
->mMaxHeight
;
2753 nsStyleUnit maxHeightUnit
= maxHeight
.GetUnit();
2754 if (eStyleUnit_None
== maxHeightUnit
) {
2755 // Specified value of 'none'
2756 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE
; // no limit
2758 // Check for percentage based values and a containing block height that
2759 // depends on the content height. Treat them like 'none'
2760 // Likewise, check for calc() with percentages on internal table elements;
2761 // that's treated as 'auto' too.
2762 // Likewise, if we're a child of a flex container who's measuring our
2763 // intrinsic height, then we want to disregard our max-height.
2764 if ((NS_AUTOHEIGHT
== aContainingBlockHeight
&&
2765 maxHeight
.HasPercent()) ||
2766 (mFrameType
== NS_CSS_FRAME_TYPE_INTERNAL_TABLE
&&
2767 maxHeight
.IsCalcUnit() && maxHeight
.CalcHasPercent()) ||
2768 mFlags
.mIsFlexContainerMeasuringHeight
) {
2769 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE
;
2771 ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight
,
2772 mStylePosition
->mBoxSizing
,
2777 // If the computed value of 'min-height' is greater than the value of
2778 // 'max-height', 'max-height' is set to the value of 'min-height'
2779 if (ComputedMinHeight() > ComputedMaxHeight()) {
2780 ComputedMaxHeight() = ComputedMinHeight();
2785 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics
& aMetrics
,
2786 nsReflowStatus
* aStatus
) const
2788 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE
&&
2789 AvailableHeight() < aMetrics
.Height() &&
2790 !mFlags
.mIsTopOfPage
) {
2791 *aStatus
|= NS_FRAME_TRUNCATED
;
2793 *aStatus
&= ~NS_FRAME_TRUNCATED
;
2798 nsHTMLReflowState::IsFloating() const
2800 return mStyleDisplay
->IsFloating(frame
);
2804 nsHTMLReflowState::GetDisplay() const
2806 return mStyleDisplay
->GetDisplay(frame
);