Coverage Report

Created: 2018-09-25 14:53

/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___ */