layout/generic/nsFlexContainerFrame.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:0e5d770cb807
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3
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/. */
7
8 /* rendering object for CSS "display: flex" */
9
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"
22
23 using namespace mozilla;
24 using namespace mozilla::css;
25 using namespace mozilla::layout;
26
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;
33
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 */
44
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?
48
49 // Helper enums
50 // ============
51
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 };
65
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 };
75
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 };
85
86 // Helper structs / classes / methods
87 // ==================================
88
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 }
96
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 }
103
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;
110
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 }
118
119 // Check that we're still in the enum's valid range
120 MOZ_ASSERT(reversedAxis >= eAxis_LR &&
121 reversedAxis <= eAxis_BT);
122
123 return reversedAxis;
124 }
125
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();
132
133 return IsAxisHorizontal(aAxis) ?
134 stylePos->mWidth :
135 stylePos->mHeight;
136 }
137
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 }
156
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 }
170
171 NS_NOTREACHED("unexpected Side enum");
172 return aMargin.left; // have to return something
173 // (but something's busted if we got here)
174 }
175
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 }
189
190 NS_NOTREACHED("unexpected Side enum");
191 return aMargin.left; // have to return something
192 // (but something's busted if we got here)
193 }
194
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_)
205
206 #define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \
207 IsAxisHorizontal((axisTracker_).GetCrossAxis()) ? (width_) : (height_)
208
209 // Encapsulates our flex container's main & cross axes.
210 class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
211 public:
212 FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame);
213
214 // Accessors:
215 AxisOrientationType GetMainAxis() const { return mMainAxis; }
216 AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
217
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 }
224
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 }
231
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 }
242
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);
265
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 }
276
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 }
284
285 private:
286 AxisOrientationType mMainAxis;
287 AxisOrientationType mCrossAxis;
288 bool mAreAxesInternallyReversed;
289 };
290
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);
306
307 // Simplified constructor, to be used only for generating "struts":
308 FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize);
309
310 // Accessors
311 nsIFrame* Frame() const { return mFrame; }
312 nscoord GetFlexBaseSize() const { return mFlexBaseSize; }
313
314 nscoord GetMainMinSize() const { return mMainMinSize; }
315 nscoord GetMainMaxSize() const { return mMainMaxSize; }
316
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; }
320
321 nscoord GetCrossMinSize() const { return mCrossMinSize; }
322 nscoord GetCrossMaxSize() const { return mCrossMaxSize; }
323
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; }
327
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 }
335
336 nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const
337 {
338 return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis);
339 }
340
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;
348
349 float GetShareOfFlexWeightSoFar() const { return mShareOfFlexWeightSoFar; }
350
351 bool IsFrozen() const { return mIsFrozen; }
352
353 bool HadMinViolation() const { return mHadMinViolation; }
354 bool HadMaxViolation() const { return mHadMaxViolation; }
355
356 // Indicates whether this item received a preliminary "measuring" reflow
357 // before its actual reflow.
358 bool HadMeasuringReflow() const { return mHadMeasuringReflow; }
359
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; }
364
365 // Indicates whether this item is a "strut" left behind by an element with
366 // visibility:collapse.
367 bool IsStrut() const { return mIsStrut; }
368
369 uint8_t GetAlignSelf() const { return mAlignSelf; }
370
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 }
381
382 if (aIsUsingFlexGrow) {
383 return mFlexGrow;
384 }
385
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 }
396
397 // Getters for margin:
398 // ===================
399 const nsMargin& GetMargin() const { return mMargin; }
400
401 // Returns the margin component for a given mozilla::css::Side
402 nscoord GetMarginComponentForSide(Side aSide) const
403 { return MarginComponentForSide(mMargin, aSide); }
404
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 }
413
414 // Getters for border/padding
415 // ==========================
416 const nsMargin& GetBorderPadding() const { return mBorderPadding; }
417
418 // Returns the border+padding component for a given mozilla::css::Side
419 nscoord GetBorderPaddingComponentForSide(Side aSide) const
420 { return MarginComponentForSide(mBorderPadding, aSide); }
421
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 }
431
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 }
440
441 // Setters
442 // =======
443
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;
452
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 }
458
459 // Setters used while we're resolving flexible lengths
460 // ---------------------------------------------------
461
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 }
468
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 }
476
477 void Freeze() { mIsFrozen = true; }
478
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; }
495
496 // Setters for values that are determined after we've resolved our main size
497 // -------------------------------------------------------------------------
498
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 }
506
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 }
513
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 }
521
522 void SetAscent(nscoord aAscent) {
523 mAscent = aAscent;
524 }
525
526 void SetHadMeasuringReflow() {
527 mHadMeasuringReflow = true;
528 }
529
530 void SetIsStretched() {
531 MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
532 mIsStretched = true;
533 }
534
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 }
541
542 void ResolveStretchedCrossSize(nscoord aLineCrossSize,
543 const FlexboxAxisTracker& aAxisTracker);
544
545 uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
546
547 protected:
548 // Our frame:
549 nsIFrame* const mFrame;
550
551 // Values that we already know in constructor: (and are hence mostly 'const')
552 const float mFlexGrow;
553 const float mFlexShrink;
554
555 const nsMargin mBorderPadding;
556 nsMargin mMargin; // non-const because we need to resolve auto margins
557
558 nscoord mFlexBaseSize;
559
560 const nscoord mMainMinSize;
561 const nscoord mMainMaxSize;
562 const nscoord mCrossMinSize;
563 const nscoord mCrossMaxSize;
564
565 // Values that we compute after constructor:
566 nscoord mMainSize;
567 nscoord mMainPosn;
568 nscoord mCrossSize;
569 nscoord mCrossPosn;
570 nscoord mAscent;
571
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;
581
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 };
592
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 {}
607
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 }
613
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 }
621
622 const FlexItem* GetFirstItem() const
623 {
624 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
625 "mNumItems bookkeeping is off");
626 return mItems.getFirst();
627 }
628
629 bool IsEmpty() const
630 {
631 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
632 "mNumItems bookkeeping is off");
633 return mItems.isEmpty();
634 }
635
636 uint32_t NumItems() const
637 {
638 MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
639 "mNumItems bookkeeping is off");
640 return mNumItems;
641 }
642
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 }
661
662 // Computes the cross-size and baseline position of this FlexLine, based on
663 // its FlexItems.
664 void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
665
666 // Returns the cross-size of this line.
667 nscoord GetLineCrossSize() const { return mLineCrossSize; }
668
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 }
675
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 }
688
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);
692
693 void PositionItemsInMainAxis(uint8_t aJustifyContent,
694 nscoord aContentBoxMainSize,
695 const FlexboxAxisTracker& aAxisTracker);
696
697 void PositionItemsInCrossAxis(nscoord aLineStartPosition,
698 const FlexboxAxisTracker& aAxisTracker);
699
700 friend class AutoFlexLineListClearer; // (needs access to mItems)
701
702 private:
703 // Helper for ResolveFlexibleLengths():
704 void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
705 bool aIsFinalIteration);
706
707 LinkedList<FlexItem> mItems; // Linked list of this line's flex items.
708
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.)
714
715 nscoord mTotalInnerHypotheticalMainSize;
716 nscoord mTotalOuterHypotheticalMainSize;
717 nscoord mLineCrossSize;
718 nscoord mBaselineOffset;
719 };
720
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 }
729
730 uint32_t mItemIdx; // Index in the child list.
731 nscoord mStrutCrossSize; // The cross-size of this strut.
732 };
733
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");
742
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 }
757
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();
764
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 }
771
772 // Otherwise, descend to its first child and repeat.
773
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 }
794
795 // USUAL CASE: Descend to the first child in principal list.
796 aFrame = aFrame->GetFirstPrincipalChild();
797 }
798 return aFrame;
799 }
800
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");
822
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 }
829
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);
834
835 int32_t order1 = aRealFrame1->StylePosition()->mOrder;
836 int32_t order2 = aRealFrame2->StylePosition()->mOrder;
837
838 if (order1 != order2) {
839 return order1 < order2;
840 }
841 }
842
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?");
851
852
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 }
871
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");
878
879 return nsContentUtils::PositionIsBefore(content1, content2);
880 }
881
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");
900
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);
904
905 int32_t order1 = aRealFrame1->StylePosition()->mOrder;
906 int32_t order2 = aRealFrame2->StylePosition()->mOrder;
907
908 return order1 <= order2;
909 }
910
911 bool
912 nsFlexContainerFrame::IsHorizontal()
913 {
914 const FlexboxAxisTracker axisTracker(this);
915 return IsAxisHorizontal(axisTracker.GetMainAxis());
916 }
917
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()));
931
932 // FLEX GROW & SHRINK WEIGHTS
933 // --------------------------
934 const nsStylePosition* stylePos = aChildFrame->StylePosition();
935 float flexGrow = stylePos->mFlexGrow;
936 float flexShrink = stylePos->mFlexShrink;
937
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");
951
952 // CROSS MIN/MAX SIZE
953 // ------------------
954
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());
961
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);
975
976 nscoord widgetMainMinSize =
977 aPresContext->DevPixelsToAppUnits(
978 aAxisTracker.GetMainComponent(widgetMinSize));
979 nscoord widgetCrossMinSize =
980 aPresContext->DevPixelsToAppUnits(
981 aAxisTracker.GetCrossComponent(widgetMinSize));
982
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);
990
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);
1003
1004 crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
1005 crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
1006 }
1007 }
1008
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);
1017
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 }
1024
1025 return item;
1026 }
1027
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 }
1040
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 }
1048
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.
1055
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);
1067
1068 aFlexItem.ResolveStretchedCrossSize(aParentReflowState.ComputedWidth(),
1069 aAxisTracker);
1070 if (aFlexItem.IsStretched()) {
1071 childRSForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize());
1072 childRSForMeasuringHeight.mFlags.mHResize = true;
1073 }
1074
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 }
1087
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);
1095
1096 MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
1097 "We gave flex item unconstrained available height, so it "
1098 "should be complete");
1099
1100 rv = FinishReflowChild(aFlexItem.Frame(), aPresContext,
1101 childDesiredSize, &childRSForMeasuringHeight,
1102 0, 0, flags);
1103 NS_ENSURE_SUCCESS(rv, rv);
1104
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);
1110
1111 aFlexItem.SetFlexBaseSizeAndMainSize(childDesiredHeight);
1112 aFlexItem.SetHadMeasuringReflow();
1113
1114 return NS_OK;
1115 }
1116
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");
1150
1151 SetFlexBaseSizeAndMainSize(aFlexBaseSize);
1152
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
1166
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 }
1172
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 }
1188
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 }
1226
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");
1239
1240 Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge];
1241
1242 nscoord marginTopToBaseline = mAscent + mMargin.top;
1243
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 }
1249
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...");
1253
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 }
1259
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 }
1271
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");
1276
1277 return numAutoMargins;
1278 }
1279
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; }
1289
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 }
1297
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 }
1305
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 }
1314
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 }
1324
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 {}
1331
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 {}
1339
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 };
1345
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);
1355
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 }
1362
1363 // Advances past the packing space (if any) between two flex items
1364 void TraversePackingSpace();
1365
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);
1369
1370 private:
1371 nscoord mPackingSpaceRemaining;
1372 uint32_t mNumAutoMarginsInMainAxis;
1373 uint32_t mNumPackingSpacesRemaining;
1374 uint8_t mJustifyContent;
1375 };
1376
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);
1388
1389 // Advances past the packing space (if any) between two flex lines
1390 void TraversePackingSpace();
1391
1392 // Advances past the given FlexLine
1393 void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
1394
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;
1404
1405 nscoord mPackingSpaceRemaining;
1406 uint32_t mNumPackingSpacesRemaining;
1407 uint8_t mAlignContent;
1408 };
1409
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);
1415
1416 void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
1417 FlexItem& aItem);
1418
1419 void EnterAlignPackingSpace(const FlexLine& aLine,
1420 const FlexItem& aItem,
1421 const FlexboxAxisTracker& aAxisTracker);
1422
1423 // Resets our position to the cross-start edge of this line.
1424 inline void ResetPosition() { mPosition = 0; }
1425 };
1426
1427 //----------------------------------------------------------------------
1428
1429 // Frame class boilerplate
1430 // =======================
1431
1432 NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
1433 NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
1434 NS_QUERYFRAME_TAIL_INHERITING(nsFlexContainerFrameSuper)
1435
1436 NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
1437
1438 nsIFrame*
1439 NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
1440 nsStyleContext* aContext)
1441 {
1442 return new (aPresShell) nsFlexContainerFrame(aContext);
1443 }
1444
1445 //----------------------------------------------------------------------
1446
1447 // nsFlexContainerFrame Method Implementations
1448 // ===========================================
1449
1450 /* virtual */
1451 nsFlexContainerFrame::~nsFlexContainerFrame()
1452 {
1453 }
1454
1455 template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
1456 /* static */ bool
1457 nsFlexContainerFrame::SortChildrenIfNeeded()
1458 {
1459 if (nsIFrame::IsFrameListSorted<IsLessThanOrEqual>(mFrames)) {
1460 return false;
1461 }
1462
1463 nsIFrame::SortFrameList<IsLessThanOrEqual>(mFrames);
1464 return true;
1465 }
1466
1467 /* virtual */
1468 nsIAtom*
1469 nsFlexContainerFrame::GetType() const
1470 {
1471 return nsGkAtoms::flexContainerFrame;
1472 }
1473
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
1481
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");
1492
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 }
1499
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");
1508
1509 DisplayBorderBackgroundOutline(aBuilder, aLists);
1510
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 }
1519
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 }
1530
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)");
1557
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
1568
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 };
1580
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 }
1589
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");
1593
1594 if (!item->IsFrozen()) {
1595 if (eFreezeEverything == freezeType ||
1596 (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
1597 (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
1598
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");
1603
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.
1613
1614 // Clear this item's violation(s), now that we've dealt with them
1615 item->ClearViolationFlags();
1616 }
1617 }
1618 }
1619
1620 void
1621 FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
1622 {
1623 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, ("ResolveFlexibleLengths\n"));
1624 if (IsEmpty()) {
1625 return;
1626 }
1627
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;
1633
1634 nscoord spaceAvailableForFlexItemsContentBoxes =
1635 aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
1636
1637 // Determine whether we're going to be growing or shrinking items.
1638 const bool isUsingFlexGrow =
1639 (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
1640
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 }
1660
1661 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1662 (" available free space = %d\n", availableFreeSpace));
1663
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)) {
1668
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");
1689
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.)
1702
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 }
1711
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()) {
1720
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();
1728
1729 MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f &&
1730 myShareOfRemainingSpace <= 1.0f,
1731 "my share should be nonnegative fractional amount");
1732
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 }
1751
1752 availableFreeSpace -= sizeDelta;
1753
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 }
1762
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:"));
1767
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 }
1783
1784 FreezeOrRestoreEachFlexibleSize(totalViolation,
1785 iterationCounter + 1 == mNumItems);
1786
1787 PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
1788 (" Total violation: %d\n", totalViolation));
1789
1790 if (totalViolation == 0) {
1791 break;
1792 }
1793 }
1794
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 }
1803
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 }
1823
1824 if (mPackingSpaceRemaining <= 0) {
1825 // No available packing space to use for resolving auto margins.
1826 mNumAutoMarginsInMainAxis = 0;
1827 }
1828
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 }
1839
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 }
1849
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;
1886
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 }
1899
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 }
1905
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;
1918
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);
1923
1924 mNumAutoMarginsInMainAxis--;
1925 mPackingSpaceRemaining -= curAutoMarginSize;
1926 }
1927 }
1928 }
1929 }
1930
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");
1939
1940 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
1941 "ran out of packing space earlier than we expected");
1942
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;
1947
1948 mPosition += curPackingSpace;
1949 mNumPackingSpacesRemaining--;
1950 mPackingSpaceRemaining -= curPackingSpace;
1951 }
1952 }
1953
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");
1966
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 }
1979
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.)
1984
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 }
1994
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 }
2007
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 }
2017
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;
2051
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)");
2066
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);
2075
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 }
2087
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");
2096
2097 MOZ_ASSERT(mPackingSpaceRemaining >= 0,
2098 "ran out of packing space earlier than we expected");
2099
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;
2104
2105 mPosition += curPackingSpace;
2106 mNumPackingSpacesRemaining--;
2107 mPackingSpaceRemaining -= curPackingSpace;
2108 }
2109 }
2110
2111 SingleLineCrossAxisPositionTracker::
2112 SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
2113 : PositionTracker(aAxisTracker.GetCrossAxis())
2114 {
2115 }
2116
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());
2126
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).
2131
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.
2157
2158 nscoord crossStartToBaseline =
2159 item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(),
2160 eAxisEdge_Start);
2161 nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
2162
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 }
2174
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;
2181
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 }
2191
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 }
2205
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 }
2211
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);
2216
2217 stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
2218
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 }
2224
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);
2234
2235 if (spaceForAutoMargins <= 0) {
2236 return; // No available space --> nothing to do
2237 }
2238
2239 uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis);
2240 if (numAutoMargins == 0) {
2241 return; // No auto margins --> nothing to do.
2242 }
2243
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");
2253
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 }
2263
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 }
2275
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 }
2282
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 }
2292
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);
2313
2314 nscoord lineBaselineOffset = aLine.GetBaselineOffset();
2315
2316 NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
2317 "failed at finding largest baseline offset");
2318
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;
2322
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 }
2340
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;
2349
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.)
2354
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.
2364
2365 // Inline dimension ("start-to-end"):
2366 AxisOrientationType inlineDimension =
2367 cssDirection == NS_STYLE_DIRECTION_RTL ? eAxis_RL : eAxis_LR;
2368
2369 // Block dimension ("before-to-after"):
2370 AxisOrientationType blockDimension = eAxis_TB;
2371
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 }
2389
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 }
2399
2400 // "flex-wrap: wrap-reverse" reverses our cross axis.
2401 if (pos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) {
2402 mCrossAxis = GetReverseAxis(mCrossAxis);
2403 }
2404
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;
2410
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 }
2420
2421 MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis),
2422 "main & cross axes should be in different dimensions");
2423 }
2424
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 }
2439
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");
2451
2452 const bool isSingleLine =
2453 NS_STYLE_FLEX_WRAP_NOWRAP == aReflowState.mStylePosition->mFlexWrap;
2454
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();
2462
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);
2466
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;
2474
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());
2483
2484 wrapThreshold = flexContainerMaxMainSize;
2485 }
2486
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 }
2494
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;
2498
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;
2502
2503 for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
2504 nsIFrame* childFrame = e.get();
2505
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 }
2511
2512 nsAutoPtr<FlexItem> item;
2513 if (nextStrutIdx < aStruts.Length() &&
2514 aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
2515
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);
2522
2523 nsresult rv = ResolveFlexItemMaxContentSizing(aPresContext, *item,
2524 aReflowState, aAxisTracker);
2525 NS_ENSURE_SUCCESS(rv,rv);
2526 }
2527
2528 nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
2529 nscoord itemOuterHypotheticalMainSize =
2530 item->GetOuterMainSize(aAxisTracker.GetMainAxis());
2531
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 }
2541
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);
2547
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 }
2555
2556 return NS_OK;
2557 }
2558
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 }
2572
2573 return GetEffectiveComputedHeight(aReflowState);
2574 }
2575
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 }
2588
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");
2600
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 }
2607
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 }
2618
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);
2628
2629 if (largestLineOuterSize <= aAvailableHeightForContent) {
2630 return aAvailableHeightForContent;
2631 }
2632 return std::min(aUnclampedMainSize, largestLineOuterSize);
2633 }
2634
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 }
2644
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");
2654
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 }
2661
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 }
2674
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 }
2688
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 }
2697
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());
2710
2711 // Resolve any main-axis 'auto' margins on aChild to an actual value.
2712 mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item);
2713
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);
2718
2719 item->SetMainPosition(mainAxisPosnTracker.GetPosition());
2720
2721 mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
2722 mainAxisPosnTracker.ExitMargin(item->GetMargin());
2723 mainAxisPosnTracker.TraversePackingSpace();
2724 }
2725 }
2726
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 }
2743
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 }
2760
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 }
2782
2783 MOZ_ASSERT(!aItem.HadMeasuringReflow(),
2784 "We shouldn't need more than one measuring reflow");
2785
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);
2802
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");
2809
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);
2815
2816 // Save the sizing info that we learned from this reflow
2817 // -----------------------------------------------------
2818
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 }
2840
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 }
2846
2847 return NS_OK;
2848 }
2849
2850 void
2851 FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
2852 const FlexboxAxisTracker& aAxisTracker)
2853 {
2854 SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
2855
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);
2861
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);
2869
2870 item->SetCrossPosition(aLineStartPosition +
2871 lineCrossAxisPosnTracker.GetPosition());
2872
2873 // Back out to cross-axis edge of the line.
2874 lineCrossAxisPosnTracker.ResetPosition();
2875 }
2876 }
2877
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));
2888
2889 if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) {
2890 return NS_OK;
2891 }
2892
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 }
2906
2907 #ifdef DEBUG
2908 SanityCheckAnonymousFlexItems();
2909 #endif // DEBUG
2910
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.
2920
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 }
2928
2929 const FlexboxAxisTracker axisTracker(this);
2930
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 }
2942
2943 nscoord contentBoxMainSize = GetMainSizeFromReflowState(aReflowState,
2944 axisTracker);
2945
2946 nsAutoTArray<StrutInfo, 1> struts;
2947 nsresult rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus,
2948 contentBoxMainSize, availableHeightForContent,
2949 struts, axisTracker);
2950
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 }
2957
2958 return rv;
2959 }
2960
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 }
2973
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 }
2983
2984 private:
2985 LinkedList<FlexLine>& mLines;
2986 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
2987 };
2988
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;
3000
3001 LinkedList<FlexLine> lines;
3002 AutoFlexLineListClearer cleanupLines(lines);
3003
3004 nsresult rv = GenerateFlexLines(aPresContext, aReflowState,
3005 aContentBoxMainSize,
3006 aAvailableHeightForContent,
3007 aStruts, aAxisTracker, lines);
3008 NS_ENSURE_SUCCESS(rv, rv);
3009
3010 aContentBoxMainSize =
3011 ClampFlexContainerMainSize(aReflowState, aAxisTracker,
3012 aContentBoxMainSize, aAvailableHeightForContent,
3013 lines.getFirst(), aStatus);
3014
3015 for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3016 line->ResolveFlexibleLengths(aContentBoxMainSize);
3017 }
3018
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 }
3038
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 }
3048
3049 bool isCrossSizeDefinite;
3050 const nscoord contentBoxCrossSize =
3051 ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes,
3052 aAvailableHeightForContent, &isCrossSizeDefinite, aStatus);
3053
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);
3061
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 }
3073
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 }
3092
3093 for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
3094
3095 // Main-Axis Alignment - Flexbox spec section 9.5
3096 // ==============================================
3097 line->PositionItemsInMainAxis(aReflowState.mStylePosition->mJustifyContent,
3098 aContentBoxMainSize,
3099 aAxisTracker);
3100
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 }
3108
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 }
3126
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);
3134
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;
3148
3149 nsHTMLReflowState childReflowState(aPresContext, aReflowState,
3150 item->Frame(),
3151 nsSize(aReflowState.ComputedWidth(),
3152 NS_UNCONSTRAINEDSIZE));
3153
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;
3158
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 }
3167
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 }
3182
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...?
3186
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.
3205
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);
3213
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");
3221
3222 childReflowState.ApplyRelativePositioning(&physicalPosn);
3223
3224 rv = FinishReflowChild(item->Frame(), aPresContext,
3225 childDesiredSize, &childReflowState,
3226 physicalPosn.x, physicalPosn.y, 0);
3227 NS_ENSURE_SUCCESS(rv, rv);
3228
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);
3235
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 }
3244
3245 nsSize desiredContentBoxSize =
3246 aAxisTracker.PhysicalSizeFromLogicalSizes(aContentBoxMainSize,
3247 contentBoxCrossSize);
3248
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;
3254
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);
3267
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;
3282
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 }
3294
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 }
3300
3301 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
3302 aReflowState, aStatus);
3303
3304 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize)
3305 return NS_OK;
3306 }
3307
3308 /* virtual */ nscoord
3309 nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
3310 {
3311 nscoord minWidth = 0;
3312 DISPLAY_MIN_WIDTH(this, minWidth);
3313
3314 FlexboxAxisTracker axisTracker(this);
3315
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 }
3333
3334 /* virtual */ nscoord
3335 nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
3336 {
3337 nscoord prefWidth = 0;
3338 DISPLAY_PREF_WIDTH(this, prefWidth);
3339
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);
3346
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 }

mercurial