Coverage Report

Created: 2018-09-25 14:53

/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
}