Line data Source code
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 "mozilla/CSSOrderAwareFrameIterator.h"
20 : #include "mozilla/Logging.h"
21 : #include <algorithm>
22 : #include "gfxContext.h"
23 : #include "mozilla/LinkedList.h"
24 : #include "mozilla/FloatingPoint.h"
25 : #include "mozilla/UniquePtr.h"
26 : #include "WritingModes.h"
27 :
28 : using namespace mozilla;
29 : using namespace mozilla::layout;
30 :
31 : // Convenience typedefs for helper classes that we forward-declare in .h file
32 : // (so that nsFlexContainerFrame methods can use them as parameters):
33 : typedef nsFlexContainerFrame::FlexItem FlexItem;
34 : typedef nsFlexContainerFrame::FlexLine FlexLine;
35 : typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
36 : typedef nsFlexContainerFrame::StrutInfo StrutInfo;
37 : typedef nsFlexContainerFrame::CachedMeasuringReflowResult
38 : CachedMeasuringReflowResult;
39 :
40 : static mozilla::LazyLogModule gFlexContainerLog("nsFlexContainerFrame");
41 :
42 : // XXXdholbert Some of this helper-stuff should be separated out into a general
43 : // "main/cross-axis utils" header, shared by grid & flexbox?
44 : // (Particularly when grid gets support for align-*/justify-* properties.)
45 :
46 : // Helper enums
47 : // ============
48 :
49 : // Represents a physical orientation for an axis.
50 : // The directional suffix indicates the direction in which the axis *grows*.
51 : // So e.g. eAxis_LR means a horizontal left-to-right axis, whereas eAxis_BT
52 : // means a vertical bottom-to-top axis.
53 : // NOTE: The order here is important -- these values are used as indices into
54 : // the static array 'kAxisOrientationToSidesMap', defined below.
55 : enum AxisOrientationType {
56 : eAxis_LR,
57 : eAxis_RL,
58 : eAxis_TB,
59 : eAxis_BT,
60 : eNumAxisOrientationTypes // For sizing arrays that use these values as indices
61 : };
62 :
63 : // Represents one or the other extreme of an axis (e.g. for the main axis, the
64 : // main-start vs. main-end edge.
65 : // NOTE: The order here is important -- these values are used as indices into
66 : // the sub-arrays in 'kAxisOrientationToSidesMap', defined below.
67 : enum AxisEdgeType {
68 : eAxisEdge_Start,
69 : eAxisEdge_End,
70 : eNumAxisEdges // For sizing arrays that use these values as indices
71 : };
72 :
73 : // This array maps each axis orientation to a pair of corresponding
74 : // [start, end] physical mozilla::Side values.
75 : static const mozilla::Side
76 : kAxisOrientationToSidesMap[eNumAxisOrientationTypes][eNumAxisEdges] = {
77 : { eSideLeft, eSideRight }, // eAxis_LR
78 : { eSideRight, eSideLeft }, // eAxis_RL
79 : { eSideTop, eSideBottom }, // eAxis_TB
80 : { eSideBottom, eSideTop } // eAxis_BT
81 : };
82 :
83 : // Helper structs / classes / methods
84 : // ==================================
85 : // Returns true iff the given nsStyleDisplay has display:-webkit-{inline-}-box.
86 : static inline bool
87 0 : IsDisplayValueLegacyBox(const nsStyleDisplay* aStyleDisp)
88 : {
89 0 : return aStyleDisp->mDisplay == mozilla::StyleDisplay::WebkitBox ||
90 0 : aStyleDisp->mDisplay == mozilla::StyleDisplay::WebkitInlineBox;
91 : }
92 :
93 : // Returns true if aFlexContainer is the frame for an element with
94 : // "display:-webkit-box" or "display:-webkit-inline-box". aFlexContainer is
95 : // expected to be an instance of nsFlexContainerFrame (enforced with an assert);
96 : // otherwise, this function's state-bit-check here is bogus.
97 : static bool
98 0 : IsLegacyBox(const nsIFrame* aFlexContainer)
99 : {
100 0 : MOZ_ASSERT(aFlexContainer->IsFlexContainerFrame(),
101 : "only flex containers may be passed to this function");
102 0 : return aFlexContainer->HasAnyStateBits(NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX);
103 : }
104 :
105 : // Returns the OrderingProperty enum that we should pass to
106 : // CSSOrderAwareFrameIterator (depending on whether it's a legacy box).
107 : static CSSOrderAwareFrameIterator::OrderingProperty
108 0 : OrderingPropertyForIter(const nsFlexContainerFrame* aFlexContainer)
109 : {
110 0 : return IsLegacyBox(aFlexContainer)
111 0 : ? CSSOrderAwareFrameIterator::OrderingProperty::eUseBoxOrdinalGroup
112 0 : : CSSOrderAwareFrameIterator::OrderingProperty::eUseOrder;
113 : }
114 :
115 : // Returns the "align-items" value that's equivalent to the legacy "box-align"
116 : // value in the given style struct.
117 : static uint8_t
118 0 : ConvertLegacyStyleToAlignItems(const nsStyleXUL* aStyleXUL)
119 : {
120 : // -[moz|webkit]-box-align corresponds to modern "align-items"
121 0 : switch (aStyleXUL->mBoxAlign) {
122 : case StyleBoxAlign::Stretch:
123 0 : return NS_STYLE_ALIGN_STRETCH;
124 : case StyleBoxAlign::Start:
125 0 : return NS_STYLE_ALIGN_FLEX_START;
126 : case StyleBoxAlign::Center:
127 0 : return NS_STYLE_ALIGN_CENTER;
128 : case StyleBoxAlign::Baseline:
129 0 : return NS_STYLE_ALIGN_BASELINE;
130 : case StyleBoxAlign::End:
131 0 : return NS_STYLE_ALIGN_FLEX_END;
132 : }
133 :
134 0 : MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxAlign enum value");
135 : // Fall back to default value of "align-items" property:
136 : return NS_STYLE_ALIGN_STRETCH;
137 : }
138 :
139 : // Returns the "justify-content" value that's equivalent to the legacy
140 : // "box-pack" value in the given style struct.
141 : static uint8_t
142 0 : ConvertLegacyStyleToJustifyContent(const nsStyleXUL* aStyleXUL)
143 : {
144 : // -[moz|webkit]-box-pack corresponds to modern "justify-content"
145 0 : switch (aStyleXUL->mBoxPack) {
146 : case StyleBoxPack::Start:
147 0 : return NS_STYLE_ALIGN_FLEX_START;
148 : case StyleBoxPack::Center:
149 0 : return NS_STYLE_ALIGN_CENTER;
150 : case StyleBoxPack::End:
151 0 : return NS_STYLE_ALIGN_FLEX_END;
152 : case StyleBoxPack::Justify:
153 0 : return NS_STYLE_ALIGN_SPACE_BETWEEN;
154 : }
155 :
156 0 : MOZ_ASSERT_UNREACHABLE("Unrecognized mBoxPack enum value");
157 : // Fall back to default value of "justify-content" property:
158 : return NS_STYLE_ALIGN_FLEX_START;
159 : }
160 :
161 : // Indicates whether advancing along the given axis is equivalent to
162 : // increasing our X or Y position (as opposed to decreasing it).
163 : static inline bool
164 0 : AxisGrowsInPositiveDirection(AxisOrientationType aAxis)
165 : {
166 0 : return eAxis_LR == aAxis || eAxis_TB == aAxis;
167 : }
168 :
169 : // Given an AxisOrientationType, returns the "reverse" AxisOrientationType
170 : // (in the same dimension, but the opposite direction)
171 : static inline AxisOrientationType
172 0 : GetReverseAxis(AxisOrientationType aAxis)
173 : {
174 : AxisOrientationType reversedAxis;
175 :
176 0 : if (aAxis % 2 == 0) {
177 : // even enum value. Add 1 to reverse.
178 0 : reversedAxis = AxisOrientationType(aAxis + 1);
179 : } else {
180 : // odd enum value. Subtract 1 to reverse.
181 0 : reversedAxis = AxisOrientationType(aAxis - 1);
182 : }
183 :
184 : // Check that we're still in the enum's valid range
185 0 : MOZ_ASSERT(reversedAxis >= eAxis_LR &&
186 : reversedAxis <= eAxis_BT);
187 :
188 0 : return reversedAxis;
189 : }
190 :
191 : /**
192 : * Converts a "flex-relative" coordinate in a single axis (a main- or cross-axis
193 : * coordinate) into a coordinate in the corresponding physical (x or y) axis. If
194 : * the flex-relative axis in question already maps *directly* to a physical
195 : * axis (i.e. if it's LTR or TTB), then the physical coordinate has the same
196 : * numeric value as the provided flex-relative coordinate. Otherwise, we have to
197 : * subtract the flex-relative coordinate from the flex container's size in that
198 : * axis, to flip the polarity. (So e.g. a main-axis position of 2px in a RTL
199 : * 20px-wide container would correspond to a physical coordinate (x-value) of
200 : * 18px.)
201 : */
202 : static nscoord
203 0 : PhysicalCoordFromFlexRelativeCoord(nscoord aFlexRelativeCoord,
204 : nscoord aContainerSize,
205 : AxisOrientationType aAxis) {
206 0 : if (AxisGrowsInPositiveDirection(aAxis)) {
207 0 : return aFlexRelativeCoord;
208 : }
209 0 : return aContainerSize - aFlexRelativeCoord;
210 : }
211 :
212 : // Helper-macro to let us pick one of two expressions to evaluate
213 : // (a width expression vs. a height expression), to get a main-axis or
214 : // cross-axis component.
215 : // For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent
216 : // and GetCrossComponent are cleaner; but in cases where we simply have
217 : // two separate expressions for width and height (which may be expensive to
218 : // evaluate), these macros will ensure that only the expression for the correct
219 : // axis gets evaluated.
220 : #define GET_MAIN_COMPONENT(axisTracker_, width_, height_) \
221 : (axisTracker_).IsMainAxisHorizontal() ? (width_) : (height_)
222 :
223 : #define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \
224 : (axisTracker_).IsCrossAxisHorizontal() ? (width_) : (height_)
225 :
226 : // Logical versions of helper-macros above:
227 : #define GET_MAIN_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_) \
228 : wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
229 : (axisTracker_).IsRowOriented() ? (isize_) : (bsize_)
230 :
231 : #define GET_CROSS_COMPONENT_LOGICAL(axisTracker_, wm_, isize_, bsize_) \
232 : wm_.IsOrthogonalTo(axisTracker_.GetWritingMode()) != \
233 : (axisTracker_).IsRowOriented() ? (bsize_) : (isize_)
234 :
235 : // Flags to customize behavior of the FlexboxAxisTracker constructor:
236 : enum AxisTrackerFlags {
237 : eNoFlags = 0x0,
238 :
239 : // Normally, FlexboxAxisTracker may attempt to reverse axes & iteration order
240 : // to avoid bottom-to-top child ordering, for saner pagination. This flag
241 : // suppresses that behavior (so that we allow bottom-to-top child ordering).
242 : // (This may be helpful e.g. when we're only dealing with a single child.)
243 : eAllowBottomToTopChildOrdering = 0x1
244 : };
245 0 : MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(AxisTrackerFlags)
246 :
247 : // Encapsulates our flex container's main & cross axes.
248 : class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
249 : public:
250 : FlexboxAxisTracker(const nsFlexContainerFrame* aFlexContainer,
251 : const WritingMode& aWM,
252 : AxisTrackerFlags aFlags = eNoFlags);
253 :
254 : // Accessors:
255 : // XXXdholbert [BEGIN DEPRECATED]
256 0 : AxisOrientationType GetMainAxis() const { return mMainAxis; }
257 0 : AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
258 :
259 0 : bool IsMainAxisHorizontal() const {
260 : // If we're row-oriented, and our writing mode is NOT vertical,
261 : // or we're column-oriented and our writing mode IS vertical,
262 : // then our main axis is horizontal. This handles all cases:
263 0 : return mIsRowOriented != mWM.IsVertical();
264 : }
265 0 : bool IsCrossAxisHorizontal() const {
266 0 : return !IsMainAxisHorizontal();
267 : }
268 : // XXXdholbert [END DEPRECATED]
269 :
270 : // Returns the flex container's writing mode.
271 0 : WritingMode GetWritingMode() const { return mWM; }
272 :
273 : // Returns true if our main axis is in the reverse direction of our
274 : // writing mode's corresponding axis. (From 'flex-direction: *-reverse')
275 0 : bool IsMainAxisReversed() const {
276 0 : return mIsMainAxisReversed;
277 : }
278 : // Returns true if our cross axis is in the reverse direction of our
279 : // writing mode's corresponding axis. (From 'flex-wrap: *-reverse')
280 0 : bool IsCrossAxisReversed() const {
281 0 : return mIsCrossAxisReversed;
282 : }
283 :
284 0 : bool IsRowOriented() const { return mIsRowOriented; }
285 0 : bool IsColumnOriented() const { return !mIsRowOriented; }
286 :
287 : nscoord GetMainComponent(const nsSize& aSize) const {
288 : return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height);
289 : }
290 0 : int32_t GetMainComponent(const LayoutDeviceIntSize& aIntSize) const {
291 0 : return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height);
292 : }
293 :
294 0 : nscoord GetCrossComponent(const nsSize& aSize) const {
295 0 : return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height);
296 : }
297 0 : int32_t GetCrossComponent(const LayoutDeviceIntSize& aIntSize) const {
298 0 : return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height);
299 : }
300 :
301 0 : nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const {
302 0 : return IsMainAxisHorizontal() ?
303 0 : aMargin.LeftRight() :
304 0 : aMargin.TopBottom();
305 : }
306 0 : nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const {
307 0 : return IsCrossAxisHorizontal() ?
308 0 : aMargin.LeftRight() :
309 0 : aMargin.TopBottom();
310 : }
311 :
312 : // Returns aFrame's computed value for 'height' or 'width' -- whichever is in
313 : // the cross-axis. (NOTE: This is cross-axis-specific for now. If we need a
314 : // main-axis version as well, we could generalize or clone this function.)
315 0 : const nsStyleCoord& ComputedCrossSize(const nsIFrame* aFrame) const {
316 0 : const nsStylePosition* stylePos = aFrame->StylePosition();
317 :
318 0 : return IsCrossAxisHorizontal() ?
319 : stylePos->mWidth :
320 0 : stylePos->mHeight;
321 : }
322 :
323 : /**
324 : * Converts a "flex-relative" point (a main-axis & cross-axis coordinate)
325 : * into a LogicalPoint, using the flex container's writing mode.
326 : *
327 : * @arg aMainCoord The main-axis coordinate -- i.e an offset from the
328 : * main-start edge of the flex container's content box.
329 : * @arg aCrossCoord The cross-axis coordinate -- i.e an offset from the
330 : * cross-start edge of the flex container's content box.
331 : * @arg aContainerMainSize The main size of flex container's content box.
332 : * @arg aContainerCrossSize The cross size of flex container's content box.
333 : * @return A LogicalPoint, with the flex container's writing mode, that
334 : * represents the same position. The logical coordinates are
335 : * relative to the flex container's content box.
336 : */
337 : LogicalPoint
338 0 : LogicalPointFromFlexRelativePoint(nscoord aMainCoord,
339 : nscoord aCrossCoord,
340 : nscoord aContainerMainSize,
341 : nscoord aContainerCrossSize) const {
342 0 : nscoord logicalCoordInMainAxis = mIsMainAxisReversed ?
343 0 : aContainerMainSize - aMainCoord : aMainCoord;
344 0 : nscoord logicalCoordInCrossAxis = mIsCrossAxisReversed ?
345 0 : aContainerCrossSize - aCrossCoord : aCrossCoord;
346 :
347 0 : return mIsRowOriented ?
348 : LogicalPoint(mWM, logicalCoordInMainAxis, logicalCoordInCrossAxis) :
349 0 : LogicalPoint(mWM, logicalCoordInCrossAxis, logicalCoordInMainAxis);
350 : }
351 :
352 : /**
353 : * Converts a "flex-relative" size (a main-axis & cross-axis size)
354 : * into a LogicalSize, using the flex container's writing mode.
355 : *
356 : * @arg aMainSize The main-axis size.
357 : * @arg aCrossSize The cross-axis size.
358 : * @return A LogicalSize, with the flex container's writing mode, that
359 : * represents the same size.
360 : */
361 0 : LogicalSize LogicalSizeFromFlexRelativeSizes(nscoord aMainSize,
362 : nscoord aCrossSize) const {
363 0 : return mIsRowOriented ?
364 : LogicalSize(mWM, aMainSize, aCrossSize) :
365 0 : LogicalSize(mWM, aCrossSize, aMainSize);
366 : }
367 :
368 : // Are my axes reversed with respect to what the author asked for?
369 : // (We may reverse the axes in the FlexboxAxisTracker constructor and set
370 : // this flag, to avoid reflowing our children in bottom-to-top order.)
371 0 : bool AreAxesInternallyReversed() const
372 : {
373 0 : return mAreAxesInternallyReversed;
374 : }
375 :
376 : private:
377 : // Delete copy-constructor & reassignment operator, to prevent accidental
378 : // (unnecessary) copying.
379 : FlexboxAxisTracker(const FlexboxAxisTracker&) = delete;
380 : FlexboxAxisTracker& operator=(const FlexboxAxisTracker&) = delete;
381 :
382 : // Helpers for constructor which determine the orientation of our axes, based
383 : // on legacy box properties (-webkit-box-orient, -webkit-box-direction) or
384 : // modern flexbox properties (flex-direction, flex-wrap) depending on whether
385 : // the flex container is a "legacy box" (as determined by IsLegacyBox).
386 : void InitAxesFromLegacyProps(const nsFlexContainerFrame* aFlexContainer);
387 : void InitAxesFromModernProps(const nsFlexContainerFrame* aFlexContainer);
388 :
389 : // XXXdholbert [BEGIN DEPRECATED]
390 : AxisOrientationType mMainAxis;
391 : AxisOrientationType mCrossAxis;
392 : // XXXdholbert [END DEPRECATED]
393 :
394 : const WritingMode mWM; // The flex container's writing mode.
395 :
396 : bool mIsRowOriented; // Is our main axis the inline axis?
397 : // (Are we 'flex-direction:row[-reverse]'?)
398 :
399 : bool mIsMainAxisReversed; // Is our main axis in the opposite direction
400 : // as mWM's corresponding axis? (e.g. RTL vs LTR)
401 : bool mIsCrossAxisReversed; // Is our cross axis in the opposite direction
402 : // as mWM's corresponding axis? (e.g. BTT vs TTB)
403 :
404 : // Implementation detail -- this indicates whether we've decided to
405 : // transparently reverse our axes & our child ordering, to avoid having
406 : // frames flow from bottom to top in either axis (& to make pagination saner).
407 : bool mAreAxesInternallyReversed;
408 : };
409 :
410 : /**
411 : * Represents a flex item.
412 : * Includes the various pieces of input that the Flexbox Layout Algorithm uses
413 : * to resolve a flexible width.
414 : */
415 0 : class nsFlexContainerFrame::FlexItem : public LinkedListElement<FlexItem>
416 : {
417 : public:
418 : // Normal constructor:
419 : FlexItem(ReflowInput& aFlexItemReflowInput,
420 : float aFlexGrow, float aFlexShrink, nscoord aMainBaseSize,
421 : nscoord aMainMinSize, nscoord aMainMaxSize,
422 : nscoord aTentativeCrossSize,
423 : nscoord aCrossMinSize, nscoord aCrossMaxSize,
424 : const FlexboxAxisTracker& aAxisTracker);
425 :
426 : // Simplified constructor, to be used only for generating "struts":
427 : // (NOTE: This "strut" constructor uses the *container's* writing mode, which
428 : // we'll use on this FlexItem instead of the child frame's real writing mode.
429 : // This is fine - it doesn't matter what writing mode we use for a
430 : // strut, since it won't render any content and we already know its size.)
431 : FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize, WritingMode aContainerWM);
432 :
433 : // Accessors
434 0 : nsIFrame* Frame() const { return mFrame; }
435 0 : nscoord GetFlexBaseSize() const { return mFlexBaseSize; }
436 :
437 0 : nscoord GetMainMinSize() const {
438 0 : MOZ_ASSERT(!mNeedsMinSizeAutoResolution,
439 : "Someone's using an unresolved 'auto' main min-size");
440 0 : return mMainMinSize;
441 : }
442 0 : nscoord GetMainMaxSize() const { return mMainMaxSize; }
443 :
444 : // Note: These return the main-axis position and size of our *content box*.
445 0 : nscoord GetMainSize() const { return mMainSize; }
446 0 : nscoord GetMainPosition() const { return mMainPosn; }
447 :
448 : nscoord GetCrossMinSize() const { return mCrossMinSize; }
449 : nscoord GetCrossMaxSize() const { return mCrossMaxSize; }
450 :
451 : // Note: These return the cross-axis position and size of our *content box*.
452 0 : nscoord GetCrossSize() const { return mCrossSize; }
453 0 : nscoord GetCrossPosition() const { return mCrossPosn; }
454 :
455 0 : nscoord ResolvedAscent(bool aUseFirstBaseline) const {
456 0 : if (mAscent == ReflowOutput::ASK_FOR_BASELINE) {
457 : // XXXdholbert We should probably be using the *container's* writing-mode
458 : // here, instead of the item's -- though it doesn't much matter right
459 : // now, because all of the baseline-handling code here essentially
460 : // assumes that the container & items have the same writing-mode. This
461 : // will matter more (& can be expanded/tested) once we officially support
462 : // logical directions & vertical writing-modes in flexbox, in bug 1079155
463 : // or a dependency.
464 : // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
465 : // or just GetLogicalBaseline() if that fails.
466 0 : bool found = aUseFirstBaseline ?
467 0 : nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
468 0 : nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
469 :
470 0 : if (!found) {
471 0 : mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
472 : BaselineSharingGroup::eFirst);
473 : }
474 : }
475 0 : return mAscent;
476 : }
477 :
478 : // Convenience methods to compute the main & cross size of our *margin-box*.
479 : // The caller is responsible for telling us the right axis, so that we can
480 : // pull out the appropriate components of our margin/border/padding structs.
481 0 : nscoord GetOuterMainSize(AxisOrientationType aMainAxis) const
482 : {
483 0 : return mMainSize + GetMarginBorderPaddingSizeInAxis(aMainAxis);
484 : }
485 :
486 0 : nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const
487 : {
488 0 : return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis);
489 : }
490 :
491 : // Returns the distance between this FlexItem's baseline and the cross-start
492 : // edge of its margin-box. Used in baseline alignment.
493 : // (This function needs to be told which edge we're measuring the baseline
494 : // from, so that it can look up the appropriate components from mMargin.)
495 : nscoord GetBaselineOffsetFromOuterCrossEdge(
496 : AxisEdgeType aEdge,
497 : const FlexboxAxisTracker& aAxisTracker,
498 : bool aUseFirstLineBaseline) const;
499 :
500 0 : float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
501 :
502 0 : bool IsFrozen() const { return mIsFrozen; }
503 :
504 0 : bool HadMinViolation() const { return mHadMinViolation; }
505 0 : bool HadMaxViolation() const { return mHadMaxViolation; }
506 :
507 : // Indicates whether this item received a preliminary "measuring" reflow
508 : // before its actual reflow.
509 0 : bool HadMeasuringReflow() const { return mHadMeasuringReflow; }
510 :
511 : // Indicates whether this item's cross-size has been stretched (from having
512 : // "align-self: stretch" with an auto cross-size and no auto margins in the
513 : // cross axis).
514 0 : bool IsStretched() const { return mIsStretched; }
515 :
516 : // Indicates whether we need to resolve an 'auto' value for the main-axis
517 : // min-[width|height] property.
518 0 : bool NeedsMinSizeAutoResolution() const
519 0 : { return mNeedsMinSizeAutoResolution; }
520 :
521 : // Indicates whether this item is a "strut" left behind by an element with
522 : // visibility:collapse.
523 : bool IsStrut() const { return mIsStrut; }
524 :
525 0 : WritingMode GetWritingMode() const { return mWM; }
526 0 : uint8_t GetAlignSelf() const { return mAlignSelf; }
527 :
528 : // Returns the flex factor (flex-grow or flex-shrink), depending on
529 : // 'aIsUsingFlexGrow'.
530 : //
531 : // Asserts fatally if called on a frozen item (since frozen items are not
532 : // flexible).
533 0 : float GetFlexFactor(bool aIsUsingFlexGrow)
534 : {
535 0 : MOZ_ASSERT(!IsFrozen(), "shouldn't need flex factor after item is frozen");
536 :
537 0 : return aIsUsingFlexGrow ? mFlexGrow : mFlexShrink;
538 : }
539 :
540 : // Returns the weight that we should use in the "resolving flexible lengths"
541 : // algorithm. If we're using the flex grow factor, we just return that;
542 : // otherwise, we return the "scaled flex shrink factor" (scaled by our flex
543 : // base size, so that when both large and small items are shrinking, the large
544 : // items shrink more).
545 : //
546 : // I'm calling this a "weight" instead of a "[scaled] flex-[grow|shrink]
547 : // factor", to more clearly distinguish it from the actual flex-grow &
548 : // flex-shrink factors.
549 : //
550 : // Asserts fatally if called on a frozen item (since frozen items are not
551 : // flexible).
552 0 : float GetWeight(bool aIsUsingFlexGrow)
553 : {
554 0 : MOZ_ASSERT(!IsFrozen(), "shouldn't need weight after item is frozen");
555 :
556 0 : if (aIsUsingFlexGrow) {
557 0 : return mFlexGrow;
558 : }
559 :
560 : // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
561 0 : if (mFlexBaseSize == 0) {
562 : // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
563 : // regardless of mFlexShrink, we should just return 0.
564 : // (This is really a special-case for when mFlexShrink is infinity, to
565 : // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
566 0 : return 0.0f;
567 : }
568 0 : return mFlexShrink * mFlexBaseSize;
569 : }
570 :
571 0 : const nsSize& IntrinsicRatio() const { return mIntrinsicRatio; }
572 0 : bool HasIntrinsicRatio() const { return mIntrinsicRatio != nsSize(); }
573 :
574 : // Getters for margin:
575 : // ===================
576 0 : const nsMargin& GetMargin() const { return mMargin; }
577 :
578 : // Returns the margin component for a given mozilla::Side
579 0 : nscoord GetMarginComponentForSide(mozilla::Side aSide) const
580 0 : { return mMargin.Side(aSide); }
581 :
582 : // Returns the total space occupied by this item's margins in the given axis
583 0 : nscoord GetMarginSizeInAxis(AxisOrientationType aAxis) const
584 : {
585 0 : mozilla::Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
586 0 : mozilla::Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
587 0 : return GetMarginComponentForSide(startSide) +
588 0 : GetMarginComponentForSide(endSide);
589 : }
590 :
591 : // Getters for border/padding
592 : // ==========================
593 0 : const nsMargin& GetBorderPadding() const { return mBorderPadding; }
594 :
595 : // Returns the border+padding component for a given mozilla::Side
596 0 : nscoord GetBorderPaddingComponentForSide(mozilla::Side aSide) const
597 0 : { return mBorderPadding.Side(aSide); }
598 :
599 : // Returns the total space occupied by this item's borders and padding in
600 : // the given axis
601 0 : nscoord GetBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
602 : {
603 0 : mozilla::Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
604 0 : mozilla::Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
605 0 : return GetBorderPaddingComponentForSide(startSide) +
606 0 : GetBorderPaddingComponentForSide(endSide);
607 : }
608 :
609 : // Getter for combined margin/border/padding
610 : // =========================================
611 : // Returns the total space occupied by this item's margins, borders and
612 : // padding in the given axis
613 0 : nscoord GetMarginBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
614 : {
615 0 : return GetMarginSizeInAxis(aAxis) + GetBorderPaddingSizeInAxis(aAxis);
616 : }
617 :
618 : // Setters
619 : // =======
620 : // Helper to set the resolved value of min-[width|height]:auto for the main
621 : // axis. (Should only be used if NeedsMinSizeAutoResolution() returns true.)
622 0 : void UpdateMainMinSize(nscoord aNewMinSize)
623 : {
624 0 : NS_ASSERTION(aNewMinSize >= 0,
625 : "How did we end up with a negative min-size?");
626 0 : MOZ_ASSERT(mMainMaxSize >= aNewMinSize,
627 : "Should only use this function for resolving min-size:auto, "
628 : "and main max-size should be an upper-bound for resolved val");
629 0 : MOZ_ASSERT(mNeedsMinSizeAutoResolution &&
630 : (mMainMinSize == 0 || mFrame->IsThemed(mFrame->StyleDisplay())),
631 : "Should only use this function for resolving min-size:auto, "
632 : "so we shouldn't already have a nonzero min-size established "
633 : "(unless it's a themed-widget-imposed minimum size)");
634 :
635 0 : if (aNewMinSize > mMainMinSize) {
636 0 : mMainMinSize = aNewMinSize;
637 : // Also clamp main-size to be >= new min-size:
638 0 : mMainSize = std::max(mMainSize, aNewMinSize);
639 : }
640 0 : mNeedsMinSizeAutoResolution = false;
641 0 : }
642 :
643 : // This sets our flex base size, and then sets our main size to the
644 : // resulting "hypothetical main size" (the base size clamped to our
645 : // main-axis [min,max] sizing constraints).
646 0 : void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize)
647 : {
648 0 : MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_INTRINSICSIZE,
649 : "flex base size shouldn't change after we're frozen "
650 : "(unless we're just resolving an intrinsic size)");
651 0 : mFlexBaseSize = aNewFlexBaseSize;
652 :
653 : // Before we've resolved flexible lengths, we keep mMainSize set to
654 : // the 'hypothetical main size', which is the flex base size, clamped
655 : // to the [min,max] range:
656 0 : mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize);
657 0 : }
658 :
659 : // Setters used while we're resolving flexible lengths
660 : // ---------------------------------------------------
661 :
662 : // Sets the main-size of our flex item's content-box.
663 0 : void SetMainSize(nscoord aNewMainSize)
664 : {
665 0 : MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
666 0 : mMainSize = aNewMainSize;
667 0 : }
668 :
669 0 : void SetShareOfWeightSoFar(float aNewShare)
670 : {
671 0 : MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f,
672 : "shouldn't be giving this item any share of the weight "
673 : "after it's frozen");
674 0 : mShareOfWeightSoFar = aNewShare;
675 0 : }
676 :
677 0 : void Freeze() { mIsFrozen = true; }
678 :
679 0 : void SetHadMinViolation()
680 : {
681 0 : MOZ_ASSERT(!mIsFrozen,
682 : "shouldn't be changing main size & having violations "
683 : "after we're frozen");
684 0 : mHadMinViolation = true;
685 0 : }
686 0 : void SetHadMaxViolation()
687 : {
688 0 : MOZ_ASSERT(!mIsFrozen,
689 : "shouldn't be changing main size & having violations "
690 : "after we're frozen");
691 0 : mHadMaxViolation = true;
692 0 : }
693 0 : void ClearViolationFlags()
694 0 : { mHadMinViolation = mHadMaxViolation = false; }
695 :
696 : // Setters for values that are determined after we've resolved our main size
697 : // -------------------------------------------------------------------------
698 :
699 : // Sets the main-axis position of our flex item's content-box.
700 : // (This is the distance between the main-start edge of the flex container
701 : // and the main-start edge of the flex item's content-box.)
702 0 : void SetMainPosition(nscoord aPosn) {
703 0 : MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
704 0 : mMainPosn = aPosn;
705 0 : }
706 :
707 : // Sets the cross-size of our flex item's content-box.
708 0 : void SetCrossSize(nscoord aCrossSize) {
709 0 : MOZ_ASSERT(!mIsStretched,
710 : "Cross size shouldn't be modified after it's been stretched");
711 0 : mCrossSize = aCrossSize;
712 0 : }
713 :
714 : // Sets the cross-axis position of our flex item's content-box.
715 : // (This is the distance between the cross-start edge of the flex container
716 : // and the cross-start edge of the flex item.)
717 0 : void SetCrossPosition(nscoord aPosn) {
718 0 : MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
719 0 : mCrossPosn = aPosn;
720 0 : }
721 :
722 : // After a FlexItem has had a reflow, this method can be used to cache its
723 : // (possibly-unresolved) ascent, in case it's needed later for
724 : // baseline-alignment or to establish the container's baseline.
725 : // (NOTE: This can be marked 'const' even though it's modifying mAscent,
726 : // because mAscent is mutable. It's nice for this to be 'const', because it
727 : // means our final reflow can iterate over const FlexItem pointers, and we
728 : // can be sure it's not modifying those FlexItems, except via this method.)
729 0 : void SetAscent(nscoord aAscent) const {
730 0 : mAscent = aAscent; // NOTE: this may be ASK_FOR_BASELINE
731 0 : }
732 :
733 0 : void SetHadMeasuringReflow() {
734 0 : mHadMeasuringReflow = true;
735 0 : }
736 :
737 : void SetIsStretched() {
738 : MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
739 : mIsStretched = true;
740 : }
741 :
742 : // Setter for margin components (for resolving "auto" margins)
743 0 : void SetMarginComponentForSide(mozilla::Side aSide, nscoord aLength)
744 : {
745 0 : MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
746 0 : mMargin.Side(aSide) = aLength;
747 0 : }
748 :
749 : void ResolveStretchedCrossSize(nscoord aLineCrossSize,
750 : const FlexboxAxisTracker& aAxisTracker);
751 :
752 : uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
753 :
754 : // Once the main size has been resolved, should we bother doing layout to
755 : // establish the cross size?
756 : bool CanMainSizeInfluenceCrossSize(const FlexboxAxisTracker& aAxisTracker) const;
757 :
758 : protected:
759 : // Helper called by the constructor, to set mNeedsMinSizeAutoResolution:
760 : void CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
761 : const FlexboxAxisTracker& aAxisTracker);
762 :
763 : // Our frame:
764 : nsIFrame* const mFrame;
765 :
766 : // Values that we already know in constructor: (and are hence mostly 'const')
767 : const float mFlexGrow;
768 : const float mFlexShrink;
769 :
770 : const nsSize mIntrinsicRatio;
771 :
772 : const nsMargin mBorderPadding;
773 : nsMargin mMargin; // non-const because we need to resolve auto margins
774 :
775 : // These are non-const so that we can lazily update them with the item's
776 : // intrinsic size (obtained via a "measuring" reflow), when necessary.
777 : // (e.g. for "flex-basis:auto;height:auto" & "min-height:auto")
778 : nscoord mFlexBaseSize;
779 : nscoord mMainMinSize;
780 : nscoord mMainMaxSize;
781 :
782 : const nscoord mCrossMinSize;
783 : const nscoord mCrossMaxSize;
784 :
785 : // Values that we compute after constructor:
786 : nscoord mMainSize;
787 : nscoord mMainPosn;
788 : nscoord mCrossSize;
789 : nscoord mCrossPosn;
790 : mutable nscoord mAscent; // Mutable b/c it's set & resolved lazily, sometimes
791 : // via const pointer. See comment above SetAscent().
792 :
793 : // Temporary state, while we're resolving flexible widths (for our main size)
794 : // XXXdholbert To save space, we could use a union to make these variables
795 : // overlay the same memory as some other member vars that aren't touched
796 : // until after main-size has been resolved. In particular, these could share
797 : // memory with mMainPosn through mAscent, and mIsStretched.
798 : float mShareOfWeightSoFar;
799 : bool mIsFrozen;
800 : bool mHadMinViolation;
801 : bool mHadMaxViolation;
802 :
803 : // Misc:
804 : bool mHadMeasuringReflow; // Did this item get a preliminary reflow,
805 : // to measure its desired height?
806 : bool mIsStretched; // See IsStretched() documentation
807 : bool mIsStrut; // Is this item a "strut" left behind by an element
808 : // with visibility:collapse?
809 :
810 : // Does this item need to resolve a min-[width|height]:auto (in main-axis).
811 : bool mNeedsMinSizeAutoResolution;
812 :
813 : const WritingMode mWM; // The flex item's writing mode.
814 : uint8_t mAlignSelf; // My "align-self" computed value (with "auto"
815 : // swapped out for parent"s "align-items" value,
816 : // in our constructor).
817 : };
818 :
819 : /**
820 : * Represents a single flex line in a flex container.
821 : * Manages a linked list of the FlexItems that are in the line.
822 : */
823 0 : class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
824 : {
825 : public:
826 0 : FlexLine()
827 0 : : mNumItems(0),
828 : mNumFrozenItems(0),
829 : mTotalInnerHypotheticalMainSize(0),
830 : mTotalOuterHypotheticalMainSize(0),
831 : mLineCrossSize(0),
832 : mFirstBaselineOffset(nscoord_MIN),
833 0 : mLastBaselineOffset(nscoord_MIN)
834 0 : {}
835 :
836 : // Returns the sum of our FlexItems' outer hypothetical main sizes.
837 : // ("outer" = margin-box, and "hypothetical" = before flexing)
838 0 : nscoord GetTotalOuterHypotheticalMainSize() const {
839 0 : return mTotalOuterHypotheticalMainSize;
840 : }
841 :
842 : // Accessors for our FlexItems & information about them:
843 0 : FlexItem* GetFirstItem()
844 : {
845 0 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
846 : "mNumItems bookkeeping is off");
847 0 : return mItems.getFirst();
848 : }
849 :
850 0 : const FlexItem* GetFirstItem() const
851 : {
852 0 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
853 : "mNumItems bookkeeping is off");
854 0 : return mItems.getFirst();
855 : }
856 :
857 0 : FlexItem* GetLastItem()
858 : {
859 0 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
860 : "mNumItems bookkeeping is off");
861 0 : return mItems.getLast();
862 : }
863 :
864 : const FlexItem* GetLastItem() const
865 : {
866 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
867 : "mNumItems bookkeeping is off");
868 : return mItems.getLast();
869 : }
870 :
871 0 : bool IsEmpty() const
872 : {
873 0 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
874 : "mNumItems bookkeeping is off");
875 0 : return mItems.isEmpty();
876 : }
877 :
878 0 : uint32_t NumItems() const
879 : {
880 0 : MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
881 : "mNumItems bookkeeping is off");
882 0 : return mNumItems;
883 : }
884 :
885 : // Adds the given FlexItem to our list of items (at the front or back
886 : // depending on aShouldInsertAtFront), and adds its hypothetical
887 : // outer & inner main sizes to our totals. Use this method instead of
888 : // directly modifying the item list, so that our bookkeeping remains correct.
889 0 : void AddItem(FlexItem* aItem,
890 : bool aShouldInsertAtFront,
891 : nscoord aItemInnerHypotheticalMainSize,
892 : nscoord aItemOuterHypotheticalMainSize)
893 : {
894 0 : if (aShouldInsertAtFront) {
895 0 : mItems.insertFront(aItem);
896 : } else {
897 0 : mItems.insertBack(aItem);
898 : }
899 :
900 : // Update our various bookkeeping member-vars:
901 0 : mNumItems++;
902 0 : if (aItem->IsFrozen()) {
903 0 : mNumFrozenItems++;
904 : }
905 0 : mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
906 0 : mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
907 0 : }
908 :
909 : // Computes the cross-size and baseline position of this FlexLine, based on
910 : // its FlexItems.
911 : void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
912 :
913 : // Returns the cross-size of this line.
914 0 : nscoord GetLineCrossSize() const { return mLineCrossSize; }
915 :
916 : // Setter for line cross-size -- needed for cases where the flex container
917 : // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
918 : // multi-line flexbox with 'align-content: stretch')
919 0 : void SetLineCrossSize(nscoord aLineCrossSize) {
920 0 : mLineCrossSize = aLineCrossSize;
921 0 : }
922 :
923 : /**
924 : * Returns the offset within this line where any baseline-aligned FlexItems
925 : * should place their baseline. Usually, this represents a distance from the
926 : * line's cross-start edge, but if we're internally reversing the axes (see
927 : * AreAxesInternallyReversed()), this instead represents the distance from
928 : * its cross-end edge.
929 : *
930 : * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
931 : */
932 0 : nscoord GetFirstBaselineOffset() const {
933 0 : return mFirstBaselineOffset;
934 : }
935 :
936 : /**
937 : * Returns the offset within this line where any last baseline-aligned
938 : * FlexItems should place their baseline. Opposite the case of the first
939 : * baseline offset, this represents a distance from the line's cross-end
940 : * edge (since last baseline-aligned items are flush to the cross-end edge).
941 : * If we're internally reversing the axes, this instead represents the
942 : * distance from the line's cross-start edge.
943 : *
944 : * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
945 : */
946 0 : nscoord GetLastBaselineOffset() const {
947 0 : return mLastBaselineOffset;
948 : }
949 :
950 : // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
951 : // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
952 : void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
953 :
954 : void PositionItemsInMainAxis(uint8_t aJustifyContent,
955 : nscoord aContentBoxMainSize,
956 : const FlexboxAxisTracker& aAxisTracker);
957 :
958 : void PositionItemsInCrossAxis(nscoord aLineStartPosition,
959 : const FlexboxAxisTracker& aAxisTracker);
960 :
961 : friend class AutoFlexLineListClearer; // (needs access to mItems)
962 :
963 : private:
964 : // Helpers for ResolveFlexibleLengths():
965 : void FreezeItemsEarly(bool aIsUsingFlexGrow);
966 :
967 : void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
968 : bool aIsFinalIteration);
969 :
970 : LinkedList<FlexItem> mItems; // Linked list of this line's flex items.
971 :
972 : uint32_t mNumItems; // Number of FlexItems in this line (in |mItems|).
973 : // (Shouldn't change after GenerateFlexLines finishes
974 : // with this line -- at least, not until we add support
975 : // for splitting lines across continuations. Then we can
976 : // update this count carefully.)
977 :
978 : // Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
979 : // Mostly used for optimization purposes, e.g. to bail out early from loops
980 : // when we can tell they have nothing left to do.
981 : uint32_t mNumFrozenItems;
982 :
983 : nscoord mTotalInnerHypotheticalMainSize;
984 : nscoord mTotalOuterHypotheticalMainSize;
985 : nscoord mLineCrossSize;
986 : nscoord mFirstBaselineOffset;
987 : nscoord mLastBaselineOffset;
988 : };
989 :
990 : // Information about a strut left behind by a FlexItem that's been collapsed
991 : // using "visibility:collapse".
992 : struct nsFlexContainerFrame::StrutInfo {
993 0 : StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
994 0 : : mItemIdx(aItemIdx),
995 0 : mStrutCrossSize(aStrutCrossSize)
996 : {
997 0 : }
998 :
999 : uint32_t mItemIdx; // Index in the child list.
1000 : nscoord mStrutCrossSize; // The cross-size of this strut.
1001 : };
1002 :
1003 : static void
1004 0 : BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine,
1005 : nsTArray<StrutInfo>& aStruts)
1006 : {
1007 0 : MOZ_ASSERT(aFirstLine, "null first line pointer");
1008 0 : MOZ_ASSERT(aStruts.IsEmpty(),
1009 : "We should only build up StrutInfo once per reflow, so "
1010 : "aStruts should be empty when this is called");
1011 :
1012 0 : uint32_t itemIdxInContainer = 0;
1013 0 : for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
1014 0 : for (const FlexItem* item = line->GetFirstItem(); item;
1015 0 : item = item->getNext()) {
1016 0 : if (NS_STYLE_VISIBILITY_COLLAPSE ==
1017 0 : item->Frame()->StyleVisibility()->mVisible) {
1018 : // Note the cross size of the line as the item's strut size.
1019 0 : aStruts.AppendElement(StrutInfo(itemIdxInContainer,
1020 0 : line->GetLineCrossSize()));
1021 : }
1022 0 : itemIdxInContainer++;
1023 : }
1024 : }
1025 0 : }
1026 :
1027 : uint8_t
1028 0 : SimplifyAlignOrJustifyContentForOneItem(uint16_t aAlignmentVal,
1029 : bool aIsAlign)
1030 : {
1031 : // Mask away any explicit fallback, to get the main (non-fallback) part of
1032 : // the specified value:
1033 0 : uint16_t specified = aAlignmentVal & NS_STYLE_ALIGN_ALL_BITS;
1034 :
1035 : // XXX strip off <overflow-position> bits until we implement it (bug 1311892)
1036 0 : specified &= ~NS_STYLE_ALIGN_FLAG_BITS;
1037 :
1038 : // FIRST: handle a special-case for "justify-content:stretch" (or equivalent),
1039 : // which requires that we ignore any author-provided explicit fallback value.
1040 0 : if (specified == NS_STYLE_ALIGN_NORMAL) {
1041 : // In a flex container, *-content: "'normal' behaves as 'stretch'".
1042 : // Do that conversion early, so it benefits from our 'stretch' special-case.
1043 : // https://drafts.csswg.org/css-align-3/#distribution-flex
1044 0 : specified = NS_STYLE_ALIGN_STRETCH;
1045 : }
1046 0 : if (!aIsAlign && specified == NS_STYLE_ALIGN_STRETCH) {
1047 : // In a flex container, in "justify-content Axis: [...] 'stretch' behaves
1048 : // as 'flex-start' (ignoring the specified fallback alignment, if any)."
1049 : // https://drafts.csswg.org/css-align-3/#distribution-flex
1050 : // So, we just directly return 'flex-start', & ignore explicit fallback..
1051 0 : return NS_STYLE_ALIGN_FLEX_START;
1052 : }
1053 :
1054 : // Now check for an explicit fallback value (and if it's present, use it).
1055 0 : uint16_t explicitFallback = aAlignmentVal >> NS_STYLE_ALIGN_ALL_SHIFT;
1056 0 : if (explicitFallback) {
1057 : // XXX strip off <overflow-position> bits until we implement it
1058 : // (bug 1311892)
1059 0 : explicitFallback &= ~NS_STYLE_ALIGN_FLAG_BITS;
1060 0 : return explicitFallback;
1061 : }
1062 :
1063 : // There's no explicit fallback. Use the implied fallback values for
1064 : // space-{between,around,evenly} (since those values only make sense with
1065 : // multiple alignment subjects), and otherwise just use the specified value:
1066 0 : switch (specified) {
1067 : case NS_STYLE_ALIGN_SPACE_BETWEEN:
1068 0 : return NS_STYLE_ALIGN_START;
1069 : case NS_STYLE_ALIGN_SPACE_AROUND:
1070 : case NS_STYLE_ALIGN_SPACE_EVENLY:
1071 0 : return NS_STYLE_ALIGN_CENTER;
1072 : default:
1073 0 : return specified;
1074 : }
1075 : }
1076 :
1077 : uint16_t
1078 0 : nsFlexContainerFrame::CSSAlignmentForAbsPosChild(
1079 : const ReflowInput& aChildRI,
1080 : LogicalAxis aLogicalAxis) const
1081 : {
1082 0 : WritingMode wm = GetWritingMode();
1083 : const FlexboxAxisTracker
1084 0 : axisTracker(this, wm, AxisTrackerFlags::eAllowBottomToTopChildOrdering);
1085 :
1086 : // If we're row-oriented and the caller is asking about our inline axis (or
1087 : // alternately, if we're column-oriented and the caller is asking about our
1088 : // block axis), then the caller is really asking about our *main* axis.
1089 : // Otherwise, the caller is asking about our cross axis.
1090 0 : const bool isMainAxis = (axisTracker.IsRowOriented() ==
1091 0 : (aLogicalAxis == eLogicalAxisInline));
1092 0 : const nsStylePosition* containerStylePos = StylePosition();
1093 0 : const bool isAxisReversed = isMainAxis ? axisTracker.IsMainAxisReversed()
1094 0 : : axisTracker.IsCrossAxisReversed();
1095 :
1096 : uint8_t alignment;
1097 0 : if (isMainAxis) {
1098 0 : alignment = SimplifyAlignOrJustifyContentForOneItem(
1099 0 : containerStylePos->mJustifyContent,
1100 0 : /*aIsAlign = */false);
1101 : } else {
1102 0 : const uint8_t alignContent = SimplifyAlignOrJustifyContentForOneItem(
1103 0 : containerStylePos->mAlignContent,
1104 0 : /*aIsAlign = */true);
1105 0 : if (NS_STYLE_FLEX_WRAP_NOWRAP != containerStylePos->mFlexWrap &&
1106 : alignContent != NS_STYLE_ALIGN_STRETCH) {
1107 : // Multi-line, align-content isn't stretch --> align-content determines
1108 : // this child's alignment in the cross axis.
1109 0 : alignment = alignContent;
1110 : } else {
1111 : // Single-line, or multi-line but the (one) line stretches to fill
1112 : // container. Respect align-self.
1113 0 : alignment = aChildRI.mStylePosition->UsedAlignSelf(StyleContext());
1114 : // XXX strip off <overflow-position> bits until we implement it
1115 : // (bug 1311892)
1116 0 : alignment &= ~NS_STYLE_ALIGN_FLAG_BITS;
1117 :
1118 0 : if (alignment == NS_STYLE_ALIGN_NORMAL) {
1119 : // "the 'normal' keyword behaves as 'start' on replaced
1120 : // absolutely-positioned boxes, and behaves as 'stretch' on all other
1121 : // absolutely-positioned boxes."
1122 : // https://drafts.csswg.org/css-align/#align-abspos
1123 0 : alignment = aChildRI.mFrame->IsFrameOfType(nsIFrame::eReplaced) ?
1124 0 : NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_STRETCH;
1125 : }
1126 : }
1127 : }
1128 :
1129 : // Resolve flex-start, flex-end, auto, left, right, baseline, last baseline;
1130 0 : if (alignment == NS_STYLE_ALIGN_FLEX_START) {
1131 0 : alignment = isAxisReversed ? NS_STYLE_ALIGN_END : NS_STYLE_ALIGN_START;
1132 0 : } else if (alignment == NS_STYLE_ALIGN_FLEX_END) {
1133 0 : alignment = isAxisReversed ? NS_STYLE_ALIGN_START : NS_STYLE_ALIGN_END;
1134 0 : } else if (alignment == NS_STYLE_ALIGN_LEFT ||
1135 : alignment == NS_STYLE_ALIGN_RIGHT) {
1136 0 : if (aLogicalAxis == eLogicalAxisInline) {
1137 0 : const bool isLeft = (alignment == NS_STYLE_ALIGN_LEFT);
1138 0 : alignment = (isLeft == wm.IsBidiLTR()) ? NS_STYLE_ALIGN_START
1139 0 : : NS_STYLE_ALIGN_END;
1140 : } else {
1141 0 : alignment = NS_STYLE_ALIGN_START;
1142 0 : }
1143 0 : } else if (alignment == NS_STYLE_ALIGN_BASELINE) {
1144 0 : alignment = NS_STYLE_ALIGN_START;
1145 0 : } else if (alignment == NS_STYLE_ALIGN_LAST_BASELINE) {
1146 0 : alignment = NS_STYLE_ALIGN_END;
1147 : }
1148 :
1149 0 : return alignment;
1150 : }
1151 :
1152 : bool
1153 0 : nsFlexContainerFrame::IsHorizontal()
1154 : {
1155 0 : const FlexboxAxisTracker axisTracker(this, GetWritingMode());
1156 0 : return axisTracker.IsMainAxisHorizontal();
1157 : }
1158 :
1159 : UniquePtr<FlexItem>
1160 0 : nsFlexContainerFrame::GenerateFlexItemForChild(
1161 : nsPresContext* aPresContext,
1162 : nsIFrame* aChildFrame,
1163 : const ReflowInput& aParentReflowInput,
1164 : const FlexboxAxisTracker& aAxisTracker)
1165 : {
1166 : // Create temporary reflow state just for sizing -- to get hypothetical
1167 : // main-size and the computed values of min / max main-size property.
1168 : // (This reflow state will _not_ be used for reflow.)
1169 : ReflowInput
1170 : childRI(aPresContext, aParentReflowInput, aChildFrame,
1171 0 : aParentReflowInput.ComputedSize(aChildFrame->GetWritingMode()));
1172 :
1173 : // FLEX GROW & SHRINK WEIGHTS
1174 : // --------------------------
1175 : float flexGrow, flexShrink;
1176 0 : if (IsLegacyBox(this)) {
1177 0 : flexGrow = flexShrink = aChildFrame->StyleXUL()->mBoxFlex;
1178 : } else {
1179 0 : const nsStylePosition* stylePos = aChildFrame->StylePosition();
1180 0 : flexGrow = stylePos->mFlexGrow;
1181 0 : flexShrink = stylePos->mFlexShrink;
1182 : }
1183 :
1184 0 : WritingMode childWM = childRI.GetWritingMode();
1185 :
1186 : // MAIN SIZES (flex base size, min/max size)
1187 : // -----------------------------------------
1188 0 : nscoord flexBaseSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
1189 : childRI.ComputedISize(),
1190 : childRI.ComputedBSize());
1191 0 : nscoord mainMinSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
1192 : childRI.ComputedMinISize(),
1193 : childRI.ComputedMinBSize());
1194 0 : nscoord mainMaxSize = GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, childWM,
1195 : childRI.ComputedMaxISize(),
1196 : childRI.ComputedMaxBSize());
1197 : // This is enforced by the ReflowInput where these values come from:
1198 0 : MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");
1199 :
1200 : // CROSS SIZES (tentative cross size, min/max cross size)
1201 : // ------------------------------------------------------
1202 : // Grab the cross size from the reflow state. This might be the right value,
1203 : // or we might resolve it to something else in SizeItemInCrossAxis(); hence,
1204 : // it's tentative. See comment under "Cross Size Determination" for more.
1205 : nscoord tentativeCrossSize =
1206 0 : GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
1207 : childRI.ComputedISize(),
1208 : childRI.ComputedBSize());
1209 : nscoord crossMinSize =
1210 0 : GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
1211 : childRI.ComputedMinISize(),
1212 : childRI.ComputedMinBSize());
1213 : nscoord crossMaxSize =
1214 0 : GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, childWM,
1215 : childRI.ComputedMaxISize(),
1216 : childRI.ComputedMaxBSize());
1217 :
1218 : // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES
1219 : // Check if we're a themed widget, in which case we might have a minimum
1220 : // main & cross size imposed by our widget (which we can't go below), or
1221 : // (more severe) our widget might have only a single valid size.
1222 0 : bool isFixedSizeWidget = false;
1223 0 : const nsStyleDisplay* disp = aChildFrame->StyleDisplay();
1224 0 : if (aChildFrame->IsThemed(disp)) {
1225 0 : LayoutDeviceIntSize widgetMinSize;
1226 0 : bool canOverride = true;
1227 0 : aPresContext->GetTheme()->
1228 0 : GetMinimumWidgetSize(aPresContext, aChildFrame,
1229 0 : disp->mAppearance,
1230 0 : &widgetMinSize, &canOverride);
1231 :
1232 : nscoord widgetMainMinSize =
1233 0 : aPresContext->DevPixelsToAppUnits(
1234 0 : aAxisTracker.GetMainComponent(widgetMinSize));
1235 : nscoord widgetCrossMinSize =
1236 0 : aPresContext->DevPixelsToAppUnits(
1237 0 : aAxisTracker.GetCrossComponent(widgetMinSize));
1238 :
1239 : // GMWS() returns border-box. We need content-box, so subtract
1240 : // borderPadding (but don't let that push our min sizes below 0).
1241 0 : nsMargin& bp = childRI.ComputedPhysicalBorderPadding();
1242 0 : widgetMainMinSize = std::max(widgetMainMinSize -
1243 0 : aAxisTracker.GetMarginSizeInMainAxis(bp), 0);
1244 0 : widgetCrossMinSize = std::max(widgetCrossMinSize -
1245 0 : aAxisTracker.GetMarginSizeInCrossAxis(bp), 0);
1246 :
1247 0 : if (!canOverride) {
1248 : // Fixed-size widget: freeze our main-size at the widget's mandated size.
1249 : // (Set min and max main-sizes to that size, too, to keep us from
1250 : // clamping to any other size later on.)
1251 0 : flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize;
1252 0 : tentativeCrossSize = crossMinSize = crossMaxSize = widgetCrossMinSize;
1253 0 : isFixedSizeWidget = true;
1254 : } else {
1255 : // Variable-size widget: ensure our min/max sizes are at least as large
1256 : // as the widget's mandated minimum size, so we don't flex below that.
1257 0 : mainMinSize = std::max(mainMinSize, widgetMainMinSize);
1258 0 : mainMaxSize = std::max(mainMaxSize, widgetMainMinSize);
1259 :
1260 0 : if (tentativeCrossSize != NS_INTRINSICSIZE) {
1261 0 : tentativeCrossSize = std::max(tentativeCrossSize, widgetCrossMinSize);
1262 : }
1263 0 : crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
1264 0 : crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
1265 : }
1266 : }
1267 :
1268 : // Construct the flex item!
1269 : auto item = MakeUnique<FlexItem>(childRI,
1270 : flexGrow, flexShrink, flexBaseSize,
1271 : mainMinSize, mainMaxSize,
1272 : tentativeCrossSize,
1273 : crossMinSize, crossMaxSize,
1274 0 : aAxisTracker);
1275 :
1276 : // If we're inflexible, we can just freeze to our hypothetical main-size
1277 : // up-front. Similarly, if we're a fixed-size widget, we only have one
1278 : // valid size, so we freeze to keep ourselves from flexing.
1279 0 : if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
1280 0 : item->Freeze();
1281 : }
1282 :
1283 : // Resolve "flex-basis:auto" and/or "min-[width|height]:auto" (which might
1284 : // require us to reflow the item to measure content height)
1285 0 : ResolveAutoFlexBasisAndMinSize(aPresContext, *item,
1286 0 : childRI, aAxisTracker);
1287 0 : return item;
1288 : }
1289 :
1290 : // Static helper-functions for ResolveAutoFlexBasisAndMinSize():
1291 : // -------------------------------------------------------------
1292 : // Indicates whether the cross-size property is set to something definite.
1293 : // The logic here should be similar to the logic for isAutoWidth/isAutoHeight
1294 : // in nsFrame::ComputeSizeWithIntrinsicDimensions().
1295 : static bool
1296 0 : IsCrossSizeDefinite(const ReflowInput& aItemReflowInput,
1297 : const FlexboxAxisTracker& aAxisTracker)
1298 : {
1299 0 : const nsStylePosition* pos = aItemReflowInput.mStylePosition;
1300 0 : if (aAxisTracker.IsCrossAxisHorizontal()) {
1301 0 : return pos->mWidth.GetUnit() != eStyleUnit_Auto;
1302 : }
1303 : // else, vertical. (We need to use IsAutoHeight() to catch e.g. %-height
1304 : // applied to indefinite-height containing block, which counts as auto.)
1305 0 : nscoord cbHeight = aItemReflowInput.mCBReflowInput->ComputedHeight();
1306 0 : return !nsLayoutUtils::IsAutoHeight(pos->mHeight, cbHeight);
1307 : }
1308 :
1309 : // If aFlexItem has a definite cross size, this function returns it, for usage
1310 : // (in combination with an intrinsic ratio) for resolving the item's main size
1311 : // or main min-size.
1312 : //
1313 : // The parameter "aMinSizeFallback" indicates whether we should fall back to
1314 : // returning the cross min-size, when the cross size is indefinite. (This param
1315 : // should be set IFF the caller intends to resolve the main min-size.) If this
1316 : // param is true, then this function is guaranteed to return a definite value
1317 : // (i.e. not NS_AUTOHEIGHT, excluding cases where huge sizes are involved).
1318 : //
1319 : // XXXdholbert the min-size behavior here is based on my understanding in
1320 : // http://lists.w3.org/Archives/Public/www-style/2014Jul/0053.html
1321 : // If my understanding there ends up being wrong, we'll need to update this.
1322 : static nscoord
1323 0 : CrossSizeToUseWithRatio(const FlexItem& aFlexItem,
1324 : const ReflowInput& aItemReflowInput,
1325 : bool aMinSizeFallback,
1326 : const FlexboxAxisTracker& aAxisTracker)
1327 : {
1328 0 : if (aFlexItem.IsStretched()) {
1329 : // Definite cross-size, imposed via 'align-self:stretch' & flex container.
1330 0 : return aFlexItem.GetCrossSize();
1331 : }
1332 :
1333 0 : if (IsCrossSizeDefinite(aItemReflowInput, aAxisTracker)) {
1334 : // Definite cross size.
1335 0 : return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
1336 : aItemReflowInput.ComputedISize(),
1337 : aItemReflowInput.ComputedBSize());
1338 : }
1339 :
1340 0 : if (aMinSizeFallback) {
1341 : // Indefinite cross-size, and we're resolving main min-size, so we'll fall
1342 : // back to ussing the cross min-size (which should be definite).
1343 0 : return GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
1344 : aItemReflowInput.ComputedMinISize(),
1345 : aItemReflowInput.ComputedMinBSize());
1346 : }
1347 :
1348 : // Indefinite cross-size.
1349 0 : return NS_AUTOHEIGHT;
1350 : }
1351 :
1352 : // Convenience function; returns a main-size, given a cross-size and an
1353 : // intrinsic ratio. The intrinsic ratio must not have 0 in its cross-axis
1354 : // component (or else we'll divide by 0).
1355 : static nscoord
1356 0 : MainSizeFromAspectRatio(nscoord aCrossSize,
1357 : const nsSize& aIntrinsicRatio,
1358 : const FlexboxAxisTracker& aAxisTracker)
1359 : {
1360 0 : MOZ_ASSERT(aAxisTracker.GetCrossComponent(aIntrinsicRatio) != 0,
1361 : "Invalid ratio; will divide by 0! Caller should've checked...");
1362 :
1363 0 : if (aAxisTracker.IsCrossAxisHorizontal()) {
1364 : // cross axis horiz --> aCrossSize is a width. Converting to height.
1365 0 : return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.height, aIntrinsicRatio.width);
1366 : }
1367 : // cross axis vert --> aCrossSize is a height. Converting to width.
1368 0 : return NSCoordMulDiv(aCrossSize, aIntrinsicRatio.width, aIntrinsicRatio.height);
1369 : }
1370 :
1371 : // Partially resolves "min-[width|height]:auto" and returns the resulting value.
1372 : // By "partially", I mean we don't consider the min-content size (but we do
1373 : // consider flex-basis, main max-size, and the intrinsic aspect ratio).
1374 : // The caller is responsible for computing & considering the min-content size
1375 : // in combination with the partially-resolved value that this function returns.
1376 : //
1377 : // Spec reference: http://dev.w3.org/csswg/css-flexbox/#min-size-auto
1378 : static nscoord
1379 0 : PartiallyResolveAutoMinSize(const FlexItem& aFlexItem,
1380 : const ReflowInput& aItemReflowInput,
1381 : const FlexboxAxisTracker& aAxisTracker)
1382 : {
1383 0 : MOZ_ASSERT(aFlexItem.NeedsMinSizeAutoResolution(),
1384 : "only call for FlexItems that need min-size auto resolution");
1385 :
1386 0 : nscoord minMainSize = nscoord_MAX; // Intentionally huge; we'll shrink it
1387 : // from here, w/ std::min().
1388 :
1389 : // We need the smallest of:
1390 : // * the used flex-basis, if the computed flex-basis was 'auto':
1391 : // XXXdholbert ('auto' might be renamed to 'main-size'; see bug 1032922)
1392 0 : if (eStyleUnit_Auto ==
1393 0 : aItemReflowInput.mStylePosition->mFlexBasis.GetUnit() &&
1394 0 : aFlexItem.GetFlexBaseSize() != NS_AUTOHEIGHT) {
1395 : // NOTE: We skip this if the flex base size depends on content & isn't yet
1396 : // resolved. This is OK, because the caller is responsible for computing
1397 : // the min-content height and min()'ing it with the value we return, which
1398 : // is equivalent to what would happen if we min()'d that at this point.
1399 0 : minMainSize = std::min(minMainSize, aFlexItem.GetFlexBaseSize());
1400 : }
1401 :
1402 : // * the computed max-width (max-height), if that value is definite:
1403 : nscoord maxSize =
1404 0 : GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, aFlexItem.GetWritingMode(),
1405 : aItemReflowInput.ComputedMaxISize(),
1406 : aItemReflowInput.ComputedMaxBSize());
1407 0 : if (maxSize != NS_UNCONSTRAINEDSIZE) {
1408 0 : minMainSize = std::min(minMainSize, maxSize);
1409 : }
1410 :
1411 : // * if the item has no intrinsic aspect ratio, its min-content size:
1412 : // --- SKIPPING THIS IN THIS FUNCTION --- caller's responsibility.
1413 :
1414 : // * if the item has an intrinsic aspect ratio, the width (height) calculated
1415 : // from the aspect ratio and any definite size constraints in the opposite
1416 : // dimension.
1417 0 : if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) {
1418 : // We have a usable aspect ratio. (not going to divide by 0)
1419 0 : const bool useMinSizeIfCrossSizeIsIndefinite = true;
1420 : nscoord crossSizeToUseWithRatio =
1421 : CrossSizeToUseWithRatio(aFlexItem, aItemReflowInput,
1422 : useMinSizeIfCrossSizeIsIndefinite,
1423 0 : aAxisTracker);
1424 : nscoord minMainSizeFromRatio =
1425 0 : MainSizeFromAspectRatio(crossSizeToUseWithRatio,
1426 0 : aFlexItem.IntrinsicRatio(), aAxisTracker);
1427 0 : minMainSize = std::min(minMainSize, minMainSizeFromRatio);
1428 : }
1429 :
1430 0 : return minMainSize;
1431 : }
1432 :
1433 : // Resolves flex-basis:auto, using the given intrinsic ratio and the flex
1434 : // item's cross size. On success, updates the flex item with its resolved
1435 : // flex-basis and returns true. On failure (e.g. if the ratio is invalid or
1436 : // the cross-size is indefinite), returns false.
1437 : static bool
1438 0 : ResolveAutoFlexBasisFromRatio(FlexItem& aFlexItem,
1439 : const ReflowInput& aItemReflowInput,
1440 : const FlexboxAxisTracker& aAxisTracker)
1441 : {
1442 0 : MOZ_ASSERT(NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize(),
1443 : "Should only be called to resolve an 'auto' flex-basis");
1444 : // If the flex item has ...
1445 : // - an intrinsic aspect ratio,
1446 : // - a [used] flex-basis of 'main-size' [auto?] [We have this, if we're here.]
1447 : // - a definite cross size
1448 : // then the flex base size is calculated from its inner cross size and the
1449 : // flex item’s intrinsic aspect ratio.
1450 0 : if (aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) != 0) {
1451 : // We have a usable aspect ratio. (not going to divide by 0)
1452 0 : const bool useMinSizeIfCrossSizeIsIndefinite = false;
1453 : nscoord crossSizeToUseWithRatio =
1454 : CrossSizeToUseWithRatio(aFlexItem, aItemReflowInput,
1455 : useMinSizeIfCrossSizeIsIndefinite,
1456 0 : aAxisTracker);
1457 0 : if (crossSizeToUseWithRatio != NS_AUTOHEIGHT) {
1458 : // We have a definite cross-size
1459 : nscoord mainSizeFromRatio =
1460 0 : MainSizeFromAspectRatio(crossSizeToUseWithRatio,
1461 0 : aFlexItem.IntrinsicRatio(), aAxisTracker);
1462 0 : aFlexItem.SetFlexBaseSizeAndMainSize(mainSizeFromRatio);
1463 0 : return true;
1464 : }
1465 : }
1466 0 : return false;
1467 : }
1468 :
1469 : // Note: If & when we handle "min-height: min-content" for flex items,
1470 : // we may want to resolve that in this function, too.
1471 : void
1472 0 : nsFlexContainerFrame::
1473 : ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
1474 : FlexItem& aFlexItem,
1475 : const ReflowInput& aItemReflowInput,
1476 : const FlexboxAxisTracker& aAxisTracker)
1477 : {
1478 : // (Note: We should never have a used flex-basis of "auto" if our main axis
1479 : // is horizontal; width values should always be resolvable without reflow.)
1480 0 : const bool isMainSizeAuto = (!aAxisTracker.IsMainAxisHorizontal() &&
1481 0 : NS_AUTOHEIGHT == aFlexItem.GetFlexBaseSize());
1482 :
1483 0 : const bool isMainMinSizeAuto = aFlexItem.NeedsMinSizeAutoResolution();
1484 :
1485 0 : if (!isMainSizeAuto && !isMainMinSizeAuto) {
1486 : // Nothing to do; this function is only needed for flex items
1487 : // with a used flex-basis of "auto" or a min-main-size of "auto".
1488 0 : return;
1489 : }
1490 :
1491 : // We may be about to do computations based on our item's cross-size
1492 : // (e.g. using it as a contstraint when measuring our content in the
1493 : // main axis, or using it with the intrinsic ratio to obtain a main size).
1494 : // BEFORE WE DO THAT, we need let the item "pre-stretch" its cross size (if
1495 : // it's got 'align-self:stretch'), for a certain case where the spec says
1496 : // the stretched cross size is considered "definite". That case is if we
1497 : // have a single-line (nowrap) flex container which itself has a definite
1498 : // cross-size. Otherwise, we'll wait to do stretching, since (in other
1499 : // cases) we don't know how much the item should stretch yet.
1500 0 : const ReflowInput* flexContainerRI = aItemReflowInput.mParentReflowInput;
1501 0 : MOZ_ASSERT(flexContainerRI,
1502 : "flex item's reflow state should have ptr to container's state");
1503 0 : if (NS_STYLE_FLEX_WRAP_NOWRAP == flexContainerRI->mStylePosition->mFlexWrap) {
1504 : // XXXdholbert Maybe this should share logic with ComputeCrossSize()...
1505 : // Alternately, maybe tentative container cross size should be passed down.
1506 : nscoord containerCrossSize =
1507 0 : GET_CROSS_COMPONENT_LOGICAL(aAxisTracker, aAxisTracker.GetWritingMode(),
1508 : flexContainerRI->ComputedISize(),
1509 : flexContainerRI->ComputedBSize());
1510 : // Is container's cross size "definite"?
1511 : // (Container's cross size is definite if cross-axis is horizontal, or if
1512 : // cross-axis is vertical and the cross-size is not NS_AUTOHEIGHT.)
1513 0 : if (aAxisTracker.IsCrossAxisHorizontal() ||
1514 : containerCrossSize != NS_AUTOHEIGHT) {
1515 0 : aFlexItem.ResolveStretchedCrossSize(containerCrossSize, aAxisTracker);
1516 : }
1517 : }
1518 :
1519 : nscoord resolvedMinSize; // (only set/used if isMainMinSizeAuto==true)
1520 0 : bool minSizeNeedsToMeasureContent = false; // assume the best
1521 0 : if (isMainMinSizeAuto) {
1522 : // Resolve the min-size, except for considering the min-content size.
1523 : // (We'll consider that later, if we need to.)
1524 0 : resolvedMinSize = PartiallyResolveAutoMinSize(aFlexItem, aItemReflowInput,
1525 : aAxisTracker);
1526 0 : if (resolvedMinSize > 0 &&
1527 0 : aAxisTracker.GetCrossComponent(aFlexItem.IntrinsicRatio()) == 0) {
1528 : // We don't have a usable aspect ratio, so we need to consider our
1529 : // min-content size as another candidate min-size, which we'll have to
1530 : // min() with the current resolvedMinSize.
1531 : // (If resolvedMinSize were already at 0, we could skip this measurement
1532 : // because it can't go any lower. But it's not 0, so we need it.)
1533 0 : minSizeNeedsToMeasureContent = true;
1534 : }
1535 : }
1536 :
1537 0 : bool flexBasisNeedsToMeasureContent = false; // assume the best
1538 0 : if (isMainSizeAuto) {
1539 0 : if (!ResolveAutoFlexBasisFromRatio(aFlexItem, aItemReflowInput,
1540 : aAxisTracker)) {
1541 0 : flexBasisNeedsToMeasureContent = true;
1542 : }
1543 : }
1544 :
1545 : // Measure content, if needed (w/ intrinsic-width method or a reflow)
1546 0 : if (minSizeNeedsToMeasureContent || flexBasisNeedsToMeasureContent) {
1547 0 : if (aAxisTracker.IsMainAxisHorizontal()) {
1548 0 : if (minSizeNeedsToMeasureContent) {
1549 : nscoord frameMinISize =
1550 0 : aFlexItem.Frame()->GetMinISize(aItemReflowInput.mRenderingContext);
1551 0 : resolvedMinSize = std::min(resolvedMinSize, frameMinISize);
1552 : }
1553 0 : NS_ASSERTION(!flexBasisNeedsToMeasureContent,
1554 : "flex-basis:auto should have been resolved in the "
1555 : "reflow state, for horizontal flexbox. It shouldn't need "
1556 : "special handling here");
1557 : } else {
1558 : // If this item is flexible (vertically), or if we're measuring the
1559 : // 'auto' min-height and our main-size is something else, then we assume
1560 : // that the computed-height we're reflowing with now could be different
1561 : // from the one we'll use for this flex item's "actual" reflow later on.
1562 : // In that case, we need to be sure the flex item treats this as a
1563 : // vertical resize, even though none of its ancestors are necessarily
1564 : // being vertically resized.
1565 : // (Note: We don't have to do this for width, because InitResizeFlags
1566 : // will always turn on mHResize on when it sees that the computed width
1567 : // is different from current width, and that's all we need.)
1568 : bool forceVerticalResizeForMeasuringReflow =
1569 0 : !aFlexItem.IsFrozen() || // Is the item flexible?
1570 0 : !flexBasisNeedsToMeasureContent; // Are we *only* measuring it for
1571 : // 'min-height:auto'?
1572 :
1573 : nscoord contentHeight =
1574 0 : MeasureFlexItemContentHeight(aPresContext, aFlexItem,
1575 : forceVerticalResizeForMeasuringReflow,
1576 0 : *flexContainerRI);
1577 0 : if (minSizeNeedsToMeasureContent) {
1578 0 : resolvedMinSize = std::min(resolvedMinSize, contentHeight);
1579 : }
1580 0 : if (flexBasisNeedsToMeasureContent) {
1581 0 : aFlexItem.SetFlexBaseSizeAndMainSize(contentHeight);
1582 : }
1583 : }
1584 : }
1585 :
1586 0 : if (isMainMinSizeAuto) {
1587 0 : aFlexItem.UpdateMainMinSize(resolvedMinSize);
1588 : }
1589 : }
1590 :
1591 : /**
1592 : * A cached result for a measuring reflow.
1593 : *
1594 : * Right now we only need to cache the available size and the computed height
1595 : * for checking that the reflow input is valid, and the height and the ascent
1596 : * to be used. This can be extended later if needed.
1597 : *
1598 : * The assumption here is that a given flex item measurement won't change until
1599 : * either the available size or computed height changes, or the flex container
1600 : * intrinsic size is marked as dirty (due to a style or DOM change).
1601 : *
1602 : * In particular the computed height may change between measuring reflows due to
1603 : * how the mIsFlexContainerMeasuringReflow flag affects size computation (see
1604 : * bug 1336708).
1605 : *
1606 : * Caching it prevents us from doing exponential reflows in cases of deeply
1607 : * nested flex and scroll frames.
1608 : *
1609 : * We store them in the frame property table for simplicity.
1610 : */
1611 : class nsFlexContainerFrame::CachedMeasuringReflowResult
1612 : {
1613 : // Members that are part of the cache key:
1614 : const LogicalSize mAvailableSize;
1615 : const nscoord mComputedHeight;
1616 :
1617 : // Members that are part of the cache value:
1618 : const nscoord mHeight;
1619 : const nscoord mAscent;
1620 :
1621 : public:
1622 0 : CachedMeasuringReflowResult(const ReflowInput& aReflowInput,
1623 : const ReflowOutput& aDesiredSize)
1624 0 : : mAvailableSize(aReflowInput.AvailableSize())
1625 0 : , mComputedHeight(aReflowInput.ComputedHeight())
1626 0 : , mHeight(aDesiredSize.Height())
1627 0 : , mAscent(aDesiredSize.BlockStartAscent())
1628 0 : {}
1629 :
1630 0 : bool IsValidFor(const ReflowInput& aReflowInput) const {
1631 0 : return mAvailableSize == aReflowInput.AvailableSize() &&
1632 0 : mComputedHeight == aReflowInput.ComputedHeight();
1633 : }
1634 :
1635 0 : nscoord Height() const { return mHeight; }
1636 :
1637 0 : nscoord Ascent() const { return mAscent; }
1638 : };
1639 :
1640 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(CachedFlexMeasuringReflow,
1641 : CachedMeasuringReflowResult);
1642 :
1643 : const CachedMeasuringReflowResult&
1644 0 : nsFlexContainerFrame::MeasureAscentAndHeightForFlexItem(
1645 : FlexItem& aItem,
1646 : nsPresContext* aPresContext,
1647 : ReflowInput& aChildReflowInput)
1648 : {
1649 0 : if (const auto* cachedResult =
1650 0 : aItem.Frame()->GetProperty(CachedFlexMeasuringReflow())) {
1651 0 : if (cachedResult->IsValidFor(aChildReflowInput)) {
1652 0 : return *cachedResult;
1653 : }
1654 : }
1655 :
1656 0 : ReflowOutput childDesiredSize(aChildReflowInput);
1657 0 : nsReflowStatus childReflowStatus;
1658 :
1659 0 : const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
1660 0 : ReflowChild(aItem.Frame(), aPresContext,
1661 : childDesiredSize, aChildReflowInput,
1662 0 : 0, 0, flags, childReflowStatus);
1663 0 : aItem.SetHadMeasuringReflow();
1664 :
1665 : // XXXdholbert Once we do pagination / splitting, we'll need to actually
1666 : // handle incomplete childReflowStatuses. But for now, we give our kids
1667 : // unconstrained available height, which means they should always complete.
1668 0 : MOZ_ASSERT(childReflowStatus.IsComplete(),
1669 : "We gave flex item unconstrained available height, so it "
1670 : "should be complete");
1671 :
1672 : // Tell the child we're done with its initial reflow.
1673 : // (Necessary for e.g. GetBaseline() to work below w/out asserting)
1674 0 : FinishReflowChild(aItem.Frame(), aPresContext,
1675 0 : childDesiredSize, &aChildReflowInput, 0, 0, flags);
1676 :
1677 : auto result =
1678 0 : new CachedMeasuringReflowResult(aChildReflowInput, childDesiredSize);
1679 :
1680 0 : aItem.Frame()->SetProperty(CachedFlexMeasuringReflow(), result);
1681 0 : return *result;
1682 : }
1683 :
1684 : /* virtual */ void
1685 0 : nsFlexContainerFrame::MarkIntrinsicISizesDirty()
1686 : {
1687 0 : for (nsIFrame* childFrame : mFrames) {
1688 0 : childFrame->DeleteProperty(CachedFlexMeasuringReflow());
1689 : }
1690 0 : nsContainerFrame::MarkIntrinsicISizesDirty();
1691 0 : }
1692 :
1693 : nscoord
1694 0 : nsFlexContainerFrame::
1695 : MeasureFlexItemContentHeight(nsPresContext* aPresContext,
1696 : FlexItem& aFlexItem,
1697 : bool aForceVerticalResizeForMeasuringReflow,
1698 : const ReflowInput& aParentReflowInput)
1699 : {
1700 : // Set up a reflow state for measuring the flex item's auto-height:
1701 0 : WritingMode wm = aFlexItem.Frame()->GetWritingMode();
1702 0 : LogicalSize availSize = aParentReflowInput.ComputedSize(wm);
1703 0 : availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
1704 : ReflowInput
1705 : childRIForMeasuringHeight(aPresContext, aParentReflowInput,
1706 : aFlexItem.Frame(), availSize,
1707 0 : nullptr, ReflowInput::CALLER_WILL_INIT);
1708 0 : childRIForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true;
1709 0 : childRIForMeasuringHeight.Init(aPresContext);
1710 :
1711 0 : if (aFlexItem.IsStretched()) {
1712 0 : childRIForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize());
1713 0 : childRIForMeasuringHeight.SetHResize(true);
1714 : }
1715 :
1716 0 : if (aForceVerticalResizeForMeasuringReflow) {
1717 0 : childRIForMeasuringHeight.SetVResize(true);
1718 : }
1719 :
1720 : const CachedMeasuringReflowResult& reflowResult =
1721 : MeasureAscentAndHeightForFlexItem(aFlexItem, aPresContext,
1722 0 : childRIForMeasuringHeight);
1723 :
1724 0 : aFlexItem.SetAscent(reflowResult.Ascent());
1725 :
1726 : // Subtract border/padding in vertical axis, to get _just_
1727 : // the effective computed value of the "height" property.
1728 0 : nscoord childDesiredHeight = reflowResult.Height() -
1729 0 : childRIForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
1730 :
1731 0 : return std::max(0, childDesiredHeight);
1732 : }
1733 :
1734 0 : FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput,
1735 : float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize,
1736 : nscoord aMainMinSize, nscoord aMainMaxSize,
1737 : nscoord aTentativeCrossSize,
1738 : nscoord aCrossMinSize, nscoord aCrossMaxSize,
1739 0 : const FlexboxAxisTracker& aAxisTracker)
1740 0 : : mFrame(aFlexItemReflowInput.mFrame),
1741 : mFlexGrow(aFlexGrow),
1742 : mFlexShrink(aFlexShrink),
1743 0 : mIntrinsicRatio(mFrame->GetIntrinsicRatio()),
1744 0 : mBorderPadding(aFlexItemReflowInput.ComputedPhysicalBorderPadding()),
1745 0 : mMargin(aFlexItemReflowInput.ComputedPhysicalMargin()),
1746 : mMainMinSize(aMainMinSize),
1747 : mMainMaxSize(aMainMaxSize),
1748 : mCrossMinSize(aCrossMinSize),
1749 : mCrossMaxSize(aCrossMaxSize),
1750 : mMainPosn(0),
1751 : mCrossSize(aTentativeCrossSize),
1752 : mCrossPosn(0),
1753 : mAscent(0),
1754 : mShareOfWeightSoFar(0.0f),
1755 : mIsFrozen(false),
1756 : mHadMinViolation(false),
1757 : mHadMaxViolation(false),
1758 : mHadMeasuringReflow(false),
1759 : mIsStretched(false),
1760 : mIsStrut(false),
1761 : // mNeedsMinSizeAutoResolution is initialized in CheckForMinSizeAuto()
1762 0 : mWM(aFlexItemReflowInput.GetWritingMode())
1763 : // mAlignSelf, see below
1764 : {
1765 0 : MOZ_ASSERT(mFrame, "expecting a non-null child frame");
1766 0 : MOZ_ASSERT(!mFrame->IsPlaceholderFrame(),
1767 : "placeholder frames should not be treated as flex items");
1768 0 : MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1769 : "out-of-flow frames should not be treated as flex items");
1770 :
1771 0 : const ReflowInput* containerRS = aFlexItemReflowInput.mParentReflowInput;
1772 0 : if (IsLegacyBox(containerRS->mFrame)) {
1773 : // For -webkit-box/-webkit-inline-box, we need to:
1774 : // (1) Use "-webkit-box-align" instead of "align-items" to determine the
1775 : // container's cross-axis alignment behavior.
1776 : // (2) Suppress the ability for flex items to override that with their own
1777 : // cross-axis alignment. (The legacy box model doesn't support this.)
1778 : // So, each FlexItem simply copies the container's converted "align-items"
1779 : // value and disregards their own "align-self" property.
1780 0 : const nsStyleXUL* containerStyleXUL = containerRS->mFrame->StyleXUL();
1781 0 : mAlignSelf = ConvertLegacyStyleToAlignItems(containerStyleXUL);
1782 : } else {
1783 0 : mAlignSelf = aFlexItemReflowInput.mStylePosition->UsedAlignSelf(
1784 0 : containerRS->mFrame->StyleContext());
1785 0 : if (MOZ_LIKELY(mAlignSelf == NS_STYLE_ALIGN_NORMAL)) {
1786 0 : mAlignSelf = NS_STYLE_ALIGN_STRETCH;
1787 : }
1788 :
1789 : // XXX strip off the <overflow-position> bit until we implement that
1790 0 : mAlignSelf &= ~NS_STYLE_ALIGN_FLAG_BITS;
1791 : }
1792 :
1793 0 : SetFlexBaseSizeAndMainSize(aFlexBaseSize);
1794 0 : CheckForMinSizeAuto(aFlexItemReflowInput, aAxisTracker);
1795 :
1796 : // Assert that any "auto" margin components are set to 0.
1797 : // (We'll resolve them later; until then, we want to treat them as 0-sized.)
1798 : #ifdef DEBUG
1799 : {
1800 : const nsStyleSides& styleMargin =
1801 0 : aFlexItemReflowInput.mStyleMargin->mMargin;
1802 0 : NS_FOR_CSS_SIDES(side) {
1803 0 : if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
1804 0 : MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
1805 : "Someone else tried to resolve our auto margin");
1806 : }
1807 : }
1808 : }
1809 : #endif // DEBUG
1810 :
1811 : // Map align-self 'baseline' value to 'start' when baseline alignment
1812 : // is not possible because the FlexItem's writing mode is orthogonal to
1813 : // the main axis of the container. If that's the case, we just directly
1814 : // convert our align-self value here, so that we don't have to handle this
1815 : // with special cases elsewhere.
1816 : // We are treating this case as one where it is appropriate to use the
1817 : // fallback values defined at https://www.w3.org/TR/css-align-3/#baseline
1818 0 : if (aAxisTracker.IsRowOriented() ==
1819 0 : aAxisTracker.GetWritingMode().IsOrthogonalTo(mWM)) {
1820 0 : if (mAlignSelf == NS_STYLE_ALIGN_BASELINE) {
1821 0 : mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
1822 0 : } else if (mAlignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
1823 0 : mAlignSelf = NS_STYLE_ALIGN_FLEX_END;
1824 : }
1825 : }
1826 0 : }
1827 :
1828 : // Simplified constructor for creating a special "strut" FlexItem, for a child
1829 : // with visibility:collapse. The strut has 0 main-size, and it only exists to
1830 : // impose a minimum cross size on whichever FlexLine it ends up in.
1831 0 : FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize,
1832 0 : WritingMode aContainerWM)
1833 : : mFrame(aChildFrame),
1834 : mFlexGrow(0.0f),
1835 : mFlexShrink(0.0f),
1836 : mIntrinsicRatio(),
1837 : // mBorderPadding uses default constructor,
1838 : // mMargin uses default constructor,
1839 : mFlexBaseSize(0),
1840 : mMainMinSize(0),
1841 : mMainMaxSize(0),
1842 : mCrossMinSize(0),
1843 : mCrossMaxSize(0),
1844 : mMainSize(0),
1845 : mMainPosn(0),
1846 : mCrossSize(aCrossSize),
1847 : mCrossPosn(0),
1848 : mAscent(0),
1849 : mShareOfWeightSoFar(0.0f),
1850 : mIsFrozen(true),
1851 : mHadMinViolation(false),
1852 : mHadMaxViolation(false),
1853 : mHadMeasuringReflow(false),
1854 : mIsStretched(false),
1855 : mIsStrut(true), // (this is the constructor for making struts, after all)
1856 : mNeedsMinSizeAutoResolution(false),
1857 : mWM(aContainerWM),
1858 0 : mAlignSelf(NS_STYLE_ALIGN_FLEX_START)
1859 : {
1860 0 : MOZ_ASSERT(mFrame, "expecting a non-null child frame");
1861 0 : MOZ_ASSERT(NS_STYLE_VISIBILITY_COLLAPSE ==
1862 : mFrame->StyleVisibility()->mVisible,
1863 : "Should only make struts for children with 'visibility:collapse'");
1864 0 : MOZ_ASSERT(!mFrame->IsPlaceholderFrame(),
1865 : "placeholder frames should not be treated as flex items");
1866 0 : MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
1867 : "out-of-flow frames should not be treated as flex items");
1868 0 : }
1869 :
1870 : void
1871 0 : FlexItem::CheckForMinSizeAuto(const ReflowInput& aFlexItemReflowInput,
1872 : const FlexboxAxisTracker& aAxisTracker)
1873 : {
1874 0 : const nsStylePosition* pos = aFlexItemReflowInput.mStylePosition;
1875 0 : const nsStyleDisplay* disp = aFlexItemReflowInput.mStyleDisplay;
1876 :
1877 : // We'll need special behavior for "min-[width|height]:auto" (whichever is in
1878 : // the main axis) iff:
1879 : // (a) its computed value is "auto"
1880 : // (b) the "overflow" sub-property in the same axis (the main axis) has a
1881 : // computed value of "visible"
1882 0 : const nsStyleCoord& minSize = GET_MAIN_COMPONENT(aAxisTracker,
1883 : pos->mMinWidth,
1884 : pos->mMinHeight);
1885 :
1886 0 : const uint8_t overflowVal = GET_MAIN_COMPONENT(aAxisTracker,
1887 : disp->mOverflowX,
1888 : disp->mOverflowY);
1889 :
1890 0 : mNeedsMinSizeAutoResolution = (minSize.GetUnit() == eStyleUnit_Auto &&
1891 : overflowVal == NS_STYLE_OVERFLOW_VISIBLE);
1892 0 : }
1893 :
1894 : nscoord
1895 0 : FlexItem::GetBaselineOffsetFromOuterCrossEdge(
1896 : AxisEdgeType aEdge,
1897 : const FlexboxAxisTracker& aAxisTracker,
1898 : bool aUseFirstLineBaseline) const
1899 : {
1900 : // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
1901 : // measurement -- it's the distance from the border-top edge of this FlexItem
1902 : // to its baseline. So, we can really only do baseline alignment when the
1903 : // cross axis is vertical. (The FlexItem constructor enforces this when
1904 : // resolving the item's "mAlignSelf" value).
1905 0 : MOZ_ASSERT(!aAxisTracker.IsCrossAxisHorizontal(),
1906 : "Only expecting to be doing baseline computations when the "
1907 : "cross axis is vertical");
1908 :
1909 0 : AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
1910 0 : mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge];
1911 :
1912 0 : nscoord marginTopToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
1913 0 : mMargin.top;
1914 :
1915 0 : if (sideToMeasureFrom == eSideTop) {
1916 : // Measuring from top (normal case): the distance from the margin-box top
1917 : // edge to the baseline is just ascent + margin-top.
1918 0 : return marginTopToBaseline;
1919 : }
1920 :
1921 0 : MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
1922 : "We already checked that we're dealing with a vertical axis, and "
1923 : "we're not using the top side, so that only leaves the bottom...");
1924 :
1925 : // Measuring from bottom: The distance from the margin-box bottom edge to the
1926 : // baseline is just the margin-box cross size (i.e. outer cross size), minus
1927 : // the already-computed distance from margin-top to baseline.
1928 0 : return GetOuterCrossSize(crossAxis) - marginTopToBaseline;
1929 : }
1930 :
1931 : uint32_t
1932 0 : FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const
1933 : {
1934 0 : uint32_t numAutoMargins = 0;
1935 0 : const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
1936 0 : for (uint32_t i = 0; i < eNumAxisEdges; i++) {
1937 0 : mozilla::Side side = kAxisOrientationToSidesMap[aAxis][i];
1938 0 : if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
1939 0 : numAutoMargins++;
1940 : }
1941 : }
1942 :
1943 : // Mostly for clarity:
1944 0 : MOZ_ASSERT(numAutoMargins <= 2,
1945 : "We're just looking at one item along one dimension, so we "
1946 : "should only have examined 2 margins");
1947 :
1948 0 : return numAutoMargins;
1949 : }
1950 :
1951 : bool
1952 0 : FlexItem::CanMainSizeInfluenceCrossSize(
1953 : const FlexboxAxisTracker& aAxisTracker) const
1954 : {
1955 0 : if (mIsStretched) {
1956 : // We've already had our cross-size stretched for "align-self:stretch").
1957 : // The container is imposing its cross size on us.
1958 0 : return false;
1959 : }
1960 :
1961 0 : if (mIsStrut) {
1962 : // Struts (for visibility:collapse items) have a predetermined size;
1963 : // no need to measure anything.
1964 0 : return false;
1965 : }
1966 :
1967 0 : if (HasIntrinsicRatio()) {
1968 : // For flex items that have an intrinsic ratio (and maintain it, i.e. are
1969 : // not stretched, which we already checked above): changes to main-size
1970 : // *do* influence the cross size.
1971 0 : return true;
1972 : }
1973 :
1974 0 : if (aAxisTracker.IsCrossAxisHorizontal()) {
1975 : // If the cross axis is horizontal, then changes to the item's main size
1976 : // (height) can't influence its cross size (width), if the item is a block
1977 : // with a horizontal writing-mode.
1978 : // XXXdholbert This doesn't account for vertical writing-modes, items with
1979 : // aspect ratios, items that are multicol elements, & items that are
1980 : // multi-line vertical flex containers. In all of those cases, a change to
1981 : // the height could influence the width.
1982 0 : return false;
1983 : }
1984 :
1985 : // Default assumption, if we haven't proven otherwise: the resolved main size
1986 : // *can* change the cross size.
1987 0 : return true;
1988 : }
1989 :
1990 : // Keeps track of our position along a particular axis (where a '0' position
1991 : // corresponds to the 'start' edge of that axis).
1992 : // This class shouldn't be instantiated directly -- rather, it should only be
1993 : // instantiated via its subclasses defined below.
1994 : class MOZ_STACK_CLASS PositionTracker {
1995 : public:
1996 : // Accessor for the current value of the position that we're tracking.
1997 0 : inline nscoord GetPosition() const { return mPosition; }
1998 0 : inline AxisOrientationType GetAxis() const { return mAxis; }
1999 :
2000 : // Advances our position across the start edge of the given margin, in the
2001 : // axis we're tracking.
2002 0 : void EnterMargin(const nsMargin& aMargin)
2003 : {
2004 0 : mozilla::Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_Start];
2005 0 : mPosition += aMargin.Side(side);
2006 0 : }
2007 :
2008 : // Advances our position across the end edge of the given margin, in the axis
2009 : // we're tracking.
2010 0 : void ExitMargin(const nsMargin& aMargin)
2011 : {
2012 0 : mozilla::Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_End];
2013 0 : mPosition += aMargin.Side(side);
2014 0 : }
2015 :
2016 : // Advances our current position from the start side of a child frame's
2017 : // border-box to the frame's upper or left edge (depending on our axis).
2018 : // (Note that this is a no-op if our axis grows in the same direction as
2019 : // the corresponding logical axis.)
2020 0 : void EnterChildFrame(nscoord aChildFrameSize)
2021 : {
2022 0 : if (mIsAxisReversed) {
2023 0 : mPosition += aChildFrameSize;
2024 : }
2025 0 : }
2026 :
2027 : // Advances our current position from a frame's upper or left border-box edge
2028 : // (whichever is in the axis we're tracking) to the 'end' side of the frame
2029 : // in the axis that we're tracking. (Note that this is a no-op if our axis
2030 : // is reversed with respect to the corresponding logical axis.)
2031 0 : void ExitChildFrame(nscoord aChildFrameSize)
2032 : {
2033 0 : if (!mIsAxisReversed) {
2034 0 : mPosition += aChildFrameSize;
2035 : }
2036 0 : }
2037 :
2038 : protected:
2039 : // Protected constructor, to be sure we're only instantiated via a subclass.
2040 0 : PositionTracker(AxisOrientationType aAxis, bool aIsAxisReversed)
2041 0 : : mPosition(0),
2042 : mAxis(aAxis),
2043 0 : mIsAxisReversed(aIsAxisReversed)
2044 0 : {}
2045 :
2046 : // Delete copy-constructor & reassignment operator, to prevent accidental
2047 : // (unnecessary) copying.
2048 : PositionTracker(const PositionTracker&) = delete;
2049 : PositionTracker& operator=(const PositionTracker&) = delete;
2050 :
2051 : // Member data:
2052 : nscoord mPosition; // The position we're tracking
2053 : // XXXdholbert [BEGIN DEPRECATED]
2054 : const AxisOrientationType mAxis; // The axis along which we're moving.
2055 : // XXXdholbert [END DEPRECATED]
2056 : const bool mIsAxisReversed; // Is the axis along which we're moving reversed
2057 : // (e.g. LTR vs RTL) with respect to the
2058 : // corresponding axis on the flex container's WM?
2059 : };
2060 :
2061 : // Tracks our position in the main axis, when we're laying out flex items.
2062 : // The "0" position represents the main-start edge of the flex container's
2063 : // content-box.
2064 : class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker {
2065 : public:
2066 : MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
2067 : const FlexLine* aLine,
2068 : uint8_t aJustifyContent,
2069 : nscoord aContentBoxMainSize);
2070 :
2071 0 : ~MainAxisPositionTracker() {
2072 0 : MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
2073 : "miscounted the number of packing spaces");
2074 0 : MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
2075 : "miscounted the number of auto margins");
2076 0 : }
2077 :
2078 : // Advances past the packing space (if any) between two flex items
2079 : void TraversePackingSpace();
2080 :
2081 : // If aItem has any 'auto' margins in the main axis, this method updates the
2082 : // corresponding values in its margin.
2083 : void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
2084 :
2085 : private:
2086 : nscoord mPackingSpaceRemaining;
2087 : uint32_t mNumAutoMarginsInMainAxis;
2088 : uint32_t mNumPackingSpacesRemaining;
2089 : // XXX this should be uint16_t when we add explicit fallback handling
2090 : uint8_t mJustifyContent;
2091 : };
2092 :
2093 : // Utility class for managing our position along the cross axis along
2094 : // the whole flex container (at a higher level than a single line).
2095 : // The "0" position represents the cross-start edge of the flex container's
2096 : // content-box.
2097 : class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
2098 : public:
2099 : CrossAxisPositionTracker(FlexLine* aFirstLine,
2100 : const ReflowInput& aReflowInput,
2101 : nscoord aContentBoxCrossSize,
2102 : bool aIsCrossSizeDefinite,
2103 : const FlexboxAxisTracker& aAxisTracker);
2104 :
2105 : // Advances past the packing space (if any) between two flex lines
2106 : void TraversePackingSpace();
2107 :
2108 : // Advances past the given FlexLine
2109 0 : void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
2110 :
2111 : private:
2112 : // Redeclare the frame-related methods from PositionTracker as private with
2113 : // = delete, to be sure (at compile time) that no client code can invoke
2114 : // them. (Unlike the other PositionTracker derived classes, this class here
2115 : // deals with FlexLines, not with individual FlexItems or frames.)
2116 : void EnterMargin(const nsMargin& aMargin) = delete;
2117 : void ExitMargin(const nsMargin& aMargin) = delete;
2118 : void EnterChildFrame(nscoord aChildFrameSize) = delete;
2119 : void ExitChildFrame(nscoord aChildFrameSize) = delete;
2120 :
2121 : nscoord mPackingSpaceRemaining;
2122 : uint32_t mNumPackingSpacesRemaining;
2123 : // XXX this should be uint16_t when we add explicit fallback handling
2124 : uint8_t mAlignContent;
2125 : };
2126 :
2127 : // Utility class for managing our position along the cross axis, *within* a
2128 : // single flex line.
2129 : class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
2130 : public:
2131 : explicit SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
2132 :
2133 : void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
2134 : FlexItem& aItem);
2135 :
2136 : void EnterAlignPackingSpace(const FlexLine& aLine,
2137 : const FlexItem& aItem,
2138 : const FlexboxAxisTracker& aAxisTracker);
2139 :
2140 : // Resets our position to the cross-start edge of this line.
2141 0 : inline void ResetPosition() { mPosition = 0; }
2142 : };
2143 :
2144 : //----------------------------------------------------------------------
2145 :
2146 : // Frame class boilerplate
2147 : // =======================
2148 :
2149 0 : NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
2150 0 : NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
2151 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
2152 :
2153 0 : NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
2154 :
2155 : nsContainerFrame*
2156 0 : NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
2157 : nsStyleContext* aContext)
2158 : {
2159 0 : return new (aPresShell) nsFlexContainerFrame(aContext);
2160 : }
2161 :
2162 : //----------------------------------------------------------------------
2163 :
2164 : // nsFlexContainerFrame Method Implementations
2165 : // ===========================================
2166 :
2167 : /* virtual */
2168 0 : nsFlexContainerFrame::~nsFlexContainerFrame()
2169 : {
2170 0 : }
2171 :
2172 : /* virtual */
2173 : void
2174 0 : nsFlexContainerFrame::Init(nsIContent* aContent,
2175 : nsContainerFrame* aParent,
2176 : nsIFrame* aPrevInFlow)
2177 : {
2178 0 : nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
2179 :
2180 0 : const nsStyleDisplay* styleDisp = StyleContext()->StyleDisplay();
2181 :
2182 : // Figure out if we should set a frame state bit to indicate that this frame
2183 : // represents a legacy -webkit-{inline-}box container.
2184 : // First, the trivial case: just check "display" directly.
2185 0 : bool isLegacyBox = IsDisplayValueLegacyBox(styleDisp);
2186 :
2187 : // If this frame is for a scrollable element, then it will actually have
2188 : // "display:block", and its *parent frame* will have the real
2189 : // flex-flavored display value. So in that case, check the parent frame to
2190 : // find out if we're legacy.
2191 0 : if (!isLegacyBox && styleDisp->mDisplay == mozilla::StyleDisplay::Block) {
2192 0 : nsStyleContext* parentStyleContext = GetParent()->StyleContext();
2193 0 : NS_ASSERTION(parentStyleContext &&
2194 : (mStyleContext->GetPseudo() == nsCSSAnonBoxes::buttonContent ||
2195 : mStyleContext->GetPseudo() == nsCSSAnonBoxes::scrolledContent),
2196 : "The only way a nsFlexContainerFrame can have 'display:block' "
2197 : "should be if it's the inner part of a scrollable or button "
2198 : "element");
2199 0 : isLegacyBox = IsDisplayValueLegacyBox(parentStyleContext->StyleDisplay());
2200 : }
2201 :
2202 0 : if (isLegacyBox) {
2203 0 : AddStateBits(NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX);
2204 : }
2205 0 : }
2206 :
2207 : #ifdef DEBUG_FRAME_DUMP
2208 : nsresult
2209 0 : nsFlexContainerFrame::GetFrameName(nsAString& aResult) const
2210 : {
2211 0 : return MakeFrameName(NS_LITERAL_STRING("FlexContainer"), aResult);
2212 : }
2213 : #endif
2214 :
2215 : nscoord
2216 0 : nsFlexContainerFrame::GetLogicalBaseline(mozilla::WritingMode aWM) const
2217 : {
2218 0 : NS_ASSERTION(mBaselineFromLastReflow != NS_INTRINSIC_WIDTH_UNKNOWN,
2219 : "baseline has not been set");
2220 :
2221 0 : if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
2222 : // Return a baseline synthesized from our margin-box.
2223 0 : return nsContainerFrame::GetLogicalBaseline(aWM);
2224 : }
2225 0 : return mBaselineFromLastReflow;
2226 : }
2227 :
2228 : // Helper for BuildDisplayList, to implement this special-case for flex items
2229 : // from the spec:
2230 : // Flex items paint exactly the same as block-level elements in the
2231 : // normal flow, except that 'z-index' values other than 'auto' create
2232 : // a stacking context even if 'position' is 'static'.
2233 : // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#painting
2234 : uint32_t
2235 0 : GetDisplayFlagsForFlexItem(nsIFrame* aFrame)
2236 : {
2237 0 : MOZ_ASSERT(aFrame->IsFlexItem(), "Should only be called on flex items");
2238 :
2239 0 : const nsStylePosition* pos = aFrame->StylePosition();
2240 0 : if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
2241 0 : return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
2242 : }
2243 0 : return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
2244 : }
2245 :
2246 : void
2247 0 : nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
2248 : const nsRect& aDirtyRect,
2249 : const nsDisplayListSet& aLists)
2250 : {
2251 0 : DisplayBorderBackgroundOutline(aBuilder, aLists);
2252 :
2253 : // Our children are all block-level, so their borders/backgrounds all go on
2254 : // the BlockBorderBackgrounds list.
2255 0 : nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
2256 :
2257 : typedef CSSOrderAwareFrameIterator::OrderState OrderState;
2258 : OrderState orderState =
2259 0 : HasAnyStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
2260 0 : ? OrderState::eKnownOrdered
2261 0 : : OrderState::eKnownUnordered;
2262 :
2263 : CSSOrderAwareFrameIterator iter(this, kPrincipalList,
2264 : CSSOrderAwareFrameIterator::eIncludeAll,
2265 : orderState,
2266 0 : OrderingPropertyForIter(this));
2267 0 : for (; !iter.AtEnd(); iter.Next()) {
2268 0 : nsIFrame* childFrame = *iter;
2269 0 : BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, childLists,
2270 0 : GetDisplayFlagsForFlexItem(childFrame));
2271 : }
2272 0 : }
2273 :
2274 : void
2275 0 : FlexLine::FreezeItemsEarly(bool aIsUsingFlexGrow)
2276 : {
2277 : // After we've established the type of flexing we're doing (growing vs.
2278 : // shrinking), and before we try to flex any items, we freeze items that
2279 : // obviously *can't* flex.
2280 : //
2281 : // Quoting the spec:
2282 : // # Freeze, setting its target main size to its hypothetical main size...
2283 : // # - any item that has a flex factor of zero
2284 : // # - if using the flex grow factor: any item that has a flex base size
2285 : // # greater than its hypothetical main size
2286 : // # - if using the flex shrink factor: any item that has a flex base size
2287 : // # smaller than its hypothetical main size
2288 : // http://dev.w3.org/csswg/css-flexbox/#resolve-flexible-lengths-flex-factors
2289 : //
2290 : // (NOTE: At this point, item->GetMainSize() *is* the item's hypothetical
2291 : // main size, since SetFlexBaseSizeAndMainSize() sets it up that way, and the
2292 : // item hasn't had a chance to flex away from that yet.)
2293 :
2294 : // Since this loop only operates on unfrozen flex items, we can break as
2295 : // soon as we have seen all of them.
2296 0 : uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
2297 0 : for (FlexItem* item = mItems.getFirst();
2298 0 : numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
2299 0 : MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
2300 :
2301 0 : if (!item->IsFrozen()) {
2302 0 : numUnfrozenItemsToBeSeen--;
2303 0 : bool shouldFreeze = (0.0f == item->GetFlexFactor(aIsUsingFlexGrow));
2304 0 : if (!shouldFreeze) {
2305 0 : if (aIsUsingFlexGrow) {
2306 0 : if (item->GetFlexBaseSize() > item->GetMainSize()) {
2307 0 : shouldFreeze = true;
2308 : }
2309 : } else { // using flex-shrink
2310 0 : if (item->GetFlexBaseSize() < item->GetMainSize()) {
2311 0 : shouldFreeze = true;
2312 : }
2313 : }
2314 : }
2315 0 : if (shouldFreeze) {
2316 : // Freeze item! (at its hypothetical main size)
2317 0 : item->Freeze();
2318 0 : mNumFrozenItems++;
2319 : }
2320 : }
2321 : }
2322 0 : }
2323 :
2324 : // Based on the sign of aTotalViolation, this function freezes a subset of our
2325 : // flexible sizes, and restores the remaining ones to their initial pref sizes.
2326 : void
2327 0 : FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
2328 : bool aIsFinalIteration)
2329 : {
2330 : enum FreezeType {
2331 : eFreezeEverything,
2332 : eFreezeMinViolations,
2333 : eFreezeMaxViolations
2334 : };
2335 :
2336 : FreezeType freezeType;
2337 0 : if (aTotalViolation == 0) {
2338 0 : freezeType = eFreezeEverything;
2339 0 : } else if (aTotalViolation > 0) {
2340 0 : freezeType = eFreezeMinViolations;
2341 : } else { // aTotalViolation < 0
2342 0 : freezeType = eFreezeMaxViolations;
2343 : }
2344 :
2345 : // Since this loop only operates on unfrozen flex items, we can break as
2346 : // soon as we have seen all of them.
2347 0 : uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
2348 0 : for (FlexItem* item = mItems.getFirst();
2349 0 : numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
2350 0 : MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
2351 0 : if (!item->IsFrozen()) {
2352 0 : numUnfrozenItemsToBeSeen--;
2353 :
2354 0 : MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(),
2355 : "Can have either min or max violation, but not both");
2356 :
2357 0 : if (eFreezeEverything == freezeType ||
2358 0 : (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
2359 0 : (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
2360 :
2361 0 : MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(),
2362 : "Freezing item at a size below its minimum");
2363 0 : MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(),
2364 : "Freezing item at a size above its maximum");
2365 :
2366 0 : item->Freeze();
2367 0 : mNumFrozenItems++;
2368 0 : } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
2369 : // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
2370 : // assertion to be fatal except in documents with enormous lengths.
2371 0 : NS_ERROR("Final iteration still has unfrozen items, this shouldn't"
2372 : " happen unless there was nscoord under/overflow.");
2373 0 : item->Freeze();
2374 0 : mNumFrozenItems++;
2375 : } // else, we'll reset this item's main size to its flex base size on the
2376 : // next iteration of this algorithm.
2377 :
2378 : // Clear this item's violation(s), now that we've dealt with them
2379 0 : item->ClearViolationFlags();
2380 : }
2381 : }
2382 0 : }
2383 :
2384 : void
2385 0 : FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
2386 : {
2387 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug, ("ResolveFlexibleLengths\n"));
2388 :
2389 : // Determine whether we're going to be growing or shrinking items.
2390 : const bool isUsingFlexGrow =
2391 0 : (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
2392 :
2393 : // Do an "early freeze" for flex items that obviously can't flex in the
2394 : // direction we've chosen:
2395 0 : FreezeItemsEarly(isUsingFlexGrow);
2396 :
2397 0 : if (mNumFrozenItems == mNumItems) {
2398 : // All our items are frozen, so we have no flexible lengths to resolve.
2399 0 : return;
2400 : }
2401 0 : MOZ_ASSERT(!IsEmpty(), "empty lines should take the early-return above");
2402 :
2403 : // Subtract space occupied by our items' margins/borders/padding, so we can
2404 : // just be dealing with the space available for our flex items' content
2405 : // boxes.
2406 : nscoord spaceReservedForMarginBorderPadding =
2407 0 : mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize;
2408 :
2409 : nscoord spaceAvailableForFlexItemsContentBoxes =
2410 0 : aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
2411 :
2412 : nscoord origAvailableFreeSpace;
2413 0 : bool isOrigAvailFreeSpaceInitialized = false;
2414 :
2415 : // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
2416 : // run the loop at MOST mNumItems times. This claim should hold up
2417 : // because we'll freeze at least one item on each loop iteration, and once
2418 : // we've run out of items to freeze, there's nothing left to do. However,
2419 : // in most cases, we'll break out of this loop long before we hit that many
2420 : // iterations.
2421 0 : for (uint32_t iterationCounter = 0;
2422 0 : iterationCounter < mNumItems; iterationCounter++) {
2423 : // Set every not-yet-frozen item's used main size to its
2424 : // flex base size, and subtract all the used main sizes from our
2425 : // total amount of space to determine the 'available free space'
2426 : // (positive or negative) to be distributed among our flexible items.
2427 0 : nscoord availableFreeSpace = spaceAvailableForFlexItemsContentBoxes;
2428 0 : for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
2429 0 : if (!item->IsFrozen()) {
2430 0 : item->SetMainSize(item->GetFlexBaseSize());
2431 : }
2432 0 : availableFreeSpace -= item->GetMainSize();
2433 : }
2434 :
2435 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
2436 : (" available free space = %d\n", availableFreeSpace));
2437 :
2438 :
2439 : // The sign of our free space should agree with the type of flexing
2440 : // (grow/shrink) that we're doing (except if we've had integer overflow;
2441 : // then, all bets are off). Any disagreement should've made us use the
2442 : // other type of flexing, or should've been resolved in FreezeItemsEarly.
2443 : // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
2444 : // assertion to be fatal except in documents with enormous lengths.
2445 0 : NS_ASSERTION((isUsingFlexGrow && availableFreeSpace >= 0) ||
2446 : (!isUsingFlexGrow && availableFreeSpace <= 0),
2447 : "availableFreeSpace's sign should match isUsingFlexGrow");
2448 :
2449 : // If we have any free space available, give each flexible item a portion
2450 : // of availableFreeSpace.
2451 0 : if (availableFreeSpace != 0) {
2452 : // The first time we do this, we initialize origAvailableFreeSpace.
2453 0 : if (!isOrigAvailFreeSpaceInitialized) {
2454 0 : origAvailableFreeSpace = availableFreeSpace;
2455 0 : isOrigAvailFreeSpaceInitialized = true;
2456 : }
2457 :
2458 : // STRATEGY: On each item, we compute & store its "share" of the total
2459 : // weight that we've seen so far:
2460 : // curWeight / weightSum
2461 : //
2462 : // Then, when we go to actually distribute the space (in the next loop),
2463 : // we can simply walk backwards through the elements and give each item
2464 : // its "share" multiplied by the remaining available space.
2465 : //
2466 : // SPECIAL CASE: If the sum of the weights is larger than the
2467 : // maximum representable float (overflowing to infinity), then we can't
2468 : // sensibly divide out proportional shares anymore. In that case, we
2469 : // simply treat the flex item(s) with the largest weights as if
2470 : // their weights were infinite (dwarfing all the others), and we
2471 : // distribute all of the available space among them.
2472 0 : float weightSum = 0.0f;
2473 0 : float flexFactorSum = 0.0f;
2474 0 : float largestWeight = 0.0f;
2475 0 : uint32_t numItemsWithLargestWeight = 0;
2476 :
2477 : // Since this loop only operates on unfrozen flex items, we can break as
2478 : // soon as we have seen all of them.
2479 0 : uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
2480 0 : for (FlexItem* item = mItems.getFirst();
2481 0 : numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
2482 0 : MOZ_ASSERT(item,
2483 : "numUnfrozenItemsToBeSeen says items remain to be seen");
2484 0 : if (!item->IsFrozen()) {
2485 0 : numUnfrozenItemsToBeSeen--;
2486 :
2487 0 : float curWeight = item->GetWeight(isUsingFlexGrow);
2488 0 : float curFlexFactor = item->GetFlexFactor(isUsingFlexGrow);
2489 0 : MOZ_ASSERT(curWeight >= 0.0f, "weights are non-negative");
2490 0 : MOZ_ASSERT(curFlexFactor >= 0.0f, "flex factors are non-negative");
2491 :
2492 0 : weightSum += curWeight;
2493 0 : flexFactorSum += curFlexFactor;
2494 :
2495 0 : if (IsFinite(weightSum)) {
2496 0 : if (curWeight == 0.0f) {
2497 0 : item->SetShareOfWeightSoFar(0.0f);
2498 : } else {
2499 0 : item->SetShareOfWeightSoFar(curWeight / weightSum);
2500 : }
2501 : } // else, the sum of weights overflows to infinity, in which
2502 : // case we don't bother with "SetShareOfWeightSoFar" since
2503 : // we know we won't use it. (instead, we'll just give every
2504 : // item with the largest weight an equal share of space.)
2505 :
2506 : // Update our largest-weight tracking vars
2507 0 : if (curWeight > largestWeight) {
2508 0 : largestWeight = curWeight;
2509 0 : numItemsWithLargestWeight = 1;
2510 0 : } else if (curWeight == largestWeight) {
2511 0 : numItemsWithLargestWeight++;
2512 : }
2513 : }
2514 : }
2515 :
2516 0 : if (weightSum != 0.0f) {
2517 0 : MOZ_ASSERT(flexFactorSum != 0.0f,
2518 : "flex factor sum can't be 0, if a weighted sum "
2519 : "of its components (weightSum) is nonzero");
2520 0 : if (flexFactorSum < 1.0f) {
2521 : // Our unfrozen flex items don't want all of the original free space!
2522 : // (Their flex factors add up to something less than 1.)
2523 : // Hence, make sure we don't distribute any more than the portion of
2524 : // our original free space that these items actually want.
2525 : nscoord totalDesiredPortionOfOrigFreeSpace =
2526 0 : NSToCoordRound(origAvailableFreeSpace * flexFactorSum);
2527 :
2528 : // Clamp availableFreeSpace to be no larger than that ^^.
2529 : // (using min or max, depending on sign).
2530 : // This should not change the sign of availableFreeSpace (except
2531 : // possibly by setting it to 0), as enforced by this assertion:
2532 0 : MOZ_ASSERT(totalDesiredPortionOfOrigFreeSpace == 0 ||
2533 : ((totalDesiredPortionOfOrigFreeSpace > 0) ==
2534 : (availableFreeSpace > 0)),
2535 : "When we reduce available free space for flex factors < 1,"
2536 : "we shouldn't change the sign of the free space...");
2537 :
2538 0 : if (availableFreeSpace > 0) {
2539 0 : availableFreeSpace = std::min(availableFreeSpace,
2540 0 : totalDesiredPortionOfOrigFreeSpace);
2541 : } else {
2542 0 : availableFreeSpace = std::max(availableFreeSpace,
2543 0 : totalDesiredPortionOfOrigFreeSpace);
2544 : }
2545 : }
2546 :
2547 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
2548 : (" Distributing available space:"));
2549 : // Since this loop only operates on unfrozen flex items, we can break as
2550 : // soon as we have seen all of them.
2551 0 : numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
2552 :
2553 : // NOTE: It's important that we traverse our items in *reverse* order
2554 : // here, for correct width distribution according to the items'
2555 : // "ShareOfWeightSoFar" progressively-calculated values.
2556 0 : for (FlexItem* item = mItems.getLast();
2557 0 : numUnfrozenItemsToBeSeen > 0; item = item->getPrevious()) {
2558 0 : MOZ_ASSERT(item,
2559 : "numUnfrozenItemsToBeSeen says items remain to be seen");
2560 0 : if (!item->IsFrozen()) {
2561 0 : numUnfrozenItemsToBeSeen--;
2562 :
2563 : // To avoid rounding issues, we compute the change in size for this
2564 : // item, and then subtract it from the remaining available space.
2565 0 : nscoord sizeDelta = 0;
2566 0 : if (IsFinite(weightSum)) {
2567 : float myShareOfRemainingSpace =
2568 0 : item->GetShareOfWeightSoFar();
2569 :
2570 0 : MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f &&
2571 : myShareOfRemainingSpace <= 1.0f,
2572 : "my share should be nonnegative fractional amount");
2573 :
2574 0 : if (myShareOfRemainingSpace == 1.0f) {
2575 : // (We special-case 1.0f to avoid float error from converting
2576 : // availableFreeSpace from integer*1.0f --> float --> integer)
2577 0 : sizeDelta = availableFreeSpace;
2578 0 : } else if (myShareOfRemainingSpace > 0.0f) {
2579 0 : sizeDelta = NSToCoordRound(availableFreeSpace *
2580 0 : myShareOfRemainingSpace);
2581 : }
2582 0 : } else if (item->GetWeight(isUsingFlexGrow) == largestWeight) {
2583 : // Total flexibility is infinite, so we're just distributing
2584 : // the available space equally among the items that are tied for
2585 : // having the largest weight (and this is one of those items).
2586 : sizeDelta =
2587 0 : NSToCoordRound(availableFreeSpace /
2588 0 : float(numItemsWithLargestWeight));
2589 0 : numItemsWithLargestWeight--;
2590 : }
2591 :
2592 0 : availableFreeSpace -= sizeDelta;
2593 :
2594 0 : item->SetMainSize(item->GetMainSize() + sizeDelta);
2595 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
2596 : (" child %p receives %d, for a total of %d\n",
2597 : item, sizeDelta, item->GetMainSize()));
2598 : }
2599 : }
2600 : }
2601 : }
2602 :
2603 : // Fix min/max violations:
2604 0 : nscoord totalViolation = 0; // keeps track of adjustments for min/max
2605 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
2606 : (" Checking for violations:"));
2607 :
2608 : // Since this loop only operates on unfrozen flex items, we can break as
2609 : // soon as we have seen all of them.
2610 0 : uint32_t numUnfrozenItemsToBeSeen = mNumItems - mNumFrozenItems;
2611 0 : for (FlexItem* item = mItems.getFirst();
2612 0 : numUnfrozenItemsToBeSeen > 0; item = item->getNext()) {
2613 0 : MOZ_ASSERT(item, "numUnfrozenItemsToBeSeen says items remain to be seen");
2614 0 : if (!item->IsFrozen()) {
2615 0 : numUnfrozenItemsToBeSeen--;
2616 :
2617 0 : if (item->GetMainSize() < item->GetMainMinSize()) {
2618 : // min violation
2619 0 : totalViolation += item->GetMainMinSize() - item->GetMainSize();
2620 0 : item->SetMainSize(item->GetMainMinSize());
2621 0 : item->SetHadMinViolation();
2622 0 : } else if (item->GetMainSize() > item->GetMainMaxSize()) {
2623 : // max violation
2624 0 : totalViolation += item->GetMainMaxSize() - item->GetMainSize();
2625 0 : item->SetMainSize(item->GetMainMaxSize());
2626 0 : item->SetHadMaxViolation();
2627 : }
2628 : }
2629 : }
2630 :
2631 0 : FreezeOrRestoreEachFlexibleSize(totalViolation,
2632 0 : iterationCounter + 1 == mNumItems);
2633 :
2634 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
2635 : (" Total violation: %d\n", totalViolation));
2636 :
2637 0 : if (mNumFrozenItems == mNumItems) {
2638 0 : break;
2639 : }
2640 :
2641 0 : MOZ_ASSERT(totalViolation != 0,
2642 : "Zero violation should've made us freeze all items & break");
2643 : }
2644 :
2645 : #ifdef DEBUG
2646 : // Post-condition: all items should've been frozen.
2647 : // Make sure the counts match:
2648 0 : MOZ_ASSERT(mNumFrozenItems == mNumItems, "All items should be frozen");
2649 :
2650 : // For good measure, check each item directly, in case our counts are busted:
2651 0 : for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
2652 0 : MOZ_ASSERT(item->IsFrozen(), "All items should be frozen");
2653 : }
2654 : #endif // DEBUG
2655 : }
2656 :
2657 0 : MainAxisPositionTracker::
2658 : MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
2659 : const FlexLine* aLine,
2660 : uint8_t aJustifyContent,
2661 0 : nscoord aContentBoxMainSize)
2662 : : PositionTracker(aAxisTracker.GetMainAxis(),
2663 0 : aAxisTracker.IsMainAxisReversed()),
2664 : mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below
2665 : mNumAutoMarginsInMainAxis(0),
2666 : mNumPackingSpacesRemaining(0),
2667 0 : mJustifyContent(aJustifyContent)
2668 : {
2669 : // 'normal' behaves as 'stretch', and 'stretch' behaves as 'flex-start',
2670 : // in the main axis
2671 : // https://drafts.csswg.org/css-align-3/#propdef-justify-content
2672 0 : if (mJustifyContent == NS_STYLE_JUSTIFY_NORMAL ||
2673 0 : mJustifyContent == NS_STYLE_JUSTIFY_STRETCH) {
2674 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
2675 : }
2676 :
2677 : // XXX strip off the <overflow-position> bit until we implement that
2678 0 : mJustifyContent &= ~NS_STYLE_JUSTIFY_FLAG_BITS;
2679 :
2680 : // mPackingSpaceRemaining is initialized to the container's main size. Now
2681 : // we'll subtract out the main sizes of our flex items, so that it ends up
2682 : // with the *actual* amount of packing space.
2683 0 : for (const FlexItem* item = aLine->GetFirstItem(); item;
2684 0 : item = item->getNext()) {
2685 0 : mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis);
2686 0 : mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
2687 : }
2688 :
2689 0 : if (mPackingSpaceRemaining <= 0) {
2690 : // No available packing space to use for resolving auto margins.
2691 0 : mNumAutoMarginsInMainAxis = 0;
2692 : }
2693 :
2694 : // If packing space is negative, 'space-between' falls back to 'flex-start',
2695 : // and 'space-around' & 'space-evenly' fall back to 'center'. In those cases,
2696 : // it's simplest to just pretend we have a different 'justify-content' value
2697 : // and share code.
2698 0 : if (mPackingSpaceRemaining < 0) {
2699 0 : if (mJustifyContent == NS_STYLE_JUSTIFY_SPACE_BETWEEN) {
2700 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
2701 0 : } else if (mJustifyContent == NS_STYLE_JUSTIFY_SPACE_AROUND ||
2702 0 : mJustifyContent == NS_STYLE_JUSTIFY_SPACE_EVENLY) {
2703 0 : mJustifyContent = NS_STYLE_JUSTIFY_CENTER;
2704 : }
2705 : }
2706 :
2707 : // Map 'left'/'right' to 'start'/'end'
2708 0 : if (mJustifyContent == NS_STYLE_JUSTIFY_LEFT ||
2709 0 : mJustifyContent == NS_STYLE_JUSTIFY_RIGHT) {
2710 0 : if (aAxisTracker.IsColumnOriented()) {
2711 : // Container's alignment axis is not parallel to the inline axis,
2712 : // so we map both 'left' and 'right' to 'start'.
2713 0 : mJustifyContent = NS_STYLE_JUSTIFY_START;
2714 : } else {
2715 : // Row-oriented, so we map 'left' and 'right' to 'start' or 'end',
2716 : // depending on left-to-right writing mode.
2717 0 : const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
2718 0 : const bool isJustifyLeft = (mJustifyContent == NS_STYLE_JUSTIFY_LEFT);
2719 0 : mJustifyContent = (isJustifyLeft == isLTR) ? NS_STYLE_JUSTIFY_START
2720 0 : : NS_STYLE_JUSTIFY_END;
2721 : }
2722 : }
2723 :
2724 : // Map 'start'/'end' to 'flex-start'/'flex-end'.
2725 0 : if (mJustifyContent == NS_STYLE_JUSTIFY_START) {
2726 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
2727 0 : } else if (mJustifyContent == NS_STYLE_JUSTIFY_END) {
2728 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_END;
2729 : }
2730 :
2731 : // If our main axis is (internally) reversed, swap the justify-content
2732 : // "flex-start" and "flex-end" behaviors:
2733 0 : if (aAxisTracker.AreAxesInternallyReversed()) {
2734 0 : if (mJustifyContent == NS_STYLE_JUSTIFY_FLEX_START) {
2735 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_END;
2736 0 : } else if (mJustifyContent == NS_STYLE_JUSTIFY_FLEX_END) {
2737 0 : mJustifyContent = NS_STYLE_JUSTIFY_FLEX_START;
2738 : }
2739 : }
2740 :
2741 : // Figure out how much space we'll set aside for auto margins or
2742 : // packing spaces, and advance past any leading packing-space.
2743 0 : if (mNumAutoMarginsInMainAxis == 0 &&
2744 0 : mPackingSpaceRemaining != 0 &&
2745 0 : !aLine->IsEmpty()) {
2746 0 : switch (mJustifyContent) {
2747 : case NS_STYLE_JUSTIFY_BASELINE:
2748 : case NS_STYLE_JUSTIFY_LAST_BASELINE:
2749 0 : NS_WARNING("NYI: justify-content:left/right/baseline/last baseline");
2750 : MOZ_FALLTHROUGH;
2751 : case NS_STYLE_JUSTIFY_FLEX_START:
2752 : // All packing space should go at the end --> nothing to do here.
2753 0 : break;
2754 : case NS_STYLE_JUSTIFY_FLEX_END:
2755 : // All packing space goes at the beginning
2756 0 : mPosition += mPackingSpaceRemaining;
2757 0 : break;
2758 : case NS_STYLE_JUSTIFY_CENTER:
2759 : // Half the packing space goes at the beginning
2760 0 : mPosition += mPackingSpaceRemaining / 2;
2761 0 : break;
2762 : case NS_STYLE_JUSTIFY_SPACE_BETWEEN:
2763 : case NS_STYLE_JUSTIFY_SPACE_AROUND:
2764 : case NS_STYLE_JUSTIFY_SPACE_EVENLY:
2765 0 : nsFlexContainerFrame::CalculatePackingSpace(aLine->NumItems(),
2766 0 : mJustifyContent,
2767 : &mPosition,
2768 : &mNumPackingSpacesRemaining,
2769 0 : &mPackingSpaceRemaining);
2770 0 : break;
2771 : default:
2772 0 : MOZ_ASSERT_UNREACHABLE("Unexpected justify-content value");
2773 : }
2774 : }
2775 :
2776 0 : MOZ_ASSERT(mNumPackingSpacesRemaining == 0 ||
2777 : mNumAutoMarginsInMainAxis == 0,
2778 : "extra space should either go to packing space or to "
2779 : "auto margins, but not to both");
2780 0 : }
2781 :
2782 : void
2783 0 : MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem)
2784 : {
2785 0 : if (mNumAutoMarginsInMainAxis) {
2786 0 : const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
2787 0 : for (uint32_t i = 0; i < eNumAxisEdges; i++) {
2788 0 : mozilla::Side side = kAxisOrientationToSidesMap[mAxis][i];
2789 0 : if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
2790 : // NOTE: This integer math will skew the distribution of remainder
2791 : // app-units towards the end, which is fine.
2792 : nscoord curAutoMarginSize =
2793 0 : mPackingSpaceRemaining / mNumAutoMarginsInMainAxis;
2794 :
2795 0 : MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
2796 : "Expecting auto margins to have value '0' before we "
2797 : "resolve them");
2798 0 : aItem.SetMarginComponentForSide(side, curAutoMarginSize);
2799 :
2800 0 : mNumAutoMarginsInMainAxis--;
2801 0 : mPackingSpaceRemaining -= curAutoMarginSize;
2802 : }
2803 : }
2804 : }
2805 0 : }
2806 :
2807 : void
2808 0 : MainAxisPositionTracker::TraversePackingSpace()
2809 : {
2810 0 : if (mNumPackingSpacesRemaining) {
2811 0 : MOZ_ASSERT(mJustifyContent == NS_STYLE_JUSTIFY_SPACE_BETWEEN ||
2812 : mJustifyContent == NS_STYLE_JUSTIFY_SPACE_AROUND ||
2813 : mJustifyContent == NS_STYLE_JUSTIFY_SPACE_EVENLY,
2814 : "mNumPackingSpacesRemaining only applies for "
2815 : "space-between/space-around/space-evenly");
2816 :
2817 0 : MOZ_ASSERT(mPackingSpaceRemaining >= 0,
2818 : "ran out of packing space earlier than we expected");
2819 :
2820 : // NOTE: This integer math will skew the distribution of remainder
2821 : // app-units towards the end, which is fine.
2822 : nscoord curPackingSpace =
2823 0 : mPackingSpaceRemaining / mNumPackingSpacesRemaining;
2824 :
2825 0 : mPosition += curPackingSpace;
2826 0 : mNumPackingSpacesRemaining--;
2827 0 : mPackingSpaceRemaining -= curPackingSpace;
2828 : }
2829 0 : }
2830 :
2831 0 : CrossAxisPositionTracker::
2832 : CrossAxisPositionTracker(FlexLine* aFirstLine,
2833 : const ReflowInput& aReflowInput,
2834 : nscoord aContentBoxCrossSize,
2835 : bool aIsCrossSizeDefinite,
2836 0 : const FlexboxAxisTracker& aAxisTracker)
2837 : : PositionTracker(aAxisTracker.GetCrossAxis(),
2838 0 : aAxisTracker.IsCrossAxisReversed()),
2839 : mPackingSpaceRemaining(0),
2840 : mNumPackingSpacesRemaining(0),
2841 0 : mAlignContent(aReflowInput.mStylePosition->mAlignContent)
2842 : {
2843 0 : MOZ_ASSERT(aFirstLine, "null first line pointer");
2844 :
2845 : // 'normal' behaves as 'stretch'
2846 0 : if (mAlignContent == NS_STYLE_ALIGN_NORMAL) {
2847 0 : mAlignContent = NS_STYLE_ALIGN_STRETCH;
2848 : }
2849 :
2850 : // XXX strip of the <overflow-position> bit until we implement that
2851 0 : mAlignContent &= ~NS_STYLE_ALIGN_FLAG_BITS;
2852 :
2853 : const bool isSingleLine =
2854 0 : NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
2855 0 : if (isSingleLine) {
2856 0 : MOZ_ASSERT(!aFirstLine->getNext(),
2857 : "If we're styled as single-line, we should only have 1 line");
2858 : // "If the flex container is single-line and has a definite cross size, the
2859 : // cross size of the flex line is the flex container's inner cross size."
2860 : //
2861 : // SOURCE: https://drafts.csswg.org/css-flexbox/#algo-cross-line
2862 : // NOTE: This means (by definition) that there's no packing space, which
2863 : // means we don't need to be concerned with "align-conent" at all and we
2864 : // can return early. This is handy, because this is the usual case (for
2865 : // single-line flexbox).
2866 0 : if (aIsCrossSizeDefinite) {
2867 0 : aFirstLine->SetLineCrossSize(aContentBoxCrossSize);
2868 0 : return;
2869 : }
2870 :
2871 : // "If the flex container is single-line, then clamp the line's
2872 : // cross-size to be within the container's computed min and max cross-size
2873 : // properties."
2874 0 : aFirstLine->SetLineCrossSize(NS_CSS_MINMAX(aFirstLine->GetLineCrossSize(),
2875 : aReflowInput.ComputedMinBSize(),
2876 0 : aReflowInput.ComputedMaxBSize()));
2877 : }
2878 :
2879 : // NOTE: The rest of this function should essentially match
2880 : // MainAxisPositionTracker's constructor, though with FlexLines instead of
2881 : // FlexItems, and with the additional value "stretch" (and of course with
2882 : // cross sizes instead of main sizes.)
2883 :
2884 : // Figure out how much packing space we have (container's cross size minus
2885 : // all the lines' cross sizes). Also, share this loop to count how many
2886 : // lines we have. (We need that count in some cases below.)
2887 0 : mPackingSpaceRemaining = aContentBoxCrossSize;
2888 0 : uint32_t numLines = 0;
2889 0 : for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
2890 0 : mPackingSpaceRemaining -= line->GetLineCrossSize();
2891 0 : numLines++;
2892 : }
2893 :
2894 : // If packing space is negative, 'space-between' and 'stretch' behave like
2895 : // 'flex-start', and 'space-around' and 'space-evenly' behave like 'center'.
2896 : // In those cases, it's simplest to just pretend we have a different
2897 : // 'align-content' value and share code.
2898 0 : if (mPackingSpaceRemaining < 0) {
2899 0 : if (mAlignContent == NS_STYLE_ALIGN_SPACE_BETWEEN ||
2900 0 : mAlignContent == NS_STYLE_ALIGN_STRETCH) {
2901 0 : mAlignContent = NS_STYLE_ALIGN_FLEX_START;
2902 0 : } else if (mAlignContent == NS_STYLE_ALIGN_SPACE_AROUND ||
2903 0 : mAlignContent == NS_STYLE_ALIGN_SPACE_EVENLY) {
2904 0 : mAlignContent = NS_STYLE_ALIGN_CENTER;
2905 : }
2906 : }
2907 :
2908 : // Map 'left'/'right' to 'start'/'end'
2909 0 : if (mAlignContent == NS_STYLE_ALIGN_LEFT ||
2910 0 : mAlignContent == NS_STYLE_ALIGN_RIGHT) {
2911 0 : if (aAxisTracker.IsRowOriented()) {
2912 : // Container's alignment axis is not parallel to the inline axis,
2913 : // so we map both 'left' and 'right' to 'start'.
2914 0 : mAlignContent = NS_STYLE_ALIGN_START;
2915 : } else {
2916 : // Column-oriented, so we map 'left' and 'right' to 'start' or 'end',
2917 : // depending on left-to-right writing mode.
2918 0 : const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
2919 0 : const bool isAlignLeft = (mAlignContent == NS_STYLE_ALIGN_LEFT);
2920 0 : mAlignContent = (isAlignLeft == isLTR) ? NS_STYLE_ALIGN_START
2921 0 : : NS_STYLE_ALIGN_END;
2922 : }
2923 : }
2924 :
2925 : // Map 'start'/'end' to 'flex-start'/'flex-end'.
2926 0 : if (mAlignContent == NS_STYLE_ALIGN_START) {
2927 0 : mAlignContent = NS_STYLE_ALIGN_FLEX_START;
2928 0 : } else if (mAlignContent == NS_STYLE_ALIGN_END) {
2929 0 : mAlignContent = NS_STYLE_ALIGN_FLEX_END;
2930 : }
2931 :
2932 : // If our cross axis is (internally) reversed, swap the align-content
2933 : // "flex-start" and "flex-end" behaviors:
2934 0 : if (aAxisTracker.AreAxesInternallyReversed()) {
2935 0 : if (mAlignContent == NS_STYLE_ALIGN_FLEX_START) {
2936 0 : mAlignContent = NS_STYLE_ALIGN_FLEX_END;
2937 0 : } else if (mAlignContent == NS_STYLE_ALIGN_FLEX_END) {
2938 0 : mAlignContent = NS_STYLE_ALIGN_FLEX_START;
2939 : }
2940 : }
2941 :
2942 : // Figure out how much space we'll set aside for packing spaces, and advance
2943 : // past any leading packing-space.
2944 0 : if (mPackingSpaceRemaining != 0) {
2945 0 : switch (mAlignContent) {
2946 : case NS_STYLE_ALIGN_SELF_START:
2947 : case NS_STYLE_ALIGN_SELF_END:
2948 : case NS_STYLE_ALIGN_BASELINE:
2949 : case NS_STYLE_ALIGN_LAST_BASELINE:
2950 0 : NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end/baseline/last baseline");
2951 : MOZ_FALLTHROUGH;
2952 : case NS_STYLE_ALIGN_FLEX_START:
2953 : // All packing space should go at the end --> nothing to do here.
2954 0 : break;
2955 : case NS_STYLE_ALIGN_FLEX_END:
2956 : // All packing space goes at the beginning
2957 0 : mPosition += mPackingSpaceRemaining;
2958 0 : break;
2959 : case NS_STYLE_ALIGN_CENTER:
2960 : // Half the packing space goes at the beginning
2961 0 : mPosition += mPackingSpaceRemaining / 2;
2962 0 : break;
2963 : case NS_STYLE_ALIGN_SPACE_BETWEEN:
2964 : case NS_STYLE_ALIGN_SPACE_AROUND:
2965 : case NS_STYLE_ALIGN_SPACE_EVENLY:
2966 0 : nsFlexContainerFrame::CalculatePackingSpace(numLines,
2967 0 : mAlignContent,
2968 : &mPosition,
2969 : &mNumPackingSpacesRemaining,
2970 0 : &mPackingSpaceRemaining);
2971 0 : break;
2972 : case NS_STYLE_ALIGN_STRETCH: {
2973 : // Split space equally between the lines:
2974 0 : MOZ_ASSERT(mPackingSpaceRemaining > 0,
2975 : "negative packing space should make us use 'flex-start' "
2976 : "instead of 'stretch' (and we shouldn't bother with this "
2977 : "code if we have 0 packing space)");
2978 :
2979 0 : uint32_t numLinesLeft = numLines;
2980 0 : for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
2981 : // Our share is the amount of space remaining, divided by the number
2982 : // of lines remainig.
2983 0 : MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines");
2984 0 : nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft;
2985 0 : nscoord newSize = line->GetLineCrossSize() + shareOfExtraSpace;
2986 0 : line->SetLineCrossSize(newSize);
2987 :
2988 0 : mPackingSpaceRemaining -= shareOfExtraSpace;
2989 0 : numLinesLeft--;
2990 : }
2991 0 : MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines");
2992 0 : break;
2993 : }
2994 : default:
2995 0 : MOZ_ASSERT_UNREACHABLE("Unexpected align-content value");
2996 : }
2997 : }
2998 : }
2999 :
3000 : void
3001 0 : CrossAxisPositionTracker::TraversePackingSpace()
3002 : {
3003 0 : if (mNumPackingSpacesRemaining) {
3004 0 : MOZ_ASSERT(mAlignContent == NS_STYLE_ALIGN_SPACE_BETWEEN ||
3005 : mAlignContent == NS_STYLE_ALIGN_SPACE_AROUND ||
3006 : mAlignContent == NS_STYLE_ALIGN_SPACE_EVENLY,
3007 : "mNumPackingSpacesRemaining only applies for "
3008 : "space-between/space-around/space-evenly");
3009 :
3010 0 : MOZ_ASSERT(mPackingSpaceRemaining >= 0,
3011 : "ran out of packing space earlier than we expected");
3012 :
3013 : // NOTE: This integer math will skew the distribution of remainder
3014 : // app-units towards the end, which is fine.
3015 : nscoord curPackingSpace =
3016 0 : mPackingSpaceRemaining / mNumPackingSpacesRemaining;
3017 :
3018 0 : mPosition += curPackingSpace;
3019 0 : mNumPackingSpacesRemaining--;
3020 0 : mPackingSpaceRemaining -= curPackingSpace;
3021 : }
3022 0 : }
3023 :
3024 0 : SingleLineCrossAxisPositionTracker::
3025 0 : SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
3026 : : PositionTracker(aAxisTracker.GetCrossAxis(),
3027 0 : aAxisTracker.IsCrossAxisReversed())
3028 : {
3029 0 : }
3030 :
3031 : void
3032 0 : FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
3033 : {
3034 0 : nscoord crossStartToFurthestFirstBaseline = nscoord_MIN;
3035 0 : nscoord crossEndToFurthestFirstBaseline = nscoord_MIN;
3036 0 : nscoord crossStartToFurthestLastBaseline = nscoord_MIN;
3037 0 : nscoord crossEndToFurthestLastBaseline = nscoord_MIN;
3038 0 : nscoord largestOuterCrossSize = 0;
3039 0 : for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
3040 : nscoord curOuterCrossSize =
3041 0 : item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
3042 :
3043 0 : if ((item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE ||
3044 0 : item->GetAlignSelf() == NS_STYLE_ALIGN_LAST_BASELINE) &&
3045 0 : item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
3046 0 : const bool useFirst = (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE);
3047 : // FIXME: Once we support "writing-mode", we'll have to do baseline
3048 : // alignment in vertical flex containers here (w/ horizontal cross-axes).
3049 :
3050 : // Find distance from our item's cross-start and cross-end margin-box
3051 : // edges to its baseline.
3052 : //
3053 : // Here's a diagram of a flex-item that we might be doing this on.
3054 : // "mmm" is the margin-box, "bbb" is the border-box. The bottom of
3055 : // the text "BASE" is the baseline.
3056 : //
3057 : // ---(cross-start)---
3058 : // ___ ___ ___
3059 : // mmmmmmmmmmmm | |margin-start |
3060 : // m m | _|_ ___ |
3061 : // m bbbbbbbb m |curOuterCrossSize | |crossStartToBaseline
3062 : // m b b m | |ascent |
3063 : // m b BASE b m | _|_ _|_
3064 : // m b b m | |
3065 : // m bbbbbbbb m | |crossEndToBaseline
3066 : // m m | |
3067 : // mmmmmmmmmmmm _|_ _|_
3068 : //
3069 : // ---(cross-end)---
3070 : //
3071 : // We already have the curOuterCrossSize, margin-start, and the ascent.
3072 : // * We can get crossStartToBaseline by adding margin-start + ascent.
3073 : // * If we subtract that from the curOuterCrossSize, we get
3074 : // crossEndToBaseline.
3075 :
3076 : nscoord crossStartToBaseline =
3077 0 : item->GetBaselineOffsetFromOuterCrossEdge(eAxisEdge_Start,
3078 : aAxisTracker,
3079 0 : useFirst);
3080 0 : nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
3081 :
3082 : // Now, update our "largest" values for these (across all the flex items
3083 : // in this flex line), so we can use them in computing the line's cross
3084 : // size below:
3085 0 : if (useFirst) {
3086 0 : crossStartToFurthestFirstBaseline =
3087 0 : std::max(crossStartToFurthestFirstBaseline, crossStartToBaseline);
3088 0 : crossEndToFurthestFirstBaseline =
3089 0 : std::max(crossEndToFurthestFirstBaseline, crossEndToBaseline);
3090 : } else {
3091 0 : crossStartToFurthestLastBaseline =
3092 0 : std::max(crossStartToFurthestLastBaseline, crossStartToBaseline);
3093 0 : crossEndToFurthestLastBaseline =
3094 0 : std::max(crossEndToFurthestLastBaseline, crossEndToBaseline);
3095 : }
3096 : } else {
3097 0 : largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
3098 : }
3099 : }
3100 :
3101 : // The line's baseline offset is the distance from the line's edge (start or
3102 : // end, depending on whether we've flipped the axes) to the furthest
3103 : // item-baseline. The item(s) with that baseline will be exactly aligned with
3104 : // the line's edge.
3105 0 : mFirstBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
3106 : crossEndToFurthestFirstBaseline : crossStartToFurthestFirstBaseline;
3107 :
3108 0 : mLastBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
3109 : crossStartToFurthestLastBaseline : crossEndToFurthestLastBaseline;
3110 :
3111 : // The line's cross-size is the larger of:
3112 : // (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
3113 : // all baseline-aligned items with no cross-axis auto margins...
3114 : // and
3115 : // (b) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
3116 : // all last baseline-aligned items with no cross-axis auto margins...
3117 : // and
3118 : // (c) largest cross-size of all other children.
3119 0 : mLineCrossSize = std::max(
3120 0 : std::max(crossStartToFurthestFirstBaseline + crossEndToFurthestFirstBaseline,
3121 0 : crossStartToFurthestLastBaseline + crossEndToFurthestLastBaseline),
3122 0 : largestOuterCrossSize);
3123 0 : }
3124 :
3125 : void
3126 0 : FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize,
3127 : const FlexboxAxisTracker& aAxisTracker)
3128 : {
3129 0 : AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
3130 : // We stretch IFF we are align-self:stretch, have no auto margins in
3131 : // cross axis, and have cross-axis size property == "auto". If any of those
3132 : // conditions don't hold up, we won't stretch.
3133 0 : if (mAlignSelf != NS_STYLE_ALIGN_STRETCH ||
3134 0 : GetNumAutoMarginsInAxis(crossAxis) != 0 ||
3135 0 : eStyleUnit_Auto != aAxisTracker.ComputedCrossSize(mFrame).GetUnit()) {
3136 0 : return;
3137 : }
3138 :
3139 : // If we've already been stretched, we can bail out early, too.
3140 : // No need to redo the calculation.
3141 0 : if (mIsStretched) {
3142 0 : return;
3143 : }
3144 :
3145 : // Reserve space for margins & border & padding, and then use whatever
3146 : // remains as our item's cross-size (clamped to its min/max range).
3147 : nscoord stretchedSize = aLineCrossSize -
3148 0 : GetMarginBorderPaddingSizeInAxis(crossAxis);
3149 :
3150 0 : stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
3151 :
3152 : // Update the cross-size & make a note that it's stretched, so we know to
3153 : // override the reflow state's computed cross-size in our final reflow.
3154 0 : SetCrossSize(stretchedSize);
3155 0 : mIsStretched = true;
3156 : }
3157 :
3158 : void
3159 0 : SingleLineCrossAxisPositionTracker::
3160 : ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
3161 : FlexItem& aItem)
3162 : {
3163 : // Subtract the space that our item is already occupying, to see how much
3164 : // space (if any) is available for its auto margins.
3165 0 : nscoord spaceForAutoMargins = aLine.GetLineCrossSize() -
3166 0 : aItem.GetOuterCrossSize(mAxis);
3167 :
3168 0 : if (spaceForAutoMargins <= 0) {
3169 0 : return; // No available space --> nothing to do
3170 : }
3171 :
3172 0 : uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis);
3173 0 : if (numAutoMargins == 0) {
3174 0 : return; // No auto margins --> nothing to do.
3175 : }
3176 :
3177 : // OK, we have at least one auto margin and we have some available space.
3178 : // Give each auto margin a share of the space.
3179 0 : const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
3180 0 : for (uint32_t i = 0; i < eNumAxisEdges; i++) {
3181 0 : mozilla::Side side = kAxisOrientationToSidesMap[mAxis][i];
3182 0 : if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
3183 0 : MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
3184 : "Expecting auto margins to have value '0' before we "
3185 : "update them");
3186 :
3187 : // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2.
3188 : // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half.
3189 0 : nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins;
3190 0 : aItem.SetMarginComponentForSide(side, curAutoMarginSize);
3191 0 : numAutoMargins--;
3192 0 : spaceForAutoMargins -= curAutoMarginSize;
3193 : }
3194 : }
3195 : }
3196 :
3197 : void
3198 0 : SingleLineCrossAxisPositionTracker::
3199 : EnterAlignPackingSpace(const FlexLine& aLine,
3200 : const FlexItem& aItem,
3201 : const FlexboxAxisTracker& aAxisTracker)
3202 : {
3203 : // We don't do align-self alignment on items that have auto margins
3204 : // in the cross axis.
3205 0 : if (aItem.GetNumAutoMarginsInAxis(mAxis)) {
3206 0 : return;
3207 : }
3208 :
3209 0 : uint8_t alignSelf = aItem.GetAlignSelf();
3210 : // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
3211 : // auto-sized items (which we've already done).
3212 0 : if (alignSelf == NS_STYLE_ALIGN_STRETCH) {
3213 0 : alignSelf = NS_STYLE_ALIGN_FLEX_START;
3214 : }
3215 :
3216 : // Map 'left'/'right' to 'start'/'end'
3217 0 : if (alignSelf == NS_STYLE_ALIGN_LEFT || alignSelf == NS_STYLE_ALIGN_RIGHT) {
3218 0 : if (aAxisTracker.IsRowOriented()) {
3219 : // Container's alignment axis is not parallel to the inline axis,
3220 : // so we map both 'left' and 'right' to 'start'.
3221 0 : alignSelf = NS_STYLE_ALIGN_START;
3222 : } else {
3223 : // Column-oriented, so we map 'left' and 'right' to 'start' or 'end',
3224 : // depending on left-to-right writing mode.
3225 0 : const bool isLTR = aAxisTracker.GetWritingMode().IsBidiLTR();
3226 0 : const bool isAlignLeft = (alignSelf == NS_STYLE_ALIGN_LEFT);
3227 0 : alignSelf = (isAlignLeft == isLTR) ? NS_STYLE_ALIGN_START
3228 0 : : NS_STYLE_ALIGN_END;
3229 : }
3230 : }
3231 :
3232 : // Map 'start'/'end' to 'flex-start'/'flex-end'.
3233 0 : if (alignSelf == NS_STYLE_ALIGN_START) {
3234 0 : alignSelf = NS_STYLE_ALIGN_FLEX_START;
3235 0 : } else if (alignSelf == NS_STYLE_ALIGN_END) {
3236 0 : alignSelf = NS_STYLE_ALIGN_FLEX_END;
3237 : }
3238 :
3239 : // If our cross axis is (internally) reversed, swap the align-self
3240 : // "flex-start" and "flex-end" behaviors:
3241 0 : if (aAxisTracker.AreAxesInternallyReversed()) {
3242 0 : if (alignSelf == NS_STYLE_ALIGN_FLEX_START) {
3243 0 : alignSelf = NS_STYLE_ALIGN_FLEX_END;
3244 0 : } else if (alignSelf == NS_STYLE_ALIGN_FLEX_END) {
3245 0 : alignSelf = NS_STYLE_ALIGN_FLEX_START;
3246 : }
3247 : }
3248 :
3249 0 : switch (alignSelf) {
3250 : case NS_STYLE_ALIGN_SELF_START:
3251 : case NS_STYLE_ALIGN_SELF_END:
3252 0 : NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end");
3253 : MOZ_FALLTHROUGH;
3254 : case NS_STYLE_ALIGN_FLEX_START:
3255 : // No space to skip over -- we're done.
3256 0 : break;
3257 : case NS_STYLE_ALIGN_FLEX_END:
3258 0 : mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
3259 0 : break;
3260 : case NS_STYLE_ALIGN_CENTER:
3261 : // Note: If cross-size is odd, the "after" space will get the extra unit.
3262 0 : mPosition +=
3263 0 : (aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
3264 0 : break;
3265 : case NS_STYLE_ALIGN_BASELINE:
3266 : case NS_STYLE_ALIGN_LAST_BASELINE: {
3267 0 : const bool useFirst = (alignSelf == NS_STYLE_ALIGN_BASELINE);
3268 :
3269 : // Normally, baseline-aligned items are collectively aligned with the
3270 : // line's cross-start edge; however, if our cross axis is (internally)
3271 : // reversed, we instead align them with the cross-end edge.
3272 : // A similar logic holds for last baseline-aligned items, but in reverse.
3273 : AxisEdgeType baselineAlignEdge =
3274 0 : aAxisTracker.AreAxesInternallyReversed() == useFirst ?
3275 0 : eAxisEdge_End : eAxisEdge_Start;
3276 :
3277 : nscoord itemBaselineOffset =
3278 0 : aItem.GetBaselineOffsetFromOuterCrossEdge(baselineAlignEdge,
3279 : aAxisTracker,
3280 0 : useFirst);
3281 :
3282 0 : nscoord lineBaselineOffset = useFirst ? aLine.GetFirstBaselineOffset()
3283 0 : : aLine.GetLastBaselineOffset();
3284 :
3285 0 : NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
3286 : "failed at finding largest baseline offset");
3287 :
3288 : // How much do we need to adjust our position (from the line edge),
3289 : // to get the item's baseline to hit the line's baseline offset:
3290 0 : nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
3291 :
3292 0 : if (aAxisTracker.AreAxesInternallyReversed() == useFirst) {
3293 : // Advance to align item w/ line's flex-end edge (as in FLEX_END case):
3294 0 : mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
3295 : // ...and step *back* by the baseline adjustment:
3296 0 : mPosition -= baselineDiff;
3297 : } else {
3298 : // mPosition is already at line's flex-start edge.
3299 : // From there, we step *forward* by the baseline adjustment:
3300 0 : mPosition += baselineDiff;
3301 : }
3302 0 : break;
3303 : }
3304 : default:
3305 0 : MOZ_ASSERT_UNREACHABLE("Unexpected align-self value");
3306 : break;
3307 : }
3308 : }
3309 :
3310 : // Utility function to convert an InlineDir to an AxisOrientationType
3311 : static inline AxisOrientationType
3312 0 : InlineDirToAxisOrientation(WritingMode::InlineDir aInlineDir)
3313 : {
3314 0 : switch (aInlineDir) {
3315 : case WritingMode::eInlineLTR:
3316 0 : return eAxis_LR;
3317 : case WritingMode::eInlineRTL:
3318 0 : return eAxis_RL;
3319 : case WritingMode::eInlineTTB:
3320 0 : return eAxis_TB;
3321 : case WritingMode::eInlineBTT:
3322 0 : return eAxis_BT;
3323 : }
3324 :
3325 0 : MOZ_ASSERT_UNREACHABLE("Unhandled InlineDir");
3326 : return eAxis_LR; // in case of unforseen error, assume English LTR text flow.
3327 : }
3328 :
3329 : // Utility function to convert a BlockDir to an AxisOrientationType
3330 : static inline AxisOrientationType
3331 0 : BlockDirToAxisOrientation(WritingMode::BlockDir aBlockDir)
3332 : {
3333 0 : switch (aBlockDir) {
3334 : case WritingMode::eBlockLR:
3335 0 : return eAxis_LR;
3336 : case WritingMode::eBlockRL:
3337 0 : return eAxis_RL;
3338 : case WritingMode::eBlockTB:
3339 0 : return eAxis_TB;
3340 : // NOTE: WritingMode::eBlockBT (bottom-to-top) does not exist.
3341 : }
3342 :
3343 0 : MOZ_ASSERT_UNREACHABLE("Unhandled BlockDir");
3344 : return eAxis_TB; // in case of unforseen error, assume English TTB block-flow
3345 : }
3346 :
3347 0 : FlexboxAxisTracker::FlexboxAxisTracker(
3348 : const nsFlexContainerFrame* aFlexContainer,
3349 : const WritingMode& aWM,
3350 0 : AxisTrackerFlags aFlags)
3351 : : mWM(aWM),
3352 0 : mAreAxesInternallyReversed(false)
3353 : {
3354 0 : if (IsLegacyBox(aFlexContainer)) {
3355 0 : InitAxesFromLegacyProps(aFlexContainer);
3356 : } else {
3357 0 : InitAxesFromModernProps(aFlexContainer);
3358 : }
3359 :
3360 : // Master switch to enable/disable bug 983427's code for reversing our axes
3361 : // and reversing some logic, to avoid reflowing children in bottom-to-top
3362 : // order. (This switch can be removed eventually, but for now, it allows
3363 : // this special-case code path to be compared against the normal code path.)
3364 : static bool sPreventBottomToTopChildOrdering = true;
3365 :
3366 : // Note: if the eAllowBottomToTopChildOrdering flag is set, that overrides
3367 : // the static boolean and makes us skip this special case.
3368 0 : if (!(aFlags & AxisTrackerFlags::eAllowBottomToTopChildOrdering) &&
3369 : sPreventBottomToTopChildOrdering) {
3370 : // If either axis is bottom-to-top, we flip both axes (and set a flag
3371 : // so that we can flip some logic to make the reversal transparent).
3372 0 : if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) {
3373 0 : mMainAxis = GetReverseAxis(mMainAxis);
3374 0 : mCrossAxis = GetReverseAxis(mCrossAxis);
3375 0 : mAreAxesInternallyReversed = true;
3376 0 : mIsMainAxisReversed = !mIsMainAxisReversed;
3377 0 : mIsCrossAxisReversed = !mIsCrossAxisReversed;
3378 : }
3379 : }
3380 0 : }
3381 :
3382 : void
3383 0 : FlexboxAxisTracker::InitAxesFromLegacyProps(
3384 : const nsFlexContainerFrame* aFlexContainer)
3385 : {
3386 0 : const nsStyleXUL* styleXUL = aFlexContainer->StyleXUL();
3387 :
3388 0 : const bool boxOrientIsVertical = (styleXUL->mBoxOrient ==
3389 0 : StyleBoxOrient::Vertical);
3390 0 : const bool wmIsVertical = mWM.IsVertical();
3391 :
3392 : // If box-orient agrees with our writing-mode, then we're "row-oriented"
3393 : // (i.e. the flexbox main axis is the same as our writing mode's inline
3394 : // direction). Otherwise, we're column-oriented (i.e. the flexbox's main
3395 : // axis is perpendicular to the writing-mode's inline direction).
3396 0 : mIsRowOriented = (boxOrientIsVertical == wmIsVertical);
3397 :
3398 : // XXXdholbert BEGIN CODE TO SET DEPRECATED MEMBER-VARS
3399 0 : if (boxOrientIsVertical) {
3400 0 : mMainAxis = eAxis_TB;
3401 0 : mCrossAxis = eAxis_LR;
3402 : } else {
3403 0 : mMainAxis = eAxis_LR;
3404 0 : mCrossAxis = eAxis_TB;
3405 : }
3406 : // "direction: rtl" reverses the writing-mode's inline axis.
3407 : // So, we need to reverse the corresponding flex axis to match.
3408 : // (Note this we don't toggle "mIsMainAxisReversed" for this condition,
3409 : // because the main axis will still match mWM's inline direction.)
3410 0 : if (!mWM.IsBidiLTR()) {
3411 0 : AxisOrientationType& axisToFlip = mIsRowOriented ? mMainAxis : mCrossAxis;
3412 0 : axisToFlip = GetReverseAxis(axisToFlip);
3413 : }
3414 : // XXXdholbert END CODE TO SET DEPRECATED MEMBER-VARS
3415 :
3416 : // Legacy flexbox can use "-webkit-box-direction: reverse" to reverse the
3417 : // main axis (so it runs in the reverse direction of the inline axis):
3418 0 : if (styleXUL->mBoxDirection == StyleBoxDirection::Reverse) {
3419 0 : mMainAxis = GetReverseAxis(mMainAxis);
3420 0 : mIsMainAxisReversed = true;
3421 : } else {
3422 0 : mIsMainAxisReversed = false;
3423 : }
3424 :
3425 : // Legacy flexbox does not support reversing the cross axis -- it has no
3426 : // equivalent of modern flexbox's "flex-wrap: wrap-reverse".
3427 0 : mIsCrossAxisReversed = false;
3428 0 : }
3429 :
3430 : void
3431 0 : FlexboxAxisTracker::InitAxesFromModernProps(
3432 : const nsFlexContainerFrame* aFlexContainer)
3433 : {
3434 0 : const nsStylePosition* stylePos = aFlexContainer->StylePosition();
3435 0 : uint32_t flexDirection = stylePos->mFlexDirection;
3436 :
3437 : // Inline dimension ("start-to-end"):
3438 : // (NOTE: I'm intentionally not calling these "inlineAxis"/"blockAxis", since
3439 : // those terms have explicit definition in the writing-modes spec, which are
3440 : // the opposite of how I'd be using them here.)
3441 : AxisOrientationType inlineDimension =
3442 0 : InlineDirToAxisOrientation(mWM.GetInlineDir());
3443 : AxisOrientationType blockDimension =
3444 0 : BlockDirToAxisOrientation(mWM.GetBlockDir());
3445 :
3446 : // Determine main axis:
3447 0 : switch (flexDirection) {
3448 : case NS_STYLE_FLEX_DIRECTION_ROW:
3449 0 : mMainAxis = inlineDimension;
3450 0 : mIsRowOriented = true;
3451 0 : mIsMainAxisReversed = false;
3452 0 : break;
3453 : case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE:
3454 0 : mMainAxis = GetReverseAxis(inlineDimension);
3455 0 : mIsRowOriented = true;
3456 0 : mIsMainAxisReversed = true;
3457 0 : break;
3458 : case NS_STYLE_FLEX_DIRECTION_COLUMN:
3459 0 : mMainAxis = blockDimension;
3460 0 : mIsRowOriented = false;
3461 0 : mIsMainAxisReversed = false;
3462 0 : break;
3463 : case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE:
3464 0 : mMainAxis = GetReverseAxis(blockDimension);
3465 0 : mIsRowOriented = false;
3466 0 : mIsMainAxisReversed = true;
3467 0 : break;
3468 : default:
3469 0 : MOZ_ASSERT_UNREACHABLE("Unexpected flex-direction value");
3470 : }
3471 :
3472 : // Determine cross axis:
3473 : // (This is set up so that a bogus |flexDirection| value will
3474 : // give us blockDimension.
3475 0 : if (flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN ||
3476 : flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE) {
3477 0 : mCrossAxis = inlineDimension;
3478 : } else {
3479 0 : mCrossAxis = blockDimension;
3480 : }
3481 :
3482 : // "flex-wrap: wrap-reverse" reverses our cross axis.
3483 0 : if (stylePos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) {
3484 0 : mCrossAxis = GetReverseAxis(mCrossAxis);
3485 0 : mIsCrossAxisReversed = true;
3486 : } else {
3487 0 : mIsCrossAxisReversed = false;
3488 : }
3489 0 : }
3490 :
3491 : // Allocates a new FlexLine, adds it to the given LinkedList (at the front or
3492 : // back depending on aShouldInsertAtFront), and returns a pointer to it.
3493 : static FlexLine*
3494 0 : AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
3495 : bool aShouldInsertAtFront)
3496 : {
3497 0 : FlexLine* newLine = new FlexLine();
3498 0 : if (aShouldInsertAtFront) {
3499 0 : aLines.insertFront(newLine);
3500 : } else {
3501 0 : aLines.insertBack(newLine);
3502 : }
3503 0 : return newLine;
3504 : }
3505 :
3506 : void
3507 0 : nsFlexContainerFrame::GenerateFlexLines(
3508 : nsPresContext* aPresContext,
3509 : const ReflowInput& aReflowInput,
3510 : nscoord aContentBoxMainSize,
3511 : nscoord aAvailableBSizeForContent,
3512 : const nsTArray<StrutInfo>& aStruts,
3513 : const FlexboxAxisTracker& aAxisTracker,
3514 : nsTArray<nsIFrame*>& aPlaceholders, /* out */
3515 : LinkedList<FlexLine>& aLines /* out */)
3516 : {
3517 0 : MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
3518 :
3519 : const bool isSingleLine =
3520 0 : NS_STYLE_FLEX_WRAP_NOWRAP == aReflowInput.mStylePosition->mFlexWrap;
3521 :
3522 : // If we're transparently reversing axes, then we'll need to link up our
3523 : // FlexItems and FlexLines in the reverse order, so that the rest of flex
3524 : // layout (with flipped axes) will still produce the correct result.
3525 : // Here, we declare a convenience bool that we'll pass when adding a new
3526 : // FlexLine or FlexItem, to make us insert it at the beginning of its list
3527 : // (so the list ends up reversed).
3528 0 : const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
3529 :
3530 : // We have at least one FlexLine. Even an empty flex container has a single
3531 : // (empty) flex line.
3532 0 : FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
3533 :
3534 : nscoord wrapThreshold;
3535 0 : if (isSingleLine) {
3536 : // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
3537 0 : wrapThreshold = NS_UNCONSTRAINEDSIZE;
3538 : } else {
3539 : // Wrapping! Set wrap threshold to flex container's content-box main-size.
3540 0 : wrapThreshold = aContentBoxMainSize;
3541 :
3542 : // If the flex container doesn't have a definite content-box main-size
3543 : // (e.g. if main axis is vertical & 'height' is 'auto'), make sure we at
3544 : // least wrap when we hit its max main-size.
3545 0 : if (wrapThreshold == NS_UNCONSTRAINEDSIZE) {
3546 : const nscoord flexContainerMaxMainSize =
3547 0 : GET_MAIN_COMPONENT_LOGICAL(aAxisTracker, aAxisTracker.GetWritingMode(),
3548 : aReflowInput.ComputedMaxISize(),
3549 : aReflowInput.ComputedMaxBSize());
3550 :
3551 0 : wrapThreshold = flexContainerMaxMainSize;
3552 : }
3553 :
3554 : // Also: if we're column-oriented and paginating in the block dimension,
3555 : // we may need to wrap to a new flex line sooner (before we grow past the
3556 : // available BSize, potentially running off the end of the page).
3557 0 : if (aAxisTracker.IsColumnOriented() &&
3558 0 : aAvailableBSizeForContent != NS_UNCONSTRAINEDSIZE) {
3559 0 : wrapThreshold = std::min(wrapThreshold, aAvailableBSizeForContent);
3560 : }
3561 : }
3562 :
3563 : // Tracks the index of the next strut, in aStruts (and when this hits
3564 : // aStruts.Length(), that means there are no more struts):
3565 0 : uint32_t nextStrutIdx = 0;
3566 :
3567 : // Overall index of the current flex item in the flex container. (This gets
3568 : // checked against entries in aStruts.)
3569 0 : uint32_t itemIdxInContainer = 0;
3570 :
3571 : CSSOrderAwareFrameIterator iter(this, kPrincipalList,
3572 : CSSOrderAwareFrameIterator::eIncludeAll,
3573 : CSSOrderAwareFrameIterator::eUnknownOrder,
3574 0 : OrderingPropertyForIter(this));
3575 :
3576 0 : if (iter.ItemsAreAlreadyInOrder()) {
3577 0 : AddStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
3578 : } else {
3579 0 : RemoveStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
3580 : }
3581 :
3582 0 : for (; !iter.AtEnd(); iter.Next()) {
3583 0 : nsIFrame* childFrame = *iter;
3584 : // Don't create flex items / lines for placeholder frames:
3585 0 : if (childFrame->IsPlaceholderFrame()) {
3586 0 : aPlaceholders.AppendElement(childFrame);
3587 0 : continue;
3588 : }
3589 :
3590 : // Honor "page-break-before", if we're multi-line and this line isn't empty:
3591 0 : if (!isSingleLine && !curLine->IsEmpty() &&
3592 0 : childFrame->StyleDisplay()->mBreakBefore) {
3593 0 : curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
3594 : }
3595 :
3596 0 : UniquePtr<FlexItem> item;
3597 0 : if (nextStrutIdx < aStruts.Length() &&
3598 0 : aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
3599 :
3600 : // Use the simplified "strut" FlexItem constructor:
3601 0 : item = MakeUnique<FlexItem>(childFrame, aStruts[nextStrutIdx].mStrutCrossSize,
3602 0 : aReflowInput.GetWritingMode());
3603 0 : nextStrutIdx++;
3604 : } else {
3605 0 : item = GenerateFlexItemForChild(aPresContext, childFrame,
3606 0 : aReflowInput, aAxisTracker);
3607 : }
3608 :
3609 0 : nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
3610 : nscoord itemOuterHypotheticalMainSize =
3611 0 : item->GetOuterMainSize(aAxisTracker.GetMainAxis());
3612 :
3613 : // Check if we need to wrap |item| to a new line
3614 : // (i.e. check if its outer hypothetical main size pushes our line over
3615 : // the threshold)
3616 0 : if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
3617 0 : !curLine->IsEmpty() && // No need to wrap at start of a line.
3618 0 : wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
3619 : itemOuterHypotheticalMainSize)) {
3620 0 : curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
3621 : }
3622 :
3623 : // Add item to current flex line (and update the line's bookkeeping about
3624 : // how large its items collectively are).
3625 0 : curLine->AddItem(item.release(), shouldInsertAtFront,
3626 : itemInnerHypotheticalMainSize,
3627 0 : itemOuterHypotheticalMainSize);
3628 :
3629 : // Honor "page-break-after", if we're multi-line and have more children:
3630 0 : if (!isSingleLine && childFrame->GetNextSibling() &&
3631 0 : childFrame->StyleDisplay()->mBreakAfter) {
3632 0 : curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
3633 : }
3634 0 : itemIdxInContainer++;
3635 : }
3636 0 : }
3637 :
3638 : // Retrieves the content-box main-size of our flex container from the
3639 : // reflow state (specifically, the main-size of *this continuation* of the
3640 : // flex container).
3641 : nscoord
3642 0 : nsFlexContainerFrame::GetMainSizeFromReflowInput(
3643 : const ReflowInput& aReflowInput,
3644 : const FlexboxAxisTracker& aAxisTracker)
3645 : {
3646 0 : if (aAxisTracker.IsRowOriented()) {
3647 : // Row-oriented --> our main axis is the inline axis, so our main size
3648 : // is our inline size (which should already be resolved).
3649 0 : NS_WARNING_ASSERTION(
3650 : aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
3651 : "Unconstrained inline size; this should only result from huge sizes "
3652 : "(not intrinsic sizing w/ orthogonal flows)");
3653 0 : return aReflowInput.ComputedISize();
3654 : }
3655 :
3656 : // Note: This may be unconstrained, if our block size is "auto":
3657 0 : return GetEffectiveComputedBSize(aReflowInput);
3658 : }
3659 :
3660 : // Returns the largest outer hypothetical main-size of any line in |aLines|.
3661 : // (i.e. the hypothetical main-size of the largest line)
3662 : static nscoord
3663 0 : GetLargestLineMainSize(const FlexLine* aFirstLine)
3664 : {
3665 0 : nscoord largestLineOuterSize = 0;
3666 0 : for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
3667 0 : largestLineOuterSize = std::max(largestLineOuterSize,
3668 0 : line->GetTotalOuterHypotheticalMainSize());
3669 : }
3670 0 : return largestLineOuterSize;
3671 : }
3672 :
3673 : /* Resolves the content-box main-size of a flex container frame,
3674 : * primarily based on:
3675 : * - the "tentative" main size, taken from the reflow state ("tentative"
3676 : * because it may be unconstrained or may run off the page).
3677 : * - the available BSize (needed if the main axis is the block axis).
3678 : * - the sizes of our lines of flex items.
3679 : *
3680 : * Guaranteed to return a definite length, i.e. not NS_UNCONSTRAINEDSIZE,
3681 : * aside from cases with huge lengths which happen to compute to that value.
3682 : *
3683 : * (Note: This function should be structurally similar to 'ComputeCrossSize()',
3684 : * except that here, the caller has already grabbed the tentative size from the
3685 : * reflow state.)
3686 : */
3687 : static nscoord
3688 0 : ResolveFlexContainerMainSize(const ReflowInput& aReflowInput,
3689 : const FlexboxAxisTracker& aAxisTracker,
3690 : nscoord aTentativeMainSize,
3691 : nscoord aAvailableBSizeForContent,
3692 : const FlexLine* aFirstLine,
3693 : nsReflowStatus& aStatus)
3694 : {
3695 0 : MOZ_ASSERT(aFirstLine, "null first line pointer");
3696 :
3697 0 : if (aAxisTracker.IsRowOriented()) {
3698 : // Row-oriented --> our main axis is the inline axis, so our main size
3699 : // is our inline size (which should already be resolved).
3700 0 : return aTentativeMainSize;
3701 : }
3702 :
3703 0 : if (aTentativeMainSize != NS_INTRINSICSIZE) {
3704 : // Column-oriented case, with fixed BSize:
3705 0 : if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE ||
3706 0 : aTentativeMainSize < aAvailableBSizeForContent) {
3707 : // Not in a fragmenting context, OR no need to fragment because we have
3708 : // more available BSize than we need. Either way, we don't need to clamp.
3709 : // (Note that the reflow state has already done the appropriate
3710 : // min/max-BSize clamping.)
3711 0 : return aTentativeMainSize;
3712 : }
3713 :
3714 : // Fragmenting *and* our fixed BSize is larger than available BSize:
3715 : // Mark incomplete so we get a next-in-flow, and take up all of the
3716 : // available BSize (or the amount of BSize required by our children, if
3717 : // that's larger; but of course not more than our own computed BSize).
3718 : // XXXdholbert For now, we don't support pushing children to our next
3719 : // continuation or splitting children, so "amount of BSize required by
3720 : // our children" is just the main-size (BSize) of our longest flex line.
3721 0 : aStatus.SetIncomplete();
3722 0 : nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
3723 :
3724 0 : if (largestLineOuterSize <= aAvailableBSizeForContent) {
3725 0 : return aAvailableBSizeForContent;
3726 : }
3727 0 : return std::min(aTentativeMainSize, largestLineOuterSize);
3728 : }
3729 :
3730 : // Column-oriented case, with auto BSize:
3731 : // Resolve auto BSize to the largest FlexLine length, clamped to our
3732 : // computed min/max main-size properties.
3733 : // XXXdholbert Handle constrained-aAvailableBSizeForContent case here.
3734 0 : nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
3735 0 : return NS_CSS_MINMAX(largestLineOuterSize,
3736 : aReflowInput.ComputedMinBSize(),
3737 0 : aReflowInput.ComputedMaxBSize());
3738 : }
3739 :
3740 : nscoord
3741 0 : nsFlexContainerFrame::ComputeCrossSize(const ReflowInput& aReflowInput,
3742 : const FlexboxAxisTracker& aAxisTracker,
3743 : nscoord aSumLineCrossSizes,
3744 : nscoord aAvailableBSizeForContent,
3745 : bool* aIsDefinite,
3746 : nsReflowStatus& aStatus)
3747 : {
3748 0 : MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null");
3749 :
3750 0 : if (aAxisTracker.IsColumnOriented()) {
3751 : // Column-oriented --> our cross axis is the inline axis, so our cross size
3752 : // is our inline size (which should already be resolved).
3753 0 : NS_WARNING_ASSERTION(
3754 : aReflowInput.ComputedISize() != NS_UNCONSTRAINEDSIZE,
3755 : "Unconstrained inline size; this should only result from huge sizes "
3756 : "(not intrinsic sizing w/ orthogonal flows)");
3757 0 : *aIsDefinite = true;
3758 0 : return aReflowInput.ComputedISize();
3759 : }
3760 :
3761 0 : nscoord effectiveComputedBSize = GetEffectiveComputedBSize(aReflowInput);
3762 0 : if (effectiveComputedBSize != NS_INTRINSICSIZE) {
3763 : // Row-oriented case (cross axis is block-axis), with fixed BSize:
3764 0 : *aIsDefinite = true;
3765 0 : if (aAvailableBSizeForContent == NS_UNCONSTRAINEDSIZE ||
3766 0 : effectiveComputedBSize < aAvailableBSizeForContent) {
3767 : // Not in a fragmenting context, OR no need to fragment because we have
3768 : // more available BSize than we need. Either way, just use our fixed
3769 : // BSize. (Note that the reflow state has already done the appropriate
3770 : // min/max-BSize clamping.)
3771 0 : return effectiveComputedBSize;
3772 : }
3773 :
3774 : // Fragmenting *and* our fixed BSize is too tall for available BSize:
3775 : // Mark incomplete so we get a next-in-flow, and take up all of the
3776 : // available BSize (or the amount of BSize required by our children, if
3777 : // that's larger; but of course not more than our own computed BSize).
3778 : // XXXdholbert For now, we don't support pushing children to our next
3779 : // continuation or splitting children, so "amount of BSize required by
3780 : // our children" is just the sum of our FlexLines' BSizes (cross sizes).
3781 0 : aStatus.SetIncomplete();
3782 0 : if (aSumLineCrossSizes <= aAvailableBSizeForContent) {
3783 0 : return aAvailableBSizeForContent;
3784 : }
3785 0 : return std::min(effectiveComputedBSize, aSumLineCrossSizes);
3786 : }
3787 :
3788 : // Row-oriented case (cross axis is block axis), with auto BSize:
3789 : // Shrink-wrap our line(s), subject to our min-size / max-size
3790 : // constraints in that (block) axis.
3791 : // XXXdholbert Handle constrained-aAvailableBSizeForContent case here.
3792 0 : *aIsDefinite = false;
3793 0 : return NS_CSS_MINMAX(aSumLineCrossSizes,
3794 : aReflowInput.ComputedMinBSize(),
3795 0 : aReflowInput.ComputedMaxBSize());
3796 : }
3797 :
3798 : void
3799 0 : FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent,
3800 : nscoord aContentBoxMainSize,
3801 : const FlexboxAxisTracker& aAxisTracker)
3802 : {
3803 : MainAxisPositionTracker mainAxisPosnTracker(aAxisTracker, this,
3804 : aJustifyContent,
3805 0 : aContentBoxMainSize);
3806 0 : for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
3807 : nscoord itemMainBorderBoxSize =
3808 0 : item->GetMainSize() +
3809 0 : item->GetBorderPaddingSizeInAxis(mainAxisPosnTracker.GetAxis());
3810 :
3811 : // Resolve any main-axis 'auto' margins on aChild to an actual value.
3812 0 : mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item);
3813 :
3814 : // Advance our position tracker to child's upper-left content-box corner,
3815 : // and use that as its position in the main axis.
3816 0 : mainAxisPosnTracker.EnterMargin(item->GetMargin());
3817 0 : mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
3818 :
3819 0 : item->SetMainPosition(mainAxisPosnTracker.GetPosition());
3820 :
3821 0 : mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
3822 0 : mainAxisPosnTracker.ExitMargin(item->GetMargin());
3823 0 : mainAxisPosnTracker.TraversePackingSpace();
3824 : }
3825 0 : }
3826 :
3827 : /**
3828 : * Given the flex container's "flex-relative ascent" (i.e. distance from the
3829 : * flex container's content-box cross-start edge to its baseline), returns
3830 : * its actual physical ascent value (the distance from the *border-box* top
3831 : * edge to its baseline).
3832 : */
3833 : static nscoord
3834 0 : ComputePhysicalAscentFromFlexRelativeAscent(
3835 : nscoord aFlexRelativeAscent,
3836 : nscoord aContentBoxCrossSize,
3837 : const ReflowInput& aReflowInput,
3838 : const FlexboxAxisTracker& aAxisTracker)
3839 : {
3840 0 : return aReflowInput.ComputedPhysicalBorderPadding().top +
3841 0 : PhysicalCoordFromFlexRelativeCoord(aFlexRelativeAscent,
3842 : aContentBoxCrossSize,
3843 0 : aAxisTracker.GetCrossAxis());
3844 : }
3845 :
3846 : void
3847 0 : nsFlexContainerFrame::SizeItemInCrossAxis(
3848 : nsPresContext* aPresContext,
3849 : const FlexboxAxisTracker& aAxisTracker,
3850 : ReflowInput& aChildReflowInput,
3851 : FlexItem& aItem)
3852 : {
3853 0 : if (aAxisTracker.IsCrossAxisHorizontal()) {
3854 0 : MOZ_ASSERT(aItem.HasIntrinsicRatio(),
3855 : "For now, caller's CanMainSizeInfluenceCrossSize check should "
3856 : "only allow us to get here for items with intrinsic ratio");
3857 : // XXXdholbert When we finish support for vertical writing-modes,
3858 : // (in bug 1079155 or a dependency), we'll relax the horizontal check in
3859 : // CanMainSizeInfluenceCrossSize, and this function will need to be able
3860 : // to measure the baseline & width (given our resolved height)
3861 : // of vertical-writing-mode flex items here.
3862 : // For now, we only expect to get here for items with an intrinsic aspect
3863 : // ratio; and for those items, we can just read the size off of the reflow
3864 : // state, without performing reflow.
3865 0 : aItem.SetCrossSize(aChildReflowInput.ComputedWidth());
3866 0 : return;
3867 : }
3868 :
3869 0 : MOZ_ASSERT(!aItem.HadMeasuringReflow(),
3870 : "We shouldn't need more than one measuring reflow");
3871 :
3872 0 : if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_STRETCH) {
3873 : // This item's got "align-self: stretch", so we probably imposed a
3874 : // stretched computed height on it during its previous reflow. We're
3875 : // not imposing that height for *this* measuring reflow, so we need to
3876 : // tell it to treat this reflow as a vertical resize (regardless of
3877 : // whether any of its ancestors are being resized).
3878 0 : aChildReflowInput.SetVResize(true);
3879 : }
3880 :
3881 : // Potentially reflow the item, and get the sizing info.
3882 : const CachedMeasuringReflowResult& reflowResult =
3883 0 : MeasureAscentAndHeightForFlexItem(aItem, aPresContext, aChildReflowInput);
3884 :
3885 : // Save the sizing info that we learned from this reflow
3886 : // -----------------------------------------------------
3887 :
3888 : // Tentatively store the child's desired content-box cross-size.
3889 : // Note that childDesiredSize is the border-box size, so we have to
3890 : // subtract border & padding to get the content-box size.
3891 : // (Note that at this point in the code, we know our cross axis is vertical,
3892 : // so we don't bother with making aAxisTracker pick the cross-axis component
3893 : // for us.)
3894 0 : nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
3895 0 : if (reflowResult.Height() < crossAxisBorderPadding) {
3896 : // Child's requested size isn't large enough for its border/padding!
3897 : // This is OK for the trivial nsFrame::Reflow() impl, but other frame
3898 : // classes should know better. So, if we get here, the child had better be
3899 : // an instance of nsFrame (i.e. it should return null from GetType()).
3900 : // XXXdholbert Once we've fixed bug 765861, we should upgrade this to an
3901 : // assertion that trivially passes if bug 765861's flag has been flipped.
3902 0 : NS_WARNING_ASSERTION(
3903 : aItem.Frame()->Type() == LayoutFrameType::None,
3904 : "Child should at least request space for border/padding");
3905 0 : aItem.SetCrossSize(0);
3906 : } else {
3907 : // (normal case)
3908 0 : aItem.SetCrossSize(reflowResult.Height() - crossAxisBorderPadding);
3909 : }
3910 :
3911 0 : aItem.SetAscent(reflowResult.Ascent());
3912 : }
3913 :
3914 : void
3915 0 : FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
3916 : const FlexboxAxisTracker& aAxisTracker)
3917 : {
3918 0 : SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
3919 :
3920 0 : for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
3921 : // First, stretch the item's cross size (if appropriate), and resolve any
3922 : // auto margins in this axis.
3923 0 : item->ResolveStretchedCrossSize(mLineCrossSize, aAxisTracker);
3924 0 : lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, *item);
3925 :
3926 : // Compute the cross-axis position of this item
3927 : nscoord itemCrossBorderBoxSize =
3928 0 : item->GetCrossSize() +
3929 0 : item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis());
3930 0 : lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker);
3931 0 : lineCrossAxisPosnTracker.EnterMargin(item->GetMargin());
3932 0 : lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
3933 :
3934 0 : item->SetCrossPosition(aLineStartPosition +
3935 0 : lineCrossAxisPosnTracker.GetPosition());
3936 :
3937 : // Back out to cross-axis edge of the line.
3938 0 : lineCrossAxisPosnTracker.ResetPosition();
3939 : }
3940 0 : }
3941 :
3942 : void
3943 0 : nsFlexContainerFrame::Reflow(nsPresContext* aPresContext,
3944 : ReflowOutput& aDesiredSize,
3945 : const ReflowInput& aReflowInput,
3946 : nsReflowStatus& aStatus)
3947 : {
3948 0 : MarkInReflow();
3949 0 : DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
3950 0 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
3951 0 : MOZ_LOG(gFlexContainerLog, LogLevel::Debug,
3952 : ("Reflow() for nsFlexContainerFrame %p\n", this));
3953 :
3954 0 : if (IsFrameTreeTooDeep(aReflowInput, aDesiredSize, aStatus)) {
3955 0 : return;
3956 : }
3957 :
3958 : // We (and our children) can only depend on our ancestor's bsize if we have
3959 : // a percent-bsize, or if we're positioned and we have "block-start" and "block-end"
3960 : // set and have block-size:auto. (There are actually other cases, too -- e.g. if
3961 : // our parent is itself a block-dir flex container and we're flexible -- but
3962 : // we'll let our ancestors handle those sorts of cases.)
3963 0 : WritingMode wm = aReflowInput.GetWritingMode();
3964 0 : const nsStylePosition* stylePos = StylePosition();
3965 0 : const nsStyleCoord& bsize = stylePos->BSize(wm);
3966 0 : if (bsize.HasPercent() ||
3967 0 : (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
3968 0 : eStyleUnit_Auto == bsize.GetUnit() &&
3969 0 : eStyleUnit_Auto != stylePos->mOffset.GetBStartUnit(wm) &&
3970 0 : eStyleUnit_Auto != stylePos->mOffset.GetBEndUnit(wm))) {
3971 0 : AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
3972 : }
3973 :
3974 0 : RenumberList();
3975 :
3976 0 : const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
3977 :
3978 : // If we're being fragmented into a constrained BSize, then subtract off
3979 : // borderpadding BStart from that constrained BSize, to get the available
3980 : // BSize for our content box. (No need to subtract the borderpadding BStart
3981 : // if we're already skipping it via GetLogicalSkipSides, though.)
3982 0 : nscoord availableBSizeForContent = aReflowInput.AvailableBSize();
3983 0 : if (availableBSizeForContent != NS_UNCONSTRAINEDSIZE &&
3984 0 : !(GetLogicalSkipSides(&aReflowInput).BStart())) {
3985 0 : availableBSizeForContent -=
3986 0 : aReflowInput.ComputedLogicalBorderPadding().BStart(wm);
3987 : // (Don't let that push availableBSizeForContent below zero, though):
3988 0 : availableBSizeForContent = std::max(availableBSizeForContent, 0);
3989 : }
3990 :
3991 : nscoord contentBoxMainSize = GetMainSizeFromReflowInput(aReflowInput,
3992 0 : axisTracker);
3993 :
3994 0 : AutoTArray<StrutInfo, 1> struts;
3995 : DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
3996 : contentBoxMainSize, availableBSizeForContent,
3997 0 : struts, axisTracker);
3998 :
3999 0 : if (!struts.IsEmpty()) {
4000 : // We're restarting flex layout, with new knowledge of collapsed items.
4001 : DoFlexLayout(aPresContext, aDesiredSize, aReflowInput, aStatus,
4002 : contentBoxMainSize, availableBSizeForContent,
4003 0 : struts, axisTracker);
4004 : }
4005 : }
4006 :
4007 : // RAII class to clean up a list of FlexLines.
4008 : // Specifically, this removes each line from the list, deletes all the
4009 : // FlexItems in its list, and deletes the FlexLine.
4010 : class MOZ_RAII AutoFlexLineListClearer
4011 : {
4012 : public:
4013 0 : explicit AutoFlexLineListClearer(LinkedList<FlexLine>& aLines
4014 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
4015 0 : : mLines(aLines)
4016 : {
4017 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
4018 0 : }
4019 :
4020 0 : ~AutoFlexLineListClearer()
4021 0 : {
4022 0 : while (FlexLine* line = mLines.popFirst()) {
4023 0 : while (FlexItem* item = line->mItems.popFirst()) {
4024 0 : delete item;
4025 0 : }
4026 0 : delete line;
4027 0 : }
4028 0 : }
4029 :
4030 : private:
4031 : LinkedList<FlexLine>& mLines;
4032 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
4033 : };
4034 :
4035 : // Class to let us temporarily provide an override value for the the main-size
4036 : // CSS property ('width' or 'height') on a flex item, for use in
4037 : // nsFrame::ComputeSizeWithIntrinsicDimensions.
4038 : // (We could use this overridden size more broadly, too, but it's probably
4039 : // better to avoid property-table accesses. So, where possible, we communicate
4040 : // the resolved main-size to the child via modifying its reflow state directly,
4041 : // instead of using this class.)
4042 : class MOZ_RAII AutoFlexItemMainSizeOverride final
4043 : {
4044 : public:
4045 0 : explicit AutoFlexItemMainSizeOverride(FlexItem& aItem
4046 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
4047 0 : : mItemFrame(aItem.Frame())
4048 : {
4049 0 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
4050 :
4051 0 : MOZ_ASSERT(!mItemFrame->HasProperty(nsIFrame::FlexItemMainSizeOverride()),
4052 : "FlexItemMainSizeOverride prop shouldn't be set already; "
4053 : "it should only be set temporarily (& not recursively)");
4054 0 : NS_ASSERTION(aItem.HasIntrinsicRatio(),
4055 : "This should only be needed for items with an aspect ratio");
4056 :
4057 0 : mItemFrame->SetProperty(nsIFrame::FlexItemMainSizeOverride(),
4058 0 : aItem.GetMainSize());
4059 0 : }
4060 :
4061 0 : ~AutoFlexItemMainSizeOverride() {
4062 0 : mItemFrame->RemoveProperty(nsIFrame::FlexItemMainSizeOverride());
4063 0 : }
4064 :
4065 : private:
4066 : nsIFrame* mItemFrame;
4067 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
4068 : };
4069 :
4070 : void
4071 0 : nsFlexContainerFrame::CalculatePackingSpace(uint32_t aNumThingsToPack,
4072 : uint8_t aAlignVal,
4073 : nscoord* aFirstSubjectOffset,
4074 : uint32_t* aNumPackingSpacesRemaining,
4075 : nscoord* aPackingSpaceRemaining)
4076 : {
4077 : MOZ_ASSERT(NS_STYLE_ALIGN_SPACE_BETWEEN == NS_STYLE_JUSTIFY_SPACE_BETWEEN &&
4078 : NS_STYLE_ALIGN_SPACE_AROUND == NS_STYLE_JUSTIFY_SPACE_AROUND &&
4079 : NS_STYLE_ALIGN_SPACE_EVENLY == NS_STYLE_JUSTIFY_SPACE_EVENLY,
4080 : "CalculatePackingSpace assumes that NS_STYLE_ALIGN_SPACE and "
4081 : "NS_STYLE_JUSTIFY_SPACE constants are interchangeable");
4082 :
4083 0 : MOZ_ASSERT(aAlignVal == NS_STYLE_ALIGN_SPACE_BETWEEN ||
4084 : aAlignVal == NS_STYLE_ALIGN_SPACE_AROUND ||
4085 : aAlignVal == NS_STYLE_ALIGN_SPACE_EVENLY,
4086 : "Unexpected alignment value");
4087 :
4088 0 : MOZ_ASSERT(*aPackingSpaceRemaining >= 0,
4089 : "Should not be called with negative packing space");
4090 :
4091 0 : MOZ_ASSERT(aNumThingsToPack >= 1,
4092 : "Should not be called with less than 1 thing to pack");
4093 :
4094 : // Packing spaces between items:
4095 0 : *aNumPackingSpacesRemaining = aNumThingsToPack - 1;
4096 :
4097 0 : if (aAlignVal == NS_STYLE_ALIGN_SPACE_BETWEEN) {
4098 : // No need to reserve space at beginning/end, so we're done.
4099 0 : return;
4100 : }
4101 :
4102 : // We need to add 1 or 2 packing spaces, split between beginning/end, for
4103 : // space-around / space-evenly:
4104 : size_t numPackingSpacesForEdges =
4105 0 : aAlignVal == NS_STYLE_JUSTIFY_SPACE_AROUND ? 1 : 2;
4106 :
4107 : // How big will each "full" packing space be:
4108 0 : nscoord packingSpaceSize = *aPackingSpaceRemaining /
4109 0 : (*aNumPackingSpacesRemaining + numPackingSpacesForEdges);
4110 : // How much packing-space are we allocating to the edges:
4111 0 : nscoord totalEdgePackingSpace = numPackingSpacesForEdges * packingSpaceSize;
4112 :
4113 : // Use half of that edge packing space right now:
4114 0 : *aFirstSubjectOffset += totalEdgePackingSpace / 2;
4115 : // ...but we need to subtract all of it right away, so that we won't
4116 : // hand out any of it to intermediate packing spaces.
4117 0 : *aPackingSpaceRemaining -= totalEdgePackingSpace;
4118 : }
4119 :
4120 : void
4121 0 : nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext,
4122 : ReflowOutput& aDesiredSize,
4123 : const ReflowInput& aReflowInput,
4124 : nsReflowStatus& aStatus,
4125 : nscoord aContentBoxMainSize,
4126 : nscoord aAvailableBSizeForContent,
4127 : nsTArray<StrutInfo>& aStruts,
4128 : const FlexboxAxisTracker& aAxisTracker)
4129 : {
4130 0 : aStatus.Reset();
4131 :
4132 0 : LinkedList<FlexLine> lines;
4133 0 : nsTArray<nsIFrame*> placeholderKids;
4134 0 : AutoFlexLineListClearer cleanupLines(lines);
4135 :
4136 : GenerateFlexLines(aPresContext, aReflowInput,
4137 : aContentBoxMainSize,
4138 : aAvailableBSizeForContent,
4139 : aStruts, aAxisTracker,
4140 0 : placeholderKids, lines);
4141 :
4142 0 : if (lines.getFirst()->IsEmpty() &&
4143 0 : !lines.getFirst()->getNext()) {
4144 : // We have no flex items, our parent should synthesize a baseline if needed.
4145 0 : AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
4146 : } else {
4147 0 : RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
4148 : }
4149 :
4150 : aContentBoxMainSize =
4151 : ResolveFlexContainerMainSize(aReflowInput, aAxisTracker,
4152 : aContentBoxMainSize, aAvailableBSizeForContent,
4153 0 : lines.getFirst(), aStatus);
4154 :
4155 0 : for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
4156 0 : line->ResolveFlexibleLengths(aContentBoxMainSize);
4157 : }
4158 :
4159 : // Cross Size Determination - Flexbox spec section 9.4
4160 : // ===================================================
4161 : // Calculate the hypothetical cross size of each item:
4162 0 : nscoord sumLineCrossSizes = 0;
4163 0 : for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
4164 0 : for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) {
4165 : // The item may already have the correct cross-size; only recalculate
4166 : // if the item's main size resolution (flexing) could have influenced it:
4167 0 : if (item->CanMainSizeInfluenceCrossSize(aAxisTracker)) {
4168 0 : Maybe<AutoFlexItemMainSizeOverride> sizeOverride;
4169 0 : if (item->HasIntrinsicRatio()) {
4170 : // For flex items with an aspect ratio, we have to impose an override
4171 : // for the main-size property *before* we even instantiate the reflow
4172 : // state, in order for aspect ratio calculations to produce the right
4173 : // cross size in the reflow state. (For other flex items, it's OK
4174 : // (and cheaper) to impose our main size *after* the reflow state has
4175 : // been constructed, since the main size shouldn't influence anything
4176 : // about cross-size measurement until we actually reflow the child.)
4177 0 : sizeOverride.emplace(*item);
4178 : }
4179 :
4180 0 : WritingMode wm = item->Frame()->GetWritingMode();
4181 0 : LogicalSize availSize = aReflowInput.ComputedSize(wm);
4182 0 : availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
4183 : ReflowInput childReflowInput(aPresContext, aReflowInput,
4184 0 : item->Frame(), availSize);
4185 0 : if (!sizeOverride) {
4186 : // Directly override the computed main-size, by tweaking reflow state:
4187 0 : if (aAxisTracker.IsMainAxisHorizontal()) {
4188 0 : childReflowInput.SetComputedWidth(item->GetMainSize());
4189 : } else {
4190 0 : childReflowInput.SetComputedHeight(item->GetMainSize());
4191 : }
4192 : }
4193 :
4194 : SizeItemInCrossAxis(aPresContext, aAxisTracker,
4195 0 : childReflowInput, *item);
4196 : }
4197 : }
4198 : // Now that we've finished with this line's items, size the line itself:
4199 0 : line->ComputeCrossSizeAndBaseline(aAxisTracker);
4200 0 : sumLineCrossSizes += line->GetLineCrossSize();
4201 : }
4202 :
4203 : bool isCrossSizeDefinite;
4204 : const nscoord contentBoxCrossSize =
4205 : ComputeCrossSize(aReflowInput, aAxisTracker, sumLineCrossSizes,
4206 0 : aAvailableBSizeForContent, &isCrossSizeDefinite, aStatus);
4207 :
4208 : // Set up state for cross-axis alignment, at a high level (outside the
4209 : // scope of a particular flex line)
4210 : CrossAxisPositionTracker
4211 : crossAxisPosnTracker(lines.getFirst(),
4212 : aReflowInput, contentBoxCrossSize,
4213 0 : isCrossSizeDefinite, aAxisTracker);
4214 :
4215 : // Now that we know the cross size of each line (including
4216 : // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
4217 : // constructor), we can create struts for any flex items with
4218 : // "visibility: collapse" (and restart flex layout).
4219 0 : if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
4220 0 : BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
4221 0 : if (!aStruts.IsEmpty()) {
4222 : // Restart flex layout, using our struts.
4223 0 : return;
4224 : }
4225 : }
4226 :
4227 : // If the container should derive its baseline from the first FlexLine,
4228 : // do that here (while crossAxisPosnTracker is conveniently pointing
4229 : // at the cross-start edge of that line, which the line's baseline offset is
4230 : // measured from):
4231 : nscoord flexContainerAscent;
4232 0 : if (!aAxisTracker.AreAxesInternallyReversed()) {
4233 0 : nscoord firstLineBaselineOffset = lines.getFirst()->GetFirstBaselineOffset();
4234 0 : if (firstLineBaselineOffset == nscoord_MIN) {
4235 : // No baseline-aligned items in line. Use sentinel value to prompt us to
4236 : // get baseline from the first FlexItem after we've reflowed it.
4237 0 : flexContainerAscent = nscoord_MIN;
4238 : } else {
4239 : flexContainerAscent =
4240 0 : ComputePhysicalAscentFromFlexRelativeAscent(
4241 0 : crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
4242 0 : contentBoxCrossSize, aReflowInput, aAxisTracker);
4243 : }
4244 : }
4245 :
4246 0 : const auto justifyContent = IsLegacyBox(aReflowInput.mFrame) ?
4247 0 : ConvertLegacyStyleToJustifyContent(StyleXUL()) :
4248 0 : aReflowInput.mStylePosition->mJustifyContent;
4249 :
4250 0 : for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
4251 : // Main-Axis Alignment - Flexbox spec section 9.5
4252 : // ==============================================
4253 0 : line->PositionItemsInMainAxis(justifyContent,
4254 : aContentBoxMainSize,
4255 0 : aAxisTracker);
4256 :
4257 : // Cross-Axis Alignment - Flexbox spec section 9.6
4258 : // ===============================================
4259 0 : line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(),
4260 0 : aAxisTracker);
4261 0 : crossAxisPosnTracker.TraverseLine(*line);
4262 0 : crossAxisPosnTracker.TraversePackingSpace();
4263 : }
4264 :
4265 : // If the container should derive its baseline from the last FlexLine,
4266 : // do that here (while crossAxisPosnTracker is conveniently pointing
4267 : // at the cross-end edge of that line, which the line's baseline offset is
4268 : // measured from):
4269 0 : if (aAxisTracker.AreAxesInternallyReversed()) {
4270 0 : nscoord lastLineBaselineOffset = lines.getLast()->GetFirstBaselineOffset();
4271 0 : if (lastLineBaselineOffset == nscoord_MIN) {
4272 : // No baseline-aligned items in line. Use sentinel value to prompt us to
4273 : // get baseline from the last FlexItem after we've reflowed it.
4274 0 : flexContainerAscent = nscoord_MIN;
4275 : } else {
4276 : flexContainerAscent =
4277 0 : ComputePhysicalAscentFromFlexRelativeAscent(
4278 0 : crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
4279 0 : contentBoxCrossSize, aReflowInput, aAxisTracker);
4280 : }
4281 : }
4282 :
4283 : // Before giving each child a final reflow, calculate the origin of the
4284 : // flex container's content box (with respect to its border-box), so that
4285 : // we can compute our flex item's final positions.
4286 0 : WritingMode flexWM = aReflowInput.GetWritingMode();
4287 0 : LogicalMargin containerBP = aReflowInput.ComputedLogicalBorderPadding();
4288 :
4289 : // Unconditionally skip block-end border & padding for now, regardless of
4290 : // writing-mode/GetLogicalSkipSides. We add it lower down, after we've
4291 : // established baseline and decided whether bottom border-padding fits (if
4292 : // we're fragmented).
4293 0 : const nscoord blockEndContainerBP = containerBP.BEnd(flexWM);
4294 : const LogicalSides skipSides =
4295 0 : GetLogicalSkipSides(&aReflowInput) | LogicalSides(eLogicalSideBitsBEnd);
4296 0 : containerBP.ApplySkipSides(skipSides);
4297 :
4298 : const LogicalPoint containerContentBoxOrigin(flexWM,
4299 0 : containerBP.IStart(flexWM),
4300 0 : containerBP.BStart(flexWM));
4301 :
4302 : // Determine flex container's border-box size (used in positioning children):
4303 : LogicalSize logSize =
4304 : aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize,
4305 0 : contentBoxCrossSize);
4306 0 : logSize += aReflowInput.ComputedLogicalBorderPadding().Size(flexWM);
4307 0 : nsSize containerSize = logSize.GetPhysicalSize(flexWM);
4308 :
4309 : // If the flex container has no baseline-aligned items, it will use this item
4310 : // (the first item, discounting any under-the-hood reversing that we've done)
4311 : // to determine its baseline:
4312 : const FlexItem* const firstItem =
4313 0 : aAxisTracker.AreAxesInternallyReversed()
4314 0 : ? lines.getLast()->GetLastItem()
4315 0 : : lines.getFirst()->GetFirstItem();
4316 :
4317 : // FINAL REFLOW: Give each child frame another chance to reflow, now that
4318 : // we know its final size and position.
4319 0 : for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
4320 0 : for (const FlexItem* item = line->GetFirstItem(); item;
4321 0 : item = item->getNext()) {
4322 : LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint(
4323 : item->GetMainPosition(),
4324 : item->GetCrossPosition(),
4325 : aContentBoxMainSize,
4326 0 : contentBoxCrossSize);
4327 : // Adjust framePos to be relative to the container's border-box
4328 : // (i.e. its frame rect), instead of the container's content-box:
4329 0 : framePos += containerContentBoxOrigin;
4330 :
4331 : // (Intentionally snapshotting this before ApplyRelativePositioning, to
4332 : // maybe use for setting the flex container's baseline.)
4333 0 : const nscoord itemNormalBPos = framePos.B(flexWM);
4334 :
4335 : // Check if we actually need to reflow the item -- if we already reflowed
4336 : // it with the right size, we can just reposition it as-needed.
4337 0 : bool itemNeedsReflow = true; // (Start out assuming the worst.)
4338 0 : if (item->HadMeasuringReflow()) {
4339 : LogicalSize finalFlexItemCBSize =
4340 : aAxisTracker.LogicalSizeFromFlexRelativeSizes(item->GetMainSize(),
4341 0 : item->GetCrossSize());
4342 : // We've already reflowed the child once. Was the size we gave it in
4343 : // that reflow the same as its final (post-flexing/stretching) size?
4344 0 : if (finalFlexItemCBSize ==
4345 0 : LogicalSize(flexWM,
4346 0 : item->Frame()->GetContentRectRelativeToSelf().Size())) {
4347 : // Even if our size hasn't changed, some of our descendants might
4348 : // care that our bsize is now considered "definite" (whereas it
4349 : // wasn't in our previous "measuring" reflow), if they have a
4350 : // relative bsize.
4351 0 : if (!(item->Frame()->GetStateBits() &
4352 : NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
4353 : // Item has the correct size (and its children don't care that
4354 : // it's now "definite"). Let's just make sure it's at the right
4355 : // position.
4356 0 : itemNeedsReflow = false;
4357 : MoveFlexItemToFinalPosition(aReflowInput, *item, framePos,
4358 0 : containerSize);
4359 : }
4360 : }
4361 : }
4362 0 : if (itemNeedsReflow) {
4363 : ReflowFlexItem(aPresContext, aAxisTracker, aReflowInput,
4364 0 : *item, framePos, containerSize);
4365 : }
4366 :
4367 : // If this is our first item and we haven't established a baseline for
4368 : // the container yet (i.e. if we don't have 'align-self: baseline' on any
4369 : // children), then use this child's first baseline as the container's
4370 : // baseline.
4371 0 : if (item == firstItem &&
4372 : flexContainerAscent == nscoord_MIN) {
4373 0 : flexContainerAscent = itemNormalBPos + item->ResolvedAscent(true);
4374 : }
4375 : }
4376 : }
4377 :
4378 0 : if (!placeholderKids.IsEmpty()) {
4379 : ReflowPlaceholders(aPresContext, aReflowInput,
4380 : placeholderKids, containerContentBoxOrigin,
4381 0 : containerSize);
4382 : }
4383 :
4384 : // Compute flex container's desired size (in its own writing-mode),
4385 : // starting w/ content-box size & growing from there:
4386 : LogicalSize desiredSizeInFlexWM =
4387 : aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize,
4388 0 : contentBoxCrossSize);
4389 : // Add border/padding (w/ skipSides already applied):
4390 0 : desiredSizeInFlexWM.ISize(flexWM) += containerBP.IStartEnd(flexWM);
4391 0 : desiredSizeInFlexWM.BSize(flexWM) += containerBP.BStartEnd(flexWM);
4392 :
4393 0 : if (flexContainerAscent == nscoord_MIN) {
4394 : // Still don't have our baseline set -- this happens if we have no
4395 : // children (or if our children are huge enough that they have nscoord_MIN
4396 : // as their baseline... in which case, we'll use the wrong baseline, but no
4397 : // big deal)
4398 0 : NS_WARNING_ASSERTION(
4399 : lines.getFirst()->IsEmpty(),
4400 : "Have flex items but didn't get an ascent - that's odd (or there are "
4401 : "just gigantic sizes involved)");
4402 : // Per spec, synthesize baseline from the flex container's content box
4403 : // (i.e. use block-end side of content-box)
4404 : // XXXdholbert This only makes sense if parent's writing mode is
4405 : // horizontal (& even then, really we should be using the BSize in terms
4406 : // of the parent's writing mode, not ours). Clean up in bug 1155322.
4407 0 : flexContainerAscent = desiredSizeInFlexWM.BSize(flexWM);
4408 : }
4409 :
4410 0 : if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
4411 : // This will force our parent to call GetLogicalBaseline, which will
4412 : // synthesize a margin-box baseline.
4413 0 : aDesiredSize.SetBlockStartAscent(ReflowOutput::ASK_FOR_BASELINE);
4414 : } else {
4415 : // XXXdholbert flexContainerAscent needs to be in terms of
4416 : // our parent's writing-mode here. See bug 1155322.
4417 0 : aDesiredSize.SetBlockStartAscent(flexContainerAscent);
4418 : }
4419 :
4420 : // Now: If we're complete, add bottom border/padding to desired height (which
4421 : // we skipped via skipSides) -- unless that pushes us over available height,
4422 : // in which case we become incomplete (unless we already weren't asking for
4423 : // any height, in which case we stay complete to avoid looping forever).
4424 : // NOTE: If we're auto-height, we allow our bottom border/padding to push us
4425 : // over the available height without requesting a continuation, for
4426 : // consistency with the behavior of "display:block" elements.
4427 0 : if (aStatus.IsComplete()) {
4428 : nscoord desiredBSizeWithBEndBP =
4429 0 : desiredSizeInFlexWM.BSize(flexWM) + blockEndContainerBP;
4430 :
4431 0 : if (aReflowInput.AvailableBSize() == NS_UNCONSTRAINEDSIZE ||
4432 0 : desiredSizeInFlexWM.BSize(flexWM) == 0 ||
4433 0 : desiredBSizeWithBEndBP <= aReflowInput.AvailableBSize() ||
4434 0 : aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
4435 : // Update desired height to include block-end border/padding
4436 0 : desiredSizeInFlexWM.BSize(flexWM) = desiredBSizeWithBEndBP;
4437 : } else {
4438 : // We couldn't fit bottom border/padding, so we'll need a continuation.
4439 0 : aStatus.SetIncomplete();
4440 : }
4441 : }
4442 :
4443 : // Calculate the container baselines so that our parent can baseline-align us.
4444 0 : mBaselineFromLastReflow = flexContainerAscent;
4445 0 : mLastBaselineFromLastReflow = lines.getLast()->GetLastBaselineOffset();
4446 0 : if (mLastBaselineFromLastReflow == nscoord_MIN) {
4447 : // XXX we fall back to a mirrored first baseline here for now, but this
4448 : // should probably use the last baseline of the last item or something.
4449 0 : mLastBaselineFromLastReflow =
4450 0 : desiredSizeInFlexWM.BSize(flexWM) - flexContainerAscent;
4451 : }
4452 :
4453 : // Convert flex container's final desired size to parent's WM, for outparam.
4454 0 : aDesiredSize.SetSize(flexWM, desiredSizeInFlexWM);
4455 :
4456 : // Overflow area = union(my overflow area, kids' overflow areas)
4457 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
4458 0 : for (nsIFrame* childFrame : mFrames) {
4459 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame);
4460 : }
4461 :
4462 0 : FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
4463 0 : aReflowInput, aStatus);
4464 :
4465 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize)
4466 : }
4467 :
4468 : void
4469 0 : nsFlexContainerFrame::MoveFlexItemToFinalPosition(
4470 : const ReflowInput& aReflowInput,
4471 : const FlexItem& aItem,
4472 : LogicalPoint& aFramePos,
4473 : const nsSize& aContainerSize)
4474 : {
4475 0 : WritingMode outerWM = aReflowInput.GetWritingMode();
4476 :
4477 : // If item is relpos, look up its offsets (cached from prev reflow)
4478 0 : LogicalMargin logicalOffsets(outerWM);
4479 0 : if (NS_STYLE_POSITION_RELATIVE == aItem.Frame()->StyleDisplay()->mPosition) {
4480 0 : nsMargin* cachedOffsets = aItem.Frame()->GetProperty(nsIFrame::ComputedOffsetProperty());
4481 0 : MOZ_ASSERT(cachedOffsets,
4482 : "relpos previously-reflowed frame should've cached its offsets");
4483 0 : logicalOffsets = LogicalMargin(outerWM, *cachedOffsets);
4484 : }
4485 0 : ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
4486 : logicalOffsets, &aFramePos,
4487 0 : aContainerSize);
4488 0 : aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize);
4489 0 : PositionFrameView(aItem.Frame());
4490 0 : PositionChildViews(aItem.Frame());
4491 0 : }
4492 :
4493 : void
4494 0 : nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext,
4495 : const FlexboxAxisTracker& aAxisTracker,
4496 : const ReflowInput& aReflowInput,
4497 : const FlexItem& aItem,
4498 : LogicalPoint& aFramePos,
4499 : const nsSize& aContainerSize)
4500 : {
4501 0 : WritingMode outerWM = aReflowInput.GetWritingMode();
4502 0 : WritingMode wm = aItem.Frame()->GetWritingMode();
4503 0 : LogicalSize availSize = aReflowInput.ComputedSize(wm);
4504 0 : availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
4505 : ReflowInput childReflowInput(aPresContext, aReflowInput,
4506 0 : aItem.Frame(), availSize);
4507 :
4508 : // Keep track of whether we've overriden the child's computed height
4509 : // and/or width, so we can set its resize flags accordingly.
4510 0 : bool didOverrideComputedWidth = false;
4511 0 : bool didOverrideComputedHeight = false;
4512 :
4513 : // Override computed main-size
4514 0 : if (aAxisTracker.IsMainAxisHorizontal()) {
4515 0 : childReflowInput.SetComputedWidth(aItem.GetMainSize());
4516 0 : didOverrideComputedWidth = true;
4517 : } else {
4518 0 : childReflowInput.SetComputedHeight(aItem.GetMainSize());
4519 0 : didOverrideComputedHeight = true;
4520 : }
4521 :
4522 : // Override reflow state's computed cross-size if either:
4523 : // - the item was stretched (in which case we're imposing a cross size)
4524 : // ...or...
4525 : // - the item it has an aspect ratio (in which case the cross-size that's
4526 : // currently in the reflow state is based on arithmetic involving a stale
4527 : // main-size value that we just stomped on above). (Note that we could handle
4528 : // this case using an AutoFlexItemMainSizeOverride, as we do elsewhere; but
4529 : // given that we *already know* the correct cross size to use here, it's
4530 : // cheaper to just directly set it instead of setting a frame property.)
4531 0 : if (aItem.IsStretched() ||
4532 0 : aItem.HasIntrinsicRatio()) {
4533 0 : if (aAxisTracker.IsCrossAxisHorizontal()) {
4534 0 : childReflowInput.SetComputedWidth(aItem.GetCrossSize());
4535 0 : didOverrideComputedWidth = true;
4536 : } else {
4537 0 : childReflowInput.SetComputedHeight(aItem.GetCrossSize());
4538 0 : didOverrideComputedHeight = true;
4539 : }
4540 : }
4541 0 : if (aItem.IsStretched() && !aAxisTracker.IsCrossAxisHorizontal()) {
4542 : // If this item's height is stretched, it's a relative height.
4543 0 : aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
4544 : }
4545 :
4546 : // XXXdholbert Might need to actually set the correct margins in the
4547 : // reflow state at some point, so that they can be saved on the frame for
4548 : // UsedMarginProperty(). Maybe doesn't matter though...?
4549 :
4550 : // If we're overriding the computed width or height, *and* we had an
4551 : // earlier "measuring" reflow, then this upcoming reflow needs to be
4552 : // treated as a resize.
4553 0 : if (aItem.HadMeasuringReflow()) {
4554 0 : if (didOverrideComputedWidth) {
4555 : // (This is somewhat redundant, since the reflow state already
4556 : // sets mHResize whenever our computed width has changed since the
4557 : // previous reflow. Still, it's nice for symmetry, and it may become
4558 : // necessary once we support orthogonal flows.)
4559 0 : childReflowInput.SetHResize(true);
4560 : }
4561 0 : if (didOverrideComputedHeight) {
4562 0 : childReflowInput.SetVResize(true);
4563 : }
4564 : }
4565 : // NOTE: Be very careful about doing anything else with childReflowInput
4566 : // after this point, because some of its methods (e.g. SetComputedWidth)
4567 : // internally call InitResizeFlags and stomp on mVResize & mHResize.
4568 :
4569 0 : ReflowOutput childDesiredSize(childReflowInput);
4570 0 : nsReflowStatus childReflowStatus;
4571 0 : ReflowChild(aItem.Frame(), aPresContext,
4572 : childDesiredSize, childReflowInput,
4573 : outerWM, aFramePos, aContainerSize,
4574 0 : 0, childReflowStatus);
4575 :
4576 : // XXXdholbert Once we do pagination / splitting, we'll need to actually
4577 : // handle incomplete childReflowStatuses. But for now, we give our kids
4578 : // unconstrained available height, which means they should always
4579 : // complete.
4580 0 : MOZ_ASSERT(childReflowStatus.IsComplete(),
4581 : "We gave flex item unconstrained available height, so it "
4582 : "should be complete");
4583 :
4584 : LogicalMargin offsets =
4585 0 : childReflowInput.ComputedLogicalOffsets().ConvertTo(outerWM, wm);
4586 0 : ReflowInput::ApplyRelativePositioning(aItem.Frame(), outerWM,
4587 : offsets, &aFramePos,
4588 0 : aContainerSize);
4589 :
4590 0 : FinishReflowChild(aItem.Frame(), aPresContext,
4591 : childDesiredSize, &childReflowInput,
4592 0 : outerWM, aFramePos, aContainerSize, 0);
4593 :
4594 0 : aItem.SetAscent(childDesiredSize.BlockStartAscent());
4595 0 : }
4596 :
4597 : void
4598 0 : nsFlexContainerFrame::ReflowPlaceholders(nsPresContext* aPresContext,
4599 : const ReflowInput& aReflowInput,
4600 : nsTArray<nsIFrame*>& aPlaceholders,
4601 : const LogicalPoint& aContentBoxOrigin,
4602 : const nsSize& aContainerSize)
4603 : {
4604 0 : WritingMode outerWM = aReflowInput.GetWritingMode();
4605 :
4606 : // As noted in this method's documentation, we'll reflow every entry in
4607 : // |aPlaceholders| at the container's content-box origin.
4608 0 : for (nsIFrame* placeholder : aPlaceholders) {
4609 0 : MOZ_ASSERT(placeholder->IsPlaceholderFrame(),
4610 : "placeholders array should only contain placeholder frames");
4611 0 : WritingMode wm = placeholder->GetWritingMode();
4612 0 : LogicalSize availSize = aReflowInput.ComputedSize(wm);
4613 : ReflowInput childReflowInput(aPresContext, aReflowInput,
4614 0 : placeholder, availSize);
4615 0 : ReflowOutput childDesiredSize(childReflowInput);
4616 0 : nsReflowStatus childReflowStatus;
4617 0 : ReflowChild(placeholder, aPresContext,
4618 : childDesiredSize, childReflowInput,
4619 : outerWM, aContentBoxOrigin, aContainerSize, 0,
4620 0 : childReflowStatus);
4621 :
4622 : FinishReflowChild(placeholder, aPresContext,
4623 : childDesiredSize, &childReflowInput,
4624 0 : outerWM, aContentBoxOrigin, aContainerSize, 0);
4625 :
4626 : // Mark the placeholder frame to indicate that it's not actually at the
4627 : // element's static position, because we need to apply CSS Alignment after
4628 : // we determine the OOF's size:
4629 0 : placeholder->AddStateBits(PLACEHOLDER_STATICPOS_NEEDS_CSSALIGN);
4630 : }
4631 0 : }
4632 :
4633 : /* virtual */ nscoord
4634 0 : nsFlexContainerFrame::GetMinISize(gfxContext* aRenderingContext)
4635 : {
4636 0 : nscoord minISize = 0;
4637 0 : DISPLAY_MIN_WIDTH(this, minISize);
4638 :
4639 0 : RenumberList();
4640 :
4641 0 : const nsStylePosition* stylePos = StylePosition();
4642 0 : const FlexboxAxisTracker axisTracker(this, GetWritingMode());
4643 :
4644 0 : for (nsIFrame* childFrame : mFrames) {
4645 : nscoord childMinISize =
4646 0 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
4647 0 : nsLayoutUtils::MIN_ISIZE);
4648 : // For a horizontal single-line flex container, the intrinsic min
4649 : // isize is the sum of its items' min isizes.
4650 : // For a column-oriented flex container, or for a multi-line row-
4651 : // oriented flex container, the intrinsic min isize is the max of
4652 : // its items' min isizes.
4653 0 : if (axisTracker.IsRowOriented() &&
4654 0 : NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) {
4655 0 : minISize += childMinISize;
4656 : } else {
4657 0 : minISize = std::max(minISize, childMinISize);
4658 : }
4659 : }
4660 0 : return minISize;
4661 : }
4662 :
4663 : /* virtual */ nscoord
4664 0 : nsFlexContainerFrame::GetPrefISize(gfxContext* aRenderingContext)
4665 : {
4666 0 : nscoord prefISize = 0;
4667 0 : DISPLAY_PREF_WIDTH(this, prefISize);
4668 :
4669 0 : RenumberList();
4670 :
4671 : // XXXdholbert Optimization: We could cache our intrinsic widths like
4672 : // nsBlockFrame does (and return it early from this function if it's set).
4673 : // Whenever anything happens that might change it, set it to
4674 : // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicISizesDirty
4675 : // does)
4676 0 : const FlexboxAxisTracker axisTracker(this, GetWritingMode());
4677 :
4678 0 : for (nsIFrame* childFrame : mFrames) {
4679 : nscoord childPrefISize =
4680 0 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
4681 0 : nsLayoutUtils::PREF_ISIZE);
4682 0 : if (axisTracker.IsRowOriented()) {
4683 0 : prefISize += childPrefISize;
4684 : } else {
4685 0 : prefISize = std::max(prefISize, childPrefISize);
4686 : }
4687 : }
4688 0 : return prefISize;
4689 : }
|