Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 <algorithm>
31 #include "mozilla/dom/HTMLInputElement.h"
33 #ifdef DEBUG
34 #undef NOISY_VERTICAL_ALIGN
35 #else
36 #undef NOISY_VERTICAL_ALIGN
37 #endif
39 using namespace mozilla;
40 using namespace mozilla::css;
41 using namespace mozilla::dom;
42 using namespace mozilla::layout;
44 enum eNormalLineHeightControl {
45 eUninitialized = -1,
46 eNoExternalLeading = 0, // does not include external leading
47 eIncludeExternalLeading, // use whatever value font vendor provides
48 eCompensateLeading // compensate leading if leading provided by font vendor is not enough
49 };
51 static eNormalLineHeightControl sNormalLineHeightControl = eUninitialized;
53 // Initialize a <b>root</b> reflow state with a rendering context to
54 // use for measuring things.
55 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
56 nsIFrame* aFrame,
57 nsRenderingContext* aRenderingContext,
58 const nsSize& aAvailableSpace,
59 uint32_t aFlags)
60 : nsCSSOffsetState(aFrame, aRenderingContext)
61 , mBlockDelta(0)
62 , mReflowDepth(0)
63 {
64 NS_PRECONDITION(aRenderingContext, "no rendering context");
65 MOZ_ASSERT(aPresContext, "no pres context");
66 MOZ_ASSERT(aFrame, "no frame");
67 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
68 parentReflowState = nullptr;
69 AvailableWidth() = aAvailableSpace.width;
70 AvailableHeight() = aAvailableSpace.height;
71 mFloatManager = nullptr;
72 mLineLayout = nullptr;
73 memset(&mFlags, 0, sizeof(mFlags));
74 mDiscoveredClearance = nullptr;
75 mPercentHeightObserver = nullptr;
77 if (aFlags & DUMMY_PARENT_REFLOW_STATE) {
78 mFlags.mDummyParentReflowState = true;
79 }
81 if (!(aFlags & CALLER_WILL_INIT)) {
82 Init(aPresContext);
83 }
84 }
86 static bool CheckNextInFlowParenthood(nsIFrame* aFrame, nsIFrame* aParent)
87 {
88 nsIFrame* frameNext = aFrame->GetNextInFlow();
89 nsIFrame* parentNext = aParent->GetNextInFlow();
90 return frameNext && parentNext && frameNext->GetParent() == parentNext;
91 }
93 /**
94 * Adjusts the margin for a list (ol, ul), if necessary, depending on
95 * font inflation settings. Unfortunately, because bullets from a list are
96 * placed in the margin area, we only have ~40px in which to place the
97 * bullets. When they are inflated, however, this causes problems, since
98 * the text takes up more space than is available in the margin.
99 *
100 * This method will return a small amount (in app units) by which the
101 * margin can be adjusted, so that the space is available for list
102 * bullets to be rendered with font inflation enabled.
103 */
104 static nscoord
105 FontSizeInflationListMarginAdjustment(const nsIFrame* aFrame)
106 {
107 float inflation = nsLayoutUtils::FontSizeInflationFor(aFrame);
108 if (aFrame->IsFrameOfType(nsIFrame::eBlockFrame)) {
109 const nsBlockFrame* blockFrame = static_cast<const nsBlockFrame*>(aFrame);
110 const nsStyleList* styleList = aFrame->StyleList();
112 // We only want to adjust the margins if we're dealing with an ordered
113 // list.
114 if (inflation > 1.0f &&
115 blockFrame->HasBullet() &&
116 styleList->mListStyleType != NS_STYLE_LIST_STYLE_NONE &&
117 styleList->mListStyleType != NS_STYLE_LIST_STYLE_DISC &&
118 styleList->mListStyleType != NS_STYLE_LIST_STYLE_CIRCLE &&
119 styleList->mListStyleType != NS_STYLE_LIST_STYLE_SQUARE &&
120 inflation > 1.0f) {
122 // The HTML spec states that the default padding for ordered lists begins
123 // at 40px, indicating that we have 40px of space to place a bullet. When
124 // performing font inflation calculations, we add space equivalent to this,
125 // but simply inflated at the same amount as the text, in app units.
126 return nsPresContext::CSSPixelsToAppUnits(40) * (inflation - 1);
127 }
128 }
130 return 0;
131 }
133 // NOTE: If we ever want to use nsCSSOffsetState for a flex item or a grid
134 // item, we need to make it take the containing-block height as well as the
135 // width, since flex items and grid items resolve vertical percent margins
136 // and padding against the containing-block height, rather than its width.
137 nsCSSOffsetState::nsCSSOffsetState(nsIFrame *aFrame,
138 nsRenderingContext *aRenderingContext,
139 nscoord aContainingBlockWidth)
140 : frame(aFrame)
141 , rendContext(aRenderingContext)
142 , mWritingMode(aFrame->GetWritingMode())
143 {
144 MOZ_ASSERT(!aFrame->IsFlexItem(),
145 "We're about to resolve vertical percent margin & padding "
146 "values against CB width, which is incorrect for flex items");
147 InitOffsets(aContainingBlockWidth, aContainingBlockWidth, frame->GetType());
148 }
150 // Initialize a reflow state for a child frame's reflow. Some state
151 // is copied from the parent reflow state; the remaining state is
152 // computed.
153 nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
154 const nsHTMLReflowState& aParentReflowState,
155 nsIFrame* aFrame,
156 const nsSize& aAvailableSpace,
157 nscoord aContainingBlockWidth,
158 nscoord aContainingBlockHeight,
159 uint32_t aFlags)
160 : nsCSSOffsetState(aFrame, aParentReflowState.rendContext)
161 , mBlockDelta(0)
162 , mReflowDepth(aParentReflowState.mReflowDepth + 1)
163 , mFlags(aParentReflowState.mFlags)
164 {
165 MOZ_ASSERT(aPresContext, "no pres context");
166 MOZ_ASSERT(aFrame, "no frame");
167 MOZ_ASSERT(aPresContext == aFrame->PresContext(), "wrong pres context");
168 NS_PRECONDITION((aContainingBlockWidth == -1) ==
169 (aContainingBlockHeight == -1),
170 "cb width and height should only be non-default together");
171 NS_PRECONDITION(!mFlags.mSpecialHeightReflow ||
172 !NS_SUBTREE_DIRTY(aFrame),
173 "frame should be clean when getting special height reflow");
175 parentReflowState = &aParentReflowState;
177 // If the parent is dirty, then the child is as well.
178 // XXX Are the other cases where the parent reflows a child a second
179 // time, as a resize?
180 if (!mFlags.mSpecialHeightReflow)
181 frame->AddStateBits(parentReflowState->frame->GetStateBits() &
182 NS_FRAME_IS_DIRTY);
184 AvailableWidth() = aAvailableSpace.width;
185 AvailableHeight() = aAvailableSpace.height;
187 mFloatManager = aParentReflowState.mFloatManager;
188 if (frame->IsFrameOfType(nsIFrame::eLineParticipant))
189 mLineLayout = aParentReflowState.mLineLayout;
190 else
191 mLineLayout = nullptr;
193 // Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in
194 // this constructor's init list, so the only flags that we need to explicitly
195 // initialize here are those that may need a value other than our parent's.
196 mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
197 CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
198 mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
199 mFlags.mHasClearance = false;
200 mFlags.mIsColumnBalancing = false;
201 mFlags.mIsFlexContainerMeasuringHeight = false;
202 mFlags.mDummyParentReflowState = false;
204 mDiscoveredClearance = nullptr;
205 mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
206 aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
207 ? aParentReflowState.mPercentHeightObserver : nullptr;
209 if (aFlags & DUMMY_PARENT_REFLOW_STATE) {
210 mFlags.mDummyParentReflowState = true;
211 }
213 if (!(aFlags & CALLER_WILL_INIT)) {
214 Init(aPresContext, aContainingBlockWidth, aContainingBlockHeight);
215 }
216 }
218 inline nscoord
219 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
220 nscoord aContentEdgeToBoxSizing,
221 nscoord aBoxSizingToMarginEdge,
222 const nsStyleCoord& aCoord)
223 {
224 return nsLayoutUtils::ComputeWidthValue(rendContext, frame,
225 aContainingBlockWidth,
226 aContentEdgeToBoxSizing,
227 aBoxSizingToMarginEdge,
228 aCoord);
229 }
231 nscoord
232 nsCSSOffsetState::ComputeWidthValue(nscoord aContainingBlockWidth,
233 uint8_t aBoxSizing,
234 const nsStyleCoord& aCoord)
235 {
236 nscoord inside = 0, outside = ComputedPhysicalBorderPadding().LeftRight() +
237 ComputedPhysicalMargin().LeftRight();
238 switch (aBoxSizing) {
239 case NS_STYLE_BOX_SIZING_BORDER:
240 inside = ComputedPhysicalBorderPadding().LeftRight();
241 break;
242 case NS_STYLE_BOX_SIZING_PADDING:
243 inside = ComputedPhysicalPadding().LeftRight();
244 break;
245 }
246 outside -= inside;
248 return ComputeWidthValue(aContainingBlockWidth, inside,
249 outside, aCoord);
250 }
252 nscoord
253 nsCSSOffsetState::ComputeHeightValue(nscoord aContainingBlockHeight,
254 uint8_t aBoxSizing,
255 const nsStyleCoord& aCoord)
256 {
257 nscoord inside = 0;
258 switch (aBoxSizing) {
259 case NS_STYLE_BOX_SIZING_BORDER:
260 inside = ComputedPhysicalBorderPadding().TopBottom();
261 break;
262 case NS_STYLE_BOX_SIZING_PADDING:
263 inside = ComputedPhysicalPadding().TopBottom();
264 break;
265 }
266 return nsLayoutUtils::ComputeHeightValue(aContainingBlockHeight,
267 inside, aCoord);
268 }
270 void
271 nsHTMLReflowState::SetComputedWidth(nscoord aComputedWidth)
272 {
273 NS_ASSERTION(frame, "Must have a frame!");
274 // It'd be nice to assert that |frame| is not in reflow, but this fails for
275 // two reasons:
276 //
277 // 1) Viewport frames reset the computed width on a copy of their reflow
278 // state when reflowing fixed-pos kids. In that case we actually don't
279 // want to mess with the resize flags, because comparing the frame's rect
280 // to the munged computed width is pointless.
281 // 2) nsFrame::BoxReflow creates a reflow state for its parent. This reflow
282 // state is not used to reflow the parent, but just as a parent for the
283 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
284 // (like a text control, for example), we'll end up creating a reflow
285 // state for the parent while the parent is reflowing.
287 NS_PRECONDITION(aComputedWidth >= 0, "Invalid computed width");
288 if (ComputedWidth() != aComputedWidth) {
289 ComputedWidth() = aComputedWidth;
290 nsIAtom* frameType = frame->GetType();
291 if (frameType != nsGkAtoms::viewportFrame) { // Or check GetParent()?
292 InitResizeFlags(frame->PresContext(), frameType);
293 }
294 }
295 }
297 void
298 nsHTMLReflowState::SetComputedHeight(nscoord aComputedHeight)
299 {
300 NS_ASSERTION(frame, "Must have a frame!");
301 // It'd be nice to assert that |frame| is not in reflow, but this fails
302 // because:
303 //
304 // nsFrame::BoxReflow creates a reflow state for its parent. This reflow
305 // state is not used to reflow the parent, but just as a parent for the
306 // frame's own reflow state. So given a nsBoxFrame inside some non-XUL
307 // (like a text control, for example), we'll end up creating a reflow
308 // state for the parent while the parent is reflowing.
310 NS_PRECONDITION(aComputedHeight >= 0, "Invalid computed height");
311 if (ComputedHeight() != aComputedHeight) {
312 ComputedHeight() = aComputedHeight;
313 InitResizeFlags(frame->PresContext(), frame->GetType());
314 }
315 }
317 void
318 nsHTMLReflowState::Init(nsPresContext* aPresContext,
319 nscoord aContainingBlockWidth,
320 nscoord aContainingBlockHeight,
321 const nsMargin* aBorder,
322 const nsMargin* aPadding)
323 {
324 NS_WARN_IF_FALSE(AvailableWidth() != NS_UNCONSTRAINEDSIZE,
325 "have unconstrained width; this should only result from "
326 "very large sizes, not attempts at intrinsic width "
327 "calculation");
329 mStylePosition = frame->StylePosition();
330 mStyleDisplay = frame->StyleDisplay();
331 mStyleVisibility = frame->StyleVisibility();
332 mStyleBorder = frame->StyleBorder();
333 mStyleMargin = frame->StyleMargin();
334 mStylePadding = frame->StylePadding();
335 mStyleText = frame->StyleText();
337 nsIAtom* type = frame->GetType();
339 InitFrameType(type);
340 InitCBReflowState();
342 InitConstraints(aPresContext, aContainingBlockWidth, aContainingBlockHeight,
343 aBorder, aPadding, type);
345 InitResizeFlags(aPresContext, type);
347 nsIFrame *parent = frame->GetParent();
348 if (parent &&
349 (parent->GetStateBits() & NS_FRAME_IN_CONSTRAINED_HEIGHT) &&
350 !(parent->GetType() == nsGkAtoms::scrollFrame &&
351 parent->StyleDisplay()->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN)) {
352 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
353 } else if (type == nsGkAtoms::svgForeignObjectFrame) {
354 // An SVG foreignObject frame is inherently constrained height.
355 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
356 } else if ((mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto ||
357 mStylePosition->mMaxHeight.GetUnit() != eStyleUnit_None) &&
358 // Don't set NS_FRAME_IN_CONSTRAINED_HEIGHT on body or html
359 // elements.
360 (frame->GetContent() &&
361 !(frame->GetContent()->IsHTML(nsGkAtoms::body) ||
362 frame->GetContent()->IsHTML(nsGkAtoms::html)))) {
364 // If our height was specified as a percentage, then this could
365 // actually resolve to 'auto', based on:
366 // http://www.w3.org/TR/CSS21/visudet.html#the-height-property
367 nsIFrame* containingBlk = frame;
368 while (containingBlk) {
369 const nsStylePosition* stylePos = containingBlk->StylePosition();
370 if ((stylePos->mHeight.IsCoordPercentCalcUnit() &&
371 !stylePos->mHeight.HasPercent()) ||
372 (stylePos->mMaxHeight.IsCoordPercentCalcUnit() &&
373 !stylePos->mMaxHeight.HasPercent())) {
374 frame->AddStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
375 break;
376 } else if ((stylePos->mHeight.IsCoordPercentCalcUnit() &&
377 stylePos->mHeight.HasPercent()) ||
378 (stylePos->mMaxHeight.IsCoordPercentCalcUnit() &&
379 stylePos->mMaxHeight.HasPercent())) {
380 if (!(containingBlk = containingBlk->GetContainingBlock())) {
381 // If we've reached the top of the tree, then we don't have
382 // a constrained height.
383 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
384 break;
385 }
387 continue;
388 } else {
389 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
390 break;
391 }
392 }
393 } else {
394 frame->RemoveStateBits(NS_FRAME_IN_CONSTRAINED_HEIGHT);
395 }
397 NS_WARN_IF_FALSE((mFrameType == NS_CSS_FRAME_TYPE_INLINE &&
398 !frame->IsFrameOfType(nsIFrame::eReplaced)) ||
399 type == nsGkAtoms::textFrame ||
400 ComputedWidth() != NS_UNCONSTRAINEDSIZE,
401 "have unconstrained width; this should only result from "
402 "very large sizes, not attempts at intrinsic width "
403 "calculation");
404 }
406 void nsHTMLReflowState::InitCBReflowState()
407 {
408 if (!parentReflowState) {
409 mCBReflowState = nullptr;
410 return;
411 }
413 if (parentReflowState->frame == frame->GetContainingBlock()) {
414 // Inner table frames need to use the containing block of the outer
415 // table frame.
416 if (frame->GetType() == nsGkAtoms::tableFrame) {
417 mCBReflowState = parentReflowState->mCBReflowState;
418 } else {
419 mCBReflowState = parentReflowState;
420 }
421 } else {
422 mCBReflowState = parentReflowState->mCBReflowState;
423 }
424 }
426 /* Check whether CalcQuirkContainingBlockHeight would stop on the
427 * given reflow state, using its block as a height. (essentially
428 * returns false for any case in which CalcQuirkContainingBlockHeight
429 * has a "continue" in its main loop.)
430 *
431 * XXX Maybe refactor CalcQuirkContainingBlockHeight so it uses
432 * this function as well
433 */
434 static bool
435 IsQuirkContainingBlockHeight(const nsHTMLReflowState* rs, nsIAtom* aFrameType)
436 {
437 if (nsGkAtoms::blockFrame == aFrameType ||
438 #ifdef MOZ_XUL
439 nsGkAtoms::XULLabelFrame == aFrameType ||
440 #endif
441 nsGkAtoms::scrollFrame == aFrameType) {
442 // Note: This next condition could change due to a style change,
443 // but that would cause a style reflow anyway, which means we're ok.
444 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
445 if (!rs->frame->IsAbsolutelyPositioned()) {
446 return false;
447 }
448 }
449 }
450 return true;
451 }
454 void
455 nsHTMLReflowState::InitResizeFlags(nsPresContext* aPresContext, nsIAtom* aFrameType)
456 {
457 bool isHResize = (frame->GetSize().width !=
458 ComputedWidth() + ComputedPhysicalBorderPadding().LeftRight()) ||
459 aPresContext->PresShell()->IsReflowOnZoomPending();
461 if ((frame->GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) &&
462 nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
463 // Create our font inflation data if we don't have it already, and
464 // give it our current width information.
465 bool dirty = nsFontInflationData::UpdateFontInflationDataWidthFor(*this) &&
466 // Avoid running this at the box-to-block interface
467 // (where we shouldn't be inflating anyway, and where
468 // reflow state construction is probably to construct a
469 // dummy parent reflow state anyway).
470 !mFlags.mDummyParentReflowState;
472 if (dirty || (!frame->GetParent() && isHResize)) {
473 // When font size inflation is enabled, a change in either:
474 // * the effective width of a font inflation flow root
475 // * the width of the frame
476 // needs to cause a dirty reflow since they change the font size
477 // inflation calculations, which in turn change the size of text,
478 // line-heights, etc. This is relatively similar to a classic
479 // case of style change reflow, except that because inflation
480 // doesn't affect the intrinsic sizing codepath, there's no need
481 // to invalidate intrinsic sizes.
482 //
483 // Note that this makes horizontal resizing a good bit more
484 // expensive. However, font size inflation is targeted at a set of
485 // devices (zoom-and-pan devices) where the main use case for
486 // horizontal resizing needing to be efficient (window resizing) is
487 // not present. It does still increase the cost of dynamic changes
488 // caused by script where a style or content change in one place
489 // causes a resize in another (e.g., rebalancing a table).
491 // FIXME: This isn't so great for the cases where
492 // nsHTMLReflowState::SetComputedWidth is called, if the first time
493 // we go through InitResizeFlags we set mHResize to true, and then
494 // the second time we'd set it to false even without the
495 // NS_FRAME_IS_DIRTY bit already set.
496 if (frame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
497 // Foreign object frames use dirty bits in a special way.
498 frame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
499 nsIFrame *kid = frame->GetFirstPrincipalChild();
500 if (kid) {
501 kid->AddStateBits(NS_FRAME_IS_DIRTY);
502 }
503 } else {
504 frame->AddStateBits(NS_FRAME_IS_DIRTY);
505 }
507 // Mark intrinsic widths on all descendants dirty. We need to do
508 // this (1) since we're changing the size of text and need to
509 // clear text runs on text frames and (2) since we actually are
510 // changing some intrinsic widths, but only those that live inside
511 // of containers.
513 // It makes sense to do this for descendants but not ancestors
514 // (which is unusual) because we're only changing the unusual
515 // inflation-dependent intrinsic widths (i.e., ones computed with
516 // nsPresContext::mInflationDisabledForShrinkWrap set to false),
517 // which should never affect anything outside of their inflation
518 // flow root (or, for that matter, even their inflation
519 // container).
521 // This is also different from what PresShell::FrameNeedsReflow
522 // does because it doesn't go through placeholders. It doesn't
523 // need to because we're actually doing something that cares about
524 // frame tree geometry (the width on an ancestor) rather than
525 // style.
527 nsAutoTArray<nsIFrame*, 32> stack;
528 stack.AppendElement(frame);
530 do {
531 nsIFrame *f = stack.ElementAt(stack.Length() - 1);
532 stack.RemoveElementAt(stack.Length() - 1);
534 nsIFrame::ChildListIterator lists(f);
535 for (; !lists.IsDone(); lists.Next()) {
536 nsFrameList::Enumerator childFrames(lists.CurrentList());
537 for (; !childFrames.AtEnd(); childFrames.Next()) {
538 nsIFrame* kid = childFrames.get();
539 kid->MarkIntrinsicWidthsDirty();
540 stack.AppendElement(kid);
541 }
542 }
543 } while (stack.Length() != 0);
544 }
545 }
547 mFlags.mHResize = !(frame->GetStateBits() & NS_FRAME_IS_DIRTY) &&
548 isHResize;
550 // XXX Should we really need to null check mCBReflowState? (We do for
551 // at least nsBoxFrame).
552 if (IS_TABLE_CELL(aFrameType) &&
553 (mFlags.mSpecialHeightReflow ||
554 (frame->FirstInFlow()->GetStateBits() &
555 NS_TABLE_CELL_HAD_SPECIAL_REFLOW)) &&
556 (frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
557 // Need to set the bit on the cell so that
558 // mCBReflowState->mFlags.mVResize is set correctly below when
559 // reflowing descendant.
560 mFlags.mVResize = true;
561 } else if (mCBReflowState && !nsLayoutUtils::IsNonWrapperBlock(frame)) {
562 // XXX Is this problematic for relatively positioned inlines acting
563 // as containing block for absolutely positioned elements?
564 // Possibly; in that case we should at least be checking
565 // NS_SUBTREE_DIRTY, I'd think.
566 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
567 } else if (ComputedHeight() == NS_AUTOHEIGHT) {
568 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
569 mCBReflowState) {
570 mFlags.mVResize = mCBReflowState->mFlags.mVResize;
571 } else {
572 mFlags.mVResize = mFlags.mHResize;
573 }
574 mFlags.mVResize = mFlags.mVResize || NS_SUBTREE_DIRTY(frame);
575 } else {
576 // not 'auto' height
577 mFlags.mVResize = frame->GetSize().height !=
578 ComputedHeight() + ComputedPhysicalBorderPadding().TopBottom();
579 }
581 bool dependsOnCBHeight =
582 (mStylePosition->HeightDependsOnContainer() &&
583 // FIXME: condition this on not-abspos?
584 mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) ||
585 mStylePosition->MinHeightDependsOnContainer() ||
586 mStylePosition->MaxHeightDependsOnContainer() ||
587 mStylePosition->OffsetHasPercent(NS_SIDE_TOP) ||
588 mStylePosition->mOffset.GetBottomUnit() != eStyleUnit_Auto ||
589 frame->IsBoxFrame();
591 if (mStyleText->mLineHeight.GetUnit() == eStyleUnit_Enumerated) {
592 NS_ASSERTION(mStyleText->mLineHeight.GetIntValue() ==
593 NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
594 "bad line-height value");
596 // line-height depends on block height
597 frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
598 // but only on containing blocks if this frame is not a suitable block
599 dependsOnCBHeight |= !nsLayoutUtils::IsNonWrapperBlock(frame);
600 }
602 // If we're the descendant of a table cell that performs special height
603 // reflows and we could be the child that requires them, always set
604 // the vertical resize in case this is the first pass before the
605 // special height reflow. However, don't do this if it actually is
606 // the special height reflow, since in that case it will already be
607 // set correctly above if we need it set.
608 if (!mFlags.mVResize && mCBReflowState &&
609 (IS_TABLE_CELL(mCBReflowState->frame->GetType()) ||
610 mCBReflowState->mFlags.mHeightDependsOnAncestorCell) &&
611 !mCBReflowState->mFlags.mSpecialHeightReflow &&
612 dependsOnCBHeight) {
613 mFlags.mVResize = true;
614 mFlags.mHeightDependsOnAncestorCell = true;
615 }
617 // Set NS_FRAME_CONTAINS_RELATIVE_HEIGHT if it's needed.
619 // It would be nice to check that |mComputedHeight != NS_AUTOHEIGHT|
620 // &&ed with the percentage height check. However, this doesn't get
621 // along with table special height reflows, since a special height
622 // reflow (a quirk that makes such percentage heights work on children
623 // of table cells) can cause not just a single percentage height to
624 // become fixed, but an entire descendant chain of percentage heights
625 // to become fixed.
626 if (dependsOnCBHeight && mCBReflowState) {
627 const nsHTMLReflowState *rs = this;
628 bool hitCBReflowState = false;
629 do {
630 rs = rs->parentReflowState;
631 if (!rs) {
632 break;
633 }
635 if (rs->frame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
636 break; // no need to go further
637 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
639 // Keep track of whether we've hit the containing block, because
640 // we need to go at least that far.
641 if (rs == mCBReflowState) {
642 hitCBReflowState = true;
643 }
645 } while (!hitCBReflowState ||
646 (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
647 !IsQuirkContainingBlockHeight(rs, rs->frame->GetType())));
648 // Note: We actually don't need to set the
649 // NS_FRAME_CONTAINS_RELATIVE_HEIGHT bit for the cases
650 // where we hit the early break statements in
651 // CalcQuirkContainingBlockHeight. But it doesn't hurt
652 // us to set the bit in these cases.
654 }
655 if (frame->GetStateBits() & NS_FRAME_IS_DIRTY) {
656 // If we're reflowing everything, then we'll find out if we need
657 // to re-set this.
658 frame->RemoveStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
659 }
660 }
662 /* static */
663 nscoord
664 nsHTMLReflowState::GetContainingBlockContentWidth(const nsHTMLReflowState* aReflowState)
665 {
666 const nsHTMLReflowState* rs = aReflowState->mCBReflowState;
667 if (!rs)
668 return 0;
669 return rs->ComputedWidth();
670 }
672 void
673 nsHTMLReflowState::InitFrameType(nsIAtom* aFrameType)
674 {
675 const nsStyleDisplay *disp = mStyleDisplay;
676 nsCSSFrameType frameType;
678 // Section 9.7 of the CSS2 spec indicates that absolute position
679 // takes precedence over float which takes precedence over display.
680 // XXXldb nsRuleNode::ComputeDisplayData should take care of this, right?
681 // Make sure the frame was actually moved out of the flow, and don't
682 // just assume what the style says, because we might not have had a
683 // useful float/absolute containing block
685 DISPLAY_INIT_TYPE(frame, this);
687 if (aFrameType == nsGkAtoms::tableFrame) {
688 mFrameType = NS_CSS_FRAME_TYPE_BLOCK;
689 return;
690 }
692 NS_ASSERTION(frame->StyleDisplay()->IsAbsolutelyPositionedStyle() ==
693 disp->IsAbsolutelyPositionedStyle(),
694 "Unexpected position style");
695 NS_ASSERTION(frame->StyleDisplay()->IsFloatingStyle() ==
696 disp->IsFloatingStyle(), "Unexpected float style");
697 if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
698 if (disp->IsAbsolutelyPositioned(frame)) {
699 frameType = NS_CSS_FRAME_TYPE_ABSOLUTE;
700 //XXXfr hack for making frames behave properly when in overflow container lists
701 // see bug 154892; need to revisit later
702 if (frame->GetPrevInFlow())
703 frameType = NS_CSS_FRAME_TYPE_BLOCK;
704 }
705 else if (disp->IsFloating(frame)) {
706 frameType = NS_CSS_FRAME_TYPE_FLOATING;
707 } else {
708 NS_ASSERTION(disp->mDisplay == NS_STYLE_DISPLAY_POPUP,
709 "unknown out of flow frame type");
710 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
711 }
712 }
713 else {
714 switch (GetDisplay()) {
715 case NS_STYLE_DISPLAY_BLOCK:
716 case NS_STYLE_DISPLAY_LIST_ITEM:
717 case NS_STYLE_DISPLAY_TABLE:
718 case NS_STYLE_DISPLAY_TABLE_CAPTION:
719 case NS_STYLE_DISPLAY_FLEX:
720 frameType = NS_CSS_FRAME_TYPE_BLOCK;
721 break;
723 case NS_STYLE_DISPLAY_INLINE:
724 case NS_STYLE_DISPLAY_INLINE_BLOCK:
725 case NS_STYLE_DISPLAY_INLINE_TABLE:
726 case NS_STYLE_DISPLAY_INLINE_BOX:
727 case NS_STYLE_DISPLAY_INLINE_XUL_GRID:
728 case NS_STYLE_DISPLAY_INLINE_STACK:
729 case NS_STYLE_DISPLAY_INLINE_FLEX:
730 frameType = NS_CSS_FRAME_TYPE_INLINE;
731 break;
733 case NS_STYLE_DISPLAY_TABLE_CELL:
734 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
735 case NS_STYLE_DISPLAY_TABLE_COLUMN:
736 case NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP:
737 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
738 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
739 case NS_STYLE_DISPLAY_TABLE_ROW:
740 frameType = NS_CSS_FRAME_TYPE_INTERNAL_TABLE;
741 break;
743 case NS_STYLE_DISPLAY_NONE:
744 default:
745 frameType = NS_CSS_FRAME_TYPE_UNKNOWN;
746 break;
747 }
748 }
750 // See if the frame is replaced
751 if (frame->IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
752 frameType = NS_FRAME_REPLACED_CONTAINS_BLOCK(frameType);
753 } else if (frame->IsFrameOfType(nsIFrame::eReplaced)) {
754 frameType = NS_FRAME_REPLACED(frameType);
755 }
757 mFrameType = frameType;
758 }
760 /* static */ void
761 nsHTMLReflowState::ComputeRelativeOffsets(uint8_t aCBDirection,
762 nsIFrame* aFrame,
763 nscoord aContainingBlockWidth,
764 nscoord aContainingBlockHeight,
765 nsMargin& aComputedOffsets)
766 {
767 const nsStylePosition* position = aFrame->StylePosition();
769 // Compute the 'left' and 'right' values. 'Left' moves the boxes to the right,
770 // and 'right' moves the boxes to the left. The computed values are always:
771 // left=-right
772 bool leftIsAuto = eStyleUnit_Auto == position->mOffset.GetLeftUnit();
773 bool rightIsAuto = eStyleUnit_Auto == position->mOffset.GetRightUnit();
775 // If neither 'left' not 'right' are auto, then we're over-constrained and
776 // we ignore one of them
777 if (!leftIsAuto && !rightIsAuto) {
778 if (aCBDirection == NS_STYLE_DIRECTION_RTL) {
779 leftIsAuto = true;
780 } else {
781 rightIsAuto = true;
782 }
783 }
785 if (leftIsAuto) {
786 if (rightIsAuto) {
787 // If both are 'auto' (their initial values), the computed values are 0
788 aComputedOffsets.left = aComputedOffsets.right = 0;
789 } else {
790 // 'Right' isn't 'auto' so compute its value
791 aComputedOffsets.right = nsLayoutUtils::
792 ComputeCBDependentValue(aContainingBlockWidth,
793 position->mOffset.GetRight());
795 // Computed value for 'left' is minus the value of 'right'
796 aComputedOffsets.left = -aComputedOffsets.right;
797 }
799 } else {
800 NS_ASSERTION(rightIsAuto, "unexpected specified constraint");
802 // 'Left' isn't 'auto' so compute its value
803 aComputedOffsets.left = nsLayoutUtils::
804 ComputeCBDependentValue(aContainingBlockWidth,
805 position->mOffset.GetLeft());
807 // Computed value for 'right' is minus the value of 'left'
808 aComputedOffsets.right = -aComputedOffsets.left;
809 }
811 // Compute the 'top' and 'bottom' values. The 'top' and 'bottom' properties
812 // move relatively positioned elements up and down. They also must be each
813 // other's negative
814 bool topIsAuto = eStyleUnit_Auto == position->mOffset.GetTopUnit();
815 bool bottomIsAuto = eStyleUnit_Auto == position->mOffset.GetBottomUnit();
817 // Check for percentage based values and a containing block height that
818 // depends on the content height. Treat them like 'auto'
819 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
820 if (position->OffsetHasPercent(NS_SIDE_TOP)) {
821 topIsAuto = true;
822 }
823 if (position->OffsetHasPercent(NS_SIDE_BOTTOM)) {
824 bottomIsAuto = true;
825 }
826 }
828 // If neither is 'auto', 'bottom' is ignored
829 if (!topIsAuto && !bottomIsAuto) {
830 bottomIsAuto = true;
831 }
833 if (topIsAuto) {
834 if (bottomIsAuto) {
835 // If both are 'auto' (their initial values), the computed values are 0
836 aComputedOffsets.top = aComputedOffsets.bottom = 0;
837 } else {
838 // 'Bottom' isn't 'auto' so compute its value
839 aComputedOffsets.bottom = nsLayoutUtils::
840 ComputeHeightDependentValue(aContainingBlockHeight,
841 position->mOffset.GetBottom());
843 // Computed value for 'top' is minus the value of 'bottom'
844 aComputedOffsets.top = -aComputedOffsets.bottom;
845 }
847 } else {
848 NS_ASSERTION(bottomIsAuto, "unexpected specified constraint");
850 // 'Top' isn't 'auto' so compute its value
851 aComputedOffsets.top = nsLayoutUtils::
852 ComputeHeightDependentValue(aContainingBlockHeight,
853 position->mOffset.GetTop());
855 // Computed value for 'bottom' is minus the value of 'top'
856 aComputedOffsets.bottom = -aComputedOffsets.top;
857 }
859 // Store the offset
860 FrameProperties props = aFrame->Properties();
861 nsMargin* offsets = static_cast<nsMargin*>
862 (props.Get(nsIFrame::ComputedOffsetProperty()));
863 if (offsets) {
864 *offsets = aComputedOffsets;
865 } else {
866 props.Set(nsIFrame::ComputedOffsetProperty(),
867 new nsMargin(aComputedOffsets));
868 }
869 }
871 /* static */ void
872 nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame,
873 const nsMargin& aComputedOffsets,
874 nsPoint* aPosition)
875 {
876 if (!aFrame->IsRelativelyPositioned()) {
877 NS_ASSERTION(!aFrame->Properties().Get(nsIFrame::NormalPositionProperty()),
878 "We assume that changing the 'position' property causes "
879 "frame reconstruction. If that ever changes, this code "
880 "should call "
881 "props.Delete(nsIFrame::NormalPositionProperty())");
882 return;
883 }
885 // Store the normal position
886 FrameProperties props = aFrame->Properties();
887 nsPoint* normalPosition = static_cast<nsPoint*>
888 (props.Get(nsIFrame::NormalPositionProperty()));
889 if (normalPosition) {
890 *normalPosition = *aPosition;
891 } else {
892 props.Set(nsIFrame::NormalPositionProperty(), new nsPoint(*aPosition));
893 }
895 const nsStyleDisplay* display = aFrame->StyleDisplay();
896 if (NS_STYLE_POSITION_RELATIVE == display->mPosition) {
897 *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top);
898 } else if (NS_STYLE_POSITION_STICKY == display->mPosition &&
899 !aFrame->GetNextContinuation() &&
900 !aFrame->GetPrevContinuation() &&
901 !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
902 // Sticky positioning for elements with multiple frames needs to be
903 // computed all at once. We can't safely do that here because we might be
904 // partway through (re)positioning the frames, so leave it until the scroll
905 // container reflows and calls StickyScrollContainer::UpdatePositions.
906 // For single-frame sticky positioned elements, though, go ahead and apply
907 // it now to avoid unnecessary overflow updates later.
908 StickyScrollContainer* ssc =
909 StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame);
910 if (ssc) {
911 *aPosition = ssc->ComputePosition(aFrame);
912 }
913 }
914 }
916 nsIFrame*
917 nsHTMLReflowState::GetHypotheticalBoxContainer(nsIFrame* aFrame,
918 nscoord& aCBLeftEdge,
919 nscoord& aCBWidth)
920 {
921 aFrame = aFrame->GetContainingBlock();
922 NS_ASSERTION(aFrame != frame, "How did that happen?");
924 /* Now aFrame is the containing block we want */
926 /* Check whether the containing block is currently being reflowed.
927 If so, use the info from the reflow state. */
928 const nsHTMLReflowState* state;
929 if (aFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
930 for (state = parentReflowState; state && state->frame != aFrame;
931 state = state->parentReflowState) {
932 /* do nothing */
933 }
934 } else {
935 state = nullptr;
936 }
938 if (state) {
939 aCBLeftEdge = state->ComputedPhysicalBorderPadding().left;
940 aCBWidth = state->ComputedWidth();
941 } else {
942 /* Didn't find a reflow state for aFrame. Just compute the information we
943 want, on the assumption that aFrame already knows its size. This really
944 ought to be true by now. */
945 NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW),
946 "aFrame shouldn't be in reflow; we'll lie if it is");
947 nsMargin borderPadding = aFrame->GetUsedBorderAndPadding();
948 aCBLeftEdge = borderPadding.left;
949 aCBWidth = aFrame->GetSize().width - borderPadding.LeftRight();
950 }
952 return aFrame;
953 }
955 // When determining the hypothetical box that would have been if the element
956 // had been in the flow we may not be able to exactly determine both the left
957 // and right edges. For example, if the element is a non-replaced inline-level
958 // element we would have to reflow it in order to determine it desired width.
959 // In that case depending on the progression direction either the left or
960 // right edge would be marked as not being exact
961 struct nsHypotheticalBox {
962 // offsets from left edge of containing block (which is a padding edge)
963 nscoord mLeft, mRight;
964 // offset from top edge of containing block (which is a padding edge)
965 nscoord mTop;
966 #ifdef DEBUG
967 bool mLeftIsExact, mRightIsExact;
968 #endif
970 nsHypotheticalBox() {
971 #ifdef DEBUG
972 mLeftIsExact = mRightIsExact = false;
973 #endif
974 }
975 };
977 static bool
978 GetIntrinsicSizeFor(nsIFrame* aFrame, nsSize& aIntrinsicSize, nsIAtom* aFrameType)
979 {
980 // See if it is an image frame
981 bool success = false;
983 // Currently the only type of replaced frame that we can get the intrinsic
984 // size for is an image frame
985 // XXX We should add back the GetReflowMetrics() function and one of the
986 // things should be the intrinsic size...
987 if (aFrameType == nsGkAtoms::imageFrame) {
988 nsImageFrame* imageFrame = (nsImageFrame*)aFrame;
990 if (NS_SUCCEEDED(imageFrame->GetIntrinsicImageSize(aIntrinsicSize))) {
991 success = (aIntrinsicSize != nsSize(0, 0));
992 }
993 }
994 return success;
995 }
997 /**
998 * aInsideBoxSizing returns the part of the horizontal padding, border,
999 * and margin that goes inside the edge given by box-sizing;
1000 * aOutsideBoxSizing returns the rest.
1001 */
1002 void
1003 nsHTMLReflowState::CalculateHorizBorderPaddingMargin(
1004 nscoord aContainingBlockWidth,
1005 nscoord* aInsideBoxSizing,
1006 nscoord* aOutsideBoxSizing)
1007 {
1008 const nsMargin& border = mStyleBorder->GetComputedBorder();
1009 nsMargin padding, margin;
1011 // See if the style system can provide us the padding directly
1012 if (!mStylePadding->GetPadding(padding)) {
1013 // We have to compute the left and right values
1014 padding.left = nsLayoutUtils::
1015 ComputeCBDependentValue(aContainingBlockWidth,
1016 mStylePadding->mPadding.GetLeft());
1017 padding.right = nsLayoutUtils::
1018 ComputeCBDependentValue(aContainingBlockWidth,
1019 mStylePadding->mPadding.GetRight());
1020 }
1022 // See if the style system can provide us the margin directly
1023 if (!mStyleMargin->GetMargin(margin)) {
1024 // We have to compute the left and right values
1025 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit()) {
1026 // XXX FIXME (or does CalculateBlockSideMargins do this?)
1027 margin.left = 0; // just ignore
1028 } else {
1029 margin.left = nsLayoutUtils::
1030 ComputeCBDependentValue(aContainingBlockWidth,
1031 mStyleMargin->mMargin.GetLeft());
1032 }
1033 if (eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit()) {
1034 // XXX FIXME (or does CalculateBlockSideMargins do this?)
1035 margin.right = 0; // just ignore
1036 } else {
1037 margin.right = nsLayoutUtils::
1038 ComputeCBDependentValue(aContainingBlockWidth,
1039 mStyleMargin->mMargin.GetRight());
1040 }
1041 }
1043 nscoord outside =
1044 padding.LeftRight() + border.LeftRight() + margin.LeftRight();
1045 nscoord inside = 0;
1046 switch (mStylePosition->mBoxSizing) {
1047 case NS_STYLE_BOX_SIZING_BORDER:
1048 inside += border.LeftRight();
1049 // fall through
1050 case NS_STYLE_BOX_SIZING_PADDING:
1051 inside += padding.LeftRight();
1052 }
1053 outside -= inside;
1054 *aInsideBoxSizing = inside;
1055 *aOutsideBoxSizing = outside;
1056 return;
1057 }
1059 /**
1060 * Returns true iff a pre-order traversal of the normal child
1061 * frames rooted at aFrame finds no non-empty frame before aDescendant.
1062 */
1063 static bool AreAllEarlierInFlowFramesEmpty(nsIFrame* aFrame,
1064 nsIFrame* aDescendant, bool* aFound) {
1065 if (aFrame == aDescendant) {
1066 *aFound = true;
1067 return true;
1068 }
1069 if (!aFrame->IsSelfEmpty()) {
1070 *aFound = false;
1071 return false;
1072 }
1073 for (nsIFrame* f = aFrame->GetFirstPrincipalChild(); f; f = f->GetNextSibling()) {
1074 bool allEmpty = AreAllEarlierInFlowFramesEmpty(f, aDescendant, aFound);
1075 if (*aFound || !allEmpty) {
1076 return allEmpty;
1077 }
1078 }
1079 *aFound = false;
1080 return true;
1081 }
1083 // Calculate the hypothetical box that the element would have if it were in
1084 // the flow. The values returned are relative to the padding edge of the
1085 // absolute containing block
1086 // aContainingBlock is the placeholder's containing block (XXX rename it?)
1087 // cbrs->frame is the actual containing block
1088 void
1089 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext* aPresContext,
1090 nsIFrame* aPlaceholderFrame,
1091 nsIFrame* aContainingBlock,
1092 nscoord aBlockLeftContentEdge,
1093 nscoord aBlockContentWidth,
1094 const nsHTMLReflowState* cbrs,
1095 nsHypotheticalBox& aHypotheticalBox,
1096 nsIAtom* aFrameType)
1097 {
1098 NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
1099 "mOriginalDisplay has not been properly initialized");
1101 // If it's a replaced element and it has a 'auto' value for 'width', see if we
1102 // can get the intrinsic size. This will allow us to exactly determine both the
1103 // left and right edges
1104 bool isAutoWidth = mStylePosition->mWidth.GetUnit() == eStyleUnit_Auto;
1105 nsSize intrinsicSize;
1106 bool knowIntrinsicSize = false;
1107 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
1108 // See if we can get the intrinsic size of the element
1109 knowIntrinsicSize = GetIntrinsicSizeFor(frame, intrinsicSize, aFrameType);
1110 }
1112 // See if we can calculate what the box width would have been if the
1113 // element had been in the flow
1114 nscoord boxWidth;
1115 bool knowBoxWidth = false;
1116 if ((NS_STYLE_DISPLAY_INLINE == mStyleDisplay->mOriginalDisplay) &&
1117 !NS_FRAME_IS_REPLACED(mFrameType)) {
1118 // For non-replaced inline-level elements the 'width' property doesn't apply,
1119 // so we don't know what the width would have been without reflowing it
1121 } else {
1122 // It's either a replaced inline-level element or a block-level element
1124 // Determine the total amount of horizontal border/padding/margin that
1125 // the element would have had if it had been in the flow. Note that we
1126 // ignore any 'auto' and 'inherit' values
1127 nscoord insideBoxSizing, outsideBoxSizing;
1128 CalculateHorizBorderPaddingMargin(aBlockContentWidth,
1129 &insideBoxSizing, &outsideBoxSizing);
1131 if (NS_FRAME_IS_REPLACED(mFrameType) && isAutoWidth) {
1132 // It's a replaced element with an 'auto' width so the box width is
1133 // its intrinsic size plus any border/padding/margin
1134 if (knowIntrinsicSize) {
1135 boxWidth = intrinsicSize.width + outsideBoxSizing + insideBoxSizing;
1136 knowBoxWidth = true;
1137 }
1139 } else if (isAutoWidth) {
1140 // The box width is the containing block width
1141 boxWidth = aBlockContentWidth;
1142 knowBoxWidth = true;
1144 } else {
1145 // We need to compute it. It's important we do this, because if it's
1146 // percentage based this computed value may be different from the computed
1147 // value calculated using the absolute containing block width
1148 boxWidth = ComputeWidthValue(aBlockContentWidth,
1149 insideBoxSizing, outsideBoxSizing,
1150 mStylePosition->mWidth) +
1151 insideBoxSizing + outsideBoxSizing;
1152 knowBoxWidth = true;
1153 }
1154 }
1156 // Get the 'direction' of the block
1157 const nsStyleVisibility* blockVis = aContainingBlock->StyleVisibility();
1159 // Get the placeholder x-offset and y-offset in the coordinate
1160 // space of its containing block
1161 // XXXbz the placeholder is not fully reflowed yet if our containing block is
1162 // relatively positioned...
1163 nsPoint placeholderOffset = aPlaceholderFrame->GetOffsetTo(aContainingBlock);
1165 // First, determine the hypothetical box's mTop. We want to check the
1166 // content insertion frame of aContainingBlock for block-ness, but make
1167 // sure to compute all coordinates in the coordinate system of
1168 // aContainingBlock.
1169 nsBlockFrame* blockFrame =
1170 nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame());
1171 if (blockFrame) {
1172 nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y;
1173 bool isValid;
1174 nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
1175 if (!isValid) {
1176 // Give up. We're probably dealing with somebody using
1177 // position:absolute inside native-anonymous content anyway.
1178 aHypotheticalBox.mTop = placeholderOffset.y;
1179 } else {
1180 NS_ASSERTION(iter.GetContainer() == blockFrame,
1181 "Found placeholder in wrong block!");
1182 nsBlockFrame::line_iterator lineBox = iter.GetLine();
1184 // How we determine the hypothetical box depends on whether the element
1185 // would have been inline-level or block-level
1186 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
1187 // Use the top of the inline box which the placeholder lives in
1188 // as the hypothetical box's top.
1189 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset;
1190 } else {
1191 // The element would have been block-level which means it would
1192 // be below the line containing the placeholder frame, unless
1193 // all the frames before it are empty. In that case, it would
1194 // have been just before this line.
1195 // XXXbz the line box is not fully reflowed yet if our
1196 // containing block is relatively positioned...
1197 if (lineBox != iter.End()) {
1198 nsIFrame * firstFrame = lineBox->mFirstChild;
1199 bool found = false;
1200 bool allEmpty = true;
1201 while (firstFrame) { // See bug 223064
1202 allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
1203 aPlaceholderFrame, &found);
1204 if (found || !allEmpty)
1205 break;
1206 firstFrame = firstFrame->GetNextSibling();
1207 }
1208 NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
1210 if (allEmpty) {
1211 // The top of the hypothetical box is the top of the line
1212 // containing the placeholder, since there is nothing in the
1213 // line before our placeholder except empty frames.
1214 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().y + blockYOffset;
1215 } else {
1216 // The top of the hypothetical box is just below the line
1217 // containing the placeholder.
1218 aHypotheticalBox.mTop = lineBox->GetPhysicalBounds().YMost() + blockYOffset;
1219 }
1220 } else {
1221 // Just use the placeholder's y-offset wrt the containing block
1222 aHypotheticalBox.mTop = placeholderOffset.y;
1223 }
1224 }
1225 }
1226 } else {
1227 // The containing block is not a block, so it's probably something
1228 // like a XUL box, etc.
1229 // Just use the placeholder's y-offset
1230 aHypotheticalBox.mTop = placeholderOffset.y;
1231 }
1233 // Second, determine the hypothetical box's mLeft & mRight
1234 // To determine the left and right offsets we need to look at the block's 'direction'
1235 if (NS_STYLE_DIRECTION_LTR == blockVis->mDirection) {
1236 // How we determine the hypothetical box depends on whether the element
1237 // would have been inline-level or block-level
1238 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
1239 // The placeholder represents the left edge of the hypothetical box
1240 aHypotheticalBox.mLeft = placeholderOffset.x;
1241 } else {
1242 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1243 }
1244 #ifdef DEBUG
1245 aHypotheticalBox.mLeftIsExact = true;
1246 #endif
1248 if (knowBoxWidth) {
1249 aHypotheticalBox.mRight = aHypotheticalBox.mLeft + boxWidth;
1250 #ifdef DEBUG
1251 aHypotheticalBox.mRightIsExact = true;
1252 #endif
1253 } else {
1254 // We can't compute the right edge because we don't know the desired
1255 // width. So instead use the right content edge of the block parent,
1256 // but remember it's not exact
1257 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1258 #ifdef DEBUG
1259 aHypotheticalBox.mRightIsExact = false;
1260 #endif
1261 }
1263 } else {
1264 // The placeholder represents the right edge of the hypothetical box
1265 if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) {
1266 aHypotheticalBox.mRight = placeholderOffset.x;
1267 } else {
1268 aHypotheticalBox.mRight = aBlockLeftContentEdge + aBlockContentWidth;
1269 }
1270 #ifdef DEBUG
1271 aHypotheticalBox.mRightIsExact = true;
1272 #endif
1274 if (knowBoxWidth) {
1275 aHypotheticalBox.mLeft = aHypotheticalBox.mRight - boxWidth;
1276 #ifdef DEBUG
1277 aHypotheticalBox.mLeftIsExact = true;
1278 #endif
1279 } else {
1280 // We can't compute the left edge because we don't know the desired
1281 // width. So instead use the left content edge of the block parent,
1282 // but remember it's not exact
1283 aHypotheticalBox.mLeft = aBlockLeftContentEdge;
1284 #ifdef DEBUG
1285 aHypotheticalBox.mLeftIsExact = false;
1286 #endif
1287 }
1289 }
1291 // The current coordinate space is that of the nearest block to the placeholder.
1292 // Convert to the coordinate space of the absolute containing block
1293 // One weird thing here is that for fixed-positioned elements we want to do
1294 // the conversion incorrectly; specifically we want to ignore any scrolling
1295 // that may have happened;
1296 nsPoint cbOffset;
1297 if (mStyleDisplay->mPosition == NS_STYLE_POSITION_FIXED &&
1298 // Exclude cases inside -moz-transform where fixed is like absolute.
1299 nsLayoutUtils::IsReallyFixedPos(frame)) {
1300 // In this case, cbrs->frame will always be an ancestor of
1301 // aContainingBlock, so can just walk our way up the frame tree.
1302 // Make sure to not add positions of frames whose parent is a
1303 // scrollFrame, since we're doing fixed positioning, which assumes
1304 // everything is scrolled to (0,0).
1305 cbOffset.MoveTo(0, 0);
1306 do {
1307 NS_ASSERTION(aContainingBlock,
1308 "Should hit cbrs->frame before we run off the frame tree!");
1309 cbOffset += aContainingBlock->GetPositionIgnoringScrolling();
1310 aContainingBlock = aContainingBlock->GetParent();
1311 } while (aContainingBlock != cbrs->frame);
1312 } else {
1313 // XXXldb We need to either ignore scrolling for the absolute
1314 // positioning case too (and take the incompatibility) or figure out
1315 // how to make these positioned elements actually *move* when we
1316 // scroll, and thus avoid the resulting incremental reflow bugs.
1317 cbOffset = aContainingBlock->GetOffsetTo(cbrs->frame);
1318 }
1319 aHypotheticalBox.mLeft += cbOffset.x;
1320 aHypotheticalBox.mTop += cbOffset.y;
1321 aHypotheticalBox.mRight += cbOffset.x;
1323 // The specified offsets are relative to the absolute containing block's
1324 // padding edge and our current values are relative to the border edge, so
1325 // translate.
1326 nsMargin border = cbrs->ComputedPhysicalBorderPadding() - cbrs->ComputedPhysicalPadding();
1327 aHypotheticalBox.mLeft -= border.left;
1328 aHypotheticalBox.mRight -= border.left;
1329 aHypotheticalBox.mTop -= border.top;
1330 }
1332 void
1333 nsHTMLReflowState::InitAbsoluteConstraints(nsPresContext* aPresContext,
1334 const nsHTMLReflowState* cbrs,
1335 nscoord containingBlockWidth,
1336 nscoord containingBlockHeight,
1337 nsIAtom* aFrameType)
1338 {
1339 NS_PRECONDITION(containingBlockHeight != NS_AUTOHEIGHT,
1340 "containing block height must be constrained");
1342 NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame,
1343 "InitAbsoluteConstraints should not be called on table frames");
1344 NS_ASSERTION(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
1345 "Why are we here?");
1347 // Get the placeholder frame
1348 nsIFrame* placeholderFrame;
1350 placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(frame);
1351 NS_ASSERTION(nullptr != placeholderFrame, "no placeholder frame");
1353 // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
1354 // 'auto', then compute the hypothetical box of where the element would
1355 // have been if it had been in the flow
1356 nsHypotheticalBox hypotheticalBox;
1357 if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
1358 (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
1359 ((eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) &&
1360 (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()))) {
1361 // Find the nearest containing block frame to the placeholder frame,
1362 // and return its left edge and width.
1363 nscoord cbLeftEdge, cbWidth;
1364 nsIFrame* cbFrame = GetHypotheticalBoxContainer(placeholderFrame,
1365 cbLeftEdge,
1366 cbWidth);
1368 CalculateHypotheticalBox(aPresContext, placeholderFrame, cbFrame,
1369 cbLeftEdge, cbWidth, cbrs, hypotheticalBox, aFrameType);
1370 }
1372 // Initialize the 'left' and 'right' computed offsets
1373 // XXX Handle new 'static-position' value...
1374 bool leftIsAuto = false, rightIsAuto = false;
1375 if (eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) {
1376 ComputedPhysicalOffsets().left = 0;
1377 leftIsAuto = true;
1378 } else {
1379 ComputedPhysicalOffsets().left = nsLayoutUtils::
1380 ComputeCBDependentValue(containingBlockWidth,
1381 mStylePosition->mOffset.GetLeft());
1382 }
1383 if (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit()) {
1384 ComputedPhysicalOffsets().right = 0;
1385 rightIsAuto = true;
1386 } else {
1387 ComputedPhysicalOffsets().right = nsLayoutUtils::
1388 ComputeCBDependentValue(containingBlockWidth,
1389 mStylePosition->mOffset.GetRight());
1390 }
1392 // Use the horizontal component of the hypothetical box in the cases
1393 // where it's needed.
1394 if (leftIsAuto && rightIsAuto) {
1395 // Use the direction of the original ("static-position") containing block
1396 // to dictate whether 'left' or 'right' is treated like 'static-position'.
1397 if (NS_STYLE_DIRECTION_LTR == placeholderFrame->GetContainingBlock()
1398 ->StyleVisibility()->mDirection) {
1399 NS_ASSERTION(hypotheticalBox.mLeftIsExact, "should always have "
1400 "exact value on containing block's start side");
1401 ComputedPhysicalOffsets().left = hypotheticalBox.mLeft;
1402 leftIsAuto = false;
1403 } else {
1404 NS_ASSERTION(hypotheticalBox.mRightIsExact, "should always have "
1405 "exact value on containing block's start side");
1406 ComputedPhysicalOffsets().right = containingBlockWidth - hypotheticalBox.mRight;
1407 rightIsAuto = false;
1408 }
1409 }
1411 // Initialize the 'top' and 'bottom' computed offsets
1412 bool topIsAuto = false, bottomIsAuto = false;
1413 if (eStyleUnit_Auto == mStylePosition->mOffset.GetTopUnit()) {
1414 ComputedPhysicalOffsets().top = 0;
1415 topIsAuto = true;
1416 } else {
1417 ComputedPhysicalOffsets().top = nsLayoutUtils::
1418 ComputeHeightDependentValue(containingBlockHeight,
1419 mStylePosition->mOffset.GetTop());
1420 }
1421 if (eStyleUnit_Auto == mStylePosition->mOffset.GetBottomUnit()) {
1422 ComputedPhysicalOffsets().bottom = 0;
1423 bottomIsAuto = true;
1424 } else {
1425 ComputedPhysicalOffsets().bottom = nsLayoutUtils::
1426 ComputeHeightDependentValue(containingBlockHeight,
1427 mStylePosition->mOffset.GetBottom());
1428 }
1430 if (topIsAuto && bottomIsAuto) {
1431 // Treat 'top' like 'static-position'
1432 ComputedPhysicalOffsets().top = hypotheticalBox.mTop;
1433 topIsAuto = false;
1434 }
1436 bool widthIsAuto = eStyleUnit_Auto == mStylePosition->mWidth.GetUnit();
1437 bool heightIsAuto = eStyleUnit_Auto == mStylePosition->mHeight.GetUnit();
1439 uint32_t computeSizeFlags = 0;
1440 if (leftIsAuto || rightIsAuto) {
1441 computeSizeFlags |= nsIFrame::eShrinkWrap;
1442 }
1444 {
1445 AutoMaybeDisableFontInflation an(frame);
1447 nsSize size =
1448 frame->ComputeSize(rendContext,
1449 nsSize(containingBlockWidth,
1450 containingBlockHeight),
1451 containingBlockWidth, // XXX or mAvailableWidth?
1452 nsSize(ComputedPhysicalMargin().LeftRight() +
1453 ComputedPhysicalOffsets().LeftRight(),
1454 ComputedPhysicalMargin().TopBottom() +
1455 ComputedPhysicalOffsets().TopBottom()),
1456 nsSize(ComputedPhysicalBorderPadding().LeftRight() -
1457 ComputedPhysicalPadding().LeftRight(),
1458 ComputedPhysicalBorderPadding().TopBottom() -
1459 ComputedPhysicalPadding().TopBottom()),
1460 nsSize(ComputedPhysicalPadding().LeftRight(),
1461 ComputedPhysicalPadding().TopBottom()),
1462 computeSizeFlags);
1463 ComputedWidth() = size.width;
1464 ComputedHeight() = size.height;
1465 }
1466 NS_ASSERTION(ComputedWidth() >= 0, "Bogus width");
1467 NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE ||
1468 ComputedHeight() >= 0, "Bogus height");
1470 // XXX Now that we have ComputeSize, can we condense many of the
1471 // branches off of widthIsAuto?
1473 if (leftIsAuto) {
1474 // We know 'right' is not 'auto' anymore thanks to the hypothetical
1475 // box code above.
1476 // Solve for 'left'.
1477 if (widthIsAuto) {
1478 // XXXldb This, and the corresponding code in
1479 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1480 // we always compute widths.
1481 ComputedPhysicalOffsets().left = NS_AUTOOFFSET;
1482 } else {
1483 ComputedPhysicalOffsets().left = containingBlockWidth - ComputedPhysicalMargin().left -
1484 ComputedPhysicalBorderPadding().left - ComputedWidth() - ComputedPhysicalBorderPadding().right -
1485 ComputedPhysicalMargin().right - ComputedPhysicalOffsets().right;
1487 }
1488 } else if (rightIsAuto) {
1489 // We know 'left' is not 'auto' anymore thanks to the hypothetical
1490 // box code above.
1491 // Solve for 'right'.
1492 if (widthIsAuto) {
1493 // XXXldb This, and the corresponding code in
1494 // nsAbsoluteContainingBlock.cpp, could probably go away now that
1495 // we always compute widths.
1496 ComputedPhysicalOffsets().right = NS_AUTOOFFSET;
1497 } else {
1498 ComputedPhysicalOffsets().right = containingBlockWidth - ComputedPhysicalOffsets().left -
1499 ComputedPhysicalMargin().left - ComputedPhysicalBorderPadding().left - ComputedWidth() -
1500 ComputedPhysicalBorderPadding().right - ComputedPhysicalMargin().right;
1501 }
1502 } else {
1503 // Neither 'left' nor 'right' is 'auto'. However, the width might
1504 // still not fill all the available space (even though we didn't
1505 // shrink-wrap) in case:
1506 // * width was specified
1507 // * we're dealing with a replaced element
1508 // * width was constrained by min-width or max-width.
1510 nscoord availMarginSpace = containingBlockWidth -
1511 ComputedPhysicalOffsets().LeftRight() -
1512 ComputedPhysicalMargin().LeftRight() -
1513 ComputedPhysicalBorderPadding().LeftRight() -
1514 ComputedWidth();
1515 bool marginLeftIsAuto =
1516 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
1517 bool marginRightIsAuto =
1518 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
1520 if (marginLeftIsAuto) {
1521 if (marginRightIsAuto) {
1522 if (availMarginSpace < 0) {
1523 // Note that this case is different from the neither-'auto'
1524 // case below, where the spec says to ignore 'left'/'right'.
1525 if (cbrs &&
1526 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1527 // Ignore the specified value for 'margin-left'.
1528 ComputedPhysicalMargin().left = availMarginSpace;
1529 } else {
1530 // Ignore the specified value for 'margin-right'.
1531 ComputedPhysicalMargin().right = availMarginSpace;
1532 }
1533 } else {
1534 // Both 'margin-left' and 'margin-right' are 'auto', so they get
1535 // equal values
1536 ComputedPhysicalMargin().left = availMarginSpace / 2;
1537 ComputedPhysicalMargin().right = availMarginSpace - ComputedPhysicalMargin().left;
1538 }
1539 } else {
1540 // Just 'margin-left' is 'auto'
1541 ComputedPhysicalMargin().left = availMarginSpace;
1542 }
1543 } else {
1544 if (marginRightIsAuto) {
1545 // Just 'margin-right' is 'auto'
1546 ComputedPhysicalMargin().right = availMarginSpace;
1547 } else {
1548 // We're over-constrained so use the direction of the containing
1549 // block to dictate which value to ignore. (And note that the
1550 // spec says to ignore 'left' or 'right' rather than
1551 // 'margin-left' or 'margin-right'.)
1552 // Note that this case is different from the both-'auto' case
1553 // above, where the spec says to ignore
1554 // 'margin-left'/'margin-right'.
1555 if (cbrs &&
1556 NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
1557 // Ignore the specified value for 'left'.
1558 ComputedPhysicalOffsets().left += availMarginSpace;
1559 } else {
1560 // Ignore the specified value for 'right'.
1561 ComputedPhysicalOffsets().right += availMarginSpace;
1562 }
1563 }
1564 }
1565 }
1567 if (topIsAuto) {
1568 // solve for 'top'
1569 if (heightIsAuto) {
1570 ComputedPhysicalOffsets().top = NS_AUTOOFFSET;
1571 } else {
1572 ComputedPhysicalOffsets().top = containingBlockHeight - ComputedPhysicalMargin().top -
1573 ComputedPhysicalBorderPadding().top - ComputedHeight() - ComputedPhysicalBorderPadding().bottom -
1574 ComputedPhysicalMargin().bottom - ComputedPhysicalOffsets().bottom;
1575 }
1576 } else if (bottomIsAuto) {
1577 // solve for 'bottom'
1578 if (heightIsAuto) {
1579 ComputedPhysicalOffsets().bottom = NS_AUTOOFFSET;
1580 } else {
1581 ComputedPhysicalOffsets().bottom = containingBlockHeight - ComputedPhysicalOffsets().top -
1582 ComputedPhysicalMargin().top - ComputedPhysicalBorderPadding().top - ComputedHeight() -
1583 ComputedPhysicalBorderPadding().bottom - ComputedPhysicalMargin().bottom;
1584 }
1585 } else {
1586 // Neither 'top' nor 'bottom' is 'auto'.
1587 nscoord autoHeight = containingBlockHeight -
1588 ComputedPhysicalOffsets().TopBottom() -
1589 ComputedPhysicalMargin().TopBottom() -
1590 ComputedPhysicalBorderPadding().TopBottom();
1591 if (autoHeight < 0) {
1592 autoHeight = 0;
1593 }
1595 if (ComputedHeight() == NS_UNCONSTRAINEDSIZE) {
1596 // For non-replaced elements with 'height' auto, the 'height'
1597 // fills the remaining space.
1598 ComputedHeight() = autoHeight;
1600 // XXX Do these need box-sizing adjustments?
1601 if (ComputedHeight() > ComputedMaxHeight())
1602 ComputedHeight() = ComputedMaxHeight();
1603 if (ComputedHeight() < ComputedMinHeight())
1604 ComputedHeight() = ComputedMinHeight();
1605 }
1607 // The height might still not fill all the available space in case:
1608 // * height was specified
1609 // * we're dealing with a replaced element
1610 // * height was constrained by min-height or max-height.
1611 nscoord availMarginSpace = autoHeight - ComputedHeight();
1612 bool marginTopIsAuto =
1613 eStyleUnit_Auto == mStyleMargin->mMargin.GetTopUnit();
1614 bool marginBottomIsAuto =
1615 eStyleUnit_Auto == mStyleMargin->mMargin.GetBottomUnit();
1617 if (marginTopIsAuto) {
1618 if (marginBottomIsAuto) {
1619 if (availMarginSpace < 0) {
1620 // FIXME: Note that the spec doesn't actually say we should do this!
1621 ComputedPhysicalMargin().bottom = availMarginSpace;
1622 } else {
1623 // Both 'margin-top' and 'margin-bottom' are 'auto', so they get
1624 // equal values
1625 ComputedPhysicalMargin().top = availMarginSpace / 2;
1626 ComputedPhysicalMargin().bottom = availMarginSpace - ComputedPhysicalMargin().top;
1627 }
1628 } else {
1629 // Just 'margin-top' is 'auto'
1630 ComputedPhysicalMargin().top = availMarginSpace;
1631 }
1632 } else {
1633 if (marginBottomIsAuto) {
1634 // Just 'margin-bottom' is 'auto'
1635 ComputedPhysicalMargin().bottom = availMarginSpace;
1636 } else {
1637 // We're over-constrained so ignore the specified value for
1638 // 'bottom'. (And note that the spec says to ignore 'bottom'
1639 // rather than 'margin-bottom'.)
1640 ComputedPhysicalOffsets().bottom += availMarginSpace;
1641 }
1642 }
1643 }
1644 }
1646 nscoord
1647 GetVerticalMarginBorderPadding(const nsHTMLReflowState* aReflowState)
1648 {
1649 nscoord result = 0;
1650 if (!aReflowState) return result;
1652 // zero auto margins
1653 nsMargin margin = aReflowState->ComputedPhysicalMargin();
1654 if (NS_AUTOMARGIN == margin.top)
1655 margin.top = 0;
1656 if (NS_AUTOMARGIN == margin.bottom)
1657 margin.bottom = 0;
1659 result += margin.top + margin.bottom;
1660 result += aReflowState->ComputedPhysicalBorderPadding().top +
1661 aReflowState->ComputedPhysicalBorderPadding().bottom;
1663 return result;
1664 }
1666 /* Get the height based on the viewport of the containing block specified
1667 * in aReflowState when the containing block has mComputedHeight == NS_AUTOHEIGHT
1668 * This will walk up the chain of containing blocks looking for a computed height
1669 * until it finds the canvas frame, or it encounters a frame that is not a block,
1670 * area, or scroll frame. This handles compatibility with IE (see bug 85016 and bug 219693)
1671 *
1672 * When we encounter scrolledContent block frames, we skip over them, since they are guaranteed to not be useful for computing the containing block.
1673 *
1674 * See also IsQuirkContainingBlockHeight.
1675 */
1676 static nscoord
1677 CalcQuirkContainingBlockHeight(const nsHTMLReflowState* aCBReflowState)
1678 {
1679 const nsHTMLReflowState* firstAncestorRS = nullptr; // a candidate for html frame
1680 const nsHTMLReflowState* secondAncestorRS = nullptr; // a candidate for body frame
1682 // initialize the default to NS_AUTOHEIGHT as this is the containings block
1683 // computed height when this function is called. It is possible that we
1684 // don't alter this height especially if we are restricted to one level
1685 nscoord result = NS_AUTOHEIGHT;
1687 const nsHTMLReflowState* rs = aCBReflowState;
1688 for (; rs; rs = rs->parentReflowState) {
1689 nsIAtom* frameType = rs->frame->GetType();
1690 // if the ancestor is auto height then skip it and continue up if it
1691 // is the first block frame and possibly the body/html
1692 if (nsGkAtoms::blockFrame == frameType ||
1693 #ifdef MOZ_XUL
1694 nsGkAtoms::XULLabelFrame == frameType ||
1695 #endif
1696 nsGkAtoms::scrollFrame == frameType) {
1698 secondAncestorRS = firstAncestorRS;
1699 firstAncestorRS = rs;
1701 // If the current frame we're looking at is positioned, we don't want to
1702 // go any further (see bug 221784). The behavior we want here is: 1) If
1703 // not auto-height, use this as the percentage base. 2) If auto-height,
1704 // keep looking, unless the frame is positioned.
1705 if (NS_AUTOHEIGHT == rs->ComputedHeight()) {
1706 if (rs->frame->IsAbsolutelyPositioned()) {
1707 break;
1708 } else {
1709 continue;
1710 }
1711 }
1712 }
1713 else if (nsGkAtoms::canvasFrame == frameType) {
1714 // Always continue on to the height calculation
1715 }
1716 else if (nsGkAtoms::pageContentFrame == frameType) {
1717 nsIFrame* prevInFlow = rs->frame->GetPrevInFlow();
1718 // only use the page content frame for a height basis if it is the first in flow
1719 if (prevInFlow)
1720 break;
1721 }
1722 else {
1723 break;
1724 }
1726 // if the ancestor is the page content frame then the percent base is
1727 // the avail height, otherwise it is the computed height
1728 result = (nsGkAtoms::pageContentFrame == frameType)
1729 ? rs->AvailableHeight() : rs->ComputedHeight();
1730 // if unconstrained - don't sutract borders - would result in huge height
1731 if (NS_AUTOHEIGHT == result) return result;
1733 // if we got to the canvas or page content frame, then subtract out
1734 // margin/border/padding for the BODY and HTML elements
1735 if ((nsGkAtoms::canvasFrame == frameType) ||
1736 (nsGkAtoms::pageContentFrame == frameType)) {
1738 result -= GetVerticalMarginBorderPadding(firstAncestorRS);
1739 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1741 #ifdef DEBUG
1742 // make sure the first ancestor is the HTML and the second is the BODY
1743 if (firstAncestorRS) {
1744 nsIContent* frameContent = firstAncestorRS->frame->GetContent();
1745 if (frameContent) {
1746 nsIAtom *contentTag = frameContent->Tag();
1747 NS_ASSERTION(contentTag == nsGkAtoms::html, "First ancestor is not HTML");
1748 }
1749 }
1750 if (secondAncestorRS) {
1751 nsIContent* frameContent = secondAncestorRS->frame->GetContent();
1752 if (frameContent) {
1753 nsIAtom *contentTag = frameContent->Tag();
1754 NS_ASSERTION(contentTag == nsGkAtoms::body, "Second ancestor is not BODY");
1755 }
1756 }
1757 #endif
1759 }
1760 // if we got to the html frame (a block child of the canvas) ...
1761 else if (nsGkAtoms::blockFrame == frameType &&
1762 rs->parentReflowState &&
1763 nsGkAtoms::canvasFrame ==
1764 rs->parentReflowState->frame->GetType()) {
1765 // ... then subtract out margin/border/padding for the BODY element
1766 result -= GetVerticalMarginBorderPadding(secondAncestorRS);
1767 }
1768 break;
1769 }
1771 // Make sure not to return a negative height here!
1772 return std::max(result, 0);
1773 }
1775 // Called by InitConstraints() to compute the containing block rectangle for
1776 // the element. Handles the special logic for absolutely positioned elements
1777 void
1778 nsHTMLReflowState::ComputeContainingBlockRectangle(nsPresContext* aPresContext,
1779 const nsHTMLReflowState* aContainingBlockRS,
1780 nscoord& aContainingBlockWidth,
1781 nscoord& aContainingBlockHeight)
1782 {
1783 // Unless the element is absolutely positioned, the containing block is
1784 // formed by the content edge of the nearest block-level ancestor
1785 aContainingBlockWidth = aContainingBlockRS->ComputedWidth();
1786 aContainingBlockHeight = aContainingBlockRS->ComputedHeight();
1788 // mFrameType for abs-pos tables is NS_CSS_FRAME_TYPE_BLOCK, so we need to
1789 // special case them here.
1790 if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE ||
1791 (frame->GetType() == nsGkAtoms::tableFrame &&
1792 frame->IsAbsolutelyPositioned() &&
1793 (frame->GetParent()->GetStateBits() & NS_FRAME_OUT_OF_FLOW))) {
1794 // See if the ancestor is block-level or inline-level
1795 if (NS_FRAME_GET_TYPE(aContainingBlockRS->mFrameType) == NS_CSS_FRAME_TYPE_INLINE) {
1796 // Base our size on the actual size of the frame. In cases when this is
1797 // completely bogus (eg initial reflow), this code shouldn't even be
1798 // called, since the code in nsInlineFrame::Reflow will pass in
1799 // the containing block dimensions to our constructor.
1800 // XXXbz we should be taking the in-flows into account too, but
1801 // that's very hard.
1802 nsMargin computedBorder = aContainingBlockRS->ComputedPhysicalBorderPadding() -
1803 aContainingBlockRS->ComputedPhysicalPadding();
1804 aContainingBlockWidth = aContainingBlockRS->frame->GetRect().width -
1805 computedBorder.LeftRight();
1806 NS_ASSERTION(aContainingBlockWidth >= 0,
1807 "Negative containing block width!");
1808 aContainingBlockHeight = aContainingBlockRS->frame->GetRect().height -
1809 computedBorder.TopBottom();
1810 NS_ASSERTION(aContainingBlockHeight >= 0,
1811 "Negative containing block height!");
1812 } else {
1813 // If the ancestor is block-level, the containing block is formed by the
1814 // padding edge of the ancestor
1815 aContainingBlockWidth += aContainingBlockRS->ComputedPhysicalPadding().LeftRight();
1816 aContainingBlockHeight += aContainingBlockRS->ComputedPhysicalPadding().TopBottom();
1817 }
1818 } else {
1819 // an element in quirks mode gets a containing block based on looking for a
1820 // parent with a non-auto height if the element has a percent height
1821 // Note: We don't emulate this quirk for percents in calc().
1822 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1823 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode() &&
1824 mStylePosition->mHeight.GetUnit() == eStyleUnit_Percent) {
1825 aContainingBlockHeight = CalcQuirkContainingBlockHeight(aContainingBlockRS);
1826 }
1827 }
1828 }
1829 }
1831 static eNormalLineHeightControl GetNormalLineHeightCalcControl(void)
1832 {
1833 if (sNormalLineHeightControl == eUninitialized) {
1834 // browser.display.normal_lineheight_calc_control is not user
1835 // changeable, so no need to register callback for it.
1836 int32_t val =
1837 Preferences::GetInt("browser.display.normal_lineheight_calc_control",
1838 eNoExternalLeading);
1839 sNormalLineHeightControl = static_cast<eNormalLineHeightControl>(val);
1840 }
1841 return sNormalLineHeightControl;
1842 }
1844 static inline bool
1845 IsSideCaption(nsIFrame* aFrame, const nsStyleDisplay* aStyleDisplay)
1846 {
1847 if (aStyleDisplay->mDisplay != NS_STYLE_DISPLAY_TABLE_CAPTION)
1848 return false;
1849 uint8_t captionSide = aFrame->StyleTableBorder()->mCaptionSide;
1850 return captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
1851 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT;
1852 }
1854 static nsFlexContainerFrame*
1855 GetFlexContainer(nsIFrame* aFrame)
1856 {
1857 nsIFrame* parent = aFrame->GetParent();
1858 if (!parent ||
1859 parent->GetType() != nsGkAtoms::flexContainerFrame) {
1860 return nullptr;
1861 }
1863 return static_cast<nsFlexContainerFrame*>(parent);
1864 }
1866 // Flex items resolve percentage margin & padding against the flex
1867 // container's height (which is the containing block height).
1868 // For everything else: the CSS21 spec requires that margin and padding
1869 // percentage values are calculated with respect to the *width* of the
1870 // containing block, even for margin & padding in the vertical axis.
1871 static nscoord
1872 VerticalOffsetPercentBasis(const nsIFrame* aFrame,
1873 nscoord aContainingBlockWidth,
1874 nscoord aContainingBlockHeight)
1875 {
1876 if (!aFrame->IsFlexItem()) {
1877 return aContainingBlockWidth;
1878 }
1880 if (aContainingBlockHeight == NS_AUTOHEIGHT) {
1881 return 0;
1882 }
1884 return aContainingBlockHeight;
1885 }
1887 // XXX refactor this code to have methods for each set of properties
1888 // we are computing: width,height,line-height; margin; offsets
1890 void
1891 nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext,
1892 nscoord aContainingBlockWidth,
1893 nscoord aContainingBlockHeight,
1894 const nsMargin* aBorder,
1895 const nsMargin* aPadding,
1896 nsIAtom* aFrameType)
1897 {
1898 DISPLAY_INIT_CONSTRAINTS(frame, this,
1899 aContainingBlockWidth, aContainingBlockHeight,
1900 aBorder, aPadding);
1902 // If this is a reflow root, then set the computed width and
1903 // height equal to the available space
1904 if (nullptr == parentReflowState || mFlags.mDummyParentReflowState) {
1905 // XXXldb This doesn't mean what it used to!
1906 InitOffsets(aContainingBlockWidth,
1907 VerticalOffsetPercentBasis(frame, aContainingBlockWidth,
1908 aContainingBlockHeight),
1909 aFrameType, aBorder, aPadding);
1910 // Override mComputedMargin since reflow roots start from the
1911 // frame's boundary, which is inside the margin.
1912 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
1913 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
1915 ComputedWidth() = AvailableWidth() - ComputedPhysicalBorderPadding().LeftRight();
1916 if (ComputedWidth() < 0)
1917 ComputedWidth() = 0;
1918 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE) {
1919 ComputedHeight() = AvailableHeight() - ComputedPhysicalBorderPadding().TopBottom();
1920 if (ComputedHeight() < 0)
1921 ComputedHeight() = 0;
1922 } else {
1923 ComputedHeight() = NS_UNCONSTRAINEDSIZE;
1924 }
1926 ComputedMinWidth() = ComputedMinHeight() = 0;
1927 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
1928 } else {
1929 // Get the containing block reflow state
1930 const nsHTMLReflowState* cbrs = mCBReflowState;
1931 NS_ASSERTION(nullptr != cbrs, "no containing block");
1933 // If we weren't given a containing block width and height, then
1934 // compute one
1935 if (aContainingBlockWidth == -1) {
1936 ComputeContainingBlockRectangle(aPresContext, cbrs, aContainingBlockWidth,
1937 aContainingBlockHeight);
1938 }
1940 // See if the containing block height is based on the size of its
1941 // content
1942 nsIAtom* fType;
1943 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1944 // See if the containing block is a cell frame which needs
1945 // to use the mComputedHeight of the cell instead of what the cell block passed in.
1946 // XXX It seems like this could lead to bugs with min-height and friends
1947 if (cbrs->parentReflowState) {
1948 fType = cbrs->frame->GetType();
1949 if (IS_TABLE_CELL(fType)) {
1950 // use the cell's computed height
1951 aContainingBlockHeight = cbrs->ComputedHeight();
1952 }
1953 }
1954 }
1956 // XXX Might need to also pass the CB height (not width) for page boxes,
1957 // too, if we implement them.
1958 InitOffsets(aContainingBlockWidth,
1959 VerticalOffsetPercentBasis(frame, aContainingBlockWidth,
1960 aContainingBlockHeight),
1961 aFrameType, aBorder, aPadding);
1963 const nsStyleCoord &height = mStylePosition->mHeight;
1964 nsStyleUnit heightUnit = height.GetUnit();
1966 // Check for a percentage based height and a containing block height
1967 // that depends on the content height
1968 // XXX twiddling heightUnit doesn't help anymore
1969 // FIXME Shouldn't we fix that?
1970 if (height.HasPercent()) {
1971 if (NS_AUTOHEIGHT == aContainingBlockHeight) {
1972 // this if clause enables %-height on replaced inline frames,
1973 // such as images. See bug 54119. The else clause "heightUnit = eStyleUnit_Auto;"
1974 // used to be called exclusively.
1975 if (NS_FRAME_REPLACED(NS_CSS_FRAME_TYPE_INLINE) == mFrameType ||
1976 NS_FRAME_REPLACED_CONTAINS_BLOCK(
1977 NS_CSS_FRAME_TYPE_INLINE) == mFrameType) {
1978 // Get the containing block reflow state
1979 NS_ASSERTION(nullptr != cbrs, "no containing block");
1980 // in quirks mode, get the cb height using the special quirk method
1981 if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
1982 if (!IS_TABLE_CELL(fType)) {
1983 aContainingBlockHeight = CalcQuirkContainingBlockHeight(cbrs);
1984 if (aContainingBlockHeight == NS_AUTOHEIGHT) {
1985 heightUnit = eStyleUnit_Auto;
1986 }
1987 }
1988 else {
1989 heightUnit = eStyleUnit_Auto;
1990 }
1991 }
1992 // in standard mode, use the cb height. if it's "auto", as will be the case
1993 // by default in BODY, use auto height as per CSS2 spec.
1994 else
1995 {
1996 if (NS_AUTOHEIGHT != cbrs->ComputedHeight())
1997 aContainingBlockHeight = cbrs->ComputedHeight();
1998 else
1999 heightUnit = eStyleUnit_Auto;
2000 }
2001 }
2002 else {
2003 // default to interpreting the height like 'auto'
2004 heightUnit = eStyleUnit_Auto;
2005 }
2006 }
2007 }
2009 // Compute our offsets if the element is relatively positioned. We need
2010 // the correct containing block width and height here, which is why we need
2011 // to do it after all the quirks-n-such above. (If the element is sticky
2012 // positioned, we need to wait until the scroll container knows its size,
2013 // so we compute offsets from StickyScrollContainer::UpdatePositions.)
2014 if (mStyleDisplay->IsRelativelyPositioned(frame) &&
2015 NS_STYLE_POSITION_RELATIVE == mStyleDisplay->mPosition) {
2016 uint8_t direction = NS_STYLE_DIRECTION_LTR;
2017 if (cbrs && NS_STYLE_DIRECTION_RTL == cbrs->mStyleVisibility->mDirection) {
2018 direction = NS_STYLE_DIRECTION_RTL;
2019 }
2020 ComputeRelativeOffsets(direction, frame, aContainingBlockWidth,
2021 aContainingBlockHeight, ComputedPhysicalOffsets());
2022 } else {
2023 // Initialize offsets to 0
2024 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0);
2025 }
2027 // Calculate the computed values for min and max properties. Note that
2028 // this MUST come after we've computed our border and padding.
2029 ComputeMinMaxValues(aContainingBlockWidth, aContainingBlockHeight, cbrs);
2031 // Calculate the computed width and height. This varies by frame type
2033 if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) {
2034 // Internal table elements. The rules vary depending on the type.
2035 // Calculate the computed width
2036 bool rowOrRowGroup = false;
2037 const nsStyleCoord &width = mStylePosition->mWidth;
2038 nsStyleUnit widthUnit = width.GetUnit();
2039 if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) ||
2040 (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) {
2041 // 'width' property doesn't apply to table rows and row groups
2042 widthUnit = eStyleUnit_Auto;
2043 rowOrRowGroup = true;
2044 }
2046 // calc() with percentages acts like auto on internal table elements
2047 if (eStyleUnit_Auto == widthUnit ||
2048 (width.IsCalcUnit() && width.CalcHasPercent())) {
2049 ComputedWidth() = AvailableWidth();
2051 if ((ComputedWidth() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){
2052 // Internal table elements don't have margins. Only tables and
2053 // cells have border and padding
2054 ComputedWidth() -= ComputedPhysicalBorderPadding().left +
2055 ComputedPhysicalBorderPadding().right;
2056 if (ComputedWidth() < 0)
2057 ComputedWidth() = 0;
2058 }
2059 NS_ASSERTION(ComputedWidth() >= 0, "Bogus computed width");
2061 } else {
2062 NS_ASSERTION(widthUnit == mStylePosition->mWidth.GetUnit(),
2063 "unexpected width unit change");
2064 ComputedWidth() = ComputeWidthValue(aContainingBlockWidth,
2065 mStylePosition->mBoxSizing,
2066 mStylePosition->mWidth);
2067 }
2069 // Calculate the computed height
2070 if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) ||
2071 (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
2072 // 'height' property doesn't apply to table columns and column groups
2073 heightUnit = eStyleUnit_Auto;
2074 }
2075 // calc() with percentages acts like 'auto' on internal table elements
2076 if (eStyleUnit_Auto == heightUnit ||
2077 (height.IsCalcUnit() && height.CalcHasPercent())) {
2078 ComputedHeight() = NS_AUTOHEIGHT;
2079 } else {
2080 NS_ASSERTION(heightUnit == mStylePosition->mHeight.GetUnit(),
2081 "unexpected height unit change");
2082 ComputedHeight() = ComputeHeightValue(aContainingBlockHeight,
2083 mStylePosition->mBoxSizing,
2084 mStylePosition->mHeight);
2085 }
2087 // Doesn't apply to table elements
2088 ComputedMinWidth() = ComputedMinHeight() = 0;
2089 ComputedMaxWidth() = ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
2091 } else if (NS_FRAME_GET_TYPE(mFrameType) == NS_CSS_FRAME_TYPE_ABSOLUTE) {
2092 // XXX not sure if this belongs here or somewhere else - cwk
2093 InitAbsoluteConstraints(aPresContext, cbrs, aContainingBlockWidth,
2094 aContainingBlockHeight, aFrameType);
2095 } else {
2096 AutoMaybeDisableFontInflation an(frame);
2098 bool isBlock = NS_CSS_FRAME_TYPE_BLOCK == NS_FRAME_GET_TYPE(mFrameType);
2099 uint32_t computeSizeFlags = isBlock ? 0 : nsIFrame::eShrinkWrap;
2101 // Make sure legend frames with display:block and width:auto still
2102 // shrink-wrap.
2103 if (isBlock &&
2104 ((aFrameType == nsGkAtoms::legendFrame &&
2105 frame->StyleContext()->GetPseudo() != nsCSSAnonBoxes::scrolledContent) ||
2106 (aFrameType == nsGkAtoms::scrollFrame &&
2107 frame->GetContentInsertionFrame()->GetType() == nsGkAtoms::legendFrame))) {
2108 computeSizeFlags |= nsIFrame::eShrinkWrap;
2109 }
2111 const nsFlexContainerFrame* flexContainerFrame = GetFlexContainer(frame);
2112 if (flexContainerFrame) {
2113 computeSizeFlags |= nsIFrame::eShrinkWrap;
2115 // If we're inside of a flex container that needs to measure our
2116 // auto height, pass that information along to ComputeSize().
2117 if (mFlags.mIsFlexContainerMeasuringHeight) {
2118 computeSizeFlags |= nsIFrame::eUseAutoHeight;
2119 }
2120 } else {
2121 MOZ_ASSERT(!mFlags.mIsFlexContainerMeasuringHeight,
2122 "We're not in a flex container, so the flag "
2123 "'mIsFlexContainerMeasuringHeight' shouldn't be set");
2124 }
2126 nsSize size =
2127 frame->ComputeSize(rendContext,
2128 nsSize(aContainingBlockWidth,
2129 aContainingBlockHeight),
2130 AvailableWidth(),
2131 nsSize(ComputedPhysicalMargin().LeftRight(),
2132 ComputedPhysicalMargin().TopBottom()),
2133 nsSize(ComputedPhysicalBorderPadding().LeftRight() -
2134 ComputedPhysicalPadding().LeftRight(),
2135 ComputedPhysicalBorderPadding().TopBottom() -
2136 ComputedPhysicalPadding().TopBottom()),
2137 nsSize(ComputedPhysicalPadding().LeftRight(),
2138 ComputedPhysicalPadding().TopBottom()),
2139 computeSizeFlags);
2141 ComputedWidth() = size.width;
2142 ComputedHeight() = size.height;
2143 NS_ASSERTION(ComputedWidth() >= 0, "Bogus width");
2144 NS_ASSERTION(ComputedHeight() == NS_UNCONSTRAINEDSIZE ||
2145 ComputedHeight() >= 0, "Bogus height");
2147 // Exclude inline tables and flex items from the block margin calculations
2148 if (isBlock &&
2149 !IsSideCaption(frame, mStyleDisplay) &&
2150 mStyleDisplay->mDisplay != NS_STYLE_DISPLAY_INLINE_TABLE &&
2151 !flexContainerFrame) {
2152 CalculateBlockSideMargins(AvailableWidth(), ComputedWidth(), aFrameType);
2153 }
2154 }
2155 }
2156 }
2158 static void
2159 UpdateProp(FrameProperties& aProps,
2160 const FramePropertyDescriptor* aProperty,
2161 bool aNeeded,
2162 nsMargin& aNewValue)
2163 {
2164 if (aNeeded) {
2165 nsMargin* propValue = static_cast<nsMargin*>(aProps.Get(aProperty));
2166 if (propValue) {
2167 *propValue = aNewValue;
2168 } else {
2169 aProps.Set(aProperty, new nsMargin(aNewValue));
2170 }
2171 } else {
2172 aProps.Delete(aProperty);
2173 }
2174 }
2176 void
2177 nsCSSOffsetState::InitOffsets(nscoord aHorizontalPercentBasis,
2178 nscoord aVerticalPercentBasis,
2179 nsIAtom* aFrameType,
2180 const nsMargin *aBorder,
2181 const nsMargin *aPadding)
2182 {
2183 DISPLAY_INIT_OFFSETS(frame, this,
2184 aHorizontalPercentBasis,
2185 aVerticalPercentBasis,
2186 aBorder, aPadding);
2188 // Since we are in reflow, we don't need to store these properties anymore
2189 // unless they are dependent on width, in which case we store the new value.
2190 nsPresContext *presContext = frame->PresContext();
2191 FrameProperties props(presContext->PropertyTable(), frame);
2192 props.Delete(nsIFrame::UsedBorderProperty());
2194 // Compute margins from the specified margin style information. These
2195 // become the default computed values, and may be adjusted below
2196 // XXX fix to provide 0,0 for the top&bottom margins for
2197 // inline-non-replaced elements
2198 bool needMarginProp = ComputeMargin(aHorizontalPercentBasis,
2199 aVerticalPercentBasis);
2200 // XXX We need to include 'auto' horizontal margins in this too!
2201 // ... but if we did that, we'd need to fix nsFrame::GetUsedMargin
2202 // to use it even when the margins are all zero (since sometimes
2203 // they get treated as auto)
2204 ::UpdateProp(props, nsIFrame::UsedMarginProperty(), needMarginProp,
2205 ComputedPhysicalMargin());
2208 const nsStyleDisplay *disp = frame->StyleDisplay();
2209 bool isThemed = frame->IsThemed(disp);
2210 bool needPaddingProp;
2211 nsIntMargin widget;
2212 if (isThemed &&
2213 presContext->GetTheme()->GetWidgetPadding(presContext->DeviceContext(),
2214 frame, disp->mAppearance,
2215 &widget)) {
2216 ComputedPhysicalPadding().top = presContext->DevPixelsToAppUnits(widget.top);
2217 ComputedPhysicalPadding().right = presContext->DevPixelsToAppUnits(widget.right);
2218 ComputedPhysicalPadding().bottom = presContext->DevPixelsToAppUnits(widget.bottom);
2219 ComputedPhysicalPadding().left = presContext->DevPixelsToAppUnits(widget.left);
2220 needPaddingProp = false;
2221 }
2222 else if (frame->IsSVGText()) {
2223 ComputedPhysicalPadding().SizeTo(0, 0, 0, 0);
2224 needPaddingProp = false;
2225 }
2226 else if (aPadding) { // padding is an input arg
2227 ComputedPhysicalPadding() = *aPadding;
2228 needPaddingProp = frame->StylePadding()->IsWidthDependent() ||
2229 (frame->GetStateBits() & NS_FRAME_REFLOW_ROOT);
2230 }
2231 else {
2232 needPaddingProp = ComputePadding(aHorizontalPercentBasis,
2233 aVerticalPercentBasis, aFrameType);
2234 }
2236 if (isThemed) {
2237 nsIntMargin widget;
2238 presContext->GetTheme()->GetWidgetBorder(presContext->DeviceContext(),
2239 frame, disp->mAppearance,
2240 &widget);
2241 ComputedPhysicalBorderPadding().top =
2242 presContext->DevPixelsToAppUnits(widget.top);
2243 ComputedPhysicalBorderPadding().right =
2244 presContext->DevPixelsToAppUnits(widget.right);
2245 ComputedPhysicalBorderPadding().bottom =
2246 presContext->DevPixelsToAppUnits(widget.bottom);
2247 ComputedPhysicalBorderPadding().left =
2248 presContext->DevPixelsToAppUnits(widget.left);
2249 }
2250 else if (frame->IsSVGText()) {
2251 ComputedPhysicalBorderPadding().SizeTo(0, 0, 0, 0);
2252 }
2253 else if (aBorder) { // border is an input arg
2254 ComputedPhysicalBorderPadding() = *aBorder;
2255 }
2256 else {
2257 ComputedPhysicalBorderPadding() = frame->StyleBorder()->GetComputedBorder();
2258 }
2259 ComputedPhysicalBorderPadding() += ComputedPhysicalPadding();
2261 if (aFrameType == nsGkAtoms::tableFrame) {
2262 nsTableFrame *tableFrame = static_cast<nsTableFrame*>(frame);
2264 if (tableFrame->IsBorderCollapse()) {
2265 // border-collapsed tables don't use any of their padding, and
2266 // only part of their border. We need to do this here before we
2267 // try to do anything like handling 'auto' widths,
2268 // 'box-sizing', or 'auto' margins.
2269 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2270 ComputedPhysicalBorderPadding() = tableFrame->GetIncludedOuterBCBorder();
2271 }
2273 // The margin is inherited to the outer table frame via
2274 // the ::-moz-table-outer rule in ua.css.
2275 ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
2276 } else if (aFrameType == nsGkAtoms::scrollbarFrame) {
2277 // scrollbars may have had their width or height smashed to zero
2278 // by the associated scrollframe, in which case we must not report
2279 // any padding or border.
2280 nsSize size(frame->GetSize());
2281 if (size.width == 0 || size.height == 0) {
2282 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2283 ComputedPhysicalBorderPadding().SizeTo(0,0,0,0);
2284 }
2285 }
2286 ::UpdateProp(props, nsIFrame::UsedPaddingProperty(), needPaddingProp,
2287 ComputedPhysicalPadding());
2288 }
2290 // This code enforces section 10.3.3 of the CSS2 spec for this formula:
2291 //
2292 // 'margin-left' + 'border-left-width' + 'padding-left' + 'width' +
2293 // 'padding-right' + 'border-right-width' + 'margin-right'
2294 // = width of containing block
2295 //
2296 // Note: the width unit is not auto when this is called
2297 void
2298 nsHTMLReflowState::CalculateBlockSideMargins(nscoord aAvailWidth,
2299 nscoord aComputedWidth,
2300 nsIAtom* aFrameType)
2301 {
2302 NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aComputedWidth &&
2303 NS_UNCONSTRAINEDSIZE != aAvailWidth,
2304 "have unconstrained width; this should only result from "
2305 "very large sizes, not attempts at intrinsic width "
2306 "calculation");
2308 nscoord sum = ComputedPhysicalMargin().left + ComputedPhysicalBorderPadding().left +
2309 aComputedWidth + ComputedPhysicalBorderPadding().right + ComputedPhysicalMargin().right;
2310 if (sum == aAvailWidth)
2311 // The sum is already correct
2312 return;
2314 // Determine the left and right margin values. The width value
2315 // remains constant while we do this.
2317 // Calculate how much space is available for margins
2318 nscoord availMarginSpace = aAvailWidth - sum;
2320 // If the available margin space is negative, then don't follow the
2321 // usual overconstraint rules.
2322 if (availMarginSpace < 0) {
2323 if (mCBReflowState &&
2324 mCBReflowState->mStyleVisibility->mDirection == NS_STYLE_DIRECTION_RTL) {
2325 ComputedPhysicalMargin().left += availMarginSpace;
2326 } else {
2327 ComputedPhysicalMargin().right += availMarginSpace;
2328 }
2329 return;
2330 }
2332 // The css2 spec clearly defines how block elements should behave
2333 // in section 10.3.3.
2334 bool isAutoLeftMargin =
2335 eStyleUnit_Auto == mStyleMargin->mMargin.GetLeftUnit();
2336 bool isAutoRightMargin =
2337 eStyleUnit_Auto == mStyleMargin->mMargin.GetRightUnit();
2338 if (!isAutoLeftMargin && !isAutoRightMargin) {
2339 // Neither margin is 'auto' so we're over constrained. Use the
2340 // 'direction' property of the parent to tell which margin to
2341 // ignore
2342 // First check if there is an HTML alignment that we should honor
2343 const nsHTMLReflowState* prs = parentReflowState;
2344 if (aFrameType == nsGkAtoms::tableFrame) {
2345 NS_ASSERTION(prs->frame->GetType() == nsGkAtoms::tableOuterFrame,
2346 "table not inside outer table");
2347 // Center the table within the outer table based on the alignment
2348 // of the outer table's parent.
2349 prs = prs->parentReflowState;
2350 }
2351 if (prs &&
2352 (prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_LEFT ||
2353 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_CENTER ||
2354 prs->mStyleText->mTextAlign == NS_STYLE_TEXT_ALIGN_MOZ_RIGHT)) {
2355 isAutoLeftMargin =
2356 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_LEFT;
2357 isAutoRightMargin =
2358 prs->mStyleText->mTextAlign != NS_STYLE_TEXT_ALIGN_MOZ_RIGHT;
2359 }
2360 // Otherwise apply the CSS rules, and ignore one margin by forcing
2361 // it to 'auto', depending on 'direction'.
2362 else if (mCBReflowState &&
2363 NS_STYLE_DIRECTION_RTL == mCBReflowState->mStyleVisibility->mDirection) {
2364 isAutoLeftMargin = true;
2365 }
2366 else {
2367 isAutoRightMargin = true;
2368 }
2369 }
2371 // Logic which is common to blocks and tables
2372 // The computed margins need not be zero because the 'auto' could come from
2373 // overconstraint or from HTML alignment so values need to be accumulated
2375 if (isAutoLeftMargin) {
2376 if (isAutoRightMargin) {
2377 // Both margins are 'auto' so the computed addition should be equal
2378 nscoord forLeft = availMarginSpace / 2;
2379 ComputedPhysicalMargin().left += forLeft;
2380 ComputedPhysicalMargin().right += availMarginSpace - forLeft;
2381 } else {
2382 ComputedPhysicalMargin().left += availMarginSpace;
2383 }
2384 } else if (isAutoRightMargin) {
2385 ComputedPhysicalMargin().right += availMarginSpace;
2386 }
2387 }
2389 #define NORMAL_LINE_HEIGHT_FACTOR 1.2f // in term of emHeight
2390 // For "normal" we use the font's normal line height (em height + leading).
2391 // If both internal leading and external leading specified by font itself
2392 // are zeros, we should compensate this by creating extra (external) leading
2393 // in eCompensateLeading mode. This is necessary because without this
2394 // compensation, normal line height might looks too tight.
2396 // For risk management, we use preference to control the behavior, and
2397 // eNoExternalLeading is the old behavior.
2398 static nscoord
2399 GetNormalLineHeight(nsFontMetrics* aFontMetrics)
2400 {
2401 NS_PRECONDITION(nullptr != aFontMetrics, "no font metrics");
2403 nscoord normalLineHeight;
2405 nscoord externalLeading = aFontMetrics->ExternalLeading();
2406 nscoord internalLeading = aFontMetrics->InternalLeading();
2407 nscoord emHeight = aFontMetrics->EmHeight();
2408 switch (GetNormalLineHeightCalcControl()) {
2409 case eIncludeExternalLeading:
2410 normalLineHeight = emHeight+ internalLeading + externalLeading;
2411 break;
2412 case eCompensateLeading:
2413 if (!internalLeading && !externalLeading)
2414 normalLineHeight = NSToCoordRound(emHeight * NORMAL_LINE_HEIGHT_FACTOR);
2415 else
2416 normalLineHeight = emHeight+ internalLeading + externalLeading;
2417 break;
2418 default:
2419 //case eNoExternalLeading:
2420 normalLineHeight = emHeight + internalLeading;
2421 }
2422 return normalLineHeight;
2423 }
2425 static inline nscoord
2426 ComputeLineHeight(nsStyleContext* aStyleContext,
2427 nscoord aBlockHeight,
2428 float aFontSizeInflation)
2429 {
2430 const nsStyleCoord& lhCoord = aStyleContext->StyleText()->mLineHeight;
2432 if (lhCoord.GetUnit() == eStyleUnit_Coord) {
2433 nscoord result = lhCoord.GetCoordValue();
2434 if (aFontSizeInflation != 1.0f) {
2435 result = NSToCoordRound(result * aFontSizeInflation);
2436 }
2437 return result;
2438 }
2440 if (lhCoord.GetUnit() == eStyleUnit_Factor)
2441 // For factor units the computed value of the line-height property
2442 // is found by multiplying the factor by the font's computed size
2443 // (adjusted for min-size prefs and text zoom).
2444 return NSToCoordRound(lhCoord.GetFactorValue() * aFontSizeInflation *
2445 aStyleContext->StyleFont()->mFont.size);
2447 NS_ASSERTION(lhCoord.GetUnit() == eStyleUnit_Normal ||
2448 lhCoord.GetUnit() == eStyleUnit_Enumerated,
2449 "bad line-height unit");
2451 if (lhCoord.GetUnit() == eStyleUnit_Enumerated) {
2452 NS_ASSERTION(lhCoord.GetIntValue() == NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT,
2453 "bad line-height value");
2454 if (aBlockHeight != NS_AUTOHEIGHT) {
2455 return aBlockHeight;
2456 }
2457 }
2459 nsRefPtr<nsFontMetrics> fm;
2460 nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext,
2461 getter_AddRefs(fm),
2462 aFontSizeInflation);
2463 return GetNormalLineHeight(fm);
2464 }
2466 nscoord
2467 nsHTMLReflowState::CalcLineHeight() const
2468 {
2469 nscoord blockHeight =
2470 nsLayoutUtils::IsNonWrapperBlock(frame) ? ComputedHeight() :
2471 (mCBReflowState ? mCBReflowState->ComputedHeight() : NS_AUTOHEIGHT);
2473 return CalcLineHeight(frame->GetContent(), frame->StyleContext(), blockHeight,
2474 nsLayoutUtils::FontSizeInflationFor(frame));
2475 }
2477 /* static */ nscoord
2478 nsHTMLReflowState::CalcLineHeight(nsIContent* aContent,
2479 nsStyleContext* aStyleContext,
2480 nscoord aBlockHeight,
2481 float aFontSizeInflation)
2482 {
2483 NS_PRECONDITION(aStyleContext, "Must have a style context");
2485 nscoord lineHeight =
2486 ComputeLineHeight(aStyleContext, aBlockHeight, aFontSizeInflation);
2488 NS_ASSERTION(lineHeight >= 0, "ComputeLineHeight screwed up");
2490 HTMLInputElement* input = HTMLInputElement::FromContentOrNull(aContent);
2491 if (input && input->IsSingleLineTextControl()) {
2492 // For Web-compatibility, single-line text input elements cannot
2493 // have a line-height smaller than one.
2494 nscoord lineHeightOne =
2495 aFontSizeInflation * aStyleContext->StyleFont()->mFont.size;
2496 if (lineHeight < lineHeightOne) {
2497 lineHeight = lineHeightOne;
2498 }
2499 }
2501 return lineHeight;
2502 }
2504 bool
2505 nsCSSOffsetState::ComputeMargin(nscoord aHorizontalPercentBasis,
2506 nscoord aVerticalPercentBasis)
2507 {
2508 // SVG text frames have no margin.
2509 if (frame->IsSVGText()) {
2510 return false;
2511 }
2513 // If style style can provide us the margin directly, then use it.
2514 const nsStyleMargin *styleMargin = frame->StyleMargin();
2515 bool isCBDependent = !styleMargin->GetMargin(ComputedPhysicalMargin());
2516 if (isCBDependent) {
2517 // We have to compute the value
2518 ComputedPhysicalMargin().left = nsLayoutUtils::
2519 ComputeCBDependentValue(aHorizontalPercentBasis,
2520 styleMargin->mMargin.GetLeft());
2521 ComputedPhysicalMargin().right = nsLayoutUtils::
2522 ComputeCBDependentValue(aHorizontalPercentBasis,
2523 styleMargin->mMargin.GetRight());
2525 ComputedPhysicalMargin().top = nsLayoutUtils::
2526 ComputeCBDependentValue(aVerticalPercentBasis,
2527 styleMargin->mMargin.GetTop());
2528 ComputedPhysicalMargin().bottom = nsLayoutUtils::
2529 ComputeCBDependentValue(aVerticalPercentBasis,
2530 styleMargin->mMargin.GetBottom());
2531 }
2533 nscoord marginAdjustment = FontSizeInflationListMarginAdjustment(frame);
2535 if (marginAdjustment > 0) {
2536 const nsStyleVisibility* visibility = frame->StyleVisibility();
2537 if (visibility->mDirection == NS_STYLE_DIRECTION_RTL) {
2538 ComputedPhysicalMargin().right = ComputedPhysicalMargin().right + marginAdjustment;
2539 } else {
2540 ComputedPhysicalMargin().left = ComputedPhysicalMargin().left + marginAdjustment;
2541 }
2542 }
2544 return isCBDependent;
2545 }
2547 bool
2548 nsCSSOffsetState::ComputePadding(nscoord aHorizontalPercentBasis,
2549 nscoord aVerticalPercentBasis,
2550 nsIAtom* aFrameType)
2551 {
2552 // If style can provide us the padding directly, then use it.
2553 const nsStylePadding *stylePadding = frame->StylePadding();
2554 bool isCBDependent = !stylePadding->GetPadding(ComputedPhysicalPadding());
2555 // a table row/col group, row/col doesn't have padding
2556 // XXXldb Neither do border-collapse tables.
2557 if (nsGkAtoms::tableRowGroupFrame == aFrameType ||
2558 nsGkAtoms::tableColGroupFrame == aFrameType ||
2559 nsGkAtoms::tableRowFrame == aFrameType ||
2560 nsGkAtoms::tableColFrame == aFrameType) {
2561 ComputedPhysicalPadding().SizeTo(0,0,0,0);
2562 }
2563 else if (isCBDependent) {
2564 // We have to compute the value
2565 // clamp negative calc() results to 0
2566 ComputedPhysicalPadding().left = std::max(0, nsLayoutUtils::
2567 ComputeCBDependentValue(aHorizontalPercentBasis,
2568 stylePadding->mPadding.GetLeft()));
2569 ComputedPhysicalPadding().right = std::max(0, nsLayoutUtils::
2570 ComputeCBDependentValue(aHorizontalPercentBasis,
2571 stylePadding->mPadding.GetRight()));
2573 ComputedPhysicalPadding().top = std::max(0, nsLayoutUtils::
2574 ComputeCBDependentValue(aVerticalPercentBasis,
2575 stylePadding->mPadding.GetTop()));
2576 ComputedPhysicalPadding().bottom = std::max(0, nsLayoutUtils::
2577 ComputeCBDependentValue(aVerticalPercentBasis,
2578 stylePadding->mPadding.GetBottom()));
2579 }
2580 return isCBDependent;
2581 }
2583 void
2584 nsHTMLReflowState::ComputeMinMaxValues(nscoord aContainingBlockWidth,
2585 nscoord aContainingBlockHeight,
2586 const nsHTMLReflowState* aContainingBlockRS)
2587 {
2588 ComputedMinWidth() = ComputeWidthValue(aContainingBlockWidth,
2589 mStylePosition->mBoxSizing,
2590 mStylePosition->mMinWidth);
2592 if (eStyleUnit_None == mStylePosition->mMaxWidth.GetUnit()) {
2593 // Specified value of 'none'
2594 ComputedMaxWidth() = NS_UNCONSTRAINEDSIZE; // no limit
2595 } else {
2596 ComputedMaxWidth() = ComputeWidthValue(aContainingBlockWidth,
2597 mStylePosition->mBoxSizing,
2598 mStylePosition->mMaxWidth);
2599 }
2601 // If the computed value of 'min-width' is greater than the value of
2602 // 'max-width', 'max-width' is set to the value of 'min-width'
2603 if (ComputedMinWidth() > ComputedMaxWidth()) {
2604 ComputedMaxWidth() = ComputedMinWidth();
2605 }
2607 // Check for percentage based values and a containing block height that
2608 // depends on the content height. Treat them like 'auto'
2609 // Likewise, check for calc() with percentages on internal table elements;
2610 // that's treated as 'auto' too.
2611 // Likewise, if we're a child of a flex container who's measuring our
2612 // intrinsic height, then we want to disregard our min-height.
2614 const nsStyleCoord &minHeight = mStylePosition->mMinHeight;
2615 if ((NS_AUTOHEIGHT == aContainingBlockHeight &&
2616 minHeight.HasPercent()) ||
2617 (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
2618 minHeight.IsCalcUnit() && minHeight.CalcHasPercent()) ||
2619 mFlags.mIsFlexContainerMeasuringHeight) {
2620 ComputedMinHeight() = 0;
2621 } else {
2622 ComputedMinHeight() = ComputeHeightValue(aContainingBlockHeight,
2623 mStylePosition->mBoxSizing,
2624 minHeight);
2625 }
2626 const nsStyleCoord &maxHeight = mStylePosition->mMaxHeight;
2627 nsStyleUnit maxHeightUnit = maxHeight.GetUnit();
2628 if (eStyleUnit_None == maxHeightUnit) {
2629 // Specified value of 'none'
2630 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE; // no limit
2631 } else {
2632 // Check for percentage based values and a containing block height that
2633 // depends on the content height. Treat them like 'none'
2634 // Likewise, check for calc() with percentages on internal table elements;
2635 // that's treated as 'auto' too.
2636 // Likewise, if we're a child of a flex container who's measuring our
2637 // intrinsic height, then we want to disregard our max-height.
2638 if ((NS_AUTOHEIGHT == aContainingBlockHeight &&
2639 maxHeight.HasPercent()) ||
2640 (mFrameType == NS_CSS_FRAME_TYPE_INTERNAL_TABLE &&
2641 maxHeight.IsCalcUnit() && maxHeight.CalcHasPercent()) ||
2642 mFlags.mIsFlexContainerMeasuringHeight) {
2643 ComputedMaxHeight() = NS_UNCONSTRAINEDSIZE;
2644 } else {
2645 ComputedMaxHeight() = ComputeHeightValue(aContainingBlockHeight,
2646 mStylePosition->mBoxSizing,
2647 maxHeight);
2648 }
2649 }
2651 // If the computed value of 'min-height' is greater than the value of
2652 // 'max-height', 'max-height' is set to the value of 'min-height'
2653 if (ComputedMinHeight() > ComputedMaxHeight()) {
2654 ComputedMaxHeight() = ComputedMinHeight();
2655 }
2656 }
2658 void
2659 nsHTMLReflowState::SetTruncated(const nsHTMLReflowMetrics& aMetrics,
2660 nsReflowStatus* aStatus) const
2661 {
2662 if (AvailableHeight() != NS_UNCONSTRAINEDSIZE &&
2663 AvailableHeight() < aMetrics.Height() &&
2664 !mFlags.mIsTopOfPage) {
2665 *aStatus |= NS_FRAME_TRUNCATED;
2666 } else {
2667 *aStatus &= ~NS_FRAME_TRUNCATED;
2668 }
2669 }
2671 bool
2672 nsHTMLReflowState::IsFloating() const
2673 {
2674 return mStyleDisplay->IsFloating(frame);
2675 }
2677 uint8_t
2678 nsHTMLReflowState::GetDisplay() const
2679 {
2680 return mStyleDisplay->GetDisplay(frame);
2681 }