Bumping manifests a=b2g-bump
[gecko.git] / layout / generic / nsHTMLReflowState.cpp
blob53a9e43c2cafc9feabbde9e0e445ff07583303b5
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"
12 #include "nsFrame.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"
31 #include <algorithm>
32 #include "mozilla/dom/HTMLInputElement.h"
34 #ifdef DEBUG
35 #undef NOISY_VERTICAL_ALIGN
36 #else
37 #undef NOISY_VERTICAL_ALIGN
38 #endif
40 using namespace mozilla;
41 using namespace mozilla::css;
42 using namespace mozilla::dom;
43 using namespace mozilla::layout;
45 enum eNormalLineHeightControl {
46 eUninitialized = -1,
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,
57 nsIFrame* aFrame,
58 nsRenderingContext* aRenderingContext,
59 const LogicalSize& aAvailableSpace,
60 uint32_t aFlags)
61 : nsCSSOffsetState(aFrame, aRenderingContext)
62 , mBlockDelta(0)
63 , mOrthogonalLimit(NS_UNCONSTRAINEDSIZE)
64 , mReflowDepth(0)
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)) {
85 Init(aPresContext);
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;
96 /**
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.
107 static nscoord
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
115 // list.
116 if (inflation > 1.0f &&
117 blockFrame->HasBullet() &&
118 inflation > 1.0f) {
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);
138 return 0;
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)
148 : frame(aFrame)
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
160 // computed.
161 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
162 const nsHTMLReflowState& aParentReflowState,
163 nsIFrame* aFrame,
164 const LogicalSize& aAvailableSpace,
165 nscoord aContainingBlockWidth,
166 nscoord aContainingBlockHeight,
167 uint32_t aFlags)
168 : nsCSSOffsetState(aFrame, aParentReflowState.rendContext)
169 , mBlockDelta(0)
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() &
191 NS_FRAME_IS_DIRTY);
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;
209 else
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);
240 inline nscoord
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,
250 aCoord);
253 nscoord
254 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
255 uint8_t aBoxSizing,
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();
263 break;
264 case NS_STYLE_BOX_SIZING_PADDING:
265 inside = ComputedPhysicalPadding().LeftRight();
266 break;
268 outside -= inside;
270 return ComputeWidthValue(aContainingBlockWidth, inside,
271 outside, aCoord);
274 nscoord
275 nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight,
276 uint8_t aBoxSizing,
277 const nsStyleCoord& aCoord)
279 nscoord inside = 0;
280 switch (aBoxSizing) {
281 case NS_STYLE_BOX_SIZING_BORDER:
282 inside = ComputedPhysicalBorderPadding().TopBottom();
283 break;
284 case NS_STYLE_BOX_SIZING_PADDING:
285 inside = ComputedPhysicalPadding().TopBottom();
286 break;
288 return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight,
289 inside, aCoord);
292 void
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
297 // two reasons:
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);
319 void
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
324 // because:
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());
339 void
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;
354 break;
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 "
362 "calculation");
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();
374 InitFrameType(type);
375 InitCBReflowState();
377 InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight,
378 aBorder, aPadding, type);
380 InitResizeFlags(aPresContext, type);
382 nsIFrame *parent = frame->GetParent();
383 if (parent &&
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
394 // elements.
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);
410 break;
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);
419 break;
422 continue;
423 } else {
424 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
425 break;
428 } else {
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 "
438 "calculation");
441 void nsHTMLReflowState::InitCBReflowState()
443 if (!parentReflowState) {
444 mCBReflowState = nullptr;
445 return;
448 if (parentReflowState->frame == frame->GetContainingBlock()) {
449 // Inner table frames need to use the containing block of the outer
450 // table frame.
451 if (frame->GetType() == nsGkAtoms::tableFrame) {
452 mCBReflowState = parentReflowState->mCBReflowState;
453 } else {
454 mCBReflowState = parentReflowState;
456 } else {
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
469 static bool
470 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType)
472 if (nsGkAtoms::blockFrame == aFrameType ||
473 #ifdef MOZ_XUL
474 nsGkAtoms::XULLabelFrame == aFrameType ||
475 #endif
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()) {
481 return false;
485 return true;
489 void
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();
535 if (kid) {
536 kid->AddStateBits(NS_FRAME_IS_DIRTY);
538 } else {
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
546 // of containers.
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
554 // container).
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
560 // style.
562 nsAutoTArray<nsIFrame*, 32> stack;
563 stack.AppendElement(frame);
565 do {
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) &&
583 isHResize);
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.
595 SetVResize(true);
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() &&
604 mCBReflowState) {
605 SetVResize(mCBReflowState->IsVResize());
606 } else {
607 SetVResize(IsHResize());
609 SetVResize(IsVResize() || NS_SUBTREE_DIRTY(frame));
610 } else {
611 // not 'auto' height
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 ||
624 frame->IsBoxFrame();
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 &&
647 dependsOnCBHeight) {
648 SetVResize(true);
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
660 // to become fixed.
661 if (dependsOnCBHeight && mCBReflowState) {
662 const nsHTMLReflowState *rs = this;
663 bool hitCBReflowState = false;
664 do {
665 rs = rs->parentReflowState;
666 if (!rs) {
667 break;
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
692 // to re-set this.
693 frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
697 /* static */
698 nscoord
699 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
701 const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
702 if (!rs)
703 return 0;
704 return rs->ComputedWidth();
707 void
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;
724 return;
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;
742 } else {
743 NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP,
744 "unknown out of flow frame type");
745 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
748 else {
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;
758 break;
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;
773 break;
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;
783 break;
785 case NS_STYLE_DISPLAY_NONE:
786 default:
787 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
788 break;
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;
802 /* static */ void
803 nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection,
804 nsIFrame* aFrame,
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:
813 // left=-right
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) {
821 leftIsAuto = true;
822 } else {
823 rightIsAuto = true;
827 if (leftIsAuto) {
828 if (rightIsAuto) {
829 // If both are 'auto' (their initial values), the computed values are 0
830 aComputedOffsets.left = aComputedOffsets.right = 0;
831 } else {
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;
841 } else {
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
855 // other's negative
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)) {
863 topIsAuto = true;
865 if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) {
866 bottomIsAuto = true;
870 // If neither is 'auto', 'bottom' is ignored
871 if (!topIsAuto && !bottomIsAuto) {
872 bottomIsAuto = true;
875 if (topIsAuto) {
876 if (bottomIsAuto) {
877 // If both are 'auto' (their initial values), the computed values are 0
878 aComputedOffsets.top = aComputedOffsets.bottom = 0;
879 } else {
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;
889 } else {
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;
901 // Store the offset
902 FrameProperties props = aFrame->Properties();
903 nsMargin* offsets = static_cast<nsMargin*>
904 (props.Get(nsIFrame::ComputedOffsetProperty()));
905 if (offsets) {
906 *offsets = aComputedOffsets;
907 } else {
908 props.Set(nsIFrame::ComputedOffsetProperty(),
909 new nsMargin(aComputedOffsets));
913 /* static */ void
914 nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame,
915 const nsMargin& aComputedOffsets,
916 nsPoint* aPosition)
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 "
922 "should call "
923 "props.Delete(nsIFrame::NormalPositionProperty())");
924 return;
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;
933 } else {
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);
952 if (ssc) {
953 *aPosition = ssc->ComputePosition(aFrame);
958 nsIFrame*
959 nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame,
960 nscoord& aCBLeftEdge,
961 nscoord& aCBWidth)
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) {
974 /* do nothing */
976 } else {
977 state = nullptr;
980 if (state) {
981 aCBLeftEdge = state->ComputedPhysicalBorderPadding().left;
982 aCBWidth = state->ComputedWidth();
983 } else {
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();
994 return aFrame;
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)
1007 nscoord mTop;
1008 #ifdef DEBUG
1009 bool mLeftIsExact, mRightIsExact;
1010 #endif
1012 nsHypotheticalBox() {
1013 #ifdef DEBUG
1014 mLeftIsExact = mRightIsExact = false;
1015 #endif
1019 static bool
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));
1036 return success;
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.
1044 void
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
1070 } else {
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
1078 } else {
1079 margin.right = nsLayoutUtils::
1080 ComputeCBDependentValue(aContainingBlockWidth,
1081 mStyleMargin->mMargin.GetRight());
1085 nscoord outside =
1086 padding.LeftRight() + border.LeftRight() + margin.LeftRight();
1087 nscoord inside = 0;
1088 switch (mStylePosition->mBoxSizing) {
1089 case NS_STYLE_BOX_SIZING_BORDER:
1090 inside += border.LeftRight();
1091 // fall through
1092 case NS_STYLE_BOX_SIZING_PADDING:
1093 inside += padding.LeftRight();
1095 outside -= inside;
1096 *aInsideBoxSizing = inside;
1097 *aOutsideBoxSizing = outside;
1098 return;
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) {
1108 *aFound = true;
1109 return true;
1111 if (!aFrame->IsSelfEmpty()) {
1112 *aFound = false;
1113 return false;
1115 for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) {
1116 bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
1117 if (*aFound || !allEmpty) {
1118 return allEmpty;
1121 *aFound = false;
1122 return true;
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
1130 void
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
1156 nscoord boxWidth;
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
1163 } else {
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;
1186 } else {
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());
1213 if (blockFrame) {
1214 nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y;
1215 bool isValid;
1216 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
1217 if (!isValid) {
1218 // Give up. We're probably dealing with somebody using
1219 // position:absolute inside native-anonymous content anyway.
1220 aHypotheticalBox.mTop = placeholderOffset.y;
1221 } else {
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;
1232 } else {
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;
1241 bool found = false;
1242 bool allEmpty = true;
1243 while (firstFrame) { // See bug 223064
1244 allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
1245 aPlaceholderFrame, &found);
1246 if (found || !allEmpty)
1247 break;
1248 firstFrame = firstFrame->GetNextSibling();
1250 NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
1252 if (allEmpty) {
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;
1257 } else {
1258 // The top of the hypothetical box is just below the line
1259 // containing the placeholder.
1260 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().YMost() + blockYOffset;
1262 } else {
1263 // Just use the placeholder's y-offset wrt the containing block
1264 aHypotheticalBox.mTop = placeholderOffset.y;
1268 } else {
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;
1283 } else {
1284 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1286 #ifdef DEBUG
1287 aHypotheticalBox.mLeftIsExact = true;
1288 #endif
1290 if (knowBoxWidth) {
1291 aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
1292 #ifdef DEBUG
1293 aHypotheticalBox.mRightIsExact = true;
1294 #endif
1295 } else {
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;
1300 #ifdef DEBUG
1301 aHypotheticalBox.mRightIsExact = false;
1302 #endif
1305 } else {
1306 // The placeholder represents the right edge of the hypothetical box
1307 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
1308 aHypotheticalBox.mRight = placeholderOffset.x;
1309 } else {
1310 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1312 #ifdef DEBUG
1313 aHypotheticalBox.mRightIsExact = true;
1314 #endif
1316 if (knowBoxWidth) {
1317 aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
1318 #ifdef DEBUG
1319 aHypotheticalBox.mLeftIsExact = true;
1320 #endif
1321 } else {
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;
1326 #ifdef DEBUG
1327 aHypotheticalBox.mLeftIsExact = false;
1328 #endif
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;
1338 nsPoint cbOffset;
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);
1348 do {
1349 cbOffset += aContainingBlock->GetPositionIgnoringScrolling();
1350 nsContainerFrame* parent = aContainingBlock->GetParent();
1351 if (!parent) {
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);
1359 break;
1361 aContainingBlock = parent;
1362 } while (aContainingBlock != cbrs->frame);
1363 } else {
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
1376 // translate.
1377 nsMargin border = cbrs->ComputedPhysicalBorderPadding() - cbrs->ComputedPhysicalPadding();
1378 aHypotheticalBox.mLeft -= border.left;
1379 aHypotheticalBox.mRight -= border.left;
1380 aHypotheticalBox.mTop -= border.top;
1383 void
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,
1416 cbLeftEdge,
1417 cbWidth);
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;
1428 leftIsAuto = true;
1429 } else {
1430 ComputedPhysicalOffsets().left = nsLayoutUtils::
1431 ComputeCBDependentValue(containingBlockWidth,
1432 mStylePosition->mOffset.GetLeft());
1434 if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
1435 ComputedPhysicalOffsets().right = 0;
1436 rightIsAuto = true;
1437 } else {
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;
1453 leftIsAuto = false;
1454 } else {
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;
1466 topIsAuto = true;
1467 } else {
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;
1475 } else {
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;
1484 topIsAuto = false;
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) {
1493 computeSizeFlags =
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));
1503 LogicalSize size =
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),
1511 computeSizeFlags);
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?
1522 if (leftIsAuto) {
1523 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1524 // box code above.
1525 // Solve for 'left'.
1526 if (widthIsAuto) {
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;
1531 } else {
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
1539 // box code above.
1540 // Solve for 'right'.
1541 if (widthIsAuto) {
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;
1546 } else {
1547 ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left -
1548 ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() -
1549 ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right;
1551 } else {
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() -
1563 ComputedWidth();
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'.
1574 if (cbrs &&
1575 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1576 // Ignore the specified value for 'margin-left'.
1577 ComputedPhysicalMargin().left = availMarginSpace;
1578 } else {
1579 // Ignore the specified value for 'margin-right'.
1580 ComputedPhysicalMargin().right = availMarginSpace;
1582 } else {
1583 // Both 'margin-left' and 'margin-right' are 'auto', so they get
1584 // equal values
1585 ComputedPhysicalMargin().left = availMarginSpace / 2;
1586 ComputedPhysicalMargin().right = availMarginSpace - ComputedPhysicalMargin().left;
1588 } else {
1589 // Just 'margin-left' is 'auto'
1590 ComputedPhysicalMargin().left = availMarginSpace;
1592 } else {
1593 if (marginRightIsAuto) {
1594 // Just 'margin-right' is 'auto'
1595 ComputedPhysicalMargin().right = availMarginSpace;
1596 } else {
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'.
1604 if (cbrs &&
1605 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1606 // Ignore the specified value for 'left'.
1607 ComputedPhysicalOffsets().left += availMarginSpace;
1608 } else {
1609 // Ignore the specified value for 'right'.
1610 ComputedPhysicalOffsets().right += availMarginSpace;
1616 if (topIsAuto) {
1617 // solve for 'top'
1618 if (heightIsAuto) {
1619 ComputedPhysicalOffsets().top = NS_AUTOOFFSET;
1620 } else {
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'
1627 if (heightIsAuto) {
1628 ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET;
1629 } else {
1630 ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top -
1631 ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() -
1632 ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom;
1634 } else {
1635 // Neither 'top' nor 'bottom' is 'auto'.
1636 nscoord autoHeight = containingBlockHeight -
1637 ComputedPhysicalOffsets().TopBottom() -
1638 ComputedPhysicalMargin().TopBottom() -
1639 ComputedPhysicalBorderPadding().TopBottom();
1640 if (autoHeight < 0) {
1641 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;
1671 } else {
1672 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1673 // equal values
1674 ComputedPhysicalMargin().top = availMarginSpace / 2;
1675 ComputedPhysicalMargin().bottom = availMarginSpace - ComputedPhysicalMargin().top;
1677 } else {
1678 // Just 'margin-top' is 'auto'
1679 ComputedPhysicalMargin().top = availMarginSpace;
1681 } else {
1682 if (marginBottomIsAuto) {
1683 // Just 'margin-bottom' is 'auto'
1684 ComputedPhysicalMargin().bottom = availMarginSpace;
1685 } else {
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;
1695 nscoord
1696 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
1698 nscoord result = 0;
1699 if (!aReflowState) return result;
1701 // zero auto margins
1702 nsMargin margin = aReflowState->ComputedPhysicalMargin();
1703 if (NS_AUTOMARGIN == margin.top)
1704 margin.top = 0;
1705 if (NS_AUTOMARGIN == margin.bottom)
1706 margin.bottom = 0;
1708 result += margin.top + margin.bottom;
1709 result += aReflowState->ComputedPhysicalBorderPadding().top +
1710 aReflowState->ComputedPhysicalBorderPadding().bottom;
1712 return result;
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.
1725 static nscoord
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 ||
1742 #ifdef MOZ_XUL
1743 nsGkAtoms::XULLabelFrame == frameType ||
1744 #endif
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()) {
1756 break;
1757 } else {
1758 continue;
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
1768 if (prevInFlow)
1769 break;
1771 else {
1772 break;
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);
1790 #ifdef DEBUG
1791 // make sure the first ancestor is the HTML and the second is the BODY
1792 if (firstAncestorRS) {
1793 nsIContent* frameContent = firstAncestorRS->frame->GetContent();
1794 if (frameContent) {
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();
1801 if (frameContent) {
1802 nsIAtom *contentTag = frameContent->Tag();
1803 NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY");
1806 #endif
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);
1817 break;
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
1826 void
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!");
1861 } else {
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();
1867 } else {
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.
1885 int32_t val =
1886 Preferences::GetInt("browser.display.normal_lineheight_calc_control",
1887 eNoExternalLeading);
1888 sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val);
1890 return sNormalLineHeightControl;
1893 static inline bool
1894 IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay)
1896 if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION)
1897 return false;
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();
1907 if (!parent ||
1908 parent->GetType() != nsGkAtoms::flexContainerFrame) {
1909 return nullptr;
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.
1920 static nscoord
1921 BlockDirOffsetPercentBasis(const nsIFrame* aFrame,
1922 nscoord aContainingBlockISize,
1923 nscoord aContainingBlockBSize)
1925 if (!aFrame->IsFlexOrGridItem()) {
1926 return aContainingBlockISize;
1929 if (aContainingBlockBSize == NS_AUTOHEIGHT) {
1930 return 0;
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
1939 void
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,
1949 aBorder, aPadding);
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),
1961 cbSize.BSize(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;
1975 } else {
1976 ComputedHeight() = NS_UNCONSTRAINEDSIZE;
1979 ComputedMinWidth() = ComputedMinHeight() = 0;
1980 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
1981 } else {
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
1987 // compute one
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
1994 // content
1995 nsIAtom* fType;
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;
2044 else {
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.
2050 else
2052 if (NS_AUTOHEIGHT != cbrs->ComputedHeight())
2053 aContainingBlockHeight = cbrs->ComputedHeight();
2054 else
2055 heightUnit = eStyleUnit_Auto;
2058 else {
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());
2078 } else {
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");
2117 } else {
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;
2135 } else {
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);
2151 } else {
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
2160 // shrink-wrap.
2161 if (isBlock &&
2162 ((aFrameType == nsGkAtoms::legendFrame &&
2163 frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) ||
2164 (aFrameType == nsGkAtoms::scrollFrame &&
2165 frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) {
2166 computeSizeFlags =
2167 ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eShrinkWrap);
2170 const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame);
2171 if (flexContainerFrame) {
2172 computeSizeFlags =
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) {
2178 computeSizeFlags =
2179 ComputeSizeFlags(computeSizeFlags | ComputeSizeFlags::eUseAutoHeight);
2181 } else {
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();
2195 LogicalSize size =
2196 frame->ComputeSize(rendContext, wm, cbSize,
2197 AvailableISize(),
2198 ComputedLogicalMargin().Size(wm),
2199 ComputedLogicalBorderPadding().Size(wm) -
2200 ComputedLogicalPadding().Size(wm),
2201 ComputedLogicalPadding().Size(wm),
2202 computeSizeFlags);
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
2211 if (isBlock &&
2212 !IsSideCaption(frame, mStyleDisplay) &&
2213 mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE &&
2214 !flexContainerFrame) {
2215 CalculateBlockSideMargins(aFrameType);
2221 static void
2222 UpdateProp(FrameProperties& aProps,
2223 const FramePropertyDescriptor* aProperty,
2224 bool aNeeded,
2225 nsMargin& aNewValue)
2227 if (aNeeded) {
2228 nsMargin* propValue = static_cast<nsMargin*>(aProps.Get(aProperty));
2229 if (propValue) {
2230 *propValue = aNewValue;
2231 } else {
2232 aProps.Set(aProperty, new nsMargin(aNewValue));
2234 } else {
2235 aProps.Delete(aProperty);
2239 void
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,
2249 aBorder, aPadding);
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;
2274 nsIntMargin widget;
2275 if (isThemed &&
2276 presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
2277 frame, disp->mAppearance,
2278 &widget)) {
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);
2294 else {
2295 needPaddingProp = ComputePadding(aInlineDirPercentBasis,
2296 aBlockDirPercentBasis, aFrameType);
2299 if (isThemed) {
2300 nsIntMargin widget;
2301 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
2302 frame, disp->mAppearance,
2303 &widget);
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;
2319 else {
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
2360 void
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.
2371 WritingMode cbWM =
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 "
2386 "calculation");
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
2396 return;
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));
2410 return;
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();
2421 } else {
2422 isAutoStartMargin = eStyleUnit_Auto == styleSides.GetBottomUnit();
2423 isAutoEndMargin = eStyleUnit_Auto == styleSides.GetTopUnit();
2425 } else {
2426 if (cbWM.IsBidiLTR()) {
2427 isAutoStartMargin = eStyleUnit_Auto == styleSides.GetLeftUnit();
2428 isAutoEndMargin = eStyleUnit_Auto == styleSides.GetRightUnit();
2429 } else {
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
2437 // ignore
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;
2447 if (prs &&
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()) {
2452 isAutoStartMargin =
2453 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
2454 isAutoEndMargin =
2455 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2456 } else {
2457 isAutoStartMargin =
2458 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2459 isAutoEndMargin =
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'.
2465 else {
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;
2480 } else {
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.
2498 static nscoord
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;
2511 break;
2512 case eCompensateLeading:
2513 if (!internalLeading && !externalLeading)
2514 normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
2515 else
2516 normalLineHeight = emHeight+ internalLeading + externalLeading;
2517 break;
2518 default:
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);
2537 return result;
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) {
2555 return aBlockBSize;
2559 nsRefPtr<nsFontMetrics> fm;
2560 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
2561 getter_AddRefs(fm),
2562 aFontSizeInflation);
2563 return GetNormalLineHeight(fm);
2566 nscoord
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;
2601 return lineHeight;
2604 bool
2605 nsCSSOffsetState::ComputeMargin(nscoord aInlineDirPercentBasis,
2606 nscoord aBlockDirPercentBasis)
2608 // SVG text frames have no margin.
2609 if (frame->IsSVGText()) {
2610 return false;
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;
2653 bool
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;
2698 void
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;
2708 } else {
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
2717 } else {
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;
2747 } else {
2748 ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight,
2749 mStylePosition->mBoxSizing,
2750 minHeight);
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
2757 } else {
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;
2770 } else {
2771 ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight,
2772 mStylePosition->mBoxSizing,
2773 maxHeight);
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();
2784 void
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;
2792 } else {
2793 *aStatus &= ~NS_FRAME_TRUNCATED;
2797 bool
2798 nsHTMLReflowState::IsFloating() const
2800 return mStyleDisplay->IsFloating(frame);
2803 uint8_t
2804 nsHTMLReflowState::GetDisplay() const
2806 return mStyleDisplay->GetDisplay(frame);