/src/mozilla-central/layout/generic/nsFlexContainerFrame.h
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" and "display: -webkit-box" */ |
8 | | |
9 | | #ifndef nsFlexContainerFrame_h___ |
10 | | #define nsFlexContainerFrame_h___ |
11 | | |
12 | | #include "nsContainerFrame.h" |
13 | | #include "mozilla/UniquePtr.h" |
14 | | |
15 | | class nsStyleCoord; |
16 | | |
17 | | namespace mozilla { |
18 | | template <class T> class LinkedList; |
19 | | class LogicalPoint; |
20 | | } // namespace mozilla |
21 | | |
22 | | nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell, |
23 | | mozilla::ComputedStyle* aStyle); |
24 | | |
25 | | /** |
26 | | * These structures are used to capture data during reflow to be |
27 | | * extracted by devtools via Chrome APIs. The structures are only |
28 | | * created when requested in GetFlexFrameWithComputedInfo(), and |
29 | | * the structures are attached to the nsFlexContainerFrame via the |
30 | | * FlexContainerInfo property. |
31 | | */ |
32 | | struct ComputedFlexItemInfo |
33 | | { |
34 | | nsCOMPtr<nsINode> mNode; |
35 | | /** |
36 | | * mMainBaseSize is a measure of the size of the item in the main |
37 | | * axis before the flex sizing algorithm is applied. In the spec, |
38 | | * this is called "flex base size", but we use this name to connect |
39 | | * the value to the other main axis sizes. |
40 | | */ |
41 | | nscoord mMainBaseSize; |
42 | | /** |
43 | | * mMainDeltaSize is the value that the flex sizing algorithm |
44 | | * "wants" to use to stretch or shrink the item, before clamping to |
45 | | * the item's main min and max sizes. Since the flex sizing |
46 | | * algorithm proceeds linearly, the mMainDeltaSize for an item only |
47 | | * respects the resolved size of items already frozen. |
48 | | */ |
49 | | nscoord mMainDeltaSize; |
50 | | nscoord mMainMinSize; |
51 | | nscoord mMainMaxSize; |
52 | | nscoord mCrossMinSize; |
53 | | nscoord mCrossMaxSize; |
54 | | }; |
55 | | |
56 | | struct ComputedFlexLineInfo |
57 | | { |
58 | | nsTArray<ComputedFlexItemInfo> mItems; |
59 | | nscoord mCrossStart; |
60 | | nscoord mCrossSize; |
61 | | nscoord mFirstBaselineOffset; |
62 | | nscoord mLastBaselineOffset; |
63 | | enum GrowthState { |
64 | | UNCHANGED, |
65 | | SHRINKING, |
66 | | GROWING, |
67 | | } mGrowthState; |
68 | | }; |
69 | | |
70 | | struct ComputedFlexContainerInfo |
71 | | { |
72 | | nsTArray<ComputedFlexLineInfo> mLines; |
73 | | }; |
74 | | |
75 | | /** |
76 | | * This is the rendering object used for laying out elements with |
77 | | * "display: flex" or "display: inline-flex". |
78 | | * |
79 | | * We also use this class for elements with "display: -webkit-box" or |
80 | | * "display: -webkit-inline-box" (but not "-moz-box" / "-moz-inline-box" -- |
81 | | * those are rendered with old-school XUL frame classes). |
82 | | * |
83 | | * Note: we represent the -webkit-box family of properties (-webkit-box-orient, |
84 | | * -webkit-box-flex, etc.) as aliases for their -moz equivalents. And for |
85 | | * -webkit-{inline-}box containers, nsFlexContainerFrame will honor those |
86 | | * "legacy" properties for alignment/flexibility/etc. *instead of* honoring the |
87 | | * modern flexbox & alignment properties. For brevity, many comments in |
88 | | * nsFlexContainerFrame.cpp simply refer to these properties using their |
89 | | * "-webkit" versions, since we're mostly expecting to encounter them in that |
90 | | * form. (Technically, the "-moz" versions of these properties *can* influence |
91 | | * layout here as well (since that's what the -webkit versions are aliased to) |
92 | | * -- but only inside of a "display:-webkit-{inline-}box" container.) |
93 | | */ |
94 | | class nsFlexContainerFrame final : public nsContainerFrame |
95 | | { |
96 | | public: |
97 | | NS_DECL_FRAMEARENA_HELPERS(nsFlexContainerFrame) |
98 | | NS_DECL_QUERYFRAME |
99 | | |
100 | | // Factory method: |
101 | | friend nsContainerFrame* NS_NewFlexContainerFrame(nsIPresShell* aPresShell, |
102 | | ComputedStyle* aStyle); |
103 | | |
104 | | // Forward-decls of helper classes |
105 | | class FlexItem; |
106 | | class FlexLine; |
107 | | class FlexboxAxisTracker; |
108 | | struct StrutInfo; |
109 | | class CachedMeasuringReflowResult; |
110 | | |
111 | | // nsIFrame overrides |
112 | | void Init(nsIContent* aContent, |
113 | | nsContainerFrame* aParent, |
114 | | nsIFrame* aPrevInFlow) override; |
115 | | |
116 | | void BuildDisplayList(nsDisplayListBuilder* aBuilder, |
117 | | const nsDisplayListSet& aLists) override; |
118 | | |
119 | | void MarkIntrinsicISizesDirty() override; |
120 | | |
121 | | void Reflow(nsPresContext* aPresContext, |
122 | | ReflowOutput& aDesiredSize, |
123 | | const ReflowInput& aReflowInput, |
124 | | nsReflowStatus& aStatus) override; |
125 | | |
126 | | nscoord GetMinISize(gfxContext* aRenderingContext) override; |
127 | | nscoord GetPrefISize(gfxContext* aRenderingContext) override; |
128 | | |
129 | | #ifdef DEBUG_FRAME_DUMP |
130 | | nsresult GetFrameName(nsAString& aResult) const override; |
131 | | #endif |
132 | | |
133 | | nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override; |
134 | | |
135 | | bool GetVerticalAlignBaseline(mozilla::WritingMode aWM, |
136 | | nscoord* aBaseline) const override |
137 | 0 | { |
138 | 0 | return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline); |
139 | 0 | } |
140 | | |
141 | | bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM, |
142 | | BaselineSharingGroup aBaselineGroup, |
143 | | nscoord* aBaseline) const override |
144 | 0 | { |
145 | 0 | if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) { |
146 | 0 | return false; |
147 | 0 | } |
148 | 0 | *aBaseline = aBaselineGroup == BaselineSharingGroup::eFirst ? |
149 | 0 | mBaselineFromLastReflow : mLastBaselineFromLastReflow; |
150 | 0 | return true; |
151 | 0 | } |
152 | | |
153 | | // nsContainerFrame overrides |
154 | | uint16_t CSSAlignmentForAbsPosChild( |
155 | | const ReflowInput& aChildRI, |
156 | | mozilla::LogicalAxis aLogicalAxis) const override; |
157 | | |
158 | | /** |
159 | | * Helper function to calculate packing space and initial offset of alignment |
160 | | * subjects in MainAxisPositionTracker() and CrossAxisPositionTracker() for |
161 | | * space-between, space-around, and space-evenly. |
162 | | * * @param aNumThingsToPack Number of alignment subjects. |
163 | | * @param aAlignVal Value for align-self or justify-self. |
164 | | * @param aFirstSubjectOffset Outparam for first subject offset. |
165 | | * @param aNumPackingSpacesRemaining Outparam for number of equal-sized |
166 | | * packing spaces to apply between each |
167 | | * alignment subject. |
168 | | * @param aPackingSpaceRemaining Outparam for total amount of packing |
169 | | * space to be divided up. |
170 | | */ |
171 | | static void CalculatePackingSpace(uint32_t aNumThingsToPack, |
172 | | uint8_t aAlignVal, |
173 | | nscoord* aFirstSubjectOffset, |
174 | | uint32_t* aNumPackingSpacesRemaining, |
175 | | nscoord* aPackingSpaceRemaining); |
176 | | |
177 | | /** |
178 | | * This property is created by a call to |
179 | | * nsFlexContainerFrame::GetFlexFrameWithComputedInfo. |
180 | | */ |
181 | | NS_DECLARE_FRAME_PROPERTY_DELETABLE(FlexContainerInfo, ComputedFlexContainerInfo) |
182 | | /** |
183 | | * This function should only be called on a nsFlexContainerFrame |
184 | | * that has just been returned by a call to |
185 | | * GetFlexFrameWithComputedInfo. |
186 | | */ |
187 | | const ComputedFlexContainerInfo* GetFlexContainerInfo() |
188 | 0 | { |
189 | 0 | const ComputedFlexContainerInfo* info = GetProperty(FlexContainerInfo()); |
190 | 0 | MOZ_ASSERT(info, "Property generation wasn't requested."); |
191 | 0 | return info; |
192 | 0 | } |
193 | | |
194 | | /** |
195 | | * Return aFrame as a flex frame after ensuring it has computed flex info. |
196 | | * @return nullptr if aFrame is null or doesn't have a flex frame |
197 | | * as its content insertion frame. |
198 | | * @note this might destroy layout/style data since it may flush layout. |
199 | | */ |
200 | | static nsFlexContainerFrame* GetFlexFrameWithComputedInfo(nsIFrame* aFrame); |
201 | | |
202 | | /** |
203 | | * Given a frame for a flex item, this method returns true IFF that flex |
204 | | * item's inline axis is the same as (i.e. not orthogonal to) its flex |
205 | | * container's main axis. |
206 | | * |
207 | | * (This method is only intended to be used from external |
208 | | * callers. Inside of flex reflow code, FlexItem::IsInlineAxisMainAxis() is |
209 | | * equivalent & more optimal.) |
210 | | * |
211 | | * @param aFrame a flex item (must return true from IsFlexItem) |
212 | | * @return true iff aFrame's inline axis is the same as (i.e. not orthogonal |
213 | | * to) its flex container's main axis. Otherwise, false. |
214 | | */ |
215 | | static bool IsItemInlineAxisMainAxis(nsIFrame* aFrame); |
216 | | |
217 | | /** |
218 | | * Returns true iff the given computed 'flex-basis' & main-size property |
219 | | * values collectively represent a used flex-basis of 'content'. |
220 | | * See https://drafts.csswg.org/css-flexbox-1/#valdef-flex-basis-auto |
221 | | * |
222 | | * @param aFlexBasis the computed 'flex-basis' for a flex item. |
223 | | * @param aMainSize the computed main-size property for a flex item. |
224 | | */ |
225 | | static bool IsUsedFlexBasisContent(const nsStyleCoord* aFlexBasis, |
226 | | const nsStyleCoord* aMainSize); |
227 | | |
228 | | protected: |
229 | | // Protected constructor & destructor |
230 | | explicit nsFlexContainerFrame(ComputedStyle* aStyle) |
231 | | : nsContainerFrame(aStyle, kClassID) |
232 | | , mCachedMinISize(NS_INTRINSIC_WIDTH_UNKNOWN) |
233 | | , mCachedPrefISize(NS_INTRINSIC_WIDTH_UNKNOWN) |
234 | | , mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN) |
235 | | , mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN) |
236 | 0 | {} |
237 | | |
238 | | virtual ~nsFlexContainerFrame(); |
239 | | |
240 | | /* |
241 | | * This method does the bulk of the flex layout, implementing the algorithm |
242 | | * described at: |
243 | | * http://dev.w3.org/csswg/css-flexbox/#layout-algorithm |
244 | | * (with a few initialization pieces happening in the caller, Reflow(). |
245 | | * |
246 | | * Since this is a helper for Reflow(), this takes all the same parameters |
247 | | * as Reflow(), plus a few more parameters that Reflow() sets up for us. |
248 | | * |
249 | | * (The logic behind the division of work between Reflow and DoFlexLayout is |
250 | | * as follows: DoFlexLayout() begins at the step that we have to jump back |
251 | | * to, if we find any visibility:collapse children, and Reflow() does |
252 | | * everything before that point.) |
253 | | */ |
254 | | void DoFlexLayout(nsPresContext* aPresContext, |
255 | | ReflowOutput& aDesiredSize, |
256 | | const ReflowInput& aReflowInput, |
257 | | nsReflowStatus& aStatus, |
258 | | nscoord aContentBoxMainSize, |
259 | | nscoord aAvailableBSizeForContent, |
260 | | nsTArray<StrutInfo>& aStruts, |
261 | | const FlexboxAxisTracker& aAxisTracker, |
262 | | nscoord aMainGapSize, |
263 | | nscoord aCrossGapSize); |
264 | | |
265 | | /** |
266 | | * Checks whether our child-frame list "mFrames" is sorted, using the given |
267 | | * IsLessThanOrEqual function, and sorts it if it's not already sorted. |
268 | | * |
269 | | * XXXdholbert Once we support pagination, we need to make this function |
270 | | * check our continuations as well (or wrap it in a function that does). |
271 | | * |
272 | | * @return true if we had to sort mFrames, false if it was already sorted. |
273 | | */ |
274 | | template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)> |
275 | | bool SortChildrenIfNeeded(); |
276 | | |
277 | | // Protected flex-container-specific methods / member-vars |
278 | | #ifdef DEBUG |
279 | | void SanityCheckAnonymousFlexItems() const; |
280 | | #endif // DEBUG |
281 | | |
282 | | /* |
283 | | * Returns a new FlexItem for the given child frame, allocated on the heap. |
284 | | * Guaranteed to return non-null. Caller is responsible for managing the |
285 | | * FlexItem's lifetime. |
286 | | * |
287 | | * Before returning, this method also processes the FlexItem to resolve its |
288 | | * flex basis (including e.g. auto-height) as well as to resolve |
289 | | * "min-height:auto", via ResolveAutoFlexBasisAndMinSize(). (Basically, the |
290 | | * returned FlexItem will be ready to participate in the "Resolve the |
291 | | * Flexible Lengths" step of the Flex Layout Algorithm.) |
292 | | */ |
293 | | mozilla::UniquePtr<FlexItem> GenerateFlexItemForChild(nsPresContext* aPresContext, |
294 | | nsIFrame* aChildFrame, |
295 | | const ReflowInput& aParentReflowInput, |
296 | | const FlexboxAxisTracker& aAxisTracker); |
297 | | |
298 | | /** |
299 | | * This method gets a cached measuring reflow for a flex item, or does it and |
300 | | * caches it. |
301 | | * |
302 | | * This avoids exponential reflows, see the comment on |
303 | | * CachedMeasuringReflowResult. |
304 | | */ |
305 | | const CachedMeasuringReflowResult& MeasureAscentAndBSizeForFlexItem( |
306 | | FlexItem& aItem, |
307 | | nsPresContext* aPresContext, |
308 | | ReflowInput& aChildReflowInput); |
309 | | |
310 | | /** |
311 | | * This method performs a "measuring" reflow to get the content BSize of |
312 | | * aFlexItem.Frame() (treating it as if it had a computed BSize of "auto"), |
313 | | * and returns the resulting BSize measurement. |
314 | | * (Helper for ResolveAutoFlexBasisAndMinSize().) |
315 | | */ |
316 | | nscoord MeasureFlexItemContentBSize(nsPresContext* aPresContext, |
317 | | FlexItem& aFlexItem, |
318 | | bool aForceBResizeForMeasuringReflow, |
319 | | const ReflowInput& aParentReflowInput); |
320 | | |
321 | | /** |
322 | | * This method resolves an "auto" flex-basis and/or min-main-size value |
323 | | * on aFlexItem, if needed. |
324 | | * (Helper for GenerateFlexItemForChild().) |
325 | | */ |
326 | | void ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext, |
327 | | FlexItem& aFlexItem, |
328 | | const ReflowInput& aItemReflowInput, |
329 | | const FlexboxAxisTracker& aAxisTracker); |
330 | | |
331 | | /** |
332 | | * Returns true if "this" is the nsFlexContainerFrame for a -moz-box or |
333 | | * a -moz-inline-box -- these boxes have special behavior for flex items with |
334 | | * "visibility:collapse". |
335 | | * |
336 | | * @param aFlexStyleDisp This frame's StyleDisplay(). (Just an optimization to |
337 | | * avoid repeated lookup; some callers already have it.) |
338 | | * @return true if "this" is the nsFlexContainerFrame for a -moz-{inline}box. |
339 | | */ |
340 | | bool ShouldUseMozBoxCollapseBehavior(const nsStyleDisplay* aFlexStyleDisp); |
341 | | |
342 | | /** |
343 | | * This method: |
344 | | * - Creates FlexItems for all of our child frames (except placeholders). |
345 | | * - Groups those FlexItems into FlexLines. |
346 | | * - Returns those FlexLines in the outparam |aLines|. |
347 | | * |
348 | | * For any child frames which are placeholders, this method will instead just |
349 | | * append that child to the outparam |aPlaceholders| for separate handling. |
350 | | * (Absolutely positioned children of a flex container are *not* flex items.) |
351 | | */ |
352 | | void GenerateFlexLines(nsPresContext* aPresContext, |
353 | | const ReflowInput& aReflowInput, |
354 | | nscoord aContentBoxMainSize, |
355 | | nscoord aAvailableBSizeForContent, |
356 | | const nsTArray<StrutInfo>& aStruts, |
357 | | const FlexboxAxisTracker& aAxisTracker, |
358 | | nscoord aMainGapSize, |
359 | | nsTArray<nsIFrame*>& aPlaceholders, |
360 | | mozilla::LinkedList<FlexLine>& aLines); |
361 | | |
362 | | nscoord GetMainSizeFromReflowInput(const ReflowInput& aReflowInput, |
363 | | const FlexboxAxisTracker& aAxisTracker); |
364 | | |
365 | | nscoord ComputeCrossSize(const ReflowInput& aReflowInput, |
366 | | const FlexboxAxisTracker& aAxisTracker, |
367 | | nscoord aSumLineCrossSizes, |
368 | | nscoord aAvailableBSizeForContent, |
369 | | bool* aIsDefinite, |
370 | | nsReflowStatus& aStatus); |
371 | | |
372 | | void SizeItemInCrossAxis(nsPresContext* aPresContext, |
373 | | const FlexboxAxisTracker& aAxisTracker, |
374 | | ReflowInput& aChildReflowInput, |
375 | | FlexItem& aItem); |
376 | | |
377 | | /** |
378 | | * Moves the given flex item's frame to the given LogicalPosition (modulo any |
379 | | * relative positioning). |
380 | | * |
381 | | * This can be used in cases where we've already done a "measuring reflow" |
382 | | * for the flex item at the correct size, and hence can skip its final reflow |
383 | | * (but still need to move it to the right final position). |
384 | | * |
385 | | * @param aReflowInput The flex container's reflow state. |
386 | | * @param aItem The flex item whose frame should be moved. |
387 | | * @param aFramePos The position where the flex item's frame should |
388 | | * be placed. (pre-relative positioning) |
389 | | * @param aContainerSize The flex container's size (required by some methods |
390 | | * that we call, to interpret aFramePos correctly). |
391 | | */ |
392 | | void MoveFlexItemToFinalPosition(const ReflowInput& aReflowInput, |
393 | | const FlexItem& aItem, |
394 | | mozilla::LogicalPoint& aFramePos, |
395 | | const nsSize& aContainerSize); |
396 | | /** |
397 | | * Helper-function to reflow a child frame, at its final position determined |
398 | | * by flex layout. |
399 | | * |
400 | | * @param aPresContext The presentation context being used in reflow. |
401 | | * @param aAxisTracker A FlexboxAxisTracker with the flex container's axes. |
402 | | * @param aReflowInput The flex container's reflow state. |
403 | | * @param aItem The flex item to be reflowed. |
404 | | * @param aFramePos The position where the flex item's frame should |
405 | | * be placed. (pre-relative positioning) |
406 | | * @param aContainerSize The flex container's size (required by some methods |
407 | | * that we call, to interpret aFramePos correctly). |
408 | | */ |
409 | | void ReflowFlexItem(nsPresContext* aPresContext, |
410 | | const FlexboxAxisTracker& aAxisTracker, |
411 | | const ReflowInput& aReflowInput, |
412 | | const FlexItem& aItem, |
413 | | mozilla::LogicalPoint& aFramePos, |
414 | | const nsSize& aContainerSize); |
415 | | |
416 | | /** |
417 | | * Helper-function to perform a "dummy reflow" on all our nsPlaceholderFrame |
418 | | * children, at the container's content-box origin. |
419 | | * |
420 | | * This doesn't actually represent the static position of the placeholders' |
421 | | * out-of-flow (OOF) frames -- we can't compute that until we've reflowed the |
422 | | * OOF, because (depending on the CSS Align properties) the static position |
423 | | * may be influenced by the OOF's size. So for now, we just co-opt the |
424 | | * placeholder to store the flex container's logical content-box origin, and |
425 | | * we defer to nsAbsoluteContainingBlock to determine the OOF's actual static |
426 | | * position (using this origin, the OOF's size, and the CSS Align |
427 | | * properties). |
428 | | * |
429 | | * @param aPresContext The presentation context being used in reflow. |
430 | | * @param aReflowInput The flex container's reflow input. |
431 | | * @param aPlaceholders An array of all the flex container's |
432 | | * nsPlaceholderFrame children. |
433 | | * @param aContentBoxOrigin The flex container's logical content-box |
434 | | * origin (in its own coordinate space). |
435 | | * @param aContainerSize The flex container's size (required by some |
436 | | * reflow methods to interpret positions correctly). |
437 | | */ |
438 | | void ReflowPlaceholders(nsPresContext* aPresContext, |
439 | | const ReflowInput& aReflowInput, |
440 | | nsTArray<nsIFrame*>& aPlaceholders, |
441 | | const mozilla::LogicalPoint& aContentBoxOrigin, |
442 | | const nsSize& aContainerSize); |
443 | | |
444 | | /** |
445 | | * Helper for GetMinISize / GetPrefISize. |
446 | | */ |
447 | | nscoord IntrinsicISize(gfxContext* aRenderingContext, |
448 | | nsLayoutUtils::IntrinsicISizeType aType); |
449 | | |
450 | | /** |
451 | | * Cached values to optimize GetMinISize/GetPrefISize. |
452 | | */ |
453 | | nscoord mCachedMinISize; |
454 | | nscoord mCachedPrefISize; |
455 | | |
456 | | nscoord mBaselineFromLastReflow; |
457 | | // Note: the last baseline is a distance from our border-box end edge. |
458 | | nscoord mLastBaselineFromLastReflow; |
459 | | }; |
460 | | |
461 | | #endif /* nsFlexContainerFrame_h___ */ |