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 /* vim: set ts=2 et sw=2 tw=80: */
4 /* This Source Code is subject to the terms of the Mozilla Public License
5 * version 2.0 (the "License"). You can obtain a copy of the License at
6 * http://mozilla.org/MPL/2.0/. */
8 /* rendering object for CSS "display: flex" */
10 #include "nsFlexContainerFrame.h"
11 #include "nsContentUtils.h"
12 #include "nsCSSAnonBoxes.h"
13 #include "nsDisplayList.h"
14 #include "nsIFrameInlines.h"
15 #include "nsLayoutUtils.h"
16 #include "nsPlaceholderFrame.h"
17 #include "nsPresContext.h"
18 #include "nsStyleContext.h"
19 #include "prlog.h"
20 #include <algorithm>
21 #include "mozilla/LinkedList.h"
23 using namespace mozilla;
24 using namespace mozilla::css;
25 using namespace mozilla::layout;
27 // Convenience typedefs for helper classes that we forward-declare in .h file
28 // (so that nsFlexContainerFrame methods can use them as parameters):
29 typedef nsFlexContainerFrame::FlexItem FlexItem;
30 typedef nsFlexContainerFrame::FlexLine FlexLine;
31 typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
32 typedef nsFlexContainerFrame::StrutInfo StrutInfo;
34 #ifdef PR_LOGGING
35 static PRLogModuleInfo*
36 GetFlexContainerLog()
37 {
38 static PRLogModuleInfo *sLog;
39 if (!sLog)
40 sLog = PR_NewLogModule("nsFlexContainerFrame");
41 return sLog;
42 }
43 #endif /* PR_LOGGING */
45 // XXXdholbert Some of this helper-stuff should be separated out into a general
46 // "LogicalAxisUtils.h" helper. Should that be a class, or a namespace (under
47 // what super-namespace?), or what?
49 // Helper enums
50 // ============
52 // Represents a physical orientation for an axis.
53 // The directional suffix indicates the direction in which the axis *grows*.
54 // So e.g. eAxis_LR means a horizontal left-to-right axis, whereas eAxis_BT
55 // means a vertical bottom-to-top axis.
56 // NOTE: The order here is important -- these values are used as indices into
57 // the static array 'kAxisOrientationToSidesMap', defined below.
58 enum AxisOrientationType {
59 eAxis_LR,
60 eAxis_RL,
61 eAxis_TB,
62 eAxis_BT,
63 eNumAxisOrientationTypes // For sizing arrays that use these values as indices
64 };
66 // Represents one or the other extreme of an axis (e.g. for the main axis, the
67 // main-start vs. main-end edge.
68 // NOTE: The order here is important -- these values are used as indices into
69 // the sub-arrays in 'kAxisOrientationToSidesMap', defined below.
70 enum AxisEdgeType {
71 eAxisEdge_Start,
72 eAxisEdge_End,
73 eNumAxisEdges // For sizing arrays that use these values as indices
74 };
76 // This array maps each axis orientation to a pair of corresponding
77 // [start, end] physical mozilla::css::Side values.
78 static const Side
79 kAxisOrientationToSidesMap[eNumAxisOrientationTypes][eNumAxisEdges] = {
80 { eSideLeft, eSideRight }, // eAxis_LR
81 { eSideRight, eSideLeft }, // eAxis_RL
82 { eSideTop, eSideBottom }, // eAxis_TB
83 { eSideBottom, eSideTop } // eAxis_BT
84 };
86 // Helper structs / classes / methods
87 // ==================================
89 // Indicates whether advancing along the given axis is equivalent to
90 // increasing our X or Y position (as opposed to decreasing it).
91 static inline bool
92 AxisGrowsInPositiveDirection(AxisOrientationType aAxis)
93 {
94 return eAxis_LR == aAxis || eAxis_TB == aAxis;
95 }
97 // Indicates whether the given axis is horizontal.
98 static inline bool
99 IsAxisHorizontal(AxisOrientationType aAxis)
100 {
101 return eAxis_LR == aAxis || eAxis_RL == aAxis;
102 }
104 // Given an AxisOrientationType, returns the "reverse" AxisOrientationType
105 // (in the same dimension, but the opposite direction)
106 static inline AxisOrientationType
107 GetReverseAxis(AxisOrientationType aAxis)
108 {
109 AxisOrientationType reversedAxis;
111 if (aAxis % 2 == 0) {
112 // even enum value. Add 1 to reverse.
113 reversedAxis = AxisOrientationType(aAxis + 1);
114 } else {
115 // odd enum value. Subtract 1 to reverse.
116 reversedAxis = AxisOrientationType(aAxis - 1);
117 }
119 // Check that we're still in the enum's valid range
120 MOZ_ASSERT(reversedAxis >= eAxis_LR &&
121 reversedAxis <= eAxis_BT);
123 return reversedAxis;
124 }
126 // Returns aFrame's computed value for 'height' or 'width' -- whichever is in
127 // the same dimension as aAxis.
128 static inline const nsStyleCoord&
129 GetSizePropertyForAxis(const nsIFrame* aFrame, AxisOrientationType aAxis)
130 {
131 const nsStylePosition* stylePos = aFrame->StylePosition();
133 return IsAxisHorizontal(aAxis) ?
134 stylePos->mWidth :
135 stylePos->mHeight;
136 }
138 /**
139 * Converts a logical position in a given axis into a position in the
140 * corresponding physical (x or y) axis. If the logical axis already maps
141 * directly onto one of our physical axes (i.e. LTR or TTB), then the logical
142 * and physical positions are equal; otherwise, we subtract the logical
143 * position from the container-size in that axis, to flip the polarity.
144 * (so e.g. a logical position of 2px in a RTL 20px-wide container
145 * would correspond to a physical position of 18px.)
146 */
147 static nscoord
148 PhysicalPosFromLogicalPos(nscoord aLogicalPosn,
149 nscoord aLogicalContainerSize,
150 AxisOrientationType aAxis) {
151 if (AxisGrowsInPositiveDirection(aAxis)) {
152 return aLogicalPosn;
153 }
154 return aLogicalContainerSize - aLogicalPosn;
155 }
157 static nscoord
158 MarginComponentForSide(const nsMargin& aMargin, Side aSide)
159 {
160 switch (aSide) {
161 case eSideLeft:
162 return aMargin.left;
163 case eSideRight:
164 return aMargin.right;
165 case eSideTop:
166 return aMargin.top;
167 case eSideBottom:
168 return aMargin.bottom;
169 }
171 NS_NOTREACHED("unexpected Side enum");
172 return aMargin.left; // have to return something
173 // (but something's busted if we got here)
174 }
176 static nscoord&
177 MarginComponentForSide(nsMargin& aMargin, Side aSide)
178 {
179 switch (aSide) {
180 case eSideLeft:
181 return aMargin.left;
182 case eSideRight:
183 return aMargin.right;
184 case eSideTop:
185 return aMargin.top;
186 case eSideBottom:
187 return aMargin.bottom;
188 }
190 NS_NOTREACHED("unexpected Side enum");
191 return aMargin.left; // have to return something
192 // (but something's busted if we got here)
193 }
195 // Helper-macro to let us pick one of two expressions to evaluate
196 // (a width expression vs. a height expression), to get a main-axis or
197 // cross-axis component.
198 // For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent
199 // and GetCrossComponent are cleaner; but in cases where we simply have
200 // two separate expressions for width and height (which may be expensive to
201 // evaluate), these macros will ensure that only the expression for the correct
202 // axis gets evaluated.
203 #define GET_MAIN_COMPONENT(axisTracker_, width_, height_) \
204 IsAxisHorizontal((axisTracker_).GetMainAxis()) ? (width_) : (height_)
206 #define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \
207 IsAxisHorizontal((axisTracker_).GetCrossAxis()) ? (width_) : (height_)
209 // Encapsulates our flex container's main & cross axes.
210 class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
211 public:
212 FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame);
214 // Accessors:
215 AxisOrientationType GetMainAxis() const { return mMainAxis; }
216 AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
218 nscoord GetMainComponent(const nsSize& aSize) const {
219 return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height);
220 }
221 int32_t GetMainComponent(const nsIntSize& aIntSize) const {
222 return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height);
223 }
225 nscoord GetCrossComponent(const nsSize& aSize) const {
226 return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height);
227 }
228 int32_t GetCrossComponent(const nsIntSize& aIntSize) const {
229 return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height);
230 }
232 nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const {
233 return IsAxisHorizontal(mMainAxis) ?
234 aMargin.LeftRight() :
235 aMargin.TopBottom();
236 }
237 nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const {
238 return IsAxisHorizontal(mCrossAxis) ?
239 aMargin.LeftRight() :
240 aMargin.TopBottom();
241 }
243 /**
244 * Converts a logical point into a "physical" x,y point.
245 *
246 * In the simplest case where the main-axis is left-to-right and the
247 * cross-axis is top-to-bottom, this just returns
248 * nsPoint(aMainPosn, aCrossPosn).
249 *
250 * @arg aMainPosn The main-axis position -- i.e an offset from the
251 * main-start edge of the container's content box.
252 * @arg aCrossPosn The cross-axis position -- i.e an offset from the
253 * cross-start edge of the container's content box.
254 * @return A nsPoint representing the same position (in coordinates
255 * relative to the container's content box).
256 */
257 nsPoint PhysicalPointFromLogicalPoint(nscoord aMainPosn,
258 nscoord aCrossPosn,
259 nscoord aContainerMainSize,
260 nscoord aContainerCrossSize) const {
261 nscoord physicalPosnInMainAxis =
262 PhysicalPosFromLogicalPos(aMainPosn, aContainerMainSize, mMainAxis);
263 nscoord physicalPosnInCrossAxis =
264 PhysicalPosFromLogicalPos(aCrossPosn, aContainerCrossSize, mCrossAxis);
266 return IsAxisHorizontal(mMainAxis) ?
267 nsPoint(physicalPosnInMainAxis, physicalPosnInCrossAxis) :
268 nsPoint(physicalPosnInCrossAxis, physicalPosnInMainAxis);
269 }
270 nsSize PhysicalSizeFromLogicalSizes(nscoord aMainSize,
271 nscoord aCrossSize) const {
272 return IsAxisHorizontal(mMainAxis) ?
273 nsSize(aMainSize, aCrossSize) :
274 nsSize(aCrossSize, aMainSize);
275 }
277 // Are my axes reversed with respect to what the author asked for?
278 // (We may reverse the axes in the FlexboxAxisTracker constructor and set
279 // this flag, to avoid reflowing our children in bottom-to-top order.)
280 bool AreAxesInternallyReversed() const
281 {
282 return mAreAxesInternallyReversed;
283 }
285 private:
286 AxisOrientationType mMainAxis;
287 AxisOrientationType mCrossAxis;
288 bool mAreAxesInternallyReversed;
289 };
291 /**
292 * Represents a flex item.
293 * Includes the various pieces of input that the Flexbox Layout Algorithm uses
294 * to resolve a flexible width.
295 */
296 class nsFlexContainerFrame::FlexItem : public LinkedListElement<FlexItem>
297 {
298 public:
299 // Normal constructor:
300 FlexItem(nsIFrame* aChildFrame,
301 float aFlexGrow, float aFlexShrink, nscoord aMainBaseSize,
302 nscoord aMainMinSize, nscoord aMainMaxSize,
303 nscoord aCrossMinSize, nscoord aCrossMaxSize,
304 nsMargin aMargin, nsMargin aBorderPadding,
305 const FlexboxAxisTracker& aAxisTracker);
307 // Simplified constructor, to be used only for generating "struts":
308 FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize);
310 // Accessors
311 nsIFrame* Frame() const { return mFrame; }
312 nscoord GetFlexBaseSize() const { return mFlexBaseSize; }
314 nscoord GetMainMinSize() const { return mMainMinSize; }
315 nscoord GetMainMaxSize() const { return mMainMaxSize; }
317 // Note: These return the main-axis position and size of our *content box*.
318 nscoord GetMainSize() const { return mMainSize; }
319 nscoord GetMainPosition() const { return mMainPosn; }
321 nscoord GetCrossMinSize() const { return mCrossMinSize; }
322 nscoord GetCrossMaxSize() const { return mCrossMaxSize; }
324 // Note: These return the cross-axis position and size of our *content box*.
325 nscoord GetCrossSize() const { return mCrossSize; }
326 nscoord GetCrossPosition() const { return mCrossPosn; }
328 // Convenience methods to compute the main & cross size of our *margin-box*.
329 // The caller is responsible for telling us the right axis, so that we can
330 // pull out the appropriate components of our margin/border/padding structs.
331 nscoord GetOuterMainSize(AxisOrientationType aMainAxis) const
332 {
333 return mMainSize + GetMarginBorderPaddingSizeInAxis(aMainAxis);
334 }
336 nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const
337 {
338 return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis);
339 }
341 // Returns the distance between this FlexItem's baseline and the cross-start
342 // edge of its margin-box. Used in baseline alignment.
343 // (This function needs to be told what cross axis is & which edge we're
344 // measuring the baseline from, so that it can look up the appropriate
345 // components from mMargin.)
346 nscoord GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
347 AxisEdgeType aEdge) const;
349 float GetShareOfFlexWeightSoFar() const { return mShareOfFlexWeightSoFar; }
351 bool IsFrozen() const { return mIsFrozen; }
353 bool HadMinViolation() const { return mHadMinViolation; }
354 bool HadMaxViolation() const { return mHadMaxViolation; }
356 // Indicates whether this item received a preliminary "measuring" reflow
357 // before its actual reflow.
358 bool HadMeasuringReflow() const { return mHadMeasuringReflow; }
360 // Indicates whether this item's cross-size has been stretched (from having
361 // "align-self: stretch" with an auto cross-size and no auto margins in the
362 // cross axis).
363 bool IsStretched() const { return mIsStretched; }
365 // Indicates whether this item is a "strut" left behind by an element with
366 // visibility:collapse.
367 bool IsStrut() const { return mIsStrut; }
369 uint8_t GetAlignSelf() const { return mAlignSelf; }
371 // Returns the flex weight that we should use in the "resolving flexible
372 // lengths" algorithm. If we're using flex grow, we just return that;
373 // otherwise, we use the "scaled flex shrink weight" (scaled by our flex
374 // base size, so that when both large and small items are shrinking,
375 // the large items shrink more).
376 float GetFlexWeightToUse(bool aIsUsingFlexGrow)
377 {
378 if (IsFrozen()) {
379 return 0.0f;
380 }
382 if (aIsUsingFlexGrow) {
383 return mFlexGrow;
384 }
386 // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
387 if (mFlexBaseSize == 0) {
388 // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
389 // regardless of mFlexShrink, we should just return 0.
390 // (This is really a special-case for when mFlexShrink is infinity, to
391 // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
392 return 0.0f;
393 }
394 return mFlexShrink * mFlexBaseSize;
395 }
397 // Getters for margin:
398 // ===================
399 const nsMargin& GetMargin() const { return mMargin; }
401 // Returns the margin component for a given mozilla::css::Side
402 nscoord GetMarginComponentForSide(Side aSide) const
403 { return MarginComponentForSide(mMargin, aSide); }
405 // Returns the total space occupied by this item's margins in the given axis
406 nscoord GetMarginSizeInAxis(AxisOrientationType aAxis) const
407 {
408 Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
409 Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
410 return GetMarginComponentForSide(startSide) +
411 GetMarginComponentForSide(endSide);
412 }
414 // Getters for border/padding
415 // ==========================
416 const nsMargin& GetBorderPadding() const { return mBorderPadding; }
418 // Returns the border+padding component for a given mozilla::css::Side
419 nscoord GetBorderPaddingComponentForSide(Side aSide) const
420 { return MarginComponentForSide(mBorderPadding, aSide); }
422 // Returns the total space occupied by this item's borders and padding in
423 // the given axis
424 nscoord GetBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
425 {
426 Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
427 Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
428 return GetBorderPaddingComponentForSide(startSide) +
429 GetBorderPaddingComponentForSide(endSide);
430 }
432 // Getter for combined margin/border/padding
433 // =========================================
434 // Returns the total space occupied by this item's margins, borders and
435 // padding in the given axis
436 nscoord GetMarginBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
437 {
438 return GetMarginSizeInAxis(aAxis) + GetBorderPaddingSizeInAxis(aAxis);
439 }
441 // Setters
442 // =======
444 // This sets our flex base size, and then updates the main size to the
445 // base size clamped to our main-axis [min,max] constraints.
446 void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize)
447 {
448 MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_INTRINSICSIZE,
449 "flex base size shouldn't change after we're frozen "
450 "(unless we're just resolving an intrinsic size)");
451 mFlexBaseSize = aNewFlexBaseSize;
453 // Before we've resolved flexible lengths, we keep mMainSize set to
454 // the 'hypothetical main size', which is the flex base size, clamped
455 // to the [min,max] range:
456 mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize);
457 }
459 // Setters used while we're resolving flexible lengths
460 // ---------------------------------------------------
462 // Sets the main-size of our flex item's content-box.
463 void SetMainSize(nscoord aNewMainSize)
464 {
465 MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
466 mMainSize = aNewMainSize;
467 }
469 void SetShareOfFlexWeightSoFar(float aNewShare)
470 {
471 MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f,
472 "shouldn't be giving this item any share of the weight "
473 "after it's frozen");
474 mShareOfFlexWeightSoFar = aNewShare;
475 }
477 void Freeze() { mIsFrozen = true; }
479 void SetHadMinViolation()
480 {
481 MOZ_ASSERT(!mIsFrozen,
482 "shouldn't be changing main size & having violations "
483 "after we're frozen");
484 mHadMinViolation = true;
485 }
486 void SetHadMaxViolation()
487 {
488 MOZ_ASSERT(!mIsFrozen,
489 "shouldn't be changing main size & having violations "
490 "after we're frozen");
491 mHadMaxViolation = true;
492 }
493 void ClearViolationFlags()
494 { mHadMinViolation = mHadMaxViolation = false; }
496 // Setters for values that are determined after we've resolved our main size
497 // -------------------------------------------------------------------------
499 // Sets the main-axis position of our flex item's content-box.
500 // (This is the distance between the main-start edge of the flex container
501 // and the main-start edge of the flex item's content-box.)
502 void SetMainPosition(nscoord aPosn) {
503 MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
504 mMainPosn = aPosn;
505 }
507 // Sets the cross-size of our flex item's content-box.
508 void SetCrossSize(nscoord aCrossSize) {
509 MOZ_ASSERT(!mIsStretched,
510 "Cross size shouldn't be modified after it's been stretched");
511 mCrossSize = aCrossSize;
512 }
514 // Sets the cross-axis position of our flex item's content-box.
515 // (This is the distance between the cross-start edge of the flex container
516 // and the cross-start edge of the flex item.)
517 void SetCrossPosition(nscoord aPosn) {
518 MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
519 mCrossPosn = aPosn;
520 }
522 void SetAscent(nscoord aAscent) {
523 mAscent = aAscent;
524 }
526 void SetHadMeasuringReflow() {
527 mHadMeasuringReflow = true;
528 }
530 void SetIsStretched() {
531 MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
532 mIsStretched = true;
533 }
535 // Setter for margin components (for resolving "auto" margins)
536 void SetMarginComponentForSide(Side aSide, nscoord aLength)
537 {
538 MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
539 MarginComponentForSide(mMargin, aSide) = aLength;
540 }
542 void ResolveStretchedCrossSize(nscoord aLineCrossSize,
543 const FlexboxAxisTracker& aAxisTracker);
545 uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
547 protected:
548 // Our frame:
549 nsIFrame* const mFrame;
551 // Values that we already know in constructor: (and are hence mostly 'const')
552 const float mFlexGrow;
553 const float mFlexShrink;
555 const nsMargin mBorderPadding;
556 nsMargin mMargin; // non-const because we need to resolve auto margins
558 nscoord mFlexBaseSize;
560 const nscoord mMainMinSize;
561 const nscoord mMainMaxSize;
562 const nscoord mCrossMinSize;
563 const nscoord mCrossMaxSize;
565 // Values that we compute after constructor:
566 nscoord mMainSize;
567 nscoord mMainPosn;
568 nscoord mCrossSize;
569 nscoord mCrossPosn;
570 nscoord mAscent;
572 // Temporary state, while we're resolving flexible widths (for our main size)
573 // XXXdholbert To save space, we could use a union to make these variables
574 // overlay the same memory as some other member vars that aren't touched
575 // until after main-size has been resolved. In particular, these could share
576 // memory with mMainPosn through mAscent, and mIsStretched.
577 float mShareOfFlexWeightSoFar;
578 bool mIsFrozen;
579 bool mHadMinViolation;
580 bool mHadMaxViolation;
582 // Misc:
583 bool mHadMeasuringReflow; // Did this item get a preliminary reflow,
584 // to measure its desired height?
585 bool mIsStretched; // See IsStretched() documentation
586 bool mIsStrut; // Is this item a "strut" left behind by an element
587 // with visibility:collapse?
588 uint8_t mAlignSelf; // My "align-self" computed value (with "auto"
589 // swapped out for parent"s "align-items" value,
590 // in our constructor).
591 };
593 /**
594 * Represents a single flex line in a flex container.
595 * Manages a linked list of the FlexItems that are in the line.
596 */
597 class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
598 {
599 public:
600 FlexLine()
601 : mNumItems(0),
602 mTotalInnerHypotheticalMainSize(0),
603 mTotalOuterHypotheticalMainSize(0),
604 mLineCrossSize(0),
605 mBaselineOffset(nscoord_MIN)
606 {}
608 // Returns the sum of our FlexItems' outer hypothetical main sizes.
609 // ("outer" = margin-box, and "hypothetical" = before flexing)
610 nscoord GetTotalOuterHypotheticalMainSize() const {
611 return mTotalOuterHypotheticalMainSize;
612 }
614 // Accessors for our FlexItems & information about them:
615 FlexItem* GetFirstItem()
616 {
617 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
618 "mNumItems bookkeeping is off");
619 return mItems.getFirst();
620 }
622 const FlexItem* GetFirstItem() const
623 {
624 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
625 "mNumItems bookkeeping is off");
626 return mItems.getFirst();
627 }
629 bool IsEmpty() const
630 {
631 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
632 "mNumItems bookkeeping is off");
633 return mItems.isEmpty();
634 }
636 uint32_t NumItems() const
637 {
638 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
639 "mNumItems bookkeeping is off");
640 return mNumItems;
641 }
643 // Adds the given FlexItem to our list of items (at the front or back
644 // depending on aShouldInsertAtFront), and adds its hypothetical
645 // outer & inner main sizes to our totals. Use this method instead of
646 // directly modifying the item list, so that our bookkeeping remains correct.
647 void AddItem(FlexItem* aItem,
648 bool aShouldInsertAtFront,
649 nscoord aItemInnerHypotheticalMainSize,
650 nscoord aItemOuterHypotheticalMainSize)
651 {
652 if (aShouldInsertAtFront) {
653 mItems.insertFront(aItem);
654 } else {
655 mItems.insertBack(aItem);
656 }
657 mNumItems++;
658 mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
659 mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
660 }
662 // Computes the cross-size and baseline position of this FlexLine, based on
663 // its FlexItems.
664 void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
666 // Returns the cross-size of this line.
667 nscoord GetLineCrossSize() const { return mLineCrossSize; }
669 // Setter for line cross-size -- needed for cases where the flex container
670 // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
671 // multi-line flexbox with 'align-content: stretch')
672 void SetLineCrossSize(nscoord aLineCrossSize) {
673 mLineCrossSize = aLineCrossSize;
674 }
676 /**
677 * Returns the offset within this line where any baseline-aligned FlexItems
678 * should place their baseline. Usually, this represents a distance from the
679 * line's cross-start edge, but if we're internally reversing the axes (see
680 * AreAxesInternallyReversed()), this instead represents the distance from
681 * its cross-end edge.
682 *
683 * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
684 */
685 nscoord GetBaselineOffset() const {
686 return mBaselineOffset;
687 }
689 // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
690 // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
691 void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
693 void PositionItemsInMainAxis(uint8_t aJustifyContent,
694 nscoord aContentBoxMainSize,
695 const FlexboxAxisTracker& aAxisTracker);
697 void PositionItemsInCrossAxis(nscoord aLineStartPosition,
698 const FlexboxAxisTracker& aAxisTracker);
700 friend class AutoFlexLineListClearer; // (needs access to mItems)
702 private:
703 // Helper for ResolveFlexibleLengths():
704 void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
705 bool aIsFinalIteration);
707 LinkedList<FlexItem> mItems; // Linked list of this line's flex items.
709 uint32_t mNumItems; // Number of FlexItems in this line (in |mItems|).
710 // (Shouldn't change after GenerateFlexLines finishes
711 // with this line -- at least, not until we add support
712 // for splitting lines across continuations. Then we can
713 // update this count carefully.)
715 nscoord mTotalInnerHypotheticalMainSize;
716 nscoord mTotalOuterHypotheticalMainSize;
717 nscoord mLineCrossSize;
718 nscoord mBaselineOffset;
719 };
721 // Information about a strut left behind by a FlexItem that's been collapsed
722 // using "visibility:collapse".
723 struct nsFlexContainerFrame::StrutInfo {
724 StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
725 : mItemIdx(aItemIdx),
726 mStrutCrossSize(aStrutCrossSize)
727 {
728 }
730 uint32_t mItemIdx; // Index in the child list.
731 nscoord mStrutCrossSize; // The cross-size of this strut.
732 };
734 static void
735 BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine,
736 nsTArray<StrutInfo>& aStruts)
737 {
738 MOZ_ASSERT(aFirstLine, "null first line pointer");
739 MOZ_ASSERT(aStruts.IsEmpty(),
740 "We should only build up StrutInfo once per reflow, so "
741 "aStruts should be empty when this is called");
743 uint32_t itemIdxInContainer = 0;
744 for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
745 for (const FlexItem* item = line->GetFirstItem(); item;
746 item = item->getNext()) {
747 if (NS_STYLE_VISIBILITY_COLLAPSE ==
748 item->Frame()->StyleVisibility()->mVisible) {
749 // Note the cross size of the line as the item's strut size.
750 aStruts.AppendElement(StrutInfo(itemIdxInContainer,
751 line->GetLineCrossSize()));
752 }
753 itemIdxInContainer++;
754 }
755 }
756 }
758 // Helper-function to find the first non-anonymous-box descendent of aFrame.
759 static nsIFrame*
760 GetFirstNonAnonBoxDescendant(nsIFrame* aFrame)
761 {
762 while (aFrame) {
763 nsIAtom* pseudoTag = aFrame->StyleContext()->GetPseudo();
765 // If aFrame isn't an anonymous container, then it'll do.
766 if (!pseudoTag || // No pseudotag.
767 !nsCSSAnonBoxes::IsAnonBox(pseudoTag) || // Pseudotag isn't anon.
768 pseudoTag == nsCSSAnonBoxes::mozNonElement) { // Text, not a container.
769 break;
770 }
772 // Otherwise, descend to its first child and repeat.
774 // SPECIAL CASE: if we're dealing with an anonymous table, then it might
775 // be wrapping something non-anonymous in its caption or col-group lists
776 // (instead of its principal child list), so we have to look there.
777 // (Note: For anonymous tables that have a non-anon cell *and* a non-anon
778 // column, we'll always return the column. This is fine; we're really just
779 // looking for a handle to *anything* with a meaningful content node inside
780 // the table, for use in DOM comparisons to things outside of the table.)
781 if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableOuterFrame)) {
782 nsIFrame* captionDescendant =
783 GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kCaptionList));
784 if (captionDescendant) {
785 return captionDescendant;
786 }
787 } else if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableFrame)) {
788 nsIFrame* colgroupDescendant =
789 GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kColGroupList));
790 if (colgroupDescendant) {
791 return colgroupDescendant;
792 }
793 }
795 // USUAL CASE: Descend to the first child in principal list.
796 aFrame = aFrame->GetFirstPrincipalChild();
797 }
798 return aFrame;
799 }
801 /**
802 * Sorting helper-function that compares two frames' "order" property-values,
803 * and if they're equal, compares the DOM positions of their corresponding
804 * content nodes. Returns true if aFrame1 is "less than or equal to" aFrame2
805 * according to this comparison.
806 *
807 * Note: This can't be a static function, because we need to pass it as a
808 * template argument. (Only functions with external linkage can be passed as
809 * template arguments.)
810 *
811 * @return true if the computed "order" property of aFrame1 is less than that
812 * of aFrame2, or if the computed "order" values are equal and aFrame1's
813 * corresponding DOM node is earlier than aFrame2's in the DOM tree.
814 * Otherwise, returns false.
815 */
816 bool
817 IsOrderLEQWithDOMFallback(nsIFrame* aFrame1,
818 nsIFrame* aFrame2)
819 {
820 MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
821 "this method only intended for comparing flex items");
823 if (aFrame1 == aFrame2) {
824 // Anything is trivially LEQ itself, so we return "true" here... but it's
825 // probably bad if we end up actually needing this, so let's assert.
826 NS_ERROR("Why are we checking if a frame is LEQ itself?");
827 return true;
828 }
830 // If we've got a placeholder frame, use its out-of-flow frame's 'order' val.
831 {
832 nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
833 nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
835 int32_t order1 = aRealFrame1->StylePosition()->mOrder;
836 int32_t order2 = aRealFrame2->StylePosition()->mOrder;
838 if (order1 != order2) {
839 return order1 < order2;
840 }
841 }
843 // The "order" values are equal, so we need to fall back on DOM comparison.
844 // For that, we need to dig through any anonymous box wrapper frames to find
845 // the actual frame that corresponds to our child content.
846 aFrame1 = GetFirstNonAnonBoxDescendant(aFrame1);
847 aFrame2 = GetFirstNonAnonBoxDescendant(aFrame2);
848 MOZ_ASSERT(aFrame1 && aFrame2,
849 "why do we have an anonymous box without any "
850 "non-anonymous descendants?");
853 // Special case:
854 // If either frame is for generated content from ::before or ::after, then
855 // we can't use nsContentUtils::PositionIsBefore(), since that method won't
856 // recognize generated content as being an actual sibling of other nodes.
857 // We know where ::before and ::after nodes *effectively* insert in the DOM
858 // tree, though (at the beginning & end), so we can just special-case them.
859 nsIAtom* pseudo1 = aFrame1->StyleContext()->GetPseudo();
860 nsIAtom* pseudo2 = aFrame2->StyleContext()->GetPseudo();
861 if (pseudo1 == nsCSSPseudoElements::before ||
862 pseudo2 == nsCSSPseudoElements::after) {
863 // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2.
864 return true;
865 }
866 if (pseudo1 == nsCSSPseudoElements::after ||
867 pseudo2 == nsCSSPseudoElements::before) {
868 // frame1 is ::after and/or frame2 is ::before => frame1 is not LEQ frame2.
869 return false;
870 }
872 // Usual case: Compare DOM position.
873 nsIContent* content1 = aFrame1->GetContent();
874 nsIContent* content2 = aFrame2->GetContent();
875 MOZ_ASSERT(content1 != content2,
876 "Two different flex items are using the same nsIContent node for "
877 "comparison, so we may be sorting them in an arbitrary order");
879 return nsContentUtils::PositionIsBefore(content1, content2);
880 }
882 /**
883 * Sorting helper-function that compares two frames' "order" property-values.
884 * Returns true if aFrame1 is "less than or equal to" aFrame2 according to this
885 * comparison.
886 *
887 * Note: This can't be a static function, because we need to pass it as a
888 * template argument. (Only functions with external linkage can be passed as
889 * template arguments.)
890 *
891 * @return true if the computed "order" property of aFrame1 is less than or
892 * equal to that of aFrame2. Otherwise, returns false.
893 */
894 bool
895 IsOrderLEQ(nsIFrame* aFrame1,
896 nsIFrame* aFrame2)
897 {
898 MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
899 "this method only intended for comparing flex items");
901 // If we've got a placeholder frame, use its out-of-flow frame's 'order' val.
902 nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
903 nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
905 int32_t order1 = aRealFrame1->StylePosition()->mOrder;
906 int32_t order2 = aRealFrame2->StylePosition()->mOrder;
908 return order1 <= order2;
909 }
911 bool
912 nsFlexContainerFrame::IsHorizontal()
913 {
914 const FlexboxAxisTracker axisTracker(this);
915 return IsAxisHorizontal(axisTracker.GetMainAxis());
916 }
918 FlexItem*
919 nsFlexContainerFrame::GenerateFlexItemForChild(
920 nsPresContext* aPresContext,
921 nsIFrame* aChildFrame,
922 const nsHTMLReflowState& aParentReflowState,
923 const FlexboxAxisTracker& aAxisTracker)
924 {
925 // Create temporary reflow state just for sizing -- to get hypothetical
926 // main-size and the computed values of min / max main-size property.
927 // (This reflow state will _not_ be used for reflow.)
928 nsHTMLReflowState childRS(aPresContext, aParentReflowState, aChildFrame,
929 nsSize(aParentReflowState.ComputedWidth(),
930 aParentReflowState.ComputedHeight()));
932 // FLEX GROW & SHRINK WEIGHTS
933 // --------------------------
934 const nsStylePosition* stylePos = aChildFrame->StylePosition();
935 float flexGrow = stylePos->mFlexGrow;
936 float flexShrink = stylePos->mFlexShrink;
938 // MAIN SIZES (flex base size, min/max size)
939 // -----------------------------------------
940 nscoord flexBaseSize = GET_MAIN_COMPONENT(aAxisTracker,
941 childRS.ComputedWidth(),
942 childRS.ComputedHeight());
943 nscoord mainMinSize = GET_MAIN_COMPONENT(aAxisTracker,
944 childRS.ComputedMinWidth(),
945 childRS.ComputedMinHeight());
946 nscoord mainMaxSize = GET_MAIN_COMPONENT(aAxisTracker,
947 childRS.ComputedMaxWidth(),
948 childRS.ComputedMaxHeight());
949 // This is enforced by the nsHTMLReflowState where these values come from:
950 MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");
952 // CROSS MIN/MAX SIZE
953 // ------------------
955 nscoord crossMinSize = GET_CROSS_COMPONENT(aAxisTracker,
956 childRS.ComputedMinWidth(),
957 childRS.ComputedMinHeight());
958 nscoord crossMaxSize = GET_CROSS_COMPONENT(aAxisTracker,
959 childRS.ComputedMaxWidth(),
960 childRS.ComputedMaxHeight());
962 // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES
963 // Check if we're a themed widget, in which case we might have a minimum
964 // main & cross size imposed by our widget (which we can't go below), or
965 // (more severe) our widget might have only a single valid size.
966 bool isFixedSizeWidget = false;
967 const nsStyleDisplay* disp = aChildFrame->StyleDisplay();
968 if (aChildFrame->IsThemed(disp)) {
969 nsIntSize widgetMinSize(0, 0);
970 bool canOverride = true;
971 aPresContext->GetTheme()->
972 GetMinimumWidgetSize(childRS.rendContext, aChildFrame,
973 disp->mAppearance,
974 &widgetMinSize, &canOverride);
976 nscoord widgetMainMinSize =
977 aPresContext->DevPixelsToAppUnits(
978 aAxisTracker.GetMainComponent(widgetMinSize));
979 nscoord widgetCrossMinSize =
980 aPresContext->DevPixelsToAppUnits(
981 aAxisTracker.GetCrossComponent(widgetMinSize));
983 // GMWS() returns border-box. We need content-box, so subtract
984 // borderPadding (but don't let that push our min sizes below 0).
985 nsMargin& bp = childRS.ComputedPhysicalBorderPadding();
986 widgetMainMinSize = std::max(widgetMainMinSize -
987 aAxisTracker.GetMarginSizeInMainAxis(bp), 0);
988 widgetCrossMinSize = std::max(widgetCrossMinSize -
989 aAxisTracker.GetMarginSizeInCrossAxis(bp), 0);
991 if (!canOverride) {
992 // Fixed-size widget: freeze our main-size at the widget's mandated size.
993 // (Set min and max main-sizes to that size, too, to keep us from
994 // clamping to any other size later on.)
995 flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize;
996 crossMinSize = crossMaxSize = widgetCrossMinSize;
997 isFixedSizeWidget = true;
998 } else {
999 // Variable-size widget: ensure our min/max sizes are at least as large
1000 // as the widget's mandated minimum size, so we don't flex below that.
1001 mainMinSize = std::max(mainMinSize, widgetMainMinSize);
1002 mainMaxSize = std::max(mainMaxSize, widgetMainMinSize);
1004 crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
1005 crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
1006 }
1007 }
1009 // Construct the flex item!
1010 FlexItem* item = new FlexItem(aChildFrame,
1011 flexGrow, flexShrink, flexBaseSize,
1012 mainMinSize, mainMaxSize,
1013 crossMinSize, crossMaxSize,
1014 childRS.ComputedPhysicalMargin(),
1015 childRS.ComputedPhysicalBorderPadding(),
1016 aAxisTracker);
1018 // If we're inflexible, we can just freeze to our hypothetical main-size
1019 // up-front. Similarly, if we're a fixed-size widget, we only have one
1020 // valid size, so we freeze to keep ourselves from flexing.
1021 if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
1022 item->Freeze();
1023 }
1025 return item;
1026 }
1028 nsresult
1029 nsFlexContainerFrame::
1030 ResolveFlexItemMaxContentSizing(nsPresContext* aPresContext,
1031 FlexItem& aFlexItem,
1032 const nsHTMLReflowState& aParentReflowState,
1033 const FlexboxAxisTracker& aAxisTracker)
1034 {
1035 if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
1036 // Nothing to do -- this function is only for measuring flex items
1037 // in a vertical flex container.
1038 return NS_OK;
1039 }
1041 if (NS_AUTOHEIGHT != aFlexItem.GetFlexBaseSize()) {
1042 // Nothing to do; this function's only relevant for flex items
1043 // with a base size of "auto" (or equivalent).
1044 // XXXdholbert If & when we handle "min-height: min-content" for flex items,
1045 // we'll want to resolve that in this function, too.
1046 return NS_OK;
1047 }
1049 // If we get here, we're vertical and our main size ended up being
1050 // unconstrained. We need to use our "max-content" height, which is what we
1051 // get from reflowing into our available width.
1052 // Note: This has to come *after* we construct the FlexItem, since we
1053 // invoke at least one convenience method (ResolveStretchedCrossSize) which
1054 // requires a FlexItem.
1056 // Give the item a special reflow with "mIsFlexContainerMeasuringHeight"
1057 // set. This tells it to behave as if it had "height: auto", regardless
1058 // of what the "height" property is actually set to.
1059 nsHTMLReflowState
1060 childRSForMeasuringHeight(aPresContext, aParentReflowState,
1061 aFlexItem.Frame(),
1062 nsSize(aParentReflowState.ComputedWidth(),
1063 NS_UNCONSTRAINEDSIZE),
1064 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
1065 childRSForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true;
1066 childRSForMeasuringHeight.Init(aPresContext);
1068 aFlexItem.ResolveStretchedCrossSize(aParentReflowState.ComputedWidth(),
1069 aAxisTracker);
1070 if (aFlexItem.IsStretched()) {
1071 childRSForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize());
1072 childRSForMeasuringHeight.mFlags.mHResize = true;
1073 }
1075 // If this item is flexible (vertically), then we assume that the
1076 // computed-height we're reflowing with now could be different
1077 // from the one we'll use for this flex item's "actual" reflow later on.
1078 // In that case, we need to be sure the flex item treats this as a
1079 // vertical resize, even though none of its ancestors are necessarily
1080 // being vertically resized.
1081 // (Note: We don't have to do this for width, because InitResizeFlags
1082 // will always turn on mHResize on when it sees that the computed width
1083 // is different from current width, and that's all we need.)
1084 if (!aFlexItem.IsFrozen()) { // Are we flexible?
1085 childRSForMeasuringHeight.mFlags.mVResize = true;
1086 }
1088 nsHTMLReflowMetrics childDesiredSize(childRSForMeasuringHeight);
1089 nsReflowStatus childReflowStatus;
1090 const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
1091 nsresult rv = ReflowChild(aFlexItem.Frame(), aPresContext,
1092 childDesiredSize, childRSForMeasuringHeight,
1093 0, 0, flags, childReflowStatus);
1094 NS_ENSURE_SUCCESS(rv, rv);
1096 MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
1097 "We gave flex item unconstrained available height, so it "
1098 "should be complete");
1100 rv = FinishReflowChild(aFlexItem.Frame(), aPresContext,
1101 childDesiredSize, &childRSForMeasuringHeight,
1102 0, 0, flags);
1103 NS_ENSURE_SUCCESS(rv, rv);
1105 // Subtract border/padding in vertical axis, to get _just_
1106 // the effective computed value of the "height" property.
1107 nscoord childDesiredHeight = childDesiredSize.Height() -
1108 childRSForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
1109 childDesiredHeight = std::max(0, childDesiredHeight);
1111 aFlexItem.SetFlexBaseSizeAndMainSize(childDesiredHeight);
1112 aFlexItem.SetHadMeasuringReflow();
1114 return NS_OK;
1115 }
1117 FlexItem::FlexItem(nsIFrame* aChildFrame,
1118 float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize,
1119 nscoord aMainMinSize, nscoord aMainMaxSize,
1120 nscoord aCrossMinSize, nscoord aCrossMaxSize,
1121 nsMargin aMargin, nsMargin aBorderPadding,
1122 const FlexboxAxisTracker& aAxisTracker)
1123 : mFrame(aChildFrame),
1124 mFlexGrow(aFlexGrow),
1125 mFlexShrink(aFlexShrink),
1126 mBorderPadding(aBorderPadding),
1127 mMargin(aMargin),
1128 mMainMinSize(aMainMinSize),
1129 mMainMaxSize(aMainMaxSize),
1130 mCrossMinSize(aCrossMinSize),
1131 mCrossMaxSize(aCrossMaxSize),
1132 mMainPosn(0),
1133 mCrossSize(0),
1134 mCrossPosn(0),
1135 mAscent(0),
1136 mShareOfFlexWeightSoFar(0.0f),
1137 mIsFrozen(false),
1138 mHadMinViolation(false),
1139 mHadMaxViolation(false),
1140 mHadMeasuringReflow(false),
1141 mIsStretched(false),
1142 mIsStrut(false),
1143 mAlignSelf(aChildFrame->StylePosition()->mAlignSelf)
1144 {
1145 MOZ_ASSERT(mFrame, "expecting a non-null child frame");
1146 MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
1147 "placeholder frames should not be treated as flex items");
1148 MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1149 "out-of-flow frames should not be treated as flex items");
1151 SetFlexBaseSizeAndMainSize(aFlexBaseSize);
1153 // Assert that any "auto" margin components are set to 0.
1154 // (We'll resolve them later; until then, we want to treat them as 0-sized.)
1155 #ifdef DEBUG
1156 {
1157 const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
1158 NS_FOR_CSS_SIDES(side) {
1159 if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
1160 MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
1161 "Someone else tried to resolve our auto margin");
1162 }
1163 }
1164 }
1165 #endif // DEBUG
1167 // Resolve "align-self: auto" to parent's "align-items" value.
1168 if (mAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) {
1169 mAlignSelf =
1170 mFrame->StyleContext()->GetParent()->StylePosition()->mAlignItems;
1171 }
1173 // If the flex item's inline axis is the same as the cross axis, then
1174 // 'align-self:baseline' is identical to 'flex-start'. If that's the case, we
1175 // just directly convert our align-self value here, so that we don't have to
1176 // handle this with special cases elsewhere.
1177 // Moreover: for the time being (until we support writing-modes),
1178 // all inline axes are horizontal -- so we can just check if the cross axis
1179 // is horizontal.
1180 // FIXME: Once we support writing-mode (vertical text), this IsAxisHorizontal
1181 // check won't be sufficient anymore -- we'll actually need to compare our
1182 // inline axis vs. the cross axis.
1183 if (mAlignSelf == NS_STYLE_ALIGN_ITEMS_BASELINE &&
1184 IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
1185 mAlignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
1186 }
1187 }
1189 // Simplified constructor for creating a special "strut" FlexItem, for a child
1190 // with visibility:collapse. The strut has 0 main-size, and it only exists to
1191 // impose a minimum cross size on whichever FlexLine it ends up in.
1192 FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize)
1193 : mFrame(aChildFrame),
1194 mFlexGrow(0.0f),
1195 mFlexShrink(0.0f),
1196 // mBorderPadding uses default constructor,
1197 // mMargin uses default constructor,
1198 mFlexBaseSize(0),
1199 mMainMinSize(0),
1200 mMainMaxSize(0),
1201 mCrossMinSize(0),
1202 mCrossMaxSize(0),
1203 mMainSize(0),
1204 mMainPosn(0),
1205 mCrossSize(aCrossSize),
1206 mCrossPosn(0),
1207 mAscent(0),
1208 mShareOfFlexWeightSoFar(0.0f),
1209 mIsFrozen(true),
1210 mHadMinViolation(false),
1211 mHadMaxViolation(false),
1212 mHadMeasuringReflow(false),
1213 mIsStretched(false),
1214 mIsStrut(true), // (this is the constructor for making struts, after all)
1215 mAlignSelf(NS_STYLE_ALIGN_ITEMS_FLEX_START)
1216 {
1217 MOZ_ASSERT(mFrame, "expecting a non-null child frame");
1218 MOZ_ASSERT(NS_STYLE_VISIBILITY_COLLAPSE ==
1219 mFrame->StyleVisibility()->mVisible,
1220 "Should only make struts for children with 'visibility:collapse'");
1221 MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
1222 "placeholder frames should not be treated as flex items");
1223 MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1224 "out-of-flow frames should not be treated as flex items");
1225 }
1227 nscoord
1228 FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
1229 AxisEdgeType aEdge) const
1230 {
1231 // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
1232 // measurement -- it's the distance from the border-top edge of this FlexItem
1233 // to its baseline. So, we can really only do baseline alignment when the
1234 // cross axis is vertical. (The FlexItem constructor enforces this when
1235 // resolving the item's "mAlignSelf" value).
1236 MOZ_ASSERT(!IsAxisHorizontal(aCrossAxis),
1237 "Only expecting to be doing baseline computations when the "
1238 "cross axis is vertical");
1240 Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge];
1242 nscoord marginTopToBaseline = mAscent + mMargin.top;
1244 if (sideToMeasureFrom == eSideTop) {
1245 // Measuring from top (normal case): the distance from the margin-box top
1246 // edge to the baseline is just ascent + margin-top.
1247 return marginTopToBaseline;
1248 }
1250 MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
1251 "We already checked that we're dealing with a vertical axis, and "
1252 "we're not using the top side, so that only leaves the bottom...");
1254 // Measuring from bottom: The distance from the margin-box bottom edge to the
1255 // baseline is just the margin-box cross size (i.e. outer cross size), minus
1256 // the already-computed distance from margin-top to baseline.
1257 return GetOuterCrossSize(aCrossAxis) - marginTopToBaseline;
1258 }
1260 uint32_t
1261 FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const
1262 {
1263 uint32_t numAutoMargins = 0;
1264 const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
1265 for (uint32_t i = 0; i < eNumAxisEdges; i++) {
1266 Side side = kAxisOrientationToSidesMap[aAxis][i];
1267 if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
1268 numAutoMargins++;
1269 }
1270 }
1272 // Mostly for clarity:
1273 MOZ_ASSERT(numAutoMargins <= 2,
1274 "We're just looking at one item along one dimension, so we "
1275 "should only have examined 2 margins");
1277 return numAutoMargins;
1278 }
1280 // Keeps track of our position along a particular axis (where a '0' position
1281 // corresponds to the 'start' edge of that axis).
1282 // This class shouldn't be instantiated directly -- rather, it should only be
1283 // instantiated via its subclasses defined below.
1284 class MOZ_STACK_CLASS PositionTracker {
1285 public:
1286 // Accessor for the current value of the position that we're tracking.
1287 inline nscoord GetPosition() const { return mPosition; }
1288 inline AxisOrientationType GetAxis() const { return mAxis; }
1290 // Advances our position across the start edge of the given margin, in the
1291 // axis we're tracking.
1292 void EnterMargin(const nsMargin& aMargin)
1293 {
1294 Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_Start];
1295 mPosition += MarginComponentForSide(aMargin, side);
1296 }
1298 // Advances our position across the end edge of the given margin, in the axis
1299 // we're tracking.
1300 void ExitMargin(const nsMargin& aMargin)
1301 {
1302 Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_End];
1303 mPosition += MarginComponentForSide(aMargin, side);
1304 }
1306 // Advances our current position from the start side of a child frame's
1307 // border-box to the frame's upper or left edge (depending on our axis).
1308 // (Note that this is a no-op if our axis grows in positive direction.)
1309 void EnterChildFrame(nscoord aChildFrameSize)
1310 {
1311 if (!AxisGrowsInPositiveDirection(mAxis))
1312 mPosition += aChildFrameSize;
1313 }
1315 // Advances our current position from a frame's upper or left border-box edge
1316 // (whichever is in the axis we're tracking) to the 'end' side of the frame
1317 // in the axis that we're tracking. (Note that this is a no-op if our axis
1318 // grows in the negative direction.)
1319 void ExitChildFrame(nscoord aChildFrameSize)
1320 {
1321 if (AxisGrowsInPositiveDirection(mAxis))
1322 mPosition += aChildFrameSize;
1323 }
1325 protected:
1326 // Protected constructor, to be sure we're only instantiated via a subclass.
1327 PositionTracker(AxisOrientationType aAxis)
1328 : mPosition(0),
1329 mAxis(aAxis)
1330 {}
1332 private:
1333 // Private copy-constructor, since we don't want any instances of our
1334 // subclasses to be accidentally copied.
1335 PositionTracker(const PositionTracker& aOther)
1336 : mPosition(aOther.mPosition),
1337 mAxis(aOther.mAxis)
1338 {}
1340 protected:
1341 // Member data:
1342 nscoord mPosition; // The position we're tracking
1343 const AxisOrientationType mAxis; // The axis along which we're moving
1344 };
1346 // Tracks our position in the main axis, when we're laying out flex items.
1347 // The "0" position represents the main-start edge of the flex container's
1348 // content-box.
1349 class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker {
1350 public:
1351 MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
1352 const FlexLine* aLine,
1353 uint8_t aJustifyContent,
1354 nscoord aContentBoxMainSize);
1356 ~MainAxisPositionTracker() {
1357 MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
1358 "miscounted the number of packing spaces");
1359 MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
1360 "miscounted the number of auto margins");
1361 }
1363 // Advances past the packing space (if any) between two flex items
1364 void TraversePackingSpace();
1366 // If aItem has any 'auto' margins in the main axis, this method updates the
1367 // corresponding values in its margin.
1368 void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
1370 private:
1371 nscoord mPackingSpaceRemaining;
1372 uint32_t mNumAutoMarginsInMainAxis;
1373 uint32_t mNumPackingSpacesRemaining;
1374 uint8_t mJustifyContent;
1375 };
1377 // Utility class for managing our position along the cross axis along
1378 // the whole flex container (at a higher level than a single line).
1379 // The "0" position represents the cross-start edge of the flex container's
1380 // content-box.
1381 class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
1382 public:
1383 CrossAxisPositionTracker(FlexLine* aFirstLine,
1384 uint8_t aAlignContent,
1385 nscoord aContentBoxCrossSize,
1386 bool aIsCrossSizeDefinite,
1387 const FlexboxAxisTracker& aAxisTracker);
1389 // Advances past the packing space (if any) between two flex lines
1390 void TraversePackingSpace();
1392 // Advances past the given FlexLine
1393 void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
1395 private:
1396 // Redeclare the frame-related methods from PositionTracker as private with
1397 // MOZ_DELETE, to be sure (at compile time) that no client code can invoke
1398 // them. (Unlike the other PositionTracker derived classes, this class here
1399 // deals with FlexLines, not with individual FlexItems or frames.)
1400 void EnterMargin(const nsMargin& aMargin) MOZ_DELETE;
1401 void ExitMargin(const nsMargin& aMargin) MOZ_DELETE;
1402 void EnterChildFrame(nscoord aChildFrameSize) MOZ_DELETE;
1403 void ExitChildFrame(nscoord aChildFrameSize) MOZ_DELETE;
1405 nscoord mPackingSpaceRemaining;
1406 uint32_t mNumPackingSpacesRemaining;
1407 uint8_t mAlignContent;
1408 };
1410 // Utility class for managing our position along the cross axis, *within* a
1411 // single flex line.
1412 class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
1413 public:
1414 SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
1416 void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
1417 FlexItem& aItem);
1419 void EnterAlignPackingSpace(const FlexLine& aLine,
1420 const FlexItem& aItem,
1421 const FlexboxAxisTracker& aAxisTracker);
1423 // Resets our position to the cross-start edge of this line.
1424 inline void ResetPosition() { mPosition = 0; }
1425 };
1427 //----------------------------------------------------------------------
1429 // Frame class boilerplate
1430 // =======================
1432 NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
1433 NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
1434 NS_QUERYFRAME_TAIL_INHERITING(nsFlexContainerFrameSuper)
1436 NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
1438 nsIFrame*
1439 NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
1440 nsStyleContext* aContext)
1441 {
1442 return new (aPresShell) nsFlexContainerFrame(aContext);
1443 }
1445 //----------------------------------------------------------------------
1447 // nsFlexContainerFrame Method Implementations
1448 // ===========================================
1450 /* virtual */
1451 nsFlexContainerFrame::~nsFlexContainerFrame()
1452 {
1453 }
1455 template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
1456 /* static */ bool
1457 nsFlexContainerFrame::SortChildrenIfNeeded()
1458 {
1459 if (nsIFrame::IsFrameListSorted<IsLessThanOrEqual>(mFrames)) {
1460 return false;
1461 }
1463 nsIFrame::SortFrameList<IsLessThanOrEqual>(mFrames);
1464 return true;
1465 }
1467 /* virtual */
1468 nsIAtom*
1469 nsFlexContainerFrame::GetType() const
1470 {
1471 return nsGkAtoms::flexContainerFrame;
1472 }
1474 #ifdef DEBUG_FRAME_DUMP
1475 nsresult
1476 nsFlexContainerFrame::GetFrameName(nsAString& aResult) const
1477 {
1478 return MakeFrameName(NS_LITERAL_STRING("FlexContainer"), aResult);
1479 }
1480 #endif
1482 // Helper for BuildDisplayList, to implement this special-case for flex items
1483 // from the spec:
1484 // Flex items paint exactly the same as block-level elements in the
1485 // normal flow, except that 'z-index' values other than 'auto' create
1486 // a stacking context even if 'position' is 'static'.
1487 // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#painting
1488 uint32_t
1489 GetDisplayFlagsForFlexItem(nsIFrame* aFrame)
1490 {
1491 MOZ_ASSERT(aFrame->IsFlexItem(), "Should only be called on flex items");
1493 const nsStylePosition* pos = aFrame->StylePosition();
1494 if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
1495 return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
1496 }
1497 return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
1498 }
1500 void
1501 nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1502 const nsRect& aDirtyRect,
1503 const nsDisplayListSet& aLists)
1504 {
1505 NS_ASSERTION(
1506 nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
1507 "Child frames aren't sorted correctly");
1509 DisplayBorderBackgroundOutline(aBuilder, aLists);
1511 // Our children are all block-level, so their borders/backgrounds all go on
1512 // the BlockBorderBackgrounds list.
1513 nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
1514 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
1515 BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, childLists,
1516 GetDisplayFlagsForFlexItem(e.get()));
1517 }
1518 }
1520 #ifdef DEBUG
1521 // helper for the debugging method below
1522 bool
1523 FrameWantsToBeInAnonymousFlexItem(nsIFrame* aFrame)
1524 {
1525 // Note: This needs to match the logic in
1526 // nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexItem()
1527 return (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
1528 nsGkAtoms::placeholderFrame == aFrame->GetType());
1529 }
1531 // Debugging method, to let us assert that our anonymous flex items are
1532 // set up correctly -- in particular, we assert:
1533 // (1) we don't have any inline non-replaced children
1534 // (2) we don't have any consecutive anonymous flex items
1535 // (3) we don't have any empty anonymous flex items
1536 //
1537 // XXXdholbert This matches what nsCSSFrameConstructor currently does, and what
1538 // the spec used to say. However, the spec has now changed regarding what
1539 // types of content get wrapped in an anonymous flexbox item. The patch that
1540 // implements those changes (in nsCSSFrameConstructor) will need to change
1541 // this method as well.
1542 void
1543 nsFlexContainerFrame::SanityCheckAnonymousFlexItems() const
1544 {
1545 bool prevChildWasAnonFlexItem = false;
1546 for (nsIFrame* child = mFrames.FirstChild(); child;
1547 child = child->GetNextSibling()) {
1548 MOZ_ASSERT(!FrameWantsToBeInAnonymousFlexItem(child),
1549 "frame wants to be inside an anonymous flex item, "
1550 "but it isn't");
1551 if (child->StyleContext()->GetPseudo() ==
1552 nsCSSAnonBoxes::anonymousFlexItem) {
1553 MOZ_ASSERT(!prevChildWasAnonFlexItem ||
1554 HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED),
1555 "two anon flex items in a row (shouldn't happen, unless our "
1556 "children have been reordered with the 'order' property)");
1558 nsIFrame* firstWrappedChild = child->GetFirstPrincipalChild();
1559 MOZ_ASSERT(firstWrappedChild,
1560 "anonymous flex item is empty (shouldn't happen)");
1561 prevChildWasAnonFlexItem = true;
1562 } else {
1563 prevChildWasAnonFlexItem = false;
1564 }
1565 }
1566 }
1567 #endif // DEBUG
1569 // Based on the sign of aTotalViolation, this function freezes a subset of our
1570 // flexible sizes, and restores the remaining ones to their initial pref sizes.
1571 void
1572 FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
1573 bool aIsFinalIteration)
1574 {
1575 enum FreezeType {
1576 eFreezeEverything,
1577 eFreezeMinViolations,
1578 eFreezeMaxViolations
1579 };
1581 FreezeType freezeType;
1582 if (aTotalViolation == 0) {
1583 freezeType = eFreezeEverything;
1584 } else if (aTotalViolation > 0) {
1585 freezeType = eFreezeMinViolations;
1586 } else { // aTotalViolation < 0
1587 freezeType = eFreezeMaxViolations;
1588 }
1590 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
1591 MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(),
1592 "Can have either min or max violation, but not both");
1594 if (!item->IsFrozen()) {
1595 if (eFreezeEverything == freezeType ||
1596 (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
1597 (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
1599 MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(),
1600 "Freezing item at a size below its minimum");
1601 MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(),
1602 "Freezing item at a size above its maximum");
1604 item->Freeze();
1605 } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
1606 // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
1607 // assertion to be fatal except in documents with enormous lengths.
1608 NS_ERROR("Final iteration still has unfrozen items, this shouldn't"
1609 " happen unless there was nscoord under/overflow.");
1610 item->Freeze();
1611 } // else, we'll reset this item's main size to its flex base size on the
1612 // next iteration of this algorithm.
1614 // Clear this item's violation(s), now that we've dealt with them
1615 item->ClearViolationFlags();
1616 }
1617 }
1618 }
1620 void
1621 FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
1622 {
1623 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, ("ResolveFlexibleLengths\n"));
1624 if (IsEmpty()) {
1625 return;
1626 }
1628 // Subtract space occupied by our items' margins/borders/padding, so we can
1629 // just be dealing with the space available for our flex items' content
1630 // boxes.
1631 nscoord spaceReservedForMarginBorderPadding =
1632 mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize;
1634 nscoord spaceAvailableForFlexItemsContentBoxes =
1635 aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
1637 // Determine whether we're going to be growing or shrinking items.
1638 const bool isUsingFlexGrow =
1639 (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
1641 // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
1642 // run the loop at MOST mNumItems times. This claim should hold up
1643 // because we'll freeze at least one item on each loop iteration, and once
1644 // we've run out of items to freeze, there's nothing left to do. However,
1645 // in most cases, we'll break out of this loop long before we hit that many
1646 // iterations.
1647 for (uint32_t iterationCounter = 0;
1648 iterationCounter < mNumItems; iterationCounter++) {
1649 // Set every not-yet-frozen item's used main size to its
1650 // flex base size, and subtract all the used main sizes from our
1651 // total amount of space to determine the 'available free space'
1652 // (positive or negative) to be distributed among our flexible items.
1653 nscoord availableFreeSpace = spaceAvailableForFlexItemsContentBoxes;
1654 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
1655 if (!item->IsFrozen()) {
1656 item->SetMainSize(item->GetFlexBaseSize());
1657 }
1658 availableFreeSpace -= item->GetMainSize();
1659 }
1661 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1662 (" available free space = %d\n", availableFreeSpace));
1664 // If sign of free space matches the type of flexing that we're doing, give
1665 // each flexible item a portion of availableFreeSpace.
1666 if ((availableFreeSpace > 0 && isUsingFlexGrow) ||
1667 (availableFreeSpace < 0 && !isUsingFlexGrow)) {
1669 // STRATEGY: On each item, we compute & store its "share" of the total
1670 // flex weight that we've seen so far:
1671 // curFlexWeight / runningFlexWeightSum
1672 //
1673 // Then, when we go to actually distribute the space (in the next loop),
1674 // we can simply walk backwards through the elements and give each item
1675 // its "share" multiplied by the remaining available space.
1676 //
1677 // SPECIAL CASE: If the sum of the flex weights is larger than the
1678 // maximum representable float (overflowing to infinity), then we can't
1679 // sensibly divide out proportional shares anymore. In that case, we
1680 // simply treat the flex item(s) with the largest flex weights as if
1681 // their weights were infinite (dwarfing all the others), and we
1682 // distribute all of the available space among them.
1683 float runningFlexWeightSum = 0.0f;
1684 float largestFlexWeight = 0.0f;
1685 uint32_t numItemsWithLargestFlexWeight = 0;
1686 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
1687 float curFlexWeight = item->GetFlexWeightToUse(isUsingFlexGrow);
1688 MOZ_ASSERT(curFlexWeight >= 0.0f, "weights are non-negative");
1690 runningFlexWeightSum += curFlexWeight;
1691 if (NS_finite(runningFlexWeightSum)) {
1692 if (curFlexWeight == 0.0f) {
1693 item->SetShareOfFlexWeightSoFar(0.0f);
1694 } else {
1695 item->SetShareOfFlexWeightSoFar(curFlexWeight /
1696 runningFlexWeightSum);
1697 }
1698 } // else, the sum of weights overflows to infinity, in which
1699 // case we don't bother with "SetShareOfFlexWeightSoFar" since
1700 // we know we won't use it. (instead, we'll just give every
1701 // item with the largest flex weight an equal share of space.)
1703 // Update our largest-flex-weight tracking vars
1704 if (curFlexWeight > largestFlexWeight) {
1705 largestFlexWeight = curFlexWeight;
1706 numItemsWithLargestFlexWeight = 1;
1707 } else if (curFlexWeight == largestFlexWeight) {
1708 numItemsWithLargestFlexWeight++;
1709 }
1710 }
1712 if (runningFlexWeightSum != 0.0f) { // no distribution if no flexibility
1713 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1714 (" Distributing available space:"));
1715 // NOTE: It's important that we traverse our items in *reverse* order
1716 // here, for correct width distribution according to the items'
1717 // "ShareOfFlexWeightSoFar" progressively-calculated values.
1718 for (FlexItem* item = mItems.getLast(); item;
1719 item = item->getPrevious()) {
1721 if (!item->IsFrozen()) {
1722 // To avoid rounding issues, we compute the change in size for this
1723 // item, and then subtract it from the remaining available space.
1724 nscoord sizeDelta = 0;
1725 if (NS_finite(runningFlexWeightSum)) {
1726 float myShareOfRemainingSpace =
1727 item->GetShareOfFlexWeightSoFar();
1729 MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f &&
1730 myShareOfRemainingSpace <= 1.0f,
1731 "my share should be nonnegative fractional amount");
1733 if (myShareOfRemainingSpace == 1.0f) {
1734 // (We special-case 1.0f to avoid float error from converting
1735 // availableFreeSpace from integer*1.0f --> float --> integer)
1736 sizeDelta = availableFreeSpace;
1737 } else if (myShareOfRemainingSpace > 0.0f) {
1738 sizeDelta = NSToCoordRound(availableFreeSpace *
1739 myShareOfRemainingSpace);
1740 }
1741 } else if (item->GetFlexWeightToUse(isUsingFlexGrow) ==
1742 largestFlexWeight) {
1743 // Total flexibility is infinite, so we're just distributing
1744 // the available space equally among the items that are tied for
1745 // having the largest weight (and this is one of those items).
1746 sizeDelta =
1747 NSToCoordRound(availableFreeSpace /
1748 float(numItemsWithLargestFlexWeight));
1749 numItemsWithLargestFlexWeight--;
1750 }
1752 availableFreeSpace -= sizeDelta;
1754 item->SetMainSize(item->GetMainSize() + sizeDelta);
1755 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1756 (" child %p receives %d, for a total of %d\n",
1757 item, sizeDelta, item->GetMainSize()));
1758 }
1759 }
1760 }
1761 }
1763 // Fix min/max violations:
1764 nscoord totalViolation = 0; // keeps track of adjustments for min/max
1765 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1766 (" Checking for violations:"));
1768 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
1769 if (!item->IsFrozen()) {
1770 if (item->GetMainSize() < item->GetMainMinSize()) {
1771 // min violation
1772 totalViolation += item->GetMainMinSize() - item->GetMainSize();
1773 item->SetMainSize(item->GetMainMinSize());
1774 item->SetHadMinViolation();
1775 } else if (item->GetMainSize() > item->GetMainMaxSize()) {
1776 // max violation
1777 totalViolation += item->GetMainMaxSize() - item->GetMainSize();
1778 item->SetMainSize(item->GetMainMaxSize());
1779 item->SetHadMaxViolation();
1780 }
1781 }
1782 }
1784 FreezeOrRestoreEachFlexibleSize(totalViolation,
1785 iterationCounter + 1 == mNumItems);
1787 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1788 (" Total violation: %d\n", totalViolation));
1790 if (totalViolation == 0) {
1791 break;
1792 }
1793 }
1795 // Post-condition: all lengths should've been frozen.
1796 #ifdef DEBUG
1797 for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
1798 MOZ_ASSERT(item->IsFrozen(),
1799 "All flexible lengths should've been resolved");
1800 }
1801 #endif // DEBUG
1802 }
1804 MainAxisPositionTracker::
1805 MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
1806 const FlexLine* aLine,
1807 uint8_t aJustifyContent,
1808 nscoord aContentBoxMainSize)
1809 : PositionTracker(aAxisTracker.GetMainAxis()),
1810 mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below
1811 mNumAutoMarginsInMainAxis(0),
1812 mNumPackingSpacesRemaining(0),
1813 mJustifyContent(aJustifyContent)
1814 {
1815 // mPackingSpaceRemaining is initialized to the container's main size. Now
1816 // we'll subtract out the main sizes of our flex items, so that it ends up
1817 // with the *actual* amount of packing space.
1818 for (const FlexItem* item = aLine->GetFirstItem(); item;
1819 item = item->getNext()) {
1820 mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis);
1821 mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
1822 }
1824 if (mPackingSpaceRemaining <= 0) {
1825 // No available packing space to use for resolving auto margins.
1826 mNumAutoMarginsInMainAxis = 0;
1827 }
1829 // If packing space is negative, 'space-between' behaves like 'flex-start',
1830 // and 'space-around' behaves like 'center'. In those cases, it's simplest to
1831 // just pretend we have a different 'justify-content' value and share code.
1832 if (mPackingSpaceRemaining < 0) {
1833 if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN) {
1834 mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
1835 } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND) {
1836 mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_CENTER;
1837 }
1838 }
1840 // If our main axis is (internally) reversed, swap the justify-content
1841 // "flex-start" and "flex-end" behaviors:
1842 if (aAxisTracker.AreAxesInternallyReversed()) {
1843 if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_START) {
1844 mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_END;
1845 } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_END) {
1846 mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
1847 }
1848 }
1850 // Figure out how much space we'll set aside for auto margins or
1851 // packing spaces, and advance past any leading packing-space.
1852 if (mNumAutoMarginsInMainAxis == 0 &&
1853 mPackingSpaceRemaining != 0 &&
1854 !aLine->IsEmpty()) {
1855 switch (mJustifyContent) {
1856 case NS_STYLE_JUSTIFY_CONTENT_FLEX_START:
1857 // All packing space should go at the end --> nothing to do here.
1858 break;
1859 case NS_STYLE_JUSTIFY_CONTENT_FLEX_END:
1860 // All packing space goes at the beginning
1861 mPosition += mPackingSpaceRemaining;
1862 break;
1863 case NS_STYLE_JUSTIFY_CONTENT_CENTER:
1864 // Half the packing space goes at the beginning
1865 mPosition += mPackingSpaceRemaining / 2;
1866 break;
1867 case NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN:
1868 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
1869 "negative packing space should make us use 'flex-start' "
1870 "instead of 'space-between'");
1871 // 1 packing space between each flex item, no packing space at ends.
1872 mNumPackingSpacesRemaining = aLine->NumItems() - 1;
1873 break;
1874 case NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND:
1875 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
1876 "negative packing space should make us use 'center' "
1877 "instead of 'space-around'");
1878 // 1 packing space between each flex item, plus half a packing space
1879 // at beginning & end. So our number of full packing-spaces is equal
1880 // to the number of flex items.
1881 mNumPackingSpacesRemaining = aLine->NumItems();
1882 if (mNumPackingSpacesRemaining > 0) {
1883 // The edges (start/end) share one full packing space
1884 nscoord totalEdgePackingSpace =
1885 mPackingSpaceRemaining / mNumPackingSpacesRemaining;
1887 // ...and we'll use half of that right now, at the start
1888 mPosition += totalEdgePackingSpace / 2;
1889 // ...but we need to subtract all of it right away, so that we won't
1890 // hand out any of it to intermediate packing spaces.
1891 mPackingSpaceRemaining -= totalEdgePackingSpace;
1892 mNumPackingSpacesRemaining--;
1893 }
1894 break;
1895 default:
1896 MOZ_CRASH("Unexpected justify-content value");
1897 }
1898 }
1900 MOZ_ASSERT(mNumPackingSpacesRemaining == 0 ||
1901 mNumAutoMarginsInMainAxis == 0,
1902 "extra space should either go to packing space or to "
1903 "auto margins, but not to both");
1904 }
1906 void
1907 MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem)
1908 {
1909 if (mNumAutoMarginsInMainAxis) {
1910 const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
1911 for (uint32_t i = 0; i < eNumAxisEdges; i++) {
1912 Side side = kAxisOrientationToSidesMap[mAxis][i];
1913 if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
1914 // NOTE: This integer math will skew the distribution of remainder
1915 // app-units towards the end, which is fine.
1916 nscoord curAutoMarginSize =
1917 mPackingSpaceRemaining / mNumAutoMarginsInMainAxis;
1919 MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
1920 "Expecting auto margins to have value '0' before we "
1921 "resolve them");
1922 aItem.SetMarginComponentForSide(side, curAutoMarginSize);
1924 mNumAutoMarginsInMainAxis--;
1925 mPackingSpaceRemaining -= curAutoMarginSize;
1926 }
1927 }
1928 }
1929 }
1931 void
1932 MainAxisPositionTracker::TraversePackingSpace()
1933 {
1934 if (mNumPackingSpacesRemaining) {
1935 MOZ_ASSERT(mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN ||
1936 mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND,
1937 "mNumPackingSpacesRemaining only applies for "
1938 "space-between/space-around");
1940 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
1941 "ran out of packing space earlier than we expected");
1943 // NOTE: This integer math will skew the distribution of remainder
1944 // app-units towards the end, which is fine.
1945 nscoord curPackingSpace =
1946 mPackingSpaceRemaining / mNumPackingSpacesRemaining;
1948 mPosition += curPackingSpace;
1949 mNumPackingSpacesRemaining--;
1950 mPackingSpaceRemaining -= curPackingSpace;
1951 }
1952 }
1954 CrossAxisPositionTracker::
1955 CrossAxisPositionTracker(FlexLine* aFirstLine,
1956 uint8_t aAlignContent,
1957 nscoord aContentBoxCrossSize,
1958 bool aIsCrossSizeDefinite,
1959 const FlexboxAxisTracker& aAxisTracker)
1960 : PositionTracker(aAxisTracker.GetCrossAxis()),
1961 mPackingSpaceRemaining(0),
1962 mNumPackingSpacesRemaining(0),
1963 mAlignContent(aAlignContent)
1964 {
1965 MOZ_ASSERT(aFirstLine, "null first line pointer");
1967 if (aIsCrossSizeDefinite && !aFirstLine->getNext()) {
1968 // "If the flex container has only a single line (even if it's a
1969 // multi-line flex container) and has a definite cross size, the cross
1970 // size of the flex line is the flex container's inner cross size."
1971 // SOURCE: http://dev.w3.org/csswg/css-flexbox/#algo-line-break
1972 // NOTE: This means (by definition) that there's no packing space, which
1973 // means we don't need to be concerned with "align-conent" at all and we
1974 // can return early. This is handy, because this is the usual case (for
1975 // single-line flexbox).
1976 aFirstLine->SetLineCrossSize(aContentBoxCrossSize);
1977 return;
1978 }
1980 // NOTE: The rest of this function should essentially match
1981 // MainAxisPositionTracker's constructor, though with FlexLines instead of
1982 // FlexItems, and with the additional value "stretch" (and of course with
1983 // cross sizes instead of main sizes.)
1985 // Figure out how much packing space we have (container's cross size minus
1986 // all the lines' cross sizes). Also, share this loop to count how many
1987 // lines we have. (We need that count in some cases below.)
1988 mPackingSpaceRemaining = aContentBoxCrossSize;
1989 uint32_t numLines = 0;
1990 for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
1991 mPackingSpaceRemaining -= line->GetLineCrossSize();
1992 numLines++;
1993 }
1995 // If packing space is negative, 'space-between' and 'stretch' behave like
1996 // 'flex-start', and 'space-around' behaves like 'center'. In those cases,
1997 // it's simplest to just pretend we have a different 'align-content' value
1998 // and share code.
1999 if (mPackingSpaceRemaining < 0) {
2000 if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN ||
2001 mAlignContent == NS_STYLE_ALIGN_CONTENT_STRETCH) {
2002 mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START;
2003 } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND) {
2004 mAlignContent = NS_STYLE_ALIGN_CONTENT_CENTER;
2005 }
2006 }
2008 // If our cross axis is (internally) reversed, swap the align-content
2009 // "flex-start" and "flex-end" behaviors:
2010 if (aAxisTracker.AreAxesInternallyReversed()) {
2011 if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_START) {
2012 mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_END;
2013 } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_END) {
2014 mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START;
2015 }
2016 }
2018 // Figure out how much space we'll set aside for packing spaces, and advance
2019 // past any leading packing-space.
2020 if (mPackingSpaceRemaining != 0) {
2021 switch (mAlignContent) {
2022 case NS_STYLE_ALIGN_CONTENT_FLEX_START:
2023 // All packing space should go at the end --> nothing to do here.
2024 break;
2025 case NS_STYLE_ALIGN_CONTENT_FLEX_END:
2026 // All packing space goes at the beginning
2027 mPosition += mPackingSpaceRemaining;
2028 break;
2029 case NS_STYLE_ALIGN_CONTENT_CENTER:
2030 // Half the packing space goes at the beginning
2031 mPosition += mPackingSpaceRemaining / 2;
2032 break;
2033 case NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN:
2034 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
2035 "negative packing space should make us use 'flex-start' "
2036 "instead of 'space-between'");
2037 // 1 packing space between each flex line, no packing space at ends.
2038 mNumPackingSpacesRemaining = numLines - 1;
2039 break;
2040 case NS_STYLE_ALIGN_CONTENT_SPACE_AROUND: {
2041 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
2042 "negative packing space should make us use 'center' "
2043 "instead of 'space-around'");
2044 // 1 packing space between each flex line, plus half a packing space
2045 // at beginning & end. So our number of full packing-spaces is equal
2046 // to the number of flex lines.
2047 mNumPackingSpacesRemaining = numLines;
2048 // The edges (start/end) share one full packing space
2049 nscoord totalEdgePackingSpace =
2050 mPackingSpaceRemaining / mNumPackingSpacesRemaining;
2052 // ...and we'll use half of that right now, at the start
2053 mPosition += totalEdgePackingSpace / 2;
2054 // ...but we need to subtract all of it right away, so that we won't
2055 // hand out any of it to intermediate packing spaces.
2056 mPackingSpaceRemaining -= totalEdgePackingSpace;
2057 mNumPackingSpacesRemaining--;
2058 break;
2059 }
2060 case NS_STYLE_ALIGN_CONTENT_STRETCH: {
2061 // Split space equally between the lines:
2062 MOZ_ASSERT(mPackingSpaceRemaining > 0,
2063 "negative packing space should make us use 'flex-start' "
2064 "instead of 'stretch' (and we shouldn't bother with this "
2065 "code if we have 0 packing space)");
2067 uint32_t numLinesLeft = numLines;
2068 for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
2069 // Our share is the amount of space remaining, divided by the number
2070 // of lines remainig.
2071 MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines");
2072 nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft;
2073 nscoord newSize = line->GetLineCrossSize() + shareOfExtraSpace;
2074 line->SetLineCrossSize(newSize);
2076 mPackingSpaceRemaining -= shareOfExtraSpace;
2077 numLinesLeft--;
2078 }
2079 MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines");
2080 break;
2081 }
2082 default:
2083 MOZ_CRASH("Unexpected align-content value");
2084 }
2085 }
2086 }
2088 void
2089 CrossAxisPositionTracker::TraversePackingSpace()
2090 {
2091 if (mNumPackingSpacesRemaining) {
2092 MOZ_ASSERT(mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN ||
2093 mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND,
2094 "mNumPackingSpacesRemaining only applies for "
2095 "space-between/space-around");
2097 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
2098 "ran out of packing space earlier than we expected");
2100 // NOTE: This integer math will skew the distribution of remainder
2101 // app-units towards the end, which is fine.
2102 nscoord curPackingSpace =
2103 mPackingSpaceRemaining / mNumPackingSpacesRemaining;
2105 mPosition += curPackingSpace;
2106 mNumPackingSpacesRemaining--;
2107 mPackingSpaceRemaining -= curPackingSpace;
2108 }
2109 }
2111 SingleLineCrossAxisPositionTracker::
2112 SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
2113 : PositionTracker(aAxisTracker.GetCrossAxis())
2114 {
2115 }
2117 void
2118 FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
2119 {
2120 nscoord crossStartToFurthestBaseline = nscoord_MIN;
2121 nscoord crossEndToFurthestBaseline = nscoord_MIN;
2122 nscoord largestOuterCrossSize = 0;
2123 for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
2124 nscoord curOuterCrossSize =
2125 item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
2127 if (item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE &&
2128 item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
2129 // FIXME: Once we support "writing-mode", we'll have to do baseline
2130 // alignment in vertical flex containers here (w/ horizontal cross-axes).
2132 // Find distance from our item's cross-start and cross-end margin-box
2133 // edges to its baseline.
2134 //
2135 // Here's a diagram of a flex-item that we might be doing this on.
2136 // "mmm" is the margin-box, "bbb" is the border-box. The bottom of
2137 // the text "BASE" is the baseline.
2138 //
2139 // ---(cross-start)---
2140 // ___ ___ ___
2141 // mmmmmmmmmmmm | |margin-start |
2142 // m m | _|_ ___ |
2143 // m bbbbbbbb m |curOuterCrossSize | |crossStartToBaseline
2144 // m b b m | |ascent |
2145 // m b BASE b m | _|_ _|_
2146 // m b b m | |
2147 // m bbbbbbbb m | |crossEndToBaseline
2148 // m m | |
2149 // mmmmmmmmmmmm _|_ _|_
2150 //
2151 // ---(cross-end)---
2152 //
2153 // We already have the curOuterCrossSize, margin-start, and the ascent.
2154 // * We can get crossStartToBaseline by adding margin-start + ascent.
2155 // * If we subtract that from the curOuterCrossSize, we get
2156 // crossEndToBaseline.
2158 nscoord crossStartToBaseline =
2159 item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(),
2160 eAxisEdge_Start);
2161 nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
2163 // Now, update our "largest" values for these (across all the flex items
2164 // in this flex line), so we can use them in computing the line's cross
2165 // size below:
2166 crossStartToFurthestBaseline = std::max(crossStartToFurthestBaseline,
2167 crossStartToBaseline);
2168 crossEndToFurthestBaseline = std::max(crossEndToFurthestBaseline,
2169 crossEndToBaseline);
2170 } else {
2171 largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
2172 }
2173 }
2175 // The line's baseline offset is the distance from the line's edge (start or
2176 // end, depending on whether we've flipped the axes) to the furthest
2177 // item-baseline. The item(s) with that baseline will be exactly aligned with
2178 // the line's edge.
2179 mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
2180 crossEndToFurthestBaseline : crossStartToFurthestBaseline;
2182 // The line's cross-size is the larger of:
2183 // (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
2184 // all baseline-aligned items with no cross-axis auto margins...
2185 // and
2186 // (b) largest cross-size of all other children.
2187 mLineCrossSize = std::max(crossStartToFurthestBaseline +
2188 crossEndToFurthestBaseline,
2189 largestOuterCrossSize);
2190 }
2192 void
2193 FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize,
2194 const FlexboxAxisTracker& aAxisTracker)
2195 {
2196 AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
2197 // We stretch IFF we are align-self:stretch, have no auto margins in
2198 // cross axis, and have cross-axis size property == "auto". If any of those
2199 // conditions don't hold up, we won't stretch.
2200 if (mAlignSelf != NS_STYLE_ALIGN_ITEMS_STRETCH ||
2201 GetNumAutoMarginsInAxis(crossAxis) != 0 ||
2202 eStyleUnit_Auto != GetSizePropertyForAxis(mFrame, crossAxis).GetUnit()) {
2203 return;
2204 }
2206 // If we've already been stretched, we can bail out early, too.
2207 // No need to redo the calculation.
2208 if (mIsStretched) {
2209 return;
2210 }
2212 // Reserve space for margins & border & padding, and then use whatever
2213 // remains as our item's cross-size (clamped to its min/max range).
2214 nscoord stretchedSize = aLineCrossSize -
2215 GetMarginBorderPaddingSizeInAxis(crossAxis);
2217 stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
2219 // Update the cross-size & make a note that it's stretched, so we know to
2220 // override the reflow state's computed cross-size in our final reflow.
2221 SetCrossSize(stretchedSize);
2222 mIsStretched = true;
2223 }
2225 void
2226 SingleLineCrossAxisPositionTracker::
2227 ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
2228 FlexItem& aItem)
2229 {
2230 // Subtract the space that our item is already occupying, to see how much
2231 // space (if any) is available for its auto margins.
2232 nscoord spaceForAutoMargins = aLine.GetLineCrossSize() -
2233 aItem.GetOuterCrossSize(mAxis);
2235 if (spaceForAutoMargins <= 0) {
2236 return; // No available space --> nothing to do
2237 }
2239 uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis);
2240 if (numAutoMargins == 0) {
2241 return; // No auto margins --> nothing to do.
2242 }
2244 // OK, we have at least one auto margin and we have some available space.
2245 // Give each auto margin a share of the space.
2246 const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
2247 for (uint32_t i = 0; i < eNumAxisEdges; i++) {
2248 Side side = kAxisOrientationToSidesMap[mAxis][i];
2249 if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
2250 MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
2251 "Expecting auto margins to have value '0' before we "
2252 "update them");
2254 // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2.
2255 // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half.
2256 nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins;
2257 aItem.SetMarginComponentForSide(side, curAutoMarginSize);
2258 numAutoMargins--;
2259 spaceForAutoMargins -= curAutoMarginSize;
2260 }
2261 }
2262 }
2264 void
2265 SingleLineCrossAxisPositionTracker::
2266 EnterAlignPackingSpace(const FlexLine& aLine,
2267 const FlexItem& aItem,
2268 const FlexboxAxisTracker& aAxisTracker)
2269 {
2270 // We don't do align-self alignment on items that have auto margins
2271 // in the cross axis.
2272 if (aItem.GetNumAutoMarginsInAxis(mAxis)) {
2273 return;
2274 }
2276 uint8_t alignSelf = aItem.GetAlignSelf();
2277 // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
2278 // auto-sized items (which we've already done).
2279 if (alignSelf == NS_STYLE_ALIGN_ITEMS_STRETCH) {
2280 alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
2281 }
2283 // If our cross axis is (internally) reversed, swap the align-self
2284 // "flex-start" and "flex-end" behaviors:
2285 if (aAxisTracker.AreAxesInternallyReversed()) {
2286 if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_START) {
2287 alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_END;
2288 } else if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_END) {
2289 alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
2290 }
2291 }
2293 switch (alignSelf) {
2294 case NS_STYLE_ALIGN_ITEMS_FLEX_START:
2295 // No space to skip over -- we're done.
2296 break;
2297 case NS_STYLE_ALIGN_ITEMS_FLEX_END:
2298 mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
2299 break;
2300 case NS_STYLE_ALIGN_ITEMS_CENTER:
2301 // Note: If cross-size is odd, the "after" space will get the extra unit.
2302 mPosition +=
2303 (aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
2304 break;
2305 case NS_STYLE_ALIGN_ITEMS_BASELINE: {
2306 // Normally, baseline-aligned items are collectively aligned with the
2307 // line's cross-start edge; however, if our cross axis is (internally)
2308 // reversed, we instead align them with the cross-end edge.
2309 nscoord itemBaselineOffset =
2310 aItem.GetBaselineOffsetFromOuterCrossEdge(mAxis,
2311 aAxisTracker.AreAxesInternallyReversed() ?
2312 eAxisEdge_End : eAxisEdge_Start);
2314 nscoord lineBaselineOffset = aLine.GetBaselineOffset();
2316 NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
2317 "failed at finding largest baseline offset");
2319 // How much do we need to adjust our position (from the line edge),
2320 // to get the item's baseline to hit the line's baseline offset:
2321 nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
2323 if (aAxisTracker.AreAxesInternallyReversed()) {
2324 // Advance to align item w/ line's flex-end edge (as in FLEX_END case):
2325 mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
2326 // ...and step *back* by the baseline adjustment:
2327 mPosition -= baselineDiff;
2328 } else {
2329 // mPosition is already at line's flex-start edge.
2330 // From there, we step *forward* by the baseline adjustment:
2331 mPosition += baselineDiff;
2332 }
2333 break;
2334 }
2335 default:
2336 NS_NOTREACHED("Unexpected align-self value");
2337 break;
2338 }
2339 }
2341 FlexboxAxisTracker::FlexboxAxisTracker(
2342 nsFlexContainerFrame* aFlexContainerFrame)
2343 : mAreAxesInternallyReversed(false)
2344 {
2345 const nsStylePosition* pos = aFlexContainerFrame->StylePosition();
2346 uint32_t flexDirection = pos->mFlexDirection;
2347 uint32_t cssDirection =
2348 aFlexContainerFrame->StyleVisibility()->mDirection;
2350 MOZ_ASSERT(cssDirection == NS_STYLE_DIRECTION_LTR ||
2351 cssDirection == NS_STYLE_DIRECTION_RTL,
2352 "Unexpected computed value for 'direction' property");
2353 // (Not asserting for flexDirection here; it's checked by the switch below.)
2355 // These are defined according to writing-modes' definitions of
2356 // start/end (for the inline dimension) and before/after (for the block
2357 // dimension), here:
2358 // http://www.w3.org/TR/css3-writing-modes/#logical-directions
2359 // (NOTE: I'm intentionally not calling this "inlineAxis"/"blockAxis", since
2360 // those terms have explicit definition in the writing-modes spec, which are
2361 // the opposite of how I'd be using them here.)
2362 // XXXdholbert Once we support the 'writing-mode' property, use its value
2363 // here to further customize inlineDimension & blockDimension.
2365 // Inline dimension ("start-to-end"):
2366 AxisOrientationType inlineDimension =
2367 cssDirection == NS_STYLE_DIRECTION_RTL ? eAxis_RL : eAxis_LR;
2369 // Block dimension ("before-to-after"):
2370 AxisOrientationType blockDimension = eAxis_TB;
2372 // Determine main axis:
2373 switch (flexDirection) {
2374 case NS_STYLE_FLEX_DIRECTION_ROW:
2375 mMainAxis = inlineDimension;
2376 break;
2377 case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE:
2378 mMainAxis = GetReverseAxis(inlineDimension);
2379 break;
2380 case NS_STYLE_FLEX_DIRECTION_COLUMN:
2381 mMainAxis = blockDimension;
2382 break;
2383 case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE:
2384 mMainAxis = GetReverseAxis(blockDimension);
2385 break;
2386 default:
2387 MOZ_CRASH("Unexpected computed value for 'flex-flow' property");
2388 }
2390 // Determine cross axis:
2391 // (This is set up so that a bogus |flexDirection| value will
2392 // give us blockDimension.
2393 if (flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN ||
2394 flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE) {
2395 mCrossAxis = inlineDimension;
2396 } else {
2397 mCrossAxis = blockDimension;
2398 }
2400 // "flex-wrap: wrap-reverse" reverses our cross axis.
2401 if (pos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) {
2402 mCrossAxis = GetReverseAxis(mCrossAxis);
2403 }
2405 // Master switch to enable/disable bug 983427's code for reversing our axes
2406 // and reversing some logic, to avoid reflowing children in bottom-to-top
2407 // order. (This switch can be removed eventually, but for now, it allows
2408 // this special-case code path to be compared against the normal code path.)
2409 static bool sPreventBottomToTopChildOrdering = true;
2411 if (sPreventBottomToTopChildOrdering) {
2412 // If either axis is bottom-to-top, we flip both axes (and set a flag
2413 // so that we can flip some logic to make the reversal transparent).
2414 if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) {
2415 mMainAxis = GetReverseAxis(mMainAxis);
2416 mCrossAxis = GetReverseAxis(mCrossAxis);
2417 mAreAxesInternallyReversed = true;
2418 }
2419 }
2421 MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis),
2422 "main & cross axes should be in different dimensions");
2423 }
2425 // Allocates a new FlexLine, adds it to the given LinkedList (at the front or
2426 // back depending on aShouldInsertAtFront), and returns a pointer to it.
2427 static FlexLine*
2428 AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
2429 bool aShouldInsertAtFront)
2430 {
2431 FlexLine* newLine = new FlexLine();
2432 if (aShouldInsertAtFront) {
2433 aLines.insertFront(newLine);
2434 } else {
2435 aLines.insertBack(newLine);
2436 }
2437 return newLine;
2438 }
2440 nsresult
2441 nsFlexContainerFrame::GenerateFlexLines(
2442 nsPresContext* aPresContext,
2443 const nsHTMLReflowState& aReflowState,
2444 nscoord aContentBoxMainSize,
2445 nscoord aAvailableHeightForContent,
2446 const nsTArray<StrutInfo>& aStruts,
2447 const FlexboxAxisTracker& aAxisTracker,
2448 LinkedList<FlexLine>& aLines)
2449 {
2450 MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
2452 const bool isSingleLine =
2453 NS_STYLE_FLEX_WRAP_NOWRAP == aReflowState.mStylePosition->mFlexWrap;
2455 // If we're transparently reversing axes, then we'll need to link up our
2456 // FlexItems and FlexLines in the reverse order, so that the rest of flex
2457 // layout (with flipped axes) will still produce the correct result.
2458 // Here, we declare a convenience bool that we'll pass when adding a new
2459 // FlexLine or FlexItem, to make us insert it at the beginning of its list
2460 // (so the list ends up reversed).
2461 const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
2463 // We have at least one FlexLine. Even an empty flex container has a single
2464 // (empty) flex line.
2465 FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
2467 nscoord wrapThreshold;
2468 if (isSingleLine) {
2469 // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
2470 wrapThreshold = NS_UNCONSTRAINEDSIZE;
2471 } else {
2472 // Wrapping! Set wrap threshold to flex container's content-box main-size.
2473 wrapThreshold = aContentBoxMainSize;
2475 // If the flex container doesn't have a definite content-box main-size
2476 // (e.g. if we're 'height:auto'), make sure we at least wrap when we hit
2477 // its max main-size.
2478 if (wrapThreshold == NS_UNCONSTRAINEDSIZE) {
2479 const nscoord flexContainerMaxMainSize =
2480 GET_MAIN_COMPONENT(aAxisTracker,
2481 aReflowState.ComputedMaxWidth(),
2482 aReflowState.ComputedMaxHeight());
2484 wrapThreshold = flexContainerMaxMainSize;
2485 }
2487 // Also: if we're vertical and paginating, we may need to wrap sooner
2488 // (before we run off the end of the page)
2489 if (!IsAxisHorizontal(aAxisTracker.GetMainAxis()) &&
2490 aAvailableHeightForContent != NS_UNCONSTRAINEDSIZE) {
2491 wrapThreshold = std::min(wrapThreshold, aAvailableHeightForContent);
2492 }
2493 }
2495 // Tracks the index of the next strut, in aStruts (and when this hits
2496 // aStruts.Length(), that means there are no more struts):
2497 uint32_t nextStrutIdx = 0;
2499 // Overall index of the current flex item in the flex container. (This gets
2500 // checked against entries in aStruts.)
2501 uint32_t itemIdxInContainer = 0;
2503 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
2504 nsIFrame* childFrame = e.get();
2506 // Honor "page-break-before", if we're multi-line and this line isn't empty:
2507 if (!isSingleLine && !curLine->IsEmpty() &&
2508 childFrame->StyleDisplay()->mBreakBefore) {
2509 curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
2510 }
2512 nsAutoPtr<FlexItem> item;
2513 if (nextStrutIdx < aStruts.Length() &&
2514 aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
2516 // Use the simplified "strut" FlexItem constructor:
2517 item = new FlexItem(childFrame, aStruts[nextStrutIdx].mStrutCrossSize);
2518 nextStrutIdx++;
2519 } else {
2520 item = GenerateFlexItemForChild(aPresContext, childFrame,
2521 aReflowState, aAxisTracker);
2523 nsresult rv = ResolveFlexItemMaxContentSizing(aPresContext, *item,
2524 aReflowState, aAxisTracker);
2525 NS_ENSURE_SUCCESS(rv,rv);
2526 }
2528 nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
2529 nscoord itemOuterHypotheticalMainSize =
2530 item->GetOuterMainSize(aAxisTracker.GetMainAxis());
2532 // Check if we need to wrap |item| to a new line
2533 // (i.e. check if its outer hypothetical main size pushes our line over
2534 // the threshold)
2535 if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
2536 !curLine->IsEmpty() && // No need to wrap at start of a line.
2537 wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
2538 itemOuterHypotheticalMainSize)) {
2539 curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
2540 }
2542 // Add item to current flex line (and update the line's bookkeeping about
2543 // how large its items collectively are).
2544 curLine->AddItem(item.forget(), shouldInsertAtFront,
2545 itemInnerHypotheticalMainSize,
2546 itemOuterHypotheticalMainSize);
2548 // Honor "page-break-after", if we're multi-line and have more children:
2549 if (!isSingleLine && childFrame->GetNextSibling() &&
2550 childFrame->StyleDisplay()->mBreakAfter) {
2551 curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
2552 }
2553 itemIdxInContainer++;
2554 }
2556 return NS_OK;
2557 }
2559 // Retrieves the content-box main-size of our flex container from the
2560 // reflow state (specifically, the main-size of *this continuation* of the
2561 // flex container).
2562 nscoord
2563 nsFlexContainerFrame::GetMainSizeFromReflowState(
2564 const nsHTMLReflowState& aReflowState,
2565 const FlexboxAxisTracker& aAxisTracker)
2566 {
2567 if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
2568 // Horizontal case is easy -- our main size is our computed width
2569 // (which is already resolved).
2570 return aReflowState.ComputedWidth();
2571 }
2573 return GetEffectiveComputedHeight(aReflowState);
2574 }
2576 // Returns the largest outer hypothetical main-size of any line in |aLines|.
2577 // (i.e. the hypothetical main-size of the largest line)
2578 static nscoord
2579 GetLargestLineMainSize(const FlexLine* aFirstLine)
2580 {
2581 nscoord largestLineOuterSize = 0;
2582 for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
2583 largestLineOuterSize = std::max(largestLineOuterSize,
2584 line->GetTotalOuterHypotheticalMainSize());
2585 }
2586 return largestLineOuterSize;
2587 }
2589 // Returns the content-box main-size of our flex container, based on the
2590 // available height (if appropriate) and the main-sizes of the flex items.
2591 static nscoord
2592 ClampFlexContainerMainSize(const nsHTMLReflowState& aReflowState,
2593 const FlexboxAxisTracker& aAxisTracker,
2594 nscoord aUnclampedMainSize,
2595 nscoord aAvailableHeightForContent,
2596 const FlexLine* aFirstLine,
2597 nsReflowStatus& aStatus)
2598 {
2599 MOZ_ASSERT(aFirstLine, "null first line pointer");
2601 if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
2602 // Horizontal case is easy -- our main size should already be resolved
2603 // before we get a call to Reflow. We don't have to worry about doing
2604 // page-breaking or shrinkwrapping in the horizontal axis.
2605 return aUnclampedMainSize;
2606 }
2608 if (aUnclampedMainSize != NS_INTRINSICSIZE) {
2609 // Vertical case, with fixed height:
2610 if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE ||
2611 aUnclampedMainSize < aAvailableHeightForContent) {
2612 // Not in a fragmenting context, OR no need to fragment because we have
2613 // more available height than we need. Either way, just use our fixed
2614 // height. (Note that the reflow state has already done the appropriate
2615 // min/max-height clamping.)
2616 return aUnclampedMainSize;
2617 }
2619 // Fragmenting *and* our fixed height is too tall for available height:
2620 // Mark incomplete so we get a next-in-flow, and take up all of the
2621 // available height (or the amount of height required by our children, if
2622 // that's larger; but of course not more than our own computed height).
2623 // XXXdholbert For now, we don't support pushing children to our next
2624 // continuation or splitting children, so "amount of height required by
2625 // our children" is just the main-size (height) of our longest flex line.
2626 NS_FRAME_SET_INCOMPLETE(aStatus);
2627 nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
2629 if (largestLineOuterSize <= aAvailableHeightForContent) {
2630 return aAvailableHeightForContent;
2631 }
2632 return std::min(aUnclampedMainSize, largestLineOuterSize);
2633 }
2635 // Vertical case, with auto-height:
2636 // Resolve auto-height to the largest FlexLine-length, clamped to our
2637 // computed min/max main-size properties (min-height & max-height).
2638 // XXXdholbert Handle constrained-aAvailableHeightForContent case here.
2639 nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
2640 return NS_CSS_MINMAX(largestLineOuterSize,
2641 aReflowState.ComputedMinHeight(),
2642 aReflowState.ComputedMaxHeight());
2643 }
2645 nscoord
2646 nsFlexContainerFrame::ComputeCrossSize(const nsHTMLReflowState& aReflowState,
2647 const FlexboxAxisTracker& aAxisTracker,
2648 nscoord aSumLineCrossSizes,
2649 nscoord aAvailableHeightForContent,
2650 bool* aIsDefinite,
2651 nsReflowStatus& aStatus)
2652 {
2653 MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null");
2655 if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
2656 // Cross axis is horizontal: our cross size is our computed width
2657 // (which is already resolved).
2658 *aIsDefinite = true;
2659 return aReflowState.ComputedWidth();
2660 }
2662 nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState);
2663 if (effectiveComputedHeight != NS_INTRINSICSIZE) {
2664 // Cross-axis is vertical, and we have a fixed height:
2665 *aIsDefinite = true;
2666 if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE ||
2667 effectiveComputedHeight < aAvailableHeightForContent) {
2668 // Not in a fragmenting context, OR no need to fragment because we have
2669 // more available height than we need. Either way, just use our fixed
2670 // height. (Note that the reflow state has already done the appropriate
2671 // min/max-height clamping.)
2672 return effectiveComputedHeight;
2673 }
2675 // Fragmenting *and* our fixed height is too tall for available height:
2676 // Mark incomplete so we get a next-in-flow, and take up all of the
2677 // available height (or the amount of height required by our children, if
2678 // that's larger; but of course not more than our own computed height).
2679 // XXXdholbert For now, we don't support pushing children to our next
2680 // continuation or splitting children, so "amount of height required by
2681 // our children" is just our line-height.
2682 NS_FRAME_SET_INCOMPLETE(aStatus);
2683 if (aSumLineCrossSizes <= aAvailableHeightForContent) {
2684 return aAvailableHeightForContent;
2685 }
2686 return std::min(effectiveComputedHeight, aSumLineCrossSizes);
2687 }
2689 // Cross axis is vertical and we have auto-height: shrink-wrap our line(s),
2690 // subject to our min-size / max-size constraints in that (vertical) axis.
2691 // XXXdholbert Handle constrained-aAvailableHeightForContent case here.
2692 *aIsDefinite = false;
2693 return NS_CSS_MINMAX(aSumLineCrossSizes,
2694 aReflowState.ComputedMinHeight(),
2695 aReflowState.ComputedMaxHeight());
2696 }
2698 void
2699 FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent,
2700 nscoord aContentBoxMainSize,
2701 const FlexboxAxisTracker& aAxisTracker)
2702 {
2703 MainAxisPositionTracker mainAxisPosnTracker(aAxisTracker, this,
2704 aJustifyContent,
2705 aContentBoxMainSize);
2706 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
2707 nscoord itemMainBorderBoxSize =
2708 item->GetMainSize() +
2709 item->GetBorderPaddingSizeInAxis(mainAxisPosnTracker.GetAxis());
2711 // Resolve any main-axis 'auto' margins on aChild to an actual value.
2712 mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item);
2714 // Advance our position tracker to child's upper-left content-box corner,
2715 // and use that as its position in the main axis.
2716 mainAxisPosnTracker.EnterMargin(item->GetMargin());
2717 mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
2719 item->SetMainPosition(mainAxisPosnTracker.GetPosition());
2721 mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
2722 mainAxisPosnTracker.ExitMargin(item->GetMargin());
2723 mainAxisPosnTracker.TraversePackingSpace();
2724 }
2725 }
2727 // Helper method to take care of children who ASK_FOR_BASELINE, when
2728 // we need their baseline.
2729 static void
2730 ResolveReflowedChildAscent(nsIFrame* aFrame,
2731 nsHTMLReflowMetrics& aChildDesiredSize)
2732 {
2733 if (aChildDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
2734 // Use GetFirstLineBaseline(), or just GetBaseline() if that fails.
2735 nscoord ascent;
2736 if (nsLayoutUtils::GetFirstLineBaseline(aFrame, &ascent)) {
2737 aChildDesiredSize.SetTopAscent(ascent);
2738 } else {
2739 aChildDesiredSize.SetTopAscent(aFrame->GetBaseline());
2740 }
2741 }
2742 }
2744 /**
2745 * Given the flex container's "logical ascent" (i.e. distance from the
2746 * flex container's content-box cross-start edge to its baseline), returns
2747 * its actual physical ascent value (the distance from the *border-box* top
2748 * edge to its baseline).
2749 */
2750 static nscoord
2751 ComputePhysicalAscentFromLogicalAscent(nscoord aLogicalAscent,
2752 nscoord aContentBoxCrossSize,
2753 const nsHTMLReflowState& aReflowState,
2754 const FlexboxAxisTracker& aAxisTracker)
2755 {
2756 return aReflowState.ComputedPhysicalBorderPadding().top +
2757 PhysicalPosFromLogicalPos(aLogicalAscent, aContentBoxCrossSize,
2758 aAxisTracker.GetCrossAxis());
2759 }
2761 nsresult
2762 nsFlexContainerFrame::SizeItemInCrossAxis(
2763 nsPresContext* aPresContext,
2764 const FlexboxAxisTracker& aAxisTracker,
2765 nsHTMLReflowState& aChildReflowState,
2766 FlexItem& aItem)
2767 {
2768 // In vertical flexbox (with horizontal cross-axis), we can just trust the
2769 // reflow state's computed-width as our cross-size. We also don't need to
2770 // record the baseline because we'll have converted any "align-self:baseline"
2771 // items to be "align-self:flex-start" in the FlexItem constructor.
2772 // FIXME: Once we support writing-mode (vertical text), we will be able to
2773 // have baseline-aligned items in a vertical flexbox, and we'll need to
2774 // record baseline information here.
2775 if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
2776 MOZ_ASSERT(aItem.GetAlignSelf() != NS_STYLE_ALIGN_ITEMS_BASELINE,
2777 "In vert flex container, we depend on FlexItem constructor to "
2778 "convert 'align-self: baseline' to 'align-self: flex-start'");
2779 aItem.SetCrossSize(aChildReflowState.ComputedWidth());
2780 return NS_OK;
2781 }
2783 MOZ_ASSERT(!aItem.HadMeasuringReflow(),
2784 "We shouldn't need more than one measuring reflow");
2786 if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH) {
2787 // This item's got "align-self: stretch", so we probably imposed a
2788 // stretched computed height on it during its previous reflow. We're
2789 // not imposing that height for *this* measuring reflow, so we need to
2790 // tell it to treat this reflow as a vertical resize (regardless of
2791 // whether any of its ancestors are being resized).
2792 aChildReflowState.mFlags.mVResize = true;
2793 }
2794 nsHTMLReflowMetrics childDesiredSize(aChildReflowState);
2795 nsReflowStatus childReflowStatus;
2796 const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
2797 nsresult rv = ReflowChild(aItem.Frame(), aPresContext,
2798 childDesiredSize, aChildReflowState,
2799 0, 0, flags, childReflowStatus);
2800 aItem.SetHadMeasuringReflow();
2801 NS_ENSURE_SUCCESS(rv, rv);
2803 // XXXdholbert Once we do pagination / splitting, we'll need to actually
2804 // handle incomplete childReflowStatuses. But for now, we give our kids
2805 // unconstrained available height, which means they should always complete.
2806 MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
2807 "We gave flex item unconstrained available height, so it "
2808 "should be complete");
2810 // Tell the child we're done with its initial reflow.
2811 // (Necessary for e.g. GetBaseline() to work below w/out asserting)
2812 rv = FinishReflowChild(aItem.Frame(), aPresContext,
2813 childDesiredSize, &aChildReflowState, 0, 0, flags);
2814 NS_ENSURE_SUCCESS(rv, rv);
2816 // Save the sizing info that we learned from this reflow
2817 // -----------------------------------------------------
2819 // Tentatively store the child's desired content-box cross-size.
2820 // Note that childDesiredSize is the border-box size, so we have to
2821 // subtract border & padding to get the content-box size.
2822 // (Note that at this point in the code, we know our cross axis is vertical,
2823 // so we don't bother with making aAxisTracker pick the cross-axis component
2824 // for us.)
2825 nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
2826 if (childDesiredSize.Height() < crossAxisBorderPadding) {
2827 // Child's requested size isn't large enough for its border/padding!
2828 // This is OK for the trivial nsFrame::Reflow() impl, but other frame
2829 // classes should know better. So, if we get here, the child had better be
2830 // an instance of nsFrame (i.e. it should return null from GetType()).
2831 // XXXdholbert Once we've fixed bug 765861, we should upgrade this to an
2832 // assertion that trivially passes if bug 765861's flag has been flipped.
2833 NS_WARN_IF_FALSE(!aItem.Frame()->GetType(),
2834 "Child should at least request space for border/padding");
2835 aItem.SetCrossSize(0);
2836 } else {
2837 // (normal case)
2838 aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding);
2839 }
2841 // If we need to do baseline-alignment, store the child's ascent.
2842 if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE) {
2843 ResolveReflowedChildAscent(aItem.Frame(), childDesiredSize);
2844 aItem.SetAscent(childDesiredSize.TopAscent());
2845 }
2847 return NS_OK;
2848 }
2850 void
2851 FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
2852 const FlexboxAxisTracker& aAxisTracker)
2853 {
2854 SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
2856 for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
2857 // First, stretch the item's cross size (if appropriate), and resolve any
2858 // auto margins in this axis.
2859 item->ResolveStretchedCrossSize(mLineCrossSize, aAxisTracker);
2860 lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, *item);
2862 // Compute the cross-axis position of this item
2863 nscoord itemCrossBorderBoxSize =
2864 item->GetCrossSize() +
2865 item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis());
2866 lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker);
2867 lineCrossAxisPosnTracker.EnterMargin(item->GetMargin());
2868 lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
2870 item->SetCrossPosition(aLineStartPosition +
2871 lineCrossAxisPosnTracker.GetPosition());
2873 // Back out to cross-axis edge of the line.
2874 lineCrossAxisPosnTracker.ResetPosition();
2875 }
2876 }
2878 nsresult
2879 nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
2880 nsHTMLReflowMetrics& aDesiredSize,
2881 const nsHTMLReflowState& aReflowState,
2882 nsReflowStatus& aStatus)
2883 {
2884 DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
2885 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
2886 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
2887 ("Reflow() for nsFlexContainerFrame %p\n", this));
2889 if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) {
2890 return NS_OK;
2891 }
2893 // We (and our children) can only depend on our ancestor's height if we have
2894 // a percent-height, or if we're positioned and we have "top" and "bottom"
2895 // set and have height:auto. (There are actually other cases, too -- e.g. if
2896 // our parent is itself a vertical flex container and we're flexible -- but
2897 // we'll let our ancestors handle those sorts of cases.)
2898 const nsStylePosition* stylePos = StylePosition();
2899 if (stylePos->mHeight.HasPercent() ||
2900 (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
2901 eStyleUnit_Auto == stylePos->mHeight.GetUnit() &&
2902 eStyleUnit_Auto != stylePos->mOffset.GetTopUnit() &&
2903 eStyleUnit_Auto != stylePos->mOffset.GetBottomUnit())) {
2904 AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
2905 }
2907 #ifdef DEBUG
2908 SanityCheckAnonymousFlexItems();
2909 #endif // DEBUG
2911 // If we've never reordered our children, then we can trust that they're
2912 // already in DOM-order, and we only need to consider their "order" property
2913 // when checking them for sortedness & sorting them.
2914 //
2915 // After we actually sort them, though, we can't trust that they're in DOM
2916 // order anymore. So, from that point on, our sort & sorted-order-checking
2917 // operations need to use a fancier LEQ function that also takes DOM order
2918 // into account, so that we can honor the spec's requirement that frames w/
2919 // equal "order" values are laid out in DOM order.
2921 if (!HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED)) {
2922 if (SortChildrenIfNeeded<IsOrderLEQ>()) {
2923 AddStateBits(NS_STATE_FLEX_CHILDREN_REORDERED);
2924 }
2925 } else {
2926 SortChildrenIfNeeded<IsOrderLEQWithDOMFallback>();
2927 }
2929 const FlexboxAxisTracker axisTracker(this);
2931 // If we're being fragmented into a constrained height, subtract off
2932 // borderpadding-top from it, to get the available height for our
2933 // content box. (Don't subtract if we're skipping top border/padding,
2934 // though.)
2935 nscoord availableHeightForContent = aReflowState.AvailableHeight();
2936 if (availableHeightForContent != NS_UNCONSTRAINEDSIZE &&
2937 !(GetSkipSides() & (1 << NS_SIDE_TOP))) {
2938 availableHeightForContent -= aReflowState.ComputedPhysicalBorderPadding().top;
2939 // (Don't let that push availableHeightForContent below zero, though):
2940 availableHeightForContent = std::max(availableHeightForContent, 0);
2941 }
2943 nscoord contentBoxMainSize = GetMainSizeFromReflowState(aReflowState,
2944 axisTracker);
2946 nsAutoTArray<StrutInfo, 1> struts;
2947 nsresult rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus,
2948 contentBoxMainSize, availableHeightForContent,
2949 struts, axisTracker);
2951 if (NS_SUCCEEDED(rv) && !struts.IsEmpty()) {
2952 // We're restarting flex layout, with new knowledge of collapsed items.
2953 rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus,
2954 contentBoxMainSize, availableHeightForContent,
2955 struts, axisTracker);
2956 }
2958 return rv;
2959 }
2961 // RAII class to clean up a list of FlexLines.
2962 // Specifically, this removes each line from the list, deletes all the
2963 // FlexItems in its list, and deletes the FlexLine.
2964 class MOZ_STACK_CLASS AutoFlexLineListClearer
2965 {
2966 public:
2967 AutoFlexLineListClearer(LinkedList<FlexLine>& aLines
2968 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
2969 : mLines(aLines)
2970 {
2971 MOZ_GUARD_OBJECT_NOTIFIER_INIT;
2972 }
2974 ~AutoFlexLineListClearer()
2975 {
2976 while (FlexLine* line = mLines.popFirst()) {
2977 while (FlexItem* item = line->mItems.popFirst()) {
2978 delete item;
2979 }
2980 delete line;
2981 }
2982 }
2984 private:
2985 LinkedList<FlexLine>& mLines;
2986 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2987 };
2989 nsresult
2990 nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
2991 nsHTMLReflowMetrics& aDesiredSize,
2992 const nsHTMLReflowState& aReflowState,
2993 nsReflowStatus& aStatus,
2994 nscoord aContentBoxMainSize,
2995 nscoord aAvailableHeightForContent,
2996 nsTArray<StrutInfo>& aStruts,
2997 const FlexboxAxisTracker& aAxisTracker)
2998 {
2999 aStatus = NS_FRAME_COMPLETE;
3001 LinkedList<FlexLine> lines;
3002 AutoFlexLineListClearer cleanupLines(lines);
3004 nsresult rv = GenerateFlexLines(aPresContext, aReflowState,
3005 aContentBoxMainSize,
3006 aAvailableHeightForContent,
3007 aStruts, aAxisTracker, lines);
3008 NS_ENSURE_SUCCESS(rv, rv);
3010 aContentBoxMainSize =
3011 ClampFlexContainerMainSize(aReflowState, aAxisTracker,
3012 aContentBoxMainSize, aAvailableHeightForContent,
3013 lines.getFirst(), aStatus);
3015 for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3016 line->ResolveFlexibleLengths(aContentBoxMainSize);
3017 }
3019 // Cross Size Determination - Flexbox spec section 9.4
3020 // ===================================================
3021 // Calculate the hypothetical cross size of each item:
3022 nscoord sumLineCrossSizes = 0;
3023 for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3024 for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) {
3025 // (If the item's already been stretched, or it's a strut, then it
3026 // already knows its cross size. Don't bother trying to recalculate it.)
3027 if (!item->IsStretched() && !item->IsStrut()) {
3028 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
3029 item->Frame(),
3030 nsSize(aReflowState.ComputedWidth(),
3031 NS_UNCONSTRAINEDSIZE));
3032 // Override computed main-size
3033 if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
3034 childReflowState.SetComputedWidth(item->GetMainSize());
3035 } else {
3036 childReflowState.SetComputedHeight(item->GetMainSize());
3037 }
3039 nsresult rv = SizeItemInCrossAxis(aPresContext, aAxisTracker,
3040 childReflowState, *item);
3041 NS_ENSURE_SUCCESS(rv, rv);
3042 }
3043 }
3044 // Now that we've finished with this line's items, size the line itself:
3045 line->ComputeCrossSizeAndBaseline(aAxisTracker);
3046 sumLineCrossSizes += line->GetLineCrossSize();
3047 }
3049 bool isCrossSizeDefinite;
3050 const nscoord contentBoxCrossSize =
3051 ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes,
3052 aAvailableHeightForContent, &isCrossSizeDefinite, aStatus);
3054 // Set up state for cross-axis alignment, at a high level (outside the
3055 // scope of a particular flex line)
3056 CrossAxisPositionTracker
3057 crossAxisPosnTracker(lines.getFirst(),
3058 aReflowState.mStylePosition->mAlignContent,
3059 contentBoxCrossSize, isCrossSizeDefinite,
3060 aAxisTracker);
3062 // Now that we know the cross size of each line (including
3063 // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
3064 // constructor), we can create struts for any flex items with
3065 // "visibility: collapse" (and restart flex layout).
3066 if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
3067 BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
3068 if (!aStruts.IsEmpty()) {
3069 // Restart flex layout, using our struts.
3070 return NS_OK;
3071 }
3072 }
3074 // If the container should derive its baseline from the first FlexLine,
3075 // do that here (while crossAxisPosnTracker is conveniently pointing
3076 // at the cross-start edge of that line, which the line's baseline offset is
3077 // measured from):
3078 nscoord flexContainerAscent;
3079 if (!aAxisTracker.AreAxesInternallyReversed()) {
3080 nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset();
3081 if (firstLineBaselineOffset == nscoord_MIN) {
3082 // No baseline-aligned items in line. Use sentinel value to prompt us to
3083 // get baseline from the first FlexItem after we've reflowed it.
3084 flexContainerAscent = nscoord_MIN;
3085 } else {
3086 flexContainerAscent =
3087 ComputePhysicalAscentFromLogicalAscent(
3088 crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
3089 contentBoxCrossSize, aReflowState, aAxisTracker);
3090 }
3091 }
3093 for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3095 // Main-Axis Alignment - Flexbox spec section 9.5
3096 // ==============================================
3097 line->PositionItemsInMainAxis(aReflowState.mStylePosition->mJustifyContent,
3098 aContentBoxMainSize,
3099 aAxisTracker);
3101 // Cross-Axis Alignment - Flexbox spec section 9.6
3102 // ===============================================
3103 line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(),
3104 aAxisTracker);
3105 crossAxisPosnTracker.TraverseLine(*line);
3106 crossAxisPosnTracker.TraversePackingSpace();
3107 }
3109 // If the container should derive its baseline from the last FlexLine,
3110 // do that here (while crossAxisPosnTracker is conveniently pointing
3111 // at the cross-end edge of that line, which the line's baseline offset is
3112 // measured from):
3113 if (aAxisTracker.AreAxesInternallyReversed()) {
3114 nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset();
3115 if (lastLineBaselineOffset == nscoord_MIN) {
3116 // No baseline-aligned items in line. Use sentinel value to prompt us to
3117 // get baseline from the last FlexItem after we've reflowed it.
3118 flexContainerAscent = nscoord_MIN;
3119 } else {
3120 flexContainerAscent =
3121 ComputePhysicalAscentFromLogicalAscent(
3122 crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
3123 contentBoxCrossSize, aReflowState, aAxisTracker);
3124 }
3125 }
3127 // Before giving each child a final reflow, calculate the origin of the
3128 // flex container's content box (with respect to its border-box), so that
3129 // we can compute our flex item's final positions.
3130 nsMargin containerBorderPadding(aReflowState.ComputedPhysicalBorderPadding());
3131 ApplySkipSides(containerBorderPadding, &aReflowState);
3132 const nsPoint containerContentBoxOrigin(containerBorderPadding.left,
3133 containerBorderPadding.top);
3135 // FINAL REFLOW: Give each child frame another chance to reflow, now that
3136 // we know its final size and position.
3137 for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3138 for (const FlexItem* item = line->GetFirstItem(); item;
3139 item = item->getNext()) {
3140 nsPoint physicalPosn = aAxisTracker.PhysicalPointFromLogicalPoint(
3141 item->GetMainPosition(),
3142 item->GetCrossPosition(),
3143 aContentBoxMainSize,
3144 contentBoxCrossSize);
3145 // Adjust physicalPosn to be relative to the container's border-box
3146 // (i.e. its frame rect), instead of the container's content-box:
3147 physicalPosn += containerContentBoxOrigin;
3149 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
3150 item->Frame(),
3151 nsSize(aReflowState.ComputedWidth(),
3152 NS_UNCONSTRAINEDSIZE));
3154 // Keep track of whether we've overriden the child's computed height
3155 // and/or width, so we can set its resize flags accordingly.
3156 bool didOverrideComputedWidth = false;
3157 bool didOverrideComputedHeight = false;
3159 // Override computed main-size
3160 if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
3161 childReflowState.SetComputedWidth(item->GetMainSize());
3162 didOverrideComputedWidth = true;
3163 } else {
3164 childReflowState.SetComputedHeight(item->GetMainSize());
3165 didOverrideComputedHeight = true;
3166 }
3168 // Override reflow state's computed cross-size, for stretched items.
3169 if (item->IsStretched()) {
3170 MOZ_ASSERT(item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH,
3171 "stretched item w/o 'align-self: stretch'?");
3172 if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
3173 childReflowState.SetComputedWidth(item->GetCrossSize());
3174 didOverrideComputedWidth = true;
3175 } else {
3176 // If this item's height is stretched, it's a relative height.
3177 item->Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
3178 childReflowState.SetComputedHeight(item->GetCrossSize());
3179 didOverrideComputedHeight = true;
3180 }
3181 }
3183 // XXXdholbert Might need to actually set the correct margins in the
3184 // reflow state at some point, so that they can be saved on the frame for
3185 // UsedMarginProperty(). Maybe doesn't matter though...?
3187 // If we're overriding the computed width or height, *and* we had an
3188 // earlier "measuring" reflow, then this upcoming reflow needs to be
3189 // treated as a resize.
3190 if (item->HadMeasuringReflow()) {
3191 if (didOverrideComputedWidth) {
3192 // (This is somewhat redundant, since the reflow state already
3193 // sets mHResize whenever our computed width has changed since the
3194 // previous reflow. Still, it's nice for symmetry, and it may become
3195 // necessary once we support orthogonal flows.)
3196 childReflowState.mFlags.mHResize = true;
3197 }
3198 if (didOverrideComputedHeight) {
3199 childReflowState.mFlags.mVResize = true;
3200 }
3201 }
3202 // NOTE: Be very careful about doing anything else with childReflowState
3203 // after this point, because some of its methods (e.g. SetComputedWidth)
3204 // internally call InitResizeFlags and stomp on mVResize & mHResize.
3206 nsHTMLReflowMetrics childDesiredSize(childReflowState);
3207 nsReflowStatus childReflowStatus;
3208 nsresult rv = ReflowChild(item->Frame(), aPresContext,
3209 childDesiredSize, childReflowState,
3210 physicalPosn.x, physicalPosn.y,
3211 0, childReflowStatus);
3212 NS_ENSURE_SUCCESS(rv, rv);
3214 // XXXdholbert Once we do pagination / splitting, we'll need to actually
3215 // handle incomplete childReflowStatuses. But for now, we give our kids
3216 // unconstrained available height, which means they should always
3217 // complete.
3218 MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
3219 "We gave flex item unconstrained available height, so it "
3220 "should be complete");
3222 childReflowState.ApplyRelativePositioning(&physicalPosn);
3224 rv = FinishReflowChild(item->Frame(), aPresContext,
3225 childDesiredSize, &childReflowState,
3226 physicalPosn.x, physicalPosn.y, 0);
3227 NS_ENSURE_SUCCESS(rv, rv);
3229 // If this is our first child and we haven't established a baseline for
3230 // the container yet (i.e. if we don't have 'align-self: baseline' on any
3231 // children), then use this child's baseline as the container's baseline.
3232 if (item->Frame() == mFrames.FirstChild() &&
3233 flexContainerAscent == nscoord_MIN) {
3234 ResolveReflowedChildAscent(item->Frame(), childDesiredSize);
3236 // (We use GetNormalPosition() instead of physicalPosn because we don't
3237 // want relative positioning on the child to affect the baseline that we
3238 // read from it).
3239 flexContainerAscent = item->Frame()->GetNormalPosition().y +
3240 childDesiredSize.TopAscent();
3241 }
3242 }
3243 }
3245 nsSize desiredContentBoxSize =
3246 aAxisTracker.PhysicalSizeFromLogicalSizes(aContentBoxMainSize,
3247 contentBoxCrossSize);
3249 aDesiredSize.Width() = desiredContentBoxSize.width +
3250 containerBorderPadding.LeftRight();
3251 // Does *NOT* include bottom border/padding yet (we add that a bit lower down)
3252 aDesiredSize.Height() = desiredContentBoxSize.height +
3253 containerBorderPadding.top;
3255 if (flexContainerAscent == nscoord_MIN) {
3256 // Still don't have our baseline set -- this happens if we have no
3257 // children (or if our children are huge enough that they have nscoord_MIN
3258 // as their baseline... in which case, we'll use the wrong baseline, but no
3259 // big deal)
3260 NS_WARN_IF_FALSE(lines.getFirst()->IsEmpty(),
3261 "Have flex items but didn't get an ascent - that's odd "
3262 "(or there are just gigantic sizes involved)");
3263 // Per spec, just use the bottom of content-box.
3264 flexContainerAscent = aDesiredSize.Height();
3265 }
3266 aDesiredSize.SetTopAscent(flexContainerAscent);
3268 // Now: If we're complete, add bottom border/padding to desired height
3269 // (unless that pushes us over available height, in which case we become
3270 // incomplete (unless we already weren't asking for any height, in which case
3271 // we stay complete to avoid looping forever)).
3272 // NOTE: If we're auto-height, we allow our bottom border/padding to push us
3273 // over the available height without requesting a continuation, for
3274 // consistency with the behavior of "display:block" elements.
3275 if (NS_FRAME_IS_COMPLETE(aStatus)) {
3276 // NOTE: We can't use containerBorderPadding.bottom for this, because if
3277 // we're auto-height, ApplySkipSides will have zeroed it (because it
3278 // assumed we might get a continuation). We have the correct value in
3279 // aReflowState.ComputedPhyiscalBorderPadding().bottom, though, so we use that.
3280 nscoord desiredHeightWithBottomBP =
3281 aDesiredSize.Height() + aReflowState.ComputedPhysicalBorderPadding().bottom;
3283 if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE ||
3284 aDesiredSize.Height() == 0 ||
3285 desiredHeightWithBottomBP <= aReflowState.AvailableHeight() ||
3286 aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
3287 // Update desired height to include bottom border/padding
3288 aDesiredSize.Height() = desiredHeightWithBottomBP;
3289 } else {
3290 // We couldn't fit bottom border/padding, so we'll need a continuation.
3291 NS_FRAME_SET_INCOMPLETE(aStatus);
3292 }
3293 }
3295 // Overflow area = union(my overflow area, kids' overflow areas)
3296 aDesiredSize.SetOverflowAreasToDesiredBounds();
3297 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
3298 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, e.get());
3299 }
3301 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
3302 aReflowState, aStatus);
3304 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize)
3305 return NS_OK;
3306 }
3308 /* virtual */ nscoord
3309 nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
3310 {
3311 nscoord minWidth = 0;
3312 DISPLAY_MIN_WIDTH(this, minWidth);
3314 FlexboxAxisTracker axisTracker(this);
3316 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
3317 nscoord childMinWidth =
3318 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),
3319 nsLayoutUtils::MIN_WIDTH);
3320 // For a horizontal single-line flex container, the intrinsic min width is
3321 // the sum of its items' min widths.
3322 // For a vertical flex container, or for a multi-line horizontal flex
3323 // container, the intrinsic min width is the max of its items' min widths.
3324 if (IsAxisHorizontal(axisTracker.GetMainAxis()) &&
3325 NS_STYLE_FLEX_WRAP_NOWRAP == StylePosition()->mFlexWrap) {
3326 minWidth += childMinWidth;
3327 } else {
3328 minWidth = std::max(minWidth, childMinWidth);
3329 }
3330 }
3331 return minWidth;
3332 }
3334 /* virtual */ nscoord
3335 nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
3336 {
3337 nscoord prefWidth = 0;
3338 DISPLAY_PREF_WIDTH(this, prefWidth);
3340 // XXXdholbert Optimization: We could cache our intrinsic widths like
3341 // nsBlockFrame does (and return it early from this function if it's set).
3342 // Whenever anything happens that might change it, set it to
3343 // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicWidthsDirty
3344 // does)
3345 FlexboxAxisTracker axisTracker(this);
3347 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
3348 nscoord childPrefWidth =
3349 nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),
3350 nsLayoutUtils::PREF_WIDTH);
3351 if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
3352 prefWidth += childPrefWidth;
3353 } else {
3354 prefWidth = std::max(prefWidth, childPrefWidth);
3355 }
3356 }
3357 return prefWidth;
3358 }