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