layout/generic/nsFlexContainerFrame.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     4 /* This Source Code is subject to the terms of the Mozilla Public License
     5  * version 2.0 (the "License"). You can obtain a copy of the License at
     6  * http://mozilla.org/MPL/2.0/. */
     8 /* rendering object for CSS "display: flex" */
    10 #include "nsFlexContainerFrame.h"
    11 #include "nsContentUtils.h"
    12 #include "nsCSSAnonBoxes.h"
    13 #include "nsDisplayList.h"
    14 #include "nsIFrameInlines.h"
    15 #include "nsLayoutUtils.h"
    16 #include "nsPlaceholderFrame.h"
    17 #include "nsPresContext.h"
    18 #include "nsStyleContext.h"
    19 #include "prlog.h"
    20 #include <algorithm>
    21 #include "mozilla/LinkedList.h"
    23 using namespace mozilla;
    24 using namespace mozilla::css;
    25 using namespace mozilla::layout;
    27 // Convenience typedefs for helper classes that we forward-declare in .h file
    28 // (so that nsFlexContainerFrame methods can use them as parameters):
    29 typedef nsFlexContainerFrame::FlexItem FlexItem;
    30 typedef nsFlexContainerFrame::FlexLine FlexLine;
    31 typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker;
    32 typedef nsFlexContainerFrame::StrutInfo StrutInfo;
    34 #ifdef PR_LOGGING
    35 static PRLogModuleInfo*
    36 GetFlexContainerLog()
    37 {
    38   static PRLogModuleInfo *sLog;
    39   if (!sLog)
    40     sLog = PR_NewLogModule("nsFlexContainerFrame");
    41   return sLog;
    42 }
    43 #endif /* PR_LOGGING */
    45 // XXXdholbert Some of this helper-stuff should be separated out into a general
    46 // "LogicalAxisUtils.h" helper.  Should that be a class, or a namespace (under
    47 // what super-namespace?), or what?
    49 // Helper enums
    50 // ============
    52 // Represents a physical orientation for an axis.
    53 // The directional suffix indicates the direction in which the axis *grows*.
    54 // So e.g. eAxis_LR means a horizontal left-to-right axis, whereas eAxis_BT
    55 // means a vertical bottom-to-top axis.
    56 // NOTE: The order here is important -- these values are used as indices into
    57 // the static array 'kAxisOrientationToSidesMap', defined below.
    58 enum AxisOrientationType {
    59   eAxis_LR,
    60   eAxis_RL,
    61   eAxis_TB,
    62   eAxis_BT,
    63   eNumAxisOrientationTypes // For sizing arrays that use these values as indices
    64 };
    66 // Represents one or the other extreme of an axis (e.g. for the main axis, the
    67 // main-start vs. main-end edge.
    68 // NOTE: The order here is important -- these values are used as indices into
    69 // the sub-arrays in 'kAxisOrientationToSidesMap', defined below.
    70 enum AxisEdgeType {
    71   eAxisEdge_Start,
    72   eAxisEdge_End,
    73   eNumAxisEdges // For sizing arrays that use these values as indices
    74 };
    76 // This array maps each axis orientation to a pair of corresponding
    77 // [start, end] physical mozilla::css::Side values.
    78 static const Side
    79 kAxisOrientationToSidesMap[eNumAxisOrientationTypes][eNumAxisEdges] = {
    80   { eSideLeft,   eSideRight  },  // eAxis_LR
    81   { eSideRight,  eSideLeft   },  // eAxis_RL
    82   { eSideTop,    eSideBottom },  // eAxis_TB
    83   { eSideBottom, eSideTop }      // eAxis_BT
    84 };
    86 // Helper structs / classes / methods
    87 // ==================================
    89 // Indicates whether advancing along the given axis is equivalent to
    90 // increasing our X or Y position (as opposed to decreasing it).
    91 static inline bool
    92 AxisGrowsInPositiveDirection(AxisOrientationType aAxis)
    93 {
    94   return eAxis_LR == aAxis || eAxis_TB == aAxis;
    95 }
    97 // Indicates whether the given axis is horizontal.
    98 static inline bool
    99 IsAxisHorizontal(AxisOrientationType aAxis)
   100 {
   101   return eAxis_LR == aAxis || eAxis_RL == aAxis;
   102 }
   104 // Given an AxisOrientationType, returns the "reverse" AxisOrientationType
   105 // (in the same dimension, but the opposite direction)
   106 static inline AxisOrientationType
   107 GetReverseAxis(AxisOrientationType aAxis)
   108 {
   109   AxisOrientationType reversedAxis;
   111   if (aAxis % 2 == 0) {
   112     // even enum value. Add 1 to reverse.
   113     reversedAxis = AxisOrientationType(aAxis + 1);
   114   } else {
   115     // odd enum value. Subtract 1 to reverse.
   116     reversedAxis = AxisOrientationType(aAxis - 1);
   117   }
   119   // Check that we're still in the enum's valid range
   120   MOZ_ASSERT(reversedAxis >= eAxis_LR &&
   121              reversedAxis <= eAxis_BT);
   123   return reversedAxis;
   124 }
   126 // Returns aFrame's computed value for 'height' or 'width' -- whichever is in
   127 // the same dimension as aAxis.
   128 static inline const nsStyleCoord&
   129 GetSizePropertyForAxis(const nsIFrame* aFrame, AxisOrientationType aAxis)
   130 {
   131   const nsStylePosition* stylePos = aFrame->StylePosition();
   133   return IsAxisHorizontal(aAxis) ?
   134     stylePos->mWidth :
   135     stylePos->mHeight;
   136 }
   138 /**
   139  * Converts a logical position in a given axis into a position in the
   140  * corresponding physical (x or y) axis. If the logical axis already maps
   141  * directly onto one of our physical axes (i.e. LTR or TTB), then the logical
   142  * and physical positions are equal; otherwise, we subtract the logical
   143  * position from the container-size in that axis, to flip the polarity.
   144  * (so e.g. a logical position of 2px in a RTL 20px-wide container
   145  * would correspond to a physical position of 18px.)
   146  */
   147 static nscoord
   148 PhysicalPosFromLogicalPos(nscoord aLogicalPosn,
   149                           nscoord aLogicalContainerSize,
   150                           AxisOrientationType aAxis) {
   151   if (AxisGrowsInPositiveDirection(aAxis)) {
   152     return aLogicalPosn;
   153   }
   154   return aLogicalContainerSize - aLogicalPosn;
   155 }
   157 static nscoord
   158 MarginComponentForSide(const nsMargin& aMargin, Side aSide)
   159 {
   160   switch (aSide) {
   161     case eSideLeft:
   162       return aMargin.left;
   163     case eSideRight:
   164       return aMargin.right;
   165     case eSideTop:
   166       return aMargin.top;
   167     case eSideBottom:
   168       return aMargin.bottom;
   169   }
   171   NS_NOTREACHED("unexpected Side enum");
   172   return aMargin.left; // have to return something
   173                        // (but something's busted if we got here)
   174 }
   176 static nscoord&
   177 MarginComponentForSide(nsMargin& aMargin, Side aSide)
   178 {
   179   switch (aSide) {
   180     case eSideLeft:
   181       return aMargin.left;
   182     case eSideRight:
   183       return aMargin.right;
   184     case eSideTop:
   185       return aMargin.top;
   186     case eSideBottom:
   187       return aMargin.bottom;
   188   }
   190   NS_NOTREACHED("unexpected Side enum");
   191   return aMargin.left; // have to return something
   192                        // (but something's busted if we got here)
   193 }
   195 // Helper-macro to let us pick one of two expressions to evaluate
   196 // (a width expression vs. a height expression), to get a main-axis or
   197 // cross-axis component.
   198 // For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent
   199 // and GetCrossComponent are cleaner; but in cases where we simply have
   200 // two separate expressions for width and height (which may be expensive to
   201 // evaluate), these macros will ensure that only the expression for the correct
   202 // axis gets evaluated.
   203 #define GET_MAIN_COMPONENT(axisTracker_, width_, height_)  \
   204   IsAxisHorizontal((axisTracker_).GetMainAxis()) ? (width_) : (height_)
   206 #define GET_CROSS_COMPONENT(axisTracker_, width_, height_)  \
   207   IsAxisHorizontal((axisTracker_).GetCrossAxis()) ? (width_) : (height_)
   209 // Encapsulates our flex container's main & cross axes.
   210 class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
   211 public:
   212   FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame);
   214   // Accessors:
   215   AxisOrientationType GetMainAxis() const  { return mMainAxis;  }
   216   AxisOrientationType GetCrossAxis() const { return mCrossAxis; }
   218   nscoord GetMainComponent(const nsSize& aSize) const {
   219     return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height);
   220   }
   221   int32_t GetMainComponent(const nsIntSize& aIntSize) const {
   222     return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height);
   223   }
   225   nscoord GetCrossComponent(const nsSize& aSize) const {
   226     return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height);
   227   }
   228   int32_t GetCrossComponent(const nsIntSize& aIntSize) const {
   229     return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height);
   230   }
   232   nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const {
   233     return IsAxisHorizontal(mMainAxis) ?
   234       aMargin.LeftRight() :
   235       aMargin.TopBottom();
   236   }
   237   nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const {
   238     return IsAxisHorizontal(mCrossAxis) ?
   239       aMargin.LeftRight() :
   240       aMargin.TopBottom();
   241   }
   243   /**
   244    * Converts a logical point into a "physical" x,y point.
   245    *
   246    * In the simplest case where the main-axis is left-to-right and the
   247    * cross-axis is top-to-bottom, this just returns
   248    * nsPoint(aMainPosn, aCrossPosn).
   249    *
   250    *  @arg aMainPosn  The main-axis position -- i.e an offset from the
   251    *                  main-start edge of the container's content box.
   252    *  @arg aCrossPosn The cross-axis position -- i.e an offset from the
   253    *                  cross-start edge of the container's content box.
   254    *  @return A nsPoint representing the same position (in coordinates
   255    *          relative to the container's content box).
   256    */
   257   nsPoint PhysicalPointFromLogicalPoint(nscoord aMainPosn,
   258                                         nscoord aCrossPosn,
   259                                         nscoord aContainerMainSize,
   260                                         nscoord aContainerCrossSize) const {
   261     nscoord physicalPosnInMainAxis =
   262       PhysicalPosFromLogicalPos(aMainPosn, aContainerMainSize, mMainAxis);
   263     nscoord physicalPosnInCrossAxis =
   264       PhysicalPosFromLogicalPos(aCrossPosn, aContainerCrossSize, mCrossAxis);
   266     return IsAxisHorizontal(mMainAxis) ?
   267       nsPoint(physicalPosnInMainAxis, physicalPosnInCrossAxis) :
   268       nsPoint(physicalPosnInCrossAxis, physicalPosnInMainAxis);
   269   }
   270   nsSize PhysicalSizeFromLogicalSizes(nscoord aMainSize,
   271                                       nscoord aCrossSize) const {
   272     return IsAxisHorizontal(mMainAxis) ?
   273       nsSize(aMainSize, aCrossSize) :
   274       nsSize(aCrossSize, aMainSize);
   275   }
   277   // Are my axes reversed with respect to what the author asked for?
   278   // (We may reverse the axes in the FlexboxAxisTracker constructor and set
   279   // this flag, to avoid reflowing our children in bottom-to-top order.)
   280   bool AreAxesInternallyReversed() const
   281   {
   282     return mAreAxesInternallyReversed;
   283   }
   285 private:
   286   AxisOrientationType mMainAxis;
   287   AxisOrientationType mCrossAxis;
   288   bool mAreAxesInternallyReversed;
   289 };
   291 /**
   292  * Represents a flex item.
   293  * Includes the various pieces of input that the Flexbox Layout Algorithm uses
   294  * to resolve a flexible width.
   295  */
   296 class nsFlexContainerFrame::FlexItem : public LinkedListElement<FlexItem>
   297 {
   298 public:
   299   // Normal constructor:
   300   FlexItem(nsIFrame* aChildFrame,
   301            float aFlexGrow, float aFlexShrink, nscoord aMainBaseSize,
   302            nscoord aMainMinSize, nscoord aMainMaxSize,
   303            nscoord aCrossMinSize, nscoord aCrossMaxSize,
   304            nsMargin aMargin, nsMargin aBorderPadding,
   305            const FlexboxAxisTracker& aAxisTracker);
   307   // Simplified constructor, to be used only for generating "struts":
   308   FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize);
   310   // Accessors
   311   nsIFrame* Frame() const          { return mFrame; }
   312   nscoord GetFlexBaseSize() const  { return mFlexBaseSize; }
   314   nscoord GetMainMinSize() const   { return mMainMinSize; }
   315   nscoord GetMainMaxSize() const   { return mMainMaxSize; }
   317   // Note: These return the main-axis position and size of our *content box*.
   318   nscoord GetMainSize() const      { return mMainSize; }
   319   nscoord GetMainPosition() const  { return mMainPosn; }
   321   nscoord GetCrossMinSize() const  { return mCrossMinSize; }
   322   nscoord GetCrossMaxSize() const  { return mCrossMaxSize; }
   324   // Note: These return the cross-axis position and size of our *content box*.
   325   nscoord GetCrossSize() const     { return mCrossSize;  }
   326   nscoord GetCrossPosition() const { return mCrossPosn; }
   328   // Convenience methods to compute the main & cross size of our *margin-box*.
   329   // The caller is responsible for telling us the right axis, so that we can
   330   // pull out the appropriate components of our margin/border/padding structs.
   331   nscoord GetOuterMainSize(AxisOrientationType aMainAxis) const
   332   {
   333     return mMainSize + GetMarginBorderPaddingSizeInAxis(aMainAxis);
   334   }
   336   nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const
   337   {
   338     return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis);
   339   }
   341   // Returns the distance between this FlexItem's baseline and the cross-start
   342   // edge of its margin-box. Used in baseline alignment.
   343   // (This function needs to be told what cross axis is & which edge we're
   344   // measuring the baseline from, so that it can look up the appropriate
   345   // components from mMargin.)
   346   nscoord GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
   347                                               AxisEdgeType aEdge) const;
   349   float GetShareOfFlexWeightSoFar() const { return mShareOfFlexWeightSoFar; }
   351   bool IsFrozen() const            { return mIsFrozen; }
   353   bool HadMinViolation() const     { return mHadMinViolation; }
   354   bool HadMaxViolation() const     { return mHadMaxViolation; }
   356   // Indicates whether this item received a preliminary "measuring" reflow
   357   // before its actual reflow.
   358   bool HadMeasuringReflow() const  { return mHadMeasuringReflow; }
   360   // Indicates whether this item's cross-size has been stretched (from having
   361   // "align-self: stretch" with an auto cross-size and no auto margins in the
   362   // cross axis).
   363   bool IsStretched() const         { return mIsStretched; }
   365   // Indicates whether this item is a "strut" left behind by an element with
   366   // visibility:collapse.
   367   bool IsStrut() const             { return mIsStrut; }
   369   uint8_t GetAlignSelf() const     { return mAlignSelf; }
   371   // Returns the flex weight that we should use in the "resolving flexible
   372   // lengths" algorithm.  If we're using flex grow, we just return that;
   373   // otherwise, we use the "scaled flex shrink weight" (scaled by our flex
   374   // base size, so that when both large and small items are shrinking,
   375   // the large items shrink more).
   376   float GetFlexWeightToUse(bool aIsUsingFlexGrow)
   377   {
   378     if (IsFrozen()) {
   379       return 0.0f;
   380     }
   382     if (aIsUsingFlexGrow) {
   383       return mFlexGrow;
   384     }
   386     // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize
   387     if (mFlexBaseSize == 0) {
   388       // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so
   389       // regardless of mFlexShrink, we should just return 0.
   390       // (This is really a special-case for when mFlexShrink is infinity, to
   391       // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.)
   392       return 0.0f;
   393     }
   394     return mFlexShrink * mFlexBaseSize;
   395   }
   397   // Getters for margin:
   398   // ===================
   399   const nsMargin& GetMargin() const { return mMargin; }
   401   // Returns the margin component for a given mozilla::css::Side
   402   nscoord GetMarginComponentForSide(Side aSide) const
   403   { return MarginComponentForSide(mMargin, aSide); }
   405   // Returns the total space occupied by this item's margins in the given axis
   406   nscoord GetMarginSizeInAxis(AxisOrientationType aAxis) const
   407   {
   408     Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
   409     Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
   410     return GetMarginComponentForSide(startSide) +
   411       GetMarginComponentForSide(endSide);
   412   }
   414   // Getters for border/padding
   415   // ==========================
   416   const nsMargin& GetBorderPadding() const { return mBorderPadding; }
   418   // Returns the border+padding component for a given mozilla::css::Side
   419   nscoord GetBorderPaddingComponentForSide(Side aSide) const
   420   { return MarginComponentForSide(mBorderPadding, aSide); }
   422   // Returns the total space occupied by this item's borders and padding in
   423   // the given axis
   424   nscoord GetBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
   425   {
   426     Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start];
   427     Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End];
   428     return GetBorderPaddingComponentForSide(startSide) +
   429       GetBorderPaddingComponentForSide(endSide);
   430   }
   432   // Getter for combined margin/border/padding
   433   // =========================================
   434   // Returns the total space occupied by this item's margins, borders and
   435   // padding in the given axis
   436   nscoord GetMarginBorderPaddingSizeInAxis(AxisOrientationType aAxis) const
   437   {
   438     return GetMarginSizeInAxis(aAxis) + GetBorderPaddingSizeInAxis(aAxis);
   439   }
   441   // Setters
   442   // =======
   444   // This sets our flex base size, and then updates the main size to the
   445   // base size clamped to our main-axis [min,max] constraints.
   446   void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize)
   447   {
   448     MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_INTRINSICSIZE,
   449                "flex base size shouldn't change after we're frozen "
   450                "(unless we're just resolving an intrinsic size)");
   451     mFlexBaseSize = aNewFlexBaseSize;
   453     // Before we've resolved flexible lengths, we keep mMainSize set to
   454     // the 'hypothetical main size', which is the flex base size, clamped
   455     // to the [min,max] range:
   456     mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize);
   457   }
   459   // Setters used while we're resolving flexible lengths
   460   // ---------------------------------------------------
   462   // Sets the main-size of our flex item's content-box.
   463   void SetMainSize(nscoord aNewMainSize)
   464   {
   465     MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen");
   466     mMainSize = aNewMainSize;
   467   }
   469   void SetShareOfFlexWeightSoFar(float aNewShare)
   470   {
   471     MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f,
   472                "shouldn't be giving this item any share of the weight "
   473                "after it's frozen");
   474     mShareOfFlexWeightSoFar = aNewShare;
   475   }
   477   void Freeze() { mIsFrozen = true; }
   479   void SetHadMinViolation()
   480   {
   481     MOZ_ASSERT(!mIsFrozen,
   482                "shouldn't be changing main size & having violations "
   483                "after we're frozen");
   484     mHadMinViolation = true;
   485   }
   486   void SetHadMaxViolation()
   487   {
   488     MOZ_ASSERT(!mIsFrozen,
   489                "shouldn't be changing main size & having violations "
   490                "after we're frozen");
   491     mHadMaxViolation = true;
   492   }
   493   void ClearViolationFlags()
   494   { mHadMinViolation = mHadMaxViolation = false; }
   496   // Setters for values that are determined after we've resolved our main size
   497   // -------------------------------------------------------------------------
   499   // Sets the main-axis position of our flex item's content-box.
   500   // (This is the distance between the main-start edge of the flex container
   501   // and the main-start edge of the flex item's content-box.)
   502   void SetMainPosition(nscoord aPosn) {
   503     MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
   504     mMainPosn  = aPosn;
   505   }
   507   // Sets the cross-size of our flex item's content-box.
   508   void SetCrossSize(nscoord aCrossSize) {
   509     MOZ_ASSERT(!mIsStretched,
   510                "Cross size shouldn't be modified after it's been stretched");
   511     mCrossSize = aCrossSize;
   512   }
   514   // Sets the cross-axis position of our flex item's content-box.
   515   // (This is the distance between the cross-start edge of the flex container
   516   // and the cross-start edge of the flex item.)
   517   void SetCrossPosition(nscoord aPosn) {
   518     MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
   519     mCrossPosn = aPosn;
   520   }
   522   void SetAscent(nscoord aAscent) {
   523     mAscent = aAscent;
   524   }
   526   void SetHadMeasuringReflow() {
   527     mHadMeasuringReflow = true;
   528   }
   530   void SetIsStretched() {
   531     MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
   532     mIsStretched = true;
   533   }
   535   // Setter for margin components (for resolving "auto" margins)
   536   void SetMarginComponentForSide(Side aSide, nscoord aLength)
   537   {
   538     MOZ_ASSERT(mIsFrozen, "main size should be resolved before this");
   539     MarginComponentForSide(mMargin, aSide) = aLength;
   540   }
   542   void ResolveStretchedCrossSize(nscoord aLineCrossSize,
   543                                  const FlexboxAxisTracker& aAxisTracker);
   545   uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const;
   547 protected:
   548   // Our frame:
   549   nsIFrame* const mFrame;
   551   // Values that we already know in constructor: (and are hence mostly 'const')
   552   const float mFlexGrow;
   553   const float mFlexShrink;
   555   const nsMargin mBorderPadding;
   556   nsMargin mMargin; // non-const because we need to resolve auto margins
   558   nscoord mFlexBaseSize;
   560   const nscoord mMainMinSize;
   561   const nscoord mMainMaxSize;
   562   const nscoord mCrossMinSize;
   563   const nscoord mCrossMaxSize;
   565   // Values that we compute after constructor:
   566   nscoord mMainSize;
   567   nscoord mMainPosn;
   568   nscoord mCrossSize;
   569   nscoord mCrossPosn;
   570   nscoord mAscent;
   572   // Temporary state, while we're resolving flexible widths (for our main size)
   573   // XXXdholbert To save space, we could use a union to make these variables
   574   // overlay the same memory as some other member vars that aren't touched
   575   // until after main-size has been resolved. In particular, these could share
   576   // memory with mMainPosn through mAscent, and mIsStretched.
   577   float mShareOfFlexWeightSoFar;
   578   bool mIsFrozen;
   579   bool mHadMinViolation;
   580   bool mHadMaxViolation;
   582   // Misc:
   583   bool mHadMeasuringReflow; // Did this item get a preliminary reflow,
   584                             // to measure its desired height?
   585   bool mIsStretched; // See IsStretched() documentation
   586   bool mIsStrut;     // Is this item a "strut" left behind by an element
   587                      // with visibility:collapse?
   588   uint8_t mAlignSelf; // My "align-self" computed value (with "auto"
   589                       // swapped out for parent"s "align-items" value,
   590                       // in our constructor).
   591 };
   593 /**
   594  * Represents a single flex line in a flex container.
   595  * Manages a linked list of the FlexItems that are in the line.
   596  */
   597 class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine>
   598 {
   599 public:
   600   FlexLine()
   601   : mNumItems(0),
   602     mTotalInnerHypotheticalMainSize(0),
   603     mTotalOuterHypotheticalMainSize(0),
   604     mLineCrossSize(0),
   605     mBaselineOffset(nscoord_MIN)
   606   {}
   608   // Returns the sum of our FlexItems' outer hypothetical main sizes.
   609   // ("outer" = margin-box, and "hypothetical" = before flexing)
   610   nscoord GetTotalOuterHypotheticalMainSize() const {
   611     return mTotalOuterHypotheticalMainSize;
   612   }
   614   // Accessors for our FlexItems & information about them:
   615   FlexItem* GetFirstItem()
   616   {
   617     MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
   618                "mNumItems bookkeeping is off");
   619     return mItems.getFirst();
   620   }
   622   const FlexItem* GetFirstItem() const
   623   {
   624     MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
   625                "mNumItems bookkeeping is off");
   626     return mItems.getFirst();
   627   }
   629   bool IsEmpty() const
   630   {
   631     MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
   632                "mNumItems bookkeeping is off");
   633     return mItems.isEmpty();
   634   }
   636   uint32_t NumItems() const
   637   {
   638     MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0),
   639                "mNumItems bookkeeping is off");
   640     return mNumItems;
   641   }
   643   // Adds the given FlexItem to our list of items (at the front or back
   644   // depending on aShouldInsertAtFront), and adds its hypothetical
   645   // outer & inner main sizes to our totals. Use this method instead of
   646   // directly modifying the item list, so that our bookkeeping remains correct.
   647   void AddItem(FlexItem* aItem,
   648                bool aShouldInsertAtFront,
   649                nscoord aItemInnerHypotheticalMainSize,
   650                nscoord aItemOuterHypotheticalMainSize)
   651   {
   652     if (aShouldInsertAtFront) {
   653       mItems.insertFront(aItem);
   654     } else {
   655       mItems.insertBack(aItem);
   656     }
   657     mNumItems++;
   658     mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize;
   659     mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize;
   660   }
   662   // Computes the cross-size and baseline position of this FlexLine, based on
   663   // its FlexItems.
   664   void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker);
   666   // Returns the cross-size of this line.
   667   nscoord GetLineCrossSize() const { return mLineCrossSize; }
   669   // Setter for line cross-size -- needed for cases where the flex container
   670   // imposes a cross-size on the line. (e.g. for single-line flexbox, or for
   671   // multi-line flexbox with 'align-content: stretch')
   672   void SetLineCrossSize(nscoord aLineCrossSize) {
   673     mLineCrossSize = aLineCrossSize;
   674   }
   676   /**
   677    * Returns the offset within this line where any baseline-aligned FlexItems
   678    * should place their baseline. Usually, this represents a distance from the
   679    * line's cross-start edge, but if we're internally reversing the axes (see
   680    * AreAxesInternallyReversed()), this instead represents the distance from
   681    * its cross-end edge.
   682    *
   683    * If there are no baseline-aligned FlexItems, returns nscoord_MIN.
   684    */
   685   nscoord GetBaselineOffset() const {
   686     return mBaselineOffset;
   687   }
   689   // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
   690   // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
   691   void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
   693   void PositionItemsInMainAxis(uint8_t aJustifyContent,
   694                                nscoord aContentBoxMainSize,
   695                                const FlexboxAxisTracker& aAxisTracker);
   697   void PositionItemsInCrossAxis(nscoord aLineStartPosition,
   698                                 const FlexboxAxisTracker& aAxisTracker);
   700   friend class AutoFlexLineListClearer; // (needs access to mItems)
   702 private:
   703   // Helper for ResolveFlexibleLengths():
   704   void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
   705                                        bool aIsFinalIteration);
   707   LinkedList<FlexItem> mItems; // Linked list of this line's flex items.
   709   uint32_t mNumItems; // Number of FlexItems in this line (in |mItems|).
   710                       // (Shouldn't change after GenerateFlexLines finishes
   711                       // with this line -- at least, not until we add support
   712                       // for splitting lines across continuations. Then we can
   713                       // update this count carefully.)
   715   nscoord mTotalInnerHypotheticalMainSize;
   716   nscoord mTotalOuterHypotheticalMainSize;
   717   nscoord mLineCrossSize;
   718   nscoord mBaselineOffset;
   719 };
   721 // Information about a strut left behind by a FlexItem that's been collapsed
   722 // using "visibility:collapse".
   723 struct nsFlexContainerFrame::StrutInfo {
   724   StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
   725     : mItemIdx(aItemIdx),
   726       mStrutCrossSize(aStrutCrossSize)
   727   {
   728   }
   730   uint32_t mItemIdx;      // Index in the child list.
   731   nscoord mStrutCrossSize; // The cross-size of this strut.
   732 };
   734 static void
   735 BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine,
   736                                  nsTArray<StrutInfo>& aStruts)
   737 {
   738   MOZ_ASSERT(aFirstLine, "null first line pointer");
   739   MOZ_ASSERT(aStruts.IsEmpty(),
   740              "We should only build up StrutInfo once per reflow, so "
   741              "aStruts should be empty when this is called");
   743   uint32_t itemIdxInContainer = 0;
   744   for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
   745     for (const FlexItem* item = line->GetFirstItem(); item;
   746          item = item->getNext()) {
   747       if (NS_STYLE_VISIBILITY_COLLAPSE ==
   748           item->Frame()->StyleVisibility()->mVisible) {
   749         // Note the cross size of the line as the item's strut size.
   750         aStruts.AppendElement(StrutInfo(itemIdxInContainer,
   751                                         line->GetLineCrossSize()));
   752       }
   753       itemIdxInContainer++;
   754     }
   755   }
   756 }
   758 // Helper-function to find the first non-anonymous-box descendent of aFrame.
   759 static nsIFrame*
   760 GetFirstNonAnonBoxDescendant(nsIFrame* aFrame)
   761 {
   762   while (aFrame) {
   763     nsIAtom* pseudoTag = aFrame->StyleContext()->GetPseudo();
   765     // If aFrame isn't an anonymous container, then it'll do.
   766     if (!pseudoTag ||                                 // No pseudotag.
   767         !nsCSSAnonBoxes::IsAnonBox(pseudoTag) ||      // Pseudotag isn't anon.
   768         pseudoTag == nsCSSAnonBoxes::mozNonElement) { // Text, not a container.
   769       break;
   770     }
   772     // Otherwise, descend to its first child and repeat.
   774     // SPECIAL CASE: if we're dealing with an anonymous table, then it might
   775     // be wrapping something non-anonymous in its caption or col-group lists
   776     // (instead of its principal child list), so we have to look there.
   777     // (Note: For anonymous tables that have a non-anon cell *and* a non-anon
   778     // column, we'll always return the column. This is fine; we're really just
   779     // looking for a handle to *anything* with a meaningful content node inside
   780     // the table, for use in DOM comparisons to things outside of the table.)
   781     if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableOuterFrame)) {
   782       nsIFrame* captionDescendant =
   783         GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kCaptionList));
   784       if (captionDescendant) {
   785         return captionDescendant;
   786       }
   787     } else if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableFrame)) {
   788       nsIFrame* colgroupDescendant =
   789         GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kColGroupList));
   790       if (colgroupDescendant) {
   791         return colgroupDescendant;
   792       }
   793     }
   795     // USUAL CASE: Descend to the first child in principal list.
   796     aFrame = aFrame->GetFirstPrincipalChild();
   797   }
   798   return aFrame;
   799 }
   801 /**
   802  * Sorting helper-function that compares two frames' "order" property-values,
   803  * and if they're equal, compares the DOM positions of their corresponding
   804  * content nodes. Returns true if aFrame1 is "less than or equal to" aFrame2
   805  * according to this comparison.
   806  *
   807  * Note: This can't be a static function, because we need to pass it as a
   808  * template argument. (Only functions with external linkage can be passed as
   809  * template arguments.)
   810  *
   811  * @return true if the computed "order" property of aFrame1 is less than that
   812  *         of aFrame2, or if the computed "order" values are equal and aFrame1's
   813  *         corresponding DOM node is earlier than aFrame2's in the DOM tree.
   814  *         Otherwise, returns false.
   815  */
   816 bool
   817 IsOrderLEQWithDOMFallback(nsIFrame* aFrame1,
   818                           nsIFrame* aFrame2)
   819 {
   820   MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
   821              "this method only intended for comparing flex items");
   823   if (aFrame1 == aFrame2) {
   824     // Anything is trivially LEQ itself, so we return "true" here... but it's
   825     // probably bad if we end up actually needing this, so let's assert.
   826     NS_ERROR("Why are we checking if a frame is LEQ itself?");
   827     return true;
   828   }
   830   // If we've got a placeholder frame, use its out-of-flow frame's 'order' val.
   831   {
   832     nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
   833     nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
   835     int32_t order1 = aRealFrame1->StylePosition()->mOrder;
   836     int32_t order2 = aRealFrame2->StylePosition()->mOrder;
   838     if (order1 != order2) {
   839       return order1 < order2;
   840     }
   841   }
   843   // The "order" values are equal, so we need to fall back on DOM comparison.
   844   // For that, we need to dig through any anonymous box wrapper frames to find
   845   // the actual frame that corresponds to our child content.
   846   aFrame1 = GetFirstNonAnonBoxDescendant(aFrame1);
   847   aFrame2 = GetFirstNonAnonBoxDescendant(aFrame2);
   848   MOZ_ASSERT(aFrame1 && aFrame2,
   849              "why do we have an anonymous box without any "
   850              "non-anonymous descendants?");
   853   // Special case:
   854   // If either frame is for generated content from ::before or ::after, then
   855   // we can't use nsContentUtils::PositionIsBefore(), since that method won't
   856   // recognize generated content as being an actual sibling of other nodes.
   857   // We know where ::before and ::after nodes *effectively* insert in the DOM
   858   // tree, though (at the beginning & end), so we can just special-case them.
   859   nsIAtom* pseudo1 = aFrame1->StyleContext()->GetPseudo();
   860   nsIAtom* pseudo2 = aFrame2->StyleContext()->GetPseudo();
   861   if (pseudo1 == nsCSSPseudoElements::before ||
   862       pseudo2 == nsCSSPseudoElements::after) {
   863     // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2.
   864     return true;
   865   }
   866   if (pseudo1 == nsCSSPseudoElements::after ||
   867       pseudo2 == nsCSSPseudoElements::before) {
   868     // frame1 is ::after and/or frame2 is ::before => frame1 is not LEQ frame2.
   869     return false;
   870   }
   872   // Usual case: Compare DOM position.
   873   nsIContent* content1 = aFrame1->GetContent();
   874   nsIContent* content2 = aFrame2->GetContent();
   875   MOZ_ASSERT(content1 != content2,
   876              "Two different flex items are using the same nsIContent node for "
   877              "comparison, so we may be sorting them in an arbitrary order");
   879   return nsContentUtils::PositionIsBefore(content1, content2);
   880 }
   882 /**
   883  * Sorting helper-function that compares two frames' "order" property-values.
   884  * Returns true if aFrame1 is "less than or equal to" aFrame2 according to this
   885  * comparison.
   886  *
   887  * Note: This can't be a static function, because we need to pass it as a
   888  * template argument. (Only functions with external linkage can be passed as
   889  * template arguments.)
   890  *
   891  * @return true if the computed "order" property of aFrame1 is less than or
   892  *         equal to that of aFrame2.  Otherwise, returns false.
   893  */
   894 bool
   895 IsOrderLEQ(nsIFrame* aFrame1,
   896            nsIFrame* aFrame2)
   897 {
   898   MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(),
   899              "this method only intended for comparing flex items");
   901   // If we've got a placeholder frame, use its out-of-flow frame's 'order' val.
   902   nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
   903   nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
   905   int32_t order1 = aRealFrame1->StylePosition()->mOrder;
   906   int32_t order2 = aRealFrame2->StylePosition()->mOrder;
   908   return order1 <= order2;
   909 }
   911 bool
   912 nsFlexContainerFrame::IsHorizontal()
   913 {
   914   const FlexboxAxisTracker axisTracker(this);
   915   return IsAxisHorizontal(axisTracker.GetMainAxis());
   916 }
   918 FlexItem*
   919 nsFlexContainerFrame::GenerateFlexItemForChild(
   920   nsPresContext* aPresContext,
   921   nsIFrame*      aChildFrame,
   922   const nsHTMLReflowState& aParentReflowState,
   923   const FlexboxAxisTracker& aAxisTracker)
   924 {
   925   // Create temporary reflow state just for sizing -- to get hypothetical
   926   // main-size and the computed values of min / max main-size property.
   927   // (This reflow state will _not_ be used for reflow.)
   928   nsHTMLReflowState childRS(aPresContext, aParentReflowState, aChildFrame,
   929                             nsSize(aParentReflowState.ComputedWidth(),
   930                                    aParentReflowState.ComputedHeight()));
   932   // FLEX GROW & SHRINK WEIGHTS
   933   // --------------------------
   934   const nsStylePosition* stylePos = aChildFrame->StylePosition();
   935   float flexGrow   = stylePos->mFlexGrow;
   936   float flexShrink = stylePos->mFlexShrink;
   938   // MAIN SIZES (flex base size, min/max size)
   939   // -----------------------------------------
   940   nscoord flexBaseSize = GET_MAIN_COMPONENT(aAxisTracker,
   941                                             childRS.ComputedWidth(),
   942                                             childRS.ComputedHeight());
   943   nscoord mainMinSize = GET_MAIN_COMPONENT(aAxisTracker,
   944                                            childRS.ComputedMinWidth(),
   945                                            childRS.ComputedMinHeight());
   946   nscoord mainMaxSize = GET_MAIN_COMPONENT(aAxisTracker,
   947                                            childRS.ComputedMaxWidth(),
   948                                            childRS.ComputedMaxHeight());
   949   // This is enforced by the nsHTMLReflowState where these values come from:
   950   MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size");
   952   // CROSS MIN/MAX SIZE
   953   // ------------------
   955   nscoord crossMinSize = GET_CROSS_COMPONENT(aAxisTracker,
   956                                              childRS.ComputedMinWidth(),
   957                                              childRS.ComputedMinHeight());
   958   nscoord crossMaxSize = GET_CROSS_COMPONENT(aAxisTracker,
   959                                              childRS.ComputedMaxWidth(),
   960                                              childRS.ComputedMaxHeight());
   962   // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES
   963   // Check if we're a themed widget, in which case we might have a minimum
   964   // main & cross size imposed by our widget (which we can't go below), or
   965   // (more severe) our widget might have only a single valid size.
   966   bool isFixedSizeWidget = false;
   967   const nsStyleDisplay* disp = aChildFrame->StyleDisplay();
   968   if (aChildFrame->IsThemed(disp)) {
   969     nsIntSize widgetMinSize(0, 0);
   970     bool canOverride = true;
   971     aPresContext->GetTheme()->
   972       GetMinimumWidgetSize(childRS.rendContext, aChildFrame,
   973                            disp->mAppearance,
   974                            &widgetMinSize, &canOverride);
   976     nscoord widgetMainMinSize =
   977       aPresContext->DevPixelsToAppUnits(
   978         aAxisTracker.GetMainComponent(widgetMinSize));
   979     nscoord widgetCrossMinSize =
   980       aPresContext->DevPixelsToAppUnits(
   981         aAxisTracker.GetCrossComponent(widgetMinSize));
   983     // GMWS() returns border-box. We need content-box, so subtract
   984     // borderPadding (but don't let that push our min sizes below 0).
   985     nsMargin& bp = childRS.ComputedPhysicalBorderPadding();
   986     widgetMainMinSize = std::max(widgetMainMinSize -
   987                                  aAxisTracker.GetMarginSizeInMainAxis(bp), 0);
   988     widgetCrossMinSize = std::max(widgetCrossMinSize -
   989                                   aAxisTracker.GetMarginSizeInCrossAxis(bp), 0);
   991     if (!canOverride) {
   992       // Fixed-size widget: freeze our main-size at the widget's mandated size.
   993       // (Set min and max main-sizes to that size, too, to keep us from
   994       // clamping to any other size later on.)
   995       flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize;
   996       crossMinSize = crossMaxSize = widgetCrossMinSize;
   997       isFixedSizeWidget = true;
   998     } else {
   999       // Variable-size widget: ensure our min/max sizes are at least as large
  1000       // as the widget's mandated minimum size, so we don't flex below that.
  1001       mainMinSize = std::max(mainMinSize, widgetMainMinSize);
  1002       mainMaxSize = std::max(mainMaxSize, widgetMainMinSize);
  1004       crossMinSize = std::max(crossMinSize, widgetCrossMinSize);
  1005       crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize);
  1009   // Construct the flex item!
  1010   FlexItem* item = new FlexItem(aChildFrame,
  1011                                 flexGrow, flexShrink, flexBaseSize,
  1012                                 mainMinSize, mainMaxSize,
  1013                                 crossMinSize, crossMaxSize,
  1014                                 childRS.ComputedPhysicalMargin(),
  1015                                 childRS.ComputedPhysicalBorderPadding(),
  1016                                 aAxisTracker);
  1018   // If we're inflexible, we can just freeze to our hypothetical main-size
  1019   // up-front. Similarly, if we're a fixed-size widget, we only have one
  1020   // valid size, so we freeze to keep ourselves from flexing.
  1021   if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) {
  1022     item->Freeze();
  1025   return item;
  1028 nsresult
  1029 nsFlexContainerFrame::
  1030   ResolveFlexItemMaxContentSizing(nsPresContext* aPresContext,
  1031                                   FlexItem& aFlexItem,
  1032                                   const nsHTMLReflowState& aParentReflowState,
  1033                                   const FlexboxAxisTracker& aAxisTracker)
  1035   if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
  1036     // Nothing to do -- this function is only for measuring flex items
  1037     // in a vertical flex container.
  1038     return NS_OK;
  1041   if (NS_AUTOHEIGHT != aFlexItem.GetFlexBaseSize()) {
  1042     // Nothing to do; this function's only relevant for flex items
  1043     // with a base size of "auto" (or equivalent).
  1044     // XXXdholbert If & when we handle "min-height: min-content" for flex items,
  1045     // we'll want to resolve that in this function, too.
  1046     return NS_OK;
  1049   // If we get here, we're vertical and our main size ended up being
  1050   // unconstrained. We need to use our "max-content" height, which is what we
  1051   // get from reflowing into our available width.
  1052   // Note: This has to come *after* we construct the FlexItem, since we
  1053   // invoke at least one convenience method (ResolveStretchedCrossSize) which
  1054   // requires a FlexItem.
  1056   // Give the item a special reflow with "mIsFlexContainerMeasuringHeight"
  1057   // set.  This tells it to behave as if it had "height: auto", regardless
  1058   // of what the "height" property is actually set to.
  1059   nsHTMLReflowState
  1060     childRSForMeasuringHeight(aPresContext, aParentReflowState,
  1061                               aFlexItem.Frame(),
  1062                               nsSize(aParentReflowState.ComputedWidth(),
  1063                                      NS_UNCONSTRAINEDSIZE),
  1064                               -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
  1065   childRSForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true;
  1066   childRSForMeasuringHeight.Init(aPresContext);
  1068   aFlexItem.ResolveStretchedCrossSize(aParentReflowState.ComputedWidth(),
  1069                                       aAxisTracker);
  1070   if (aFlexItem.IsStretched()) {
  1071     childRSForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize());
  1072     childRSForMeasuringHeight.mFlags.mHResize = true;
  1075   // If this item is flexible (vertically), then we assume that the
  1076   // computed-height we're reflowing with now could be different
  1077   // from the one we'll use for this flex item's "actual" reflow later on.
  1078   // In that case, we need to be sure the flex item treats this as a
  1079   // vertical resize, even though none of its ancestors are necessarily
  1080   // being vertically resized.
  1081   // (Note: We don't have to do this for width, because InitResizeFlags
  1082   // will always turn on mHResize on when it sees that the computed width
  1083   // is different from current width, and that's all we need.)
  1084   if (!aFlexItem.IsFrozen()) {  // Are we flexible?
  1085     childRSForMeasuringHeight.mFlags.mVResize = true;
  1088   nsHTMLReflowMetrics childDesiredSize(childRSForMeasuringHeight);
  1089   nsReflowStatus childReflowStatus;
  1090   const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
  1091   nsresult rv = ReflowChild(aFlexItem.Frame(), aPresContext,
  1092                             childDesiredSize, childRSForMeasuringHeight,
  1093                             0, 0, flags, childReflowStatus);
  1094   NS_ENSURE_SUCCESS(rv, rv);
  1096   MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
  1097              "We gave flex item unconstrained available height, so it "
  1098              "should be complete");
  1100   rv = FinishReflowChild(aFlexItem.Frame(), aPresContext,
  1101                          childDesiredSize, &childRSForMeasuringHeight,
  1102                          0, 0, flags);
  1103   NS_ENSURE_SUCCESS(rv, rv);
  1105   // Subtract border/padding in vertical axis, to get _just_
  1106   // the effective computed value of the "height" property.
  1107   nscoord childDesiredHeight = childDesiredSize.Height() -
  1108     childRSForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom();
  1109   childDesiredHeight = std::max(0, childDesiredHeight);
  1111   aFlexItem.SetFlexBaseSizeAndMainSize(childDesiredHeight);
  1112   aFlexItem.SetHadMeasuringReflow();
  1114   return NS_OK;
  1117 FlexItem::FlexItem(nsIFrame* aChildFrame,
  1118                    float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize,
  1119                    nscoord aMainMinSize,  nscoord aMainMaxSize,
  1120                    nscoord aCrossMinSize, nscoord aCrossMaxSize,
  1121                    nsMargin aMargin, nsMargin aBorderPadding,
  1122                    const FlexboxAxisTracker& aAxisTracker)
  1123   : mFrame(aChildFrame),
  1124     mFlexGrow(aFlexGrow),
  1125     mFlexShrink(aFlexShrink),
  1126     mBorderPadding(aBorderPadding),
  1127     mMargin(aMargin),
  1128     mMainMinSize(aMainMinSize),
  1129     mMainMaxSize(aMainMaxSize),
  1130     mCrossMinSize(aCrossMinSize),
  1131     mCrossMaxSize(aCrossMaxSize),
  1132     mMainPosn(0),
  1133     mCrossSize(0),
  1134     mCrossPosn(0),
  1135     mAscent(0),
  1136     mShareOfFlexWeightSoFar(0.0f),
  1137     mIsFrozen(false),
  1138     mHadMinViolation(false),
  1139     mHadMaxViolation(false),
  1140     mHadMeasuringReflow(false),
  1141     mIsStretched(false),
  1142     mIsStrut(false),
  1143     mAlignSelf(aChildFrame->StylePosition()->mAlignSelf)
  1145   MOZ_ASSERT(mFrame, "expecting a non-null child frame");
  1146   MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
  1147              "placeholder frames should not be treated as flex items");
  1148   MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
  1149              "out-of-flow frames should not be treated as flex items");
  1151   SetFlexBaseSizeAndMainSize(aFlexBaseSize);
  1153   // Assert that any "auto" margin components are set to 0.
  1154   // (We'll resolve them later; until then, we want to treat them as 0-sized.)
  1155 #ifdef DEBUG
  1157     const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
  1158     NS_FOR_CSS_SIDES(side) {
  1159       if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
  1160         MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
  1161                    "Someone else tried to resolve our auto margin");
  1165 #endif // DEBUG
  1167   // Resolve "align-self: auto" to parent's "align-items" value.
  1168   if (mAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) {
  1169     mAlignSelf =
  1170       mFrame->StyleContext()->GetParent()->StylePosition()->mAlignItems;
  1173   // If the flex item's inline axis is the same as the cross axis, then
  1174   // 'align-self:baseline' is identical to 'flex-start'. If that's the case, we
  1175   // just directly convert our align-self value here, so that we don't have to
  1176   // handle this with special cases elsewhere.
  1177   // Moreover: for the time being (until we support writing-modes),
  1178   // all inline axes are horizontal -- so we can just check if the cross axis
  1179   // is horizontal.
  1180   // FIXME: Once we support writing-mode (vertical text), this IsAxisHorizontal
  1181   // check won't be sufficient anymore -- we'll actually need to compare our
  1182   // inline axis vs. the cross axis.
  1183   if (mAlignSelf == NS_STYLE_ALIGN_ITEMS_BASELINE &&
  1184       IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
  1185     mAlignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
  1189 // Simplified constructor for creating a special "strut" FlexItem, for a child
  1190 // with visibility:collapse. The strut has 0 main-size, and it only exists to
  1191 // impose a minimum cross size on whichever FlexLine it ends up in.
  1192 FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize)
  1193   : mFrame(aChildFrame),
  1194     mFlexGrow(0.0f),
  1195     mFlexShrink(0.0f),
  1196     // mBorderPadding uses default constructor,
  1197     // mMargin uses default constructor,
  1198     mFlexBaseSize(0),
  1199     mMainMinSize(0),
  1200     mMainMaxSize(0),
  1201     mCrossMinSize(0),
  1202     mCrossMaxSize(0),
  1203     mMainSize(0),
  1204     mMainPosn(0),
  1205     mCrossSize(aCrossSize),
  1206     mCrossPosn(0),
  1207     mAscent(0),
  1208     mShareOfFlexWeightSoFar(0.0f),
  1209     mIsFrozen(true),
  1210     mHadMinViolation(false),
  1211     mHadMaxViolation(false),
  1212     mHadMeasuringReflow(false),
  1213     mIsStretched(false),
  1214     mIsStrut(true), // (this is the constructor for making struts, after all)
  1215     mAlignSelf(NS_STYLE_ALIGN_ITEMS_FLEX_START)
  1217   MOZ_ASSERT(mFrame, "expecting a non-null child frame");
  1218   MOZ_ASSERT(NS_STYLE_VISIBILITY_COLLAPSE ==
  1219              mFrame->StyleVisibility()->mVisible,
  1220              "Should only make struts for children with 'visibility:collapse'");
  1221   MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame,
  1222              "placeholder frames should not be treated as flex items");
  1223   MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW),
  1224              "out-of-flow frames should not be treated as flex items");
  1227 nscoord
  1228 FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis,
  1229                                               AxisEdgeType aEdge) const
  1231   // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
  1232   // measurement -- it's the distance from the border-top edge of this FlexItem
  1233   // to its baseline. So, we can really only do baseline alignment when the
  1234   // cross axis is vertical. (The FlexItem constructor enforces this when
  1235   // resolving the item's "mAlignSelf" value).
  1236   MOZ_ASSERT(!IsAxisHorizontal(aCrossAxis),
  1237              "Only expecting to be doing baseline computations when the "
  1238              "cross axis is vertical");
  1240   Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge];
  1242   nscoord marginTopToBaseline = mAscent + mMargin.top;
  1244   if (sideToMeasureFrom == eSideTop) {
  1245     // Measuring from top (normal case): the distance from the margin-box top
  1246     // edge to the baseline is just ascent + margin-top.
  1247     return marginTopToBaseline;
  1250   MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
  1251              "We already checked that we're dealing with a vertical axis, and "
  1252              "we're not using the top side, so that only leaves the bottom...");
  1254   // Measuring from bottom: The distance from the margin-box bottom edge to the
  1255   // baseline is just the margin-box cross size (i.e. outer cross size), minus
  1256   // the already-computed distance from margin-top to baseline.
  1257   return GetOuterCrossSize(aCrossAxis) - marginTopToBaseline;
  1260 uint32_t
  1261 FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const
  1263   uint32_t numAutoMargins = 0;
  1264   const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin;
  1265   for (uint32_t i = 0; i < eNumAxisEdges; i++) {
  1266     Side side = kAxisOrientationToSidesMap[aAxis][i];
  1267     if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
  1268       numAutoMargins++;
  1272   // Mostly for clarity:
  1273   MOZ_ASSERT(numAutoMargins <= 2,
  1274              "We're just looking at one item along one dimension, so we "
  1275              "should only have examined 2 margins");
  1277   return numAutoMargins;
  1280 // Keeps track of our position along a particular axis (where a '0' position
  1281 // corresponds to the 'start' edge of that axis).
  1282 // This class shouldn't be instantiated directly -- rather, it should only be
  1283 // instantiated via its subclasses defined below.
  1284 class MOZ_STACK_CLASS PositionTracker {
  1285 public:
  1286   // Accessor for the current value of the position that we're tracking.
  1287   inline nscoord GetPosition() const { return mPosition; }
  1288   inline AxisOrientationType GetAxis() const { return mAxis; }
  1290   // Advances our position across the start edge of the given margin, in the
  1291   // axis we're tracking.
  1292   void EnterMargin(const nsMargin& aMargin)
  1294     Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_Start];
  1295     mPosition += MarginComponentForSide(aMargin, side);
  1298   // Advances our position across the end edge of the given margin, in the axis
  1299   // we're tracking.
  1300   void ExitMargin(const nsMargin& aMargin)
  1302     Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_End];
  1303     mPosition += MarginComponentForSide(aMargin, side);
  1306   // Advances our current position from the start side of a child frame's
  1307   // border-box to the frame's upper or left edge (depending on our axis).
  1308   // (Note that this is a no-op if our axis grows in positive direction.)
  1309   void EnterChildFrame(nscoord aChildFrameSize)
  1311     if (!AxisGrowsInPositiveDirection(mAxis))
  1312       mPosition += aChildFrameSize;
  1315   // Advances our current position from a frame's upper or left border-box edge
  1316   // (whichever is in the axis we're tracking) to the 'end' side of the frame
  1317   // in the axis that we're tracking. (Note that this is a no-op if our axis
  1318   // grows in the negative direction.)
  1319   void ExitChildFrame(nscoord aChildFrameSize)
  1321     if (AxisGrowsInPositiveDirection(mAxis))
  1322       mPosition += aChildFrameSize;
  1325 protected:
  1326   // Protected constructor, to be sure we're only instantiated via a subclass.
  1327   PositionTracker(AxisOrientationType aAxis)
  1328     : mPosition(0),
  1329       mAxis(aAxis)
  1330   {}
  1332 private:
  1333   // Private copy-constructor, since we don't want any instances of our
  1334   // subclasses to be accidentally copied.
  1335   PositionTracker(const PositionTracker& aOther)
  1336     : mPosition(aOther.mPosition),
  1337       mAxis(aOther.mAxis)
  1338   {}
  1340 protected:
  1341   // Member data:
  1342   nscoord mPosition;               // The position we're tracking
  1343   const AxisOrientationType mAxis; // The axis along which we're moving
  1344 };
  1346 // Tracks our position in the main axis, when we're laying out flex items.
  1347 // The "0" position represents the main-start edge of the flex container's
  1348 // content-box.
  1349 class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker {
  1350 public:
  1351   MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
  1352                           const FlexLine* aLine,
  1353                           uint8_t aJustifyContent,
  1354                           nscoord aContentBoxMainSize);
  1356   ~MainAxisPositionTracker() {
  1357     MOZ_ASSERT(mNumPackingSpacesRemaining == 0,
  1358                "miscounted the number of packing spaces");
  1359     MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0,
  1360                "miscounted the number of auto margins");
  1363   // Advances past the packing space (if any) between two flex items
  1364   void TraversePackingSpace();
  1366   // If aItem has any 'auto' margins in the main axis, this method updates the
  1367   // corresponding values in its margin.
  1368   void ResolveAutoMarginsInMainAxis(FlexItem& aItem);
  1370 private:
  1371   nscoord  mPackingSpaceRemaining;
  1372   uint32_t mNumAutoMarginsInMainAxis;
  1373   uint32_t mNumPackingSpacesRemaining;
  1374   uint8_t  mJustifyContent;
  1375 };
  1377 // Utility class for managing our position along the cross axis along
  1378 // the whole flex container (at a higher level than a single line).
  1379 // The "0" position represents the cross-start edge of the flex container's
  1380 // content-box.
  1381 class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker {
  1382 public:
  1383   CrossAxisPositionTracker(FlexLine* aFirstLine,
  1384                            uint8_t aAlignContent,
  1385                            nscoord aContentBoxCrossSize,
  1386                            bool aIsCrossSizeDefinite,
  1387                            const FlexboxAxisTracker& aAxisTracker);
  1389   // Advances past the packing space (if any) between two flex lines
  1390   void TraversePackingSpace();
  1392   // Advances past the given FlexLine
  1393   void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); }
  1395 private:
  1396   // Redeclare the frame-related methods from PositionTracker as private with
  1397   // MOZ_DELETE, to be sure (at compile time) that no client code can invoke
  1398   // them. (Unlike the other PositionTracker derived classes, this class here
  1399   // deals with FlexLines, not with individual FlexItems or frames.)
  1400   void EnterMargin(const nsMargin& aMargin) MOZ_DELETE;
  1401   void ExitMargin(const nsMargin& aMargin) MOZ_DELETE;
  1402   void EnterChildFrame(nscoord aChildFrameSize) MOZ_DELETE;
  1403   void ExitChildFrame(nscoord aChildFrameSize) MOZ_DELETE;
  1405   nscoord  mPackingSpaceRemaining;
  1406   uint32_t mNumPackingSpacesRemaining;
  1407   uint8_t  mAlignContent;
  1408 };
  1410 // Utility class for managing our position along the cross axis, *within* a
  1411 // single flex line.
  1412 class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker {
  1413 public:
  1414   SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker);
  1416   void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
  1417                                      FlexItem& aItem);
  1419   void EnterAlignPackingSpace(const FlexLine& aLine,
  1420                               const FlexItem& aItem,
  1421                               const FlexboxAxisTracker& aAxisTracker);
  1423   // Resets our position to the cross-start edge of this line.
  1424   inline void ResetPosition() { mPosition = 0; }
  1425 };
  1427 //----------------------------------------------------------------------
  1429 // Frame class boilerplate
  1430 // =======================
  1432 NS_QUERYFRAME_HEAD(nsFlexContainerFrame)
  1433   NS_QUERYFRAME_ENTRY(nsFlexContainerFrame)
  1434 NS_QUERYFRAME_TAIL_INHERITING(nsFlexContainerFrameSuper)
  1436 NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame)
  1438 nsIFrame*
  1439 NS_NewFlexContainerFrame(nsIPresShell* aPresShell,
  1440                          nsStyleContext* aContext)
  1442   return new (aPresShell) nsFlexContainerFrame(aContext);
  1445 //----------------------------------------------------------------------
  1447 // nsFlexContainerFrame Method Implementations
  1448 // ===========================================
  1450 /* virtual */
  1451 nsFlexContainerFrame::~nsFlexContainerFrame()
  1455 template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)>
  1456 /* static */ bool
  1457 nsFlexContainerFrame::SortChildrenIfNeeded()
  1459   if (nsIFrame::IsFrameListSorted<IsLessThanOrEqual>(mFrames)) {
  1460     return false;
  1463   nsIFrame::SortFrameList<IsLessThanOrEqual>(mFrames);
  1464   return true;
  1467 /* virtual */
  1468 nsIAtom*
  1469 nsFlexContainerFrame::GetType() const
  1471   return nsGkAtoms::flexContainerFrame;
  1474 #ifdef DEBUG_FRAME_DUMP
  1475 nsresult
  1476 nsFlexContainerFrame::GetFrameName(nsAString& aResult) const
  1478   return MakeFrameName(NS_LITERAL_STRING("FlexContainer"), aResult);
  1480 #endif
  1482 // Helper for BuildDisplayList, to implement this special-case for flex items
  1483 // from the spec:
  1484 //    Flex items paint exactly the same as block-level elements in the
  1485 //    normal flow, except that 'z-index' values other than 'auto' create
  1486 //    a stacking context even if 'position' is 'static'.
  1487 // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#painting
  1488 uint32_t
  1489 GetDisplayFlagsForFlexItem(nsIFrame* aFrame)
  1491   MOZ_ASSERT(aFrame->IsFlexItem(), "Should only be called on flex items");
  1493   const nsStylePosition* pos = aFrame->StylePosition();
  1494   if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) {
  1495     return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT;
  1497   return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT;
  1500 void
  1501 nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
  1502                                        const nsRect&           aDirtyRect,
  1503                                        const nsDisplayListSet& aLists)
  1505   NS_ASSERTION(
  1506     nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames),
  1507     "Child frames aren't sorted correctly");
  1509   DisplayBorderBackgroundOutline(aBuilder, aLists);
  1511   // Our children are all block-level, so their borders/backgrounds all go on
  1512   // the BlockBorderBackgrounds list.
  1513   nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds());
  1514   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
  1515     BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, childLists,
  1516                              GetDisplayFlagsForFlexItem(e.get()));
  1520 #ifdef DEBUG
  1521 // helper for the debugging method below
  1522 bool
  1523 FrameWantsToBeInAnonymousFlexItem(nsIFrame* aFrame)
  1525   // Note: This needs to match the logic in
  1526   // nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexItem()
  1527   return (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) ||
  1528           nsGkAtoms::placeholderFrame == aFrame->GetType());
  1531 // Debugging method, to let us assert that our anonymous flex items are
  1532 // set up correctly -- in particular, we assert:
  1533 //  (1) we don't have any inline non-replaced children
  1534 //  (2) we don't have any consecutive anonymous flex items
  1535 //  (3) we don't have any empty anonymous flex items
  1536 //
  1537 // XXXdholbert This matches what nsCSSFrameConstructor currently does, and what
  1538 // the spec used to say.  However, the spec has now changed regarding what
  1539 // types of content get wrapped in an anonymous flexbox item.  The patch that
  1540 // implements those changes (in nsCSSFrameConstructor) will need to change
  1541 // this method as well.
  1542 void
  1543 nsFlexContainerFrame::SanityCheckAnonymousFlexItems() const
  1545   bool prevChildWasAnonFlexItem = false;
  1546   for (nsIFrame* child = mFrames.FirstChild(); child;
  1547        child = child->GetNextSibling()) {
  1548     MOZ_ASSERT(!FrameWantsToBeInAnonymousFlexItem(child),
  1549                "frame wants to be inside an anonymous flex item, "
  1550                "but it isn't");
  1551     if (child->StyleContext()->GetPseudo() ==
  1552         nsCSSAnonBoxes::anonymousFlexItem) {
  1553       MOZ_ASSERT(!prevChildWasAnonFlexItem ||
  1554                  HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED),
  1555                  "two anon flex items in a row (shouldn't happen, unless our "
  1556                  "children have been reordered with the 'order' property)");
  1558       nsIFrame* firstWrappedChild = child->GetFirstPrincipalChild();
  1559       MOZ_ASSERT(firstWrappedChild,
  1560                  "anonymous flex item is empty (shouldn't happen)");
  1561       prevChildWasAnonFlexItem = true;
  1562     } else {
  1563       prevChildWasAnonFlexItem = false;
  1567 #endif // DEBUG
  1569 // Based on the sign of aTotalViolation, this function freezes a subset of our
  1570 // flexible sizes, and restores the remaining ones to their initial pref sizes.
  1571 void
  1572 FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation,
  1573                                           bool aIsFinalIteration)
  1575   enum FreezeType {
  1576     eFreezeEverything,
  1577     eFreezeMinViolations,
  1578     eFreezeMaxViolations
  1579   };
  1581   FreezeType freezeType;
  1582   if (aTotalViolation == 0) {
  1583     freezeType = eFreezeEverything;
  1584   } else if (aTotalViolation > 0) {
  1585     freezeType = eFreezeMinViolations;
  1586   } else { // aTotalViolation < 0
  1587     freezeType = eFreezeMaxViolations;
  1590   for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  1591     MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(),
  1592                "Can have either min or max violation, but not both");
  1594     if (!item->IsFrozen()) {
  1595       if (eFreezeEverything == freezeType ||
  1596           (eFreezeMinViolations == freezeType && item->HadMinViolation()) ||
  1597           (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) {
  1599         MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(),
  1600                    "Freezing item at a size below its minimum");
  1601         MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(),
  1602                    "Freezing item at a size above its maximum");
  1604         item->Freeze();
  1605       } else if (MOZ_UNLIKELY(aIsFinalIteration)) {
  1606         // XXXdholbert If & when bug 765861 is fixed, we should upgrade this
  1607         // assertion to be fatal except in documents with enormous lengths.
  1608         NS_ERROR("Final iteration still has unfrozen items, this shouldn't"
  1609                  " happen unless there was nscoord under/overflow.");
  1610         item->Freeze();
  1611       } // else, we'll reset this item's main size to its flex base size on the
  1612         // next iteration of this algorithm.
  1614       // Clear this item's violation(s), now that we've dealt with them
  1615       item->ClearViolationFlags();
  1620 void
  1621 FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize)
  1623   PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, ("ResolveFlexibleLengths\n"));
  1624   if (IsEmpty()) {
  1625     return;
  1628   // Subtract space occupied by our items' margins/borders/padding, so we can
  1629   // just be dealing with the space available for our flex items' content
  1630   // boxes.
  1631   nscoord spaceReservedForMarginBorderPadding =
  1632     mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize;
  1634   nscoord spaceAvailableForFlexItemsContentBoxes =
  1635     aFlexContainerMainSize - spaceReservedForMarginBorderPadding;
  1637   // Determine whether we're going to be growing or shrinking items.
  1638   const bool isUsingFlexGrow =
  1639     (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize);
  1641   // NOTE: I claim that this chunk of the algorithm (the looping part) needs to
  1642   // run the loop at MOST mNumItems times.  This claim should hold up
  1643   // because we'll freeze at least one item on each loop iteration, and once
  1644   // we've run out of items to freeze, there's nothing left to do.  However,
  1645   // in most cases, we'll break out of this loop long before we hit that many
  1646   // iterations.
  1647   for (uint32_t iterationCounter = 0;
  1648        iterationCounter < mNumItems; iterationCounter++) {
  1649     // Set every not-yet-frozen item's used main size to its
  1650     // flex base size, and subtract all the used main sizes from our
  1651     // total amount of space to determine the 'available free space'
  1652     // (positive or negative) to be distributed among our flexible items.
  1653     nscoord availableFreeSpace = spaceAvailableForFlexItemsContentBoxes;
  1654     for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  1655       if (!item->IsFrozen()) {
  1656         item->SetMainSize(item->GetFlexBaseSize());
  1658       availableFreeSpace -= item->GetMainSize();
  1661     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  1662            (" available free space = %d\n", availableFreeSpace));
  1664     // If sign of free space matches the type of flexing that we're doing, give
  1665     // each flexible item a portion of availableFreeSpace.
  1666     if ((availableFreeSpace > 0 && isUsingFlexGrow) ||
  1667         (availableFreeSpace < 0 && !isUsingFlexGrow)) {
  1669       // STRATEGY: On each item, we compute & store its "share" of the total
  1670       // flex weight that we've seen so far:
  1671       //   curFlexWeight / runningFlexWeightSum
  1672       //
  1673       // Then, when we go to actually distribute the space (in the next loop),
  1674       // we can simply walk backwards through the elements and give each item
  1675       // its "share" multiplied by the remaining available space.
  1676       //
  1677       // SPECIAL CASE: If the sum of the flex weights is larger than the
  1678       // maximum representable float (overflowing to infinity), then we can't
  1679       // sensibly divide out proportional shares anymore. In that case, we
  1680       // simply treat the flex item(s) with the largest flex weights as if
  1681       // their weights were infinite (dwarfing all the others), and we
  1682       // distribute all of the available space among them.
  1683       float runningFlexWeightSum = 0.0f;
  1684       float largestFlexWeight = 0.0f;
  1685       uint32_t numItemsWithLargestFlexWeight = 0;
  1686       for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  1687         float curFlexWeight = item->GetFlexWeightToUse(isUsingFlexGrow);
  1688         MOZ_ASSERT(curFlexWeight >= 0.0f, "weights are non-negative");
  1690         runningFlexWeightSum += curFlexWeight;
  1691         if (NS_finite(runningFlexWeightSum)) {
  1692           if (curFlexWeight == 0.0f) {
  1693             item->SetShareOfFlexWeightSoFar(0.0f);
  1694           } else {
  1695             item->SetShareOfFlexWeightSoFar(curFlexWeight /
  1696                                             runningFlexWeightSum);
  1698         } // else, the sum of weights overflows to infinity, in which
  1699           // case we don't bother with "SetShareOfFlexWeightSoFar" since
  1700           // we know we won't use it. (instead, we'll just give every
  1701           // item with the largest flex weight an equal share of space.)
  1703         // Update our largest-flex-weight tracking vars
  1704         if (curFlexWeight > largestFlexWeight) {
  1705           largestFlexWeight = curFlexWeight;
  1706           numItemsWithLargestFlexWeight = 1;
  1707         } else if (curFlexWeight == largestFlexWeight) {
  1708           numItemsWithLargestFlexWeight++;
  1712       if (runningFlexWeightSum != 0.0f) { // no distribution if no flexibility
  1713         PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  1714                (" Distributing available space:"));
  1715         // NOTE: It's important that we traverse our items in *reverse* order
  1716         // here, for correct width distribution according to the items'
  1717         // "ShareOfFlexWeightSoFar" progressively-calculated values.
  1718         for (FlexItem* item = mItems.getLast(); item;
  1719              item = item->getPrevious()) {
  1721           if (!item->IsFrozen()) {
  1722             // To avoid rounding issues, we compute the change in size for this
  1723             // item, and then subtract it from the remaining available space.
  1724             nscoord sizeDelta = 0;
  1725             if (NS_finite(runningFlexWeightSum)) {
  1726               float myShareOfRemainingSpace =
  1727                 item->GetShareOfFlexWeightSoFar();
  1729               MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f &&
  1730                          myShareOfRemainingSpace <= 1.0f,
  1731                          "my share should be nonnegative fractional amount");
  1733               if (myShareOfRemainingSpace == 1.0f) {
  1734                 // (We special-case 1.0f to avoid float error from converting
  1735                 // availableFreeSpace from integer*1.0f --> float --> integer)
  1736                 sizeDelta = availableFreeSpace;
  1737               } else if (myShareOfRemainingSpace > 0.0f) {
  1738                 sizeDelta = NSToCoordRound(availableFreeSpace *
  1739                                            myShareOfRemainingSpace);
  1741             } else if (item->GetFlexWeightToUse(isUsingFlexGrow) ==
  1742                        largestFlexWeight) {
  1743               // Total flexibility is infinite, so we're just distributing
  1744               // the available space equally among the items that are tied for
  1745               // having the largest weight (and this is one of those items).
  1746               sizeDelta =
  1747                 NSToCoordRound(availableFreeSpace /
  1748                                float(numItemsWithLargestFlexWeight));
  1749               numItemsWithLargestFlexWeight--;
  1752             availableFreeSpace -= sizeDelta;
  1754             item->SetMainSize(item->GetMainSize() + sizeDelta);
  1755             PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  1756                    ("  child %p receives %d, for a total of %d\n",
  1757                     item, sizeDelta, item->GetMainSize()));
  1763     // Fix min/max violations:
  1764     nscoord totalViolation = 0; // keeps track of adjustments for min/max
  1765     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  1766            (" Checking for violations:"));
  1768     for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  1769       if (!item->IsFrozen()) {
  1770         if (item->GetMainSize() < item->GetMainMinSize()) {
  1771           // min violation
  1772           totalViolation += item->GetMainMinSize() - item->GetMainSize();
  1773           item->SetMainSize(item->GetMainMinSize());
  1774           item->SetHadMinViolation();
  1775         } else if (item->GetMainSize() > item->GetMainMaxSize()) {
  1776           // max violation
  1777           totalViolation += item->GetMainMaxSize() - item->GetMainSize();
  1778           item->SetMainSize(item->GetMainMaxSize());
  1779           item->SetHadMaxViolation();
  1784     FreezeOrRestoreEachFlexibleSize(totalViolation,
  1785                                     iterationCounter + 1 == mNumItems);
  1787     PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  1788            (" Total violation: %d\n", totalViolation));
  1790     if (totalViolation == 0) {
  1791       break;
  1795   // Post-condition: all lengths should've been frozen.
  1796 #ifdef DEBUG
  1797   for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  1798     MOZ_ASSERT(item->IsFrozen(),
  1799                "All flexible lengths should've been resolved");
  1801 #endif // DEBUG
  1804 MainAxisPositionTracker::
  1805   MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker,
  1806                           const FlexLine* aLine,
  1807                           uint8_t aJustifyContent,
  1808                           nscoord aContentBoxMainSize)
  1809   : PositionTracker(aAxisTracker.GetMainAxis()),
  1810     mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below
  1811     mNumAutoMarginsInMainAxis(0),
  1812     mNumPackingSpacesRemaining(0),
  1813     mJustifyContent(aJustifyContent)
  1815   // mPackingSpaceRemaining is initialized to the container's main size.  Now
  1816   // we'll subtract out the main sizes of our flex items, so that it ends up
  1817   // with the *actual* amount of packing space.
  1818   for (const FlexItem* item = aLine->GetFirstItem(); item;
  1819        item = item->getNext()) {
  1820     mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis);
  1821     mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis);
  1824   if (mPackingSpaceRemaining <= 0) {
  1825     // No available packing space to use for resolving auto margins.
  1826     mNumAutoMarginsInMainAxis = 0;
  1829   // If packing space is negative, 'space-between' behaves like 'flex-start',
  1830   // and 'space-around' behaves like 'center'. In those cases, it's simplest to
  1831   // just pretend we have a different 'justify-content' value and share code.
  1832   if (mPackingSpaceRemaining < 0) {
  1833     if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN) {
  1834       mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
  1835     } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND) {
  1836       mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_CENTER;
  1840   // If our main axis is (internally) reversed, swap the justify-content
  1841   // "flex-start" and "flex-end" behaviors:
  1842   if (aAxisTracker.AreAxesInternallyReversed()) {
  1843     if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_START) {
  1844       mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_END;
  1845     } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_END) {
  1846       mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
  1850   // Figure out how much space we'll set aside for auto margins or
  1851   // packing spaces, and advance past any leading packing-space.
  1852   if (mNumAutoMarginsInMainAxis == 0 &&
  1853       mPackingSpaceRemaining != 0 &&
  1854       !aLine->IsEmpty()) {
  1855     switch (mJustifyContent) {
  1856       case NS_STYLE_JUSTIFY_CONTENT_FLEX_START:
  1857         // All packing space should go at the end --> nothing to do here.
  1858         break;
  1859       case NS_STYLE_JUSTIFY_CONTENT_FLEX_END:
  1860         // All packing space goes at the beginning
  1861         mPosition += mPackingSpaceRemaining;
  1862         break;
  1863       case NS_STYLE_JUSTIFY_CONTENT_CENTER:
  1864         // Half the packing space goes at the beginning
  1865         mPosition += mPackingSpaceRemaining / 2;
  1866         break;
  1867       case NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN:
  1868         MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  1869                    "negative packing space should make us use 'flex-start' "
  1870                    "instead of 'space-between'");
  1871         // 1 packing space between each flex item, no packing space at ends.
  1872         mNumPackingSpacesRemaining = aLine->NumItems() - 1;
  1873         break;
  1874       case NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND:
  1875         MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  1876                    "negative packing space should make us use 'center' "
  1877                    "instead of 'space-around'");
  1878         // 1 packing space between each flex item, plus half a packing space
  1879         // at beginning & end.  So our number of full packing-spaces is equal
  1880         // to the number of flex items.
  1881         mNumPackingSpacesRemaining = aLine->NumItems();
  1882         if (mNumPackingSpacesRemaining > 0) {
  1883           // The edges (start/end) share one full packing space
  1884           nscoord totalEdgePackingSpace =
  1885             mPackingSpaceRemaining / mNumPackingSpacesRemaining;
  1887           // ...and we'll use half of that right now, at the start
  1888           mPosition += totalEdgePackingSpace / 2;
  1889           // ...but we need to subtract all of it right away, so that we won't
  1890           // hand out any of it to intermediate packing spaces.
  1891           mPackingSpaceRemaining -= totalEdgePackingSpace;
  1892           mNumPackingSpacesRemaining--;
  1894         break;
  1895       default:
  1896         MOZ_CRASH("Unexpected justify-content value");
  1900   MOZ_ASSERT(mNumPackingSpacesRemaining == 0 ||
  1901              mNumAutoMarginsInMainAxis == 0,
  1902              "extra space should either go to packing space or to "
  1903              "auto margins, but not to both");
  1906 void
  1907 MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem)
  1909   if (mNumAutoMarginsInMainAxis) {
  1910     const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
  1911     for (uint32_t i = 0; i < eNumAxisEdges; i++) {
  1912       Side side = kAxisOrientationToSidesMap[mAxis][i];
  1913       if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
  1914         // NOTE: This integer math will skew the distribution of remainder
  1915         // app-units towards the end, which is fine.
  1916         nscoord curAutoMarginSize =
  1917           mPackingSpaceRemaining / mNumAutoMarginsInMainAxis;
  1919         MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
  1920                    "Expecting auto margins to have value '0' before we "
  1921                    "resolve them");
  1922         aItem.SetMarginComponentForSide(side, curAutoMarginSize);
  1924         mNumAutoMarginsInMainAxis--;
  1925         mPackingSpaceRemaining -= curAutoMarginSize;
  1931 void
  1932 MainAxisPositionTracker::TraversePackingSpace()
  1934   if (mNumPackingSpacesRemaining) {
  1935     MOZ_ASSERT(mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN ||
  1936                mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND,
  1937                "mNumPackingSpacesRemaining only applies for "
  1938                "space-between/space-around");
  1940     MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  1941                "ran out of packing space earlier than we expected");
  1943     // NOTE: This integer math will skew the distribution of remainder
  1944     // app-units towards the end, which is fine.
  1945     nscoord curPackingSpace =
  1946       mPackingSpaceRemaining / mNumPackingSpacesRemaining;
  1948     mPosition += curPackingSpace;
  1949     mNumPackingSpacesRemaining--;
  1950     mPackingSpaceRemaining -= curPackingSpace;
  1954 CrossAxisPositionTracker::
  1955   CrossAxisPositionTracker(FlexLine* aFirstLine,
  1956                            uint8_t aAlignContent,
  1957                            nscoord aContentBoxCrossSize,
  1958                            bool aIsCrossSizeDefinite,
  1959                            const FlexboxAxisTracker& aAxisTracker)
  1960   : PositionTracker(aAxisTracker.GetCrossAxis()),
  1961     mPackingSpaceRemaining(0),
  1962     mNumPackingSpacesRemaining(0),
  1963     mAlignContent(aAlignContent)
  1965   MOZ_ASSERT(aFirstLine, "null first line pointer");
  1967   if (aIsCrossSizeDefinite && !aFirstLine->getNext()) {
  1968     // "If the flex container has only a single line (even if it's a
  1969     // multi-line flex container) and has a definite cross size, the cross
  1970     // size of the flex line is the flex container's inner cross size."
  1971     // SOURCE: http://dev.w3.org/csswg/css-flexbox/#algo-line-break
  1972     // NOTE: This means (by definition) that there's no packing space, which
  1973     // means we don't need to be concerned with "align-conent" at all and we
  1974     // can return early. This is handy, because this is the usual case (for
  1975     // single-line flexbox).
  1976     aFirstLine->SetLineCrossSize(aContentBoxCrossSize);
  1977     return;
  1980   // NOTE: The rest of this function should essentially match
  1981   // MainAxisPositionTracker's constructor, though with FlexLines instead of
  1982   // FlexItems, and with the additional value "stretch" (and of course with
  1983   // cross sizes instead of main sizes.)
  1985   // Figure out how much packing space we have (container's cross size minus
  1986   // all the lines' cross sizes).  Also, share this loop to count how many
  1987   // lines we have. (We need that count in some cases below.)
  1988   mPackingSpaceRemaining = aContentBoxCrossSize;
  1989   uint32_t numLines = 0;
  1990   for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
  1991     mPackingSpaceRemaining -= line->GetLineCrossSize();
  1992     numLines++;
  1995   // If packing space is negative, 'space-between' and 'stretch' behave like
  1996   // 'flex-start', and 'space-around' behaves like 'center'. In those cases,
  1997   // it's simplest to just pretend we have a different 'align-content' value
  1998   // and share code.
  1999   if (mPackingSpaceRemaining < 0) {
  2000     if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN ||
  2001         mAlignContent == NS_STYLE_ALIGN_CONTENT_STRETCH) {
  2002       mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START;
  2003     } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND) {
  2004       mAlignContent = NS_STYLE_ALIGN_CONTENT_CENTER;
  2008   // If our cross axis is (internally) reversed, swap the align-content
  2009   // "flex-start" and "flex-end" behaviors:
  2010   if (aAxisTracker.AreAxesInternallyReversed()) {
  2011     if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_START) {
  2012       mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_END;
  2013     } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_END) {
  2014       mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START;
  2018   // Figure out how much space we'll set aside for packing spaces, and advance
  2019   // past any leading packing-space.
  2020   if (mPackingSpaceRemaining != 0) {
  2021     switch (mAlignContent) {
  2022       case NS_STYLE_ALIGN_CONTENT_FLEX_START:
  2023         // All packing space should go at the end --> nothing to do here.
  2024         break;
  2025       case NS_STYLE_ALIGN_CONTENT_FLEX_END:
  2026         // All packing space goes at the beginning
  2027         mPosition += mPackingSpaceRemaining;
  2028         break;
  2029       case NS_STYLE_ALIGN_CONTENT_CENTER:
  2030         // Half the packing space goes at the beginning
  2031         mPosition += mPackingSpaceRemaining / 2;
  2032         break;
  2033       case NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN:
  2034         MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  2035                    "negative packing space should make us use 'flex-start' "
  2036                    "instead of 'space-between'");
  2037         // 1 packing space between each flex line, no packing space at ends.
  2038         mNumPackingSpacesRemaining = numLines - 1;
  2039         break;
  2040       case NS_STYLE_ALIGN_CONTENT_SPACE_AROUND: {
  2041         MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  2042                    "negative packing space should make us use 'center' "
  2043                    "instead of 'space-around'");
  2044         // 1 packing space between each flex line, plus half a packing space
  2045         // at beginning & end.  So our number of full packing-spaces is equal
  2046         // to the number of flex lines.
  2047         mNumPackingSpacesRemaining = numLines;
  2048         // The edges (start/end) share one full packing space
  2049         nscoord totalEdgePackingSpace =
  2050           mPackingSpaceRemaining / mNumPackingSpacesRemaining;
  2052         // ...and we'll use half of that right now, at the start
  2053         mPosition += totalEdgePackingSpace / 2;
  2054         // ...but we need to subtract all of it right away, so that we won't
  2055         // hand out any of it to intermediate packing spaces.
  2056         mPackingSpaceRemaining -= totalEdgePackingSpace;
  2057         mNumPackingSpacesRemaining--;
  2058         break;
  2060       case NS_STYLE_ALIGN_CONTENT_STRETCH: {
  2061         // Split space equally between the lines:
  2062         MOZ_ASSERT(mPackingSpaceRemaining > 0,
  2063                    "negative packing space should make us use 'flex-start' "
  2064                    "instead of 'stretch' (and we shouldn't bother with this "
  2065                    "code if we have 0 packing space)");
  2067         uint32_t numLinesLeft = numLines;
  2068         for (FlexLine* line = aFirstLine; line; line = line->getNext()) {
  2069           // Our share is the amount of space remaining, divided by the number
  2070           // of lines remainig.
  2071           MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines");
  2072           nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft;
  2073           nscoord newSize = line->GetLineCrossSize() + shareOfExtraSpace;
  2074           line->SetLineCrossSize(newSize);
  2076           mPackingSpaceRemaining -= shareOfExtraSpace;
  2077           numLinesLeft--;
  2079         MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines");
  2080         break;
  2082       default:
  2083         MOZ_CRASH("Unexpected align-content value");
  2088 void
  2089 CrossAxisPositionTracker::TraversePackingSpace()
  2091   if (mNumPackingSpacesRemaining) {
  2092     MOZ_ASSERT(mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN ||
  2093                mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND,
  2094                "mNumPackingSpacesRemaining only applies for "
  2095                "space-between/space-around");
  2097     MOZ_ASSERT(mPackingSpaceRemaining >= 0,
  2098                "ran out of packing space earlier than we expected");
  2100     // NOTE: This integer math will skew the distribution of remainder
  2101     // app-units towards the end, which is fine.
  2102     nscoord curPackingSpace =
  2103       mPackingSpaceRemaining / mNumPackingSpacesRemaining;
  2105     mPosition += curPackingSpace;
  2106     mNumPackingSpacesRemaining--;
  2107     mPackingSpaceRemaining -= curPackingSpace;
  2111 SingleLineCrossAxisPositionTracker::
  2112   SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker)
  2113   : PositionTracker(aAxisTracker.GetCrossAxis())
  2117 void
  2118 FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
  2120   nscoord crossStartToFurthestBaseline = nscoord_MIN;
  2121   nscoord crossEndToFurthestBaseline = nscoord_MIN;
  2122   nscoord largestOuterCrossSize = 0;
  2123   for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  2124     nscoord curOuterCrossSize =
  2125       item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
  2127     if (item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE &&
  2128         item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
  2129       // FIXME: Once we support "writing-mode", we'll have to do baseline
  2130       // alignment in vertical flex containers here (w/ horizontal cross-axes).
  2132       // Find distance from our item's cross-start and cross-end margin-box
  2133       // edges to its baseline.
  2134       //
  2135       // Here's a diagram of a flex-item that we might be doing this on.
  2136       // "mmm" is the margin-box, "bbb" is the border-box. The bottom of
  2137       // the text "BASE" is the baseline.
  2138       //
  2139       // ---(cross-start)---
  2140       //                ___              ___            ___
  2141       //   mmmmmmmmmmmm  |                |margin-start  |
  2142       //   m          m  |               _|_   ___       |
  2143       //   m bbbbbbbb m  |curOuterCrossSize     |        |crossStartToBaseline
  2144       //   m b      b m  |                      |ascent  |
  2145       //   m b BASE b m  |                     _|_      _|_
  2146       //   m b      b m  |                               |
  2147       //   m bbbbbbbb m  |                               |crossEndToBaseline
  2148       //   m          m  |                               |
  2149       //   mmmmmmmmmmmm _|_                             _|_
  2150       //
  2151       // ---(cross-end)---
  2152       //
  2153       // We already have the curOuterCrossSize, margin-start, and the ascent.
  2154       // * We can get crossStartToBaseline by adding margin-start + ascent.
  2155       // * If we subtract that from the curOuterCrossSize, we get
  2156       //   crossEndToBaseline.
  2158       nscoord crossStartToBaseline =
  2159         item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(),
  2160                                                   eAxisEdge_Start);
  2161       nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
  2163       // Now, update our "largest" values for these (across all the flex items
  2164       // in this flex line), so we can use them in computing the line's cross
  2165       // size below:
  2166       crossStartToFurthestBaseline = std::max(crossStartToFurthestBaseline,
  2167                                               crossStartToBaseline);
  2168       crossEndToFurthestBaseline = std::max(crossEndToFurthestBaseline,
  2169                                             crossEndToBaseline);
  2170     } else {
  2171       largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
  2175   // The line's baseline offset is the distance from the line's edge (start or
  2176   // end, depending on whether we've flipped the axes) to the furthest
  2177   // item-baseline. The item(s) with that baseline will be exactly aligned with
  2178   // the line's edge.
  2179   mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
  2180     crossEndToFurthestBaseline : crossStartToFurthestBaseline;
  2182   // The line's cross-size is the larger of:
  2183   //  (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
  2184   //      all baseline-aligned items with no cross-axis auto margins...
  2185   // and
  2186   //  (b) largest cross-size of all other children.
  2187   mLineCrossSize = std::max(crossStartToFurthestBaseline +
  2188                             crossEndToFurthestBaseline,
  2189                             largestOuterCrossSize);
  2192 void
  2193 FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize,
  2194                                     const FlexboxAxisTracker& aAxisTracker)
  2196   AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
  2197   // We stretch IFF we are align-self:stretch, have no auto margins in
  2198   // cross axis, and have cross-axis size property == "auto". If any of those
  2199   // conditions don't hold up, we won't stretch.
  2200   if (mAlignSelf != NS_STYLE_ALIGN_ITEMS_STRETCH ||
  2201       GetNumAutoMarginsInAxis(crossAxis) != 0 ||
  2202       eStyleUnit_Auto != GetSizePropertyForAxis(mFrame, crossAxis).GetUnit()) {
  2203     return;
  2206   // If we've already been stretched, we can bail out early, too.
  2207   // No need to redo the calculation.
  2208   if (mIsStretched) {
  2209     return;
  2212   // Reserve space for margins & border & padding, and then use whatever
  2213   // remains as our item's cross-size (clamped to its min/max range).
  2214   nscoord stretchedSize = aLineCrossSize -
  2215     GetMarginBorderPaddingSizeInAxis(crossAxis);
  2217   stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize);
  2219   // Update the cross-size & make a note that it's stretched, so we know to
  2220   // override the reflow state's computed cross-size in our final reflow.
  2221   SetCrossSize(stretchedSize);
  2222   mIsStretched = true;
  2225 void
  2226 SingleLineCrossAxisPositionTracker::
  2227   ResolveAutoMarginsInCrossAxis(const FlexLine& aLine,
  2228                                 FlexItem& aItem)
  2230   // Subtract the space that our item is already occupying, to see how much
  2231   // space (if any) is available for its auto margins.
  2232   nscoord spaceForAutoMargins = aLine.GetLineCrossSize() -
  2233     aItem.GetOuterCrossSize(mAxis);
  2235   if (spaceForAutoMargins <= 0) {
  2236     return; // No available space  --> nothing to do
  2239   uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis);
  2240   if (numAutoMargins == 0) {
  2241     return; // No auto margins --> nothing to do.
  2244   // OK, we have at least one auto margin and we have some available space.
  2245   // Give each auto margin a share of the space.
  2246   const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin;
  2247   for (uint32_t i = 0; i < eNumAxisEdges; i++) {
  2248     Side side = kAxisOrientationToSidesMap[mAxis][i];
  2249     if (styleMargin.GetUnit(side) == eStyleUnit_Auto) {
  2250       MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0,
  2251                  "Expecting auto margins to have value '0' before we "
  2252                  "update them");
  2254       // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2.
  2255       // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half.
  2256       nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins;
  2257       aItem.SetMarginComponentForSide(side, curAutoMarginSize);
  2258       numAutoMargins--;
  2259       spaceForAutoMargins -= curAutoMarginSize;
  2264 void
  2265 SingleLineCrossAxisPositionTracker::
  2266   EnterAlignPackingSpace(const FlexLine& aLine,
  2267                          const FlexItem& aItem,
  2268                          const FlexboxAxisTracker& aAxisTracker)
  2270   // We don't do align-self alignment on items that have auto margins
  2271   // in the cross axis.
  2272   if (aItem.GetNumAutoMarginsInAxis(mAxis)) {
  2273     return;
  2276   uint8_t alignSelf = aItem.GetAlignSelf();
  2277   // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any
  2278   // auto-sized items (which we've already done).
  2279   if (alignSelf == NS_STYLE_ALIGN_ITEMS_STRETCH) {
  2280     alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
  2283   // If our cross axis is (internally) reversed, swap the align-self
  2284   // "flex-start" and "flex-end" behaviors:
  2285   if (aAxisTracker.AreAxesInternallyReversed()) {
  2286     if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_START) {
  2287       alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_END;
  2288     } else if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_END) {
  2289       alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START;
  2293   switch (alignSelf) {
  2294     case NS_STYLE_ALIGN_ITEMS_FLEX_START:
  2295       // No space to skip over -- we're done.
  2296       break;
  2297     case NS_STYLE_ALIGN_ITEMS_FLEX_END:
  2298       mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
  2299       break;
  2300     case NS_STYLE_ALIGN_ITEMS_CENTER:
  2301       // Note: If cross-size is odd, the "after" space will get the extra unit.
  2302       mPosition +=
  2303         (aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
  2304       break;
  2305     case NS_STYLE_ALIGN_ITEMS_BASELINE: {
  2306       // Normally, baseline-aligned items are collectively aligned with the
  2307       // line's cross-start edge; however, if our cross axis is (internally)
  2308       // reversed, we instead align them with the cross-end edge.
  2309       nscoord itemBaselineOffset =
  2310         aItem.GetBaselineOffsetFromOuterCrossEdge(mAxis,
  2311           aAxisTracker.AreAxesInternallyReversed() ?
  2312           eAxisEdge_End : eAxisEdge_Start);
  2314       nscoord lineBaselineOffset = aLine.GetBaselineOffset();
  2316       NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
  2317                    "failed at finding largest baseline offset");
  2319       // How much do we need to adjust our position (from the line edge),
  2320       // to get the item's baseline to hit the line's baseline offset:
  2321       nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
  2323       if (aAxisTracker.AreAxesInternallyReversed()) {
  2324         // Advance to align item w/ line's flex-end edge (as in FLEX_END case):
  2325         mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
  2326         // ...and step *back* by the baseline adjustment:
  2327         mPosition -= baselineDiff;
  2328       } else {
  2329         // mPosition is already at line's flex-start edge.
  2330         // From there, we step *forward* by the baseline adjustment:
  2331         mPosition += baselineDiff;
  2333       break;
  2335     default:
  2336       NS_NOTREACHED("Unexpected align-self value");
  2337       break;
  2341 FlexboxAxisTracker::FlexboxAxisTracker(
  2342   nsFlexContainerFrame* aFlexContainerFrame)
  2343   : mAreAxesInternallyReversed(false)
  2345   const nsStylePosition* pos = aFlexContainerFrame->StylePosition();
  2346   uint32_t flexDirection = pos->mFlexDirection;
  2347   uint32_t cssDirection =
  2348     aFlexContainerFrame->StyleVisibility()->mDirection;
  2350   MOZ_ASSERT(cssDirection == NS_STYLE_DIRECTION_LTR ||
  2351              cssDirection == NS_STYLE_DIRECTION_RTL,
  2352              "Unexpected computed value for 'direction' property");
  2353   // (Not asserting for flexDirection here; it's checked by the switch below.)
  2355   // These are defined according to writing-modes' definitions of
  2356   // start/end (for the inline dimension) and before/after (for the block
  2357   // dimension), here:
  2358   //   http://www.w3.org/TR/css3-writing-modes/#logical-directions
  2359   // (NOTE: I'm intentionally not calling this "inlineAxis"/"blockAxis", since
  2360   // those terms have explicit definition in the writing-modes spec, which are
  2361   // the opposite of how I'd be using them here.)
  2362   // XXXdholbert Once we support the 'writing-mode' property, use its value
  2363   // here to further customize inlineDimension & blockDimension.
  2365   // Inline dimension ("start-to-end"):
  2366   AxisOrientationType inlineDimension =
  2367     cssDirection == NS_STYLE_DIRECTION_RTL ? eAxis_RL : eAxis_LR;
  2369   // Block dimension ("before-to-after"):
  2370   AxisOrientationType blockDimension = eAxis_TB;
  2372   // Determine main axis:
  2373   switch (flexDirection) {
  2374     case NS_STYLE_FLEX_DIRECTION_ROW:
  2375       mMainAxis = inlineDimension;
  2376       break;
  2377     case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE:
  2378       mMainAxis = GetReverseAxis(inlineDimension);
  2379       break;
  2380     case NS_STYLE_FLEX_DIRECTION_COLUMN:
  2381       mMainAxis = blockDimension;
  2382       break;
  2383     case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE:
  2384       mMainAxis = GetReverseAxis(blockDimension);
  2385       break;
  2386     default:
  2387       MOZ_CRASH("Unexpected computed value for 'flex-flow' property");
  2390   // Determine cross axis:
  2391   // (This is set up so that a bogus |flexDirection| value will
  2392   // give us blockDimension.
  2393   if (flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN ||
  2394       flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE) {
  2395     mCrossAxis = inlineDimension;
  2396   } else {
  2397     mCrossAxis = blockDimension;
  2400   // "flex-wrap: wrap-reverse" reverses our cross axis.
  2401   if (pos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) {
  2402     mCrossAxis = GetReverseAxis(mCrossAxis);
  2405   // Master switch to enable/disable bug 983427's code for reversing our axes
  2406   // and reversing some logic, to avoid reflowing children in bottom-to-top
  2407   // order. (This switch can be removed eventually, but for now, it allows
  2408   // this special-case code path to be compared against the normal code path.)
  2409   static bool sPreventBottomToTopChildOrdering = true;
  2411   if (sPreventBottomToTopChildOrdering) {
  2412     // If either axis is bottom-to-top, we flip both axes (and set a flag
  2413     // so that we can flip some logic to make the reversal transparent).
  2414     if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) {
  2415       mMainAxis = GetReverseAxis(mMainAxis);
  2416       mCrossAxis = GetReverseAxis(mCrossAxis);
  2417       mAreAxesInternallyReversed = true;
  2421   MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis),
  2422              "main & cross axes should be in different dimensions");
  2425 // Allocates a new FlexLine, adds it to the given LinkedList (at the front or
  2426 // back depending on aShouldInsertAtFront), and returns a pointer to it.
  2427 static FlexLine*
  2428 AddNewFlexLineToList(LinkedList<FlexLine>& aLines,
  2429                      bool aShouldInsertAtFront)
  2431   FlexLine* newLine = new FlexLine();
  2432   if (aShouldInsertAtFront) {
  2433     aLines.insertFront(newLine);
  2434   } else {
  2435     aLines.insertBack(newLine);
  2437   return newLine;
  2440 nsresult
  2441 nsFlexContainerFrame::GenerateFlexLines(
  2442   nsPresContext* aPresContext,
  2443   const nsHTMLReflowState& aReflowState,
  2444   nscoord aContentBoxMainSize,
  2445   nscoord aAvailableHeightForContent,
  2446   const nsTArray<StrutInfo>& aStruts,
  2447   const FlexboxAxisTracker& aAxisTracker,
  2448   LinkedList<FlexLine>& aLines)
  2450   MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty");
  2452   const bool isSingleLine =
  2453     NS_STYLE_FLEX_WRAP_NOWRAP == aReflowState.mStylePosition->mFlexWrap;
  2455   // If we're transparently reversing axes, then we'll need to link up our
  2456   // FlexItems and FlexLines in the reverse order, so that the rest of flex
  2457   // layout (with flipped axes) will still produce the correct result.
  2458   // Here, we declare a convenience bool that we'll pass when adding a new
  2459   // FlexLine or FlexItem, to make us insert it at the beginning of its list
  2460   // (so the list ends up reversed).
  2461   const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed();
  2463   // We have at least one FlexLine. Even an empty flex container has a single
  2464   // (empty) flex line.
  2465   FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
  2467   nscoord wrapThreshold;
  2468   if (isSingleLine) {
  2469     // Not wrapping. Set threshold to sentinel value that tells us not to wrap.
  2470     wrapThreshold = NS_UNCONSTRAINEDSIZE;
  2471   } else {
  2472     // Wrapping! Set wrap threshold to flex container's content-box main-size.
  2473     wrapThreshold = aContentBoxMainSize;
  2475     // If the flex container doesn't have a definite content-box main-size
  2476     // (e.g. if we're 'height:auto'), make sure we at least wrap when we hit
  2477     // its max main-size.
  2478     if (wrapThreshold == NS_UNCONSTRAINEDSIZE) {
  2479       const nscoord flexContainerMaxMainSize =
  2480         GET_MAIN_COMPONENT(aAxisTracker,
  2481                            aReflowState.ComputedMaxWidth(),
  2482                            aReflowState.ComputedMaxHeight());
  2484       wrapThreshold = flexContainerMaxMainSize;
  2487     // Also: if we're vertical and paginating, we may need to wrap sooner
  2488     // (before we run off the end of the page)
  2489     if (!IsAxisHorizontal(aAxisTracker.GetMainAxis()) &&
  2490         aAvailableHeightForContent != NS_UNCONSTRAINEDSIZE) {
  2491       wrapThreshold = std::min(wrapThreshold, aAvailableHeightForContent);
  2495   // Tracks the index of the next strut, in aStruts (and when this hits
  2496   // aStruts.Length(), that means there are no more struts):
  2497   uint32_t nextStrutIdx = 0;
  2499   // Overall index of the current flex item in the flex container. (This gets
  2500   // checked against entries in aStruts.)
  2501   uint32_t itemIdxInContainer = 0;
  2503   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
  2504     nsIFrame* childFrame = e.get();
  2506     // Honor "page-break-before", if we're multi-line and this line isn't empty:
  2507     if (!isSingleLine && !curLine->IsEmpty() &&
  2508         childFrame->StyleDisplay()->mBreakBefore) {
  2509       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
  2512     nsAutoPtr<FlexItem> item;
  2513     if (nextStrutIdx < aStruts.Length() &&
  2514         aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
  2516       // Use the simplified "strut" FlexItem constructor:
  2517       item = new FlexItem(childFrame, aStruts[nextStrutIdx].mStrutCrossSize);
  2518       nextStrutIdx++;
  2519     } else {
  2520       item = GenerateFlexItemForChild(aPresContext, childFrame,
  2521                                       aReflowState, aAxisTracker);
  2523       nsresult rv = ResolveFlexItemMaxContentSizing(aPresContext, *item,
  2524                                                     aReflowState, aAxisTracker);
  2525       NS_ENSURE_SUCCESS(rv,rv);
  2528     nscoord itemInnerHypotheticalMainSize = item->GetMainSize();
  2529     nscoord itemOuterHypotheticalMainSize =
  2530       item->GetOuterMainSize(aAxisTracker.GetMainAxis());
  2532     // Check if we need to wrap |item| to a new line
  2533     // (i.e. check if its outer hypothetical main size pushes our line over
  2534     // the threshold)
  2535     if (wrapThreshold != NS_UNCONSTRAINEDSIZE &&
  2536         !curLine->IsEmpty() && // No need to wrap at start of a line.
  2537         wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() +
  2538                          itemOuterHypotheticalMainSize)) {
  2539       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
  2542     // Add item to current flex line (and update the line's bookkeeping about
  2543     // how large its items collectively are).
  2544     curLine->AddItem(item.forget(), shouldInsertAtFront,
  2545                      itemInnerHypotheticalMainSize,
  2546                      itemOuterHypotheticalMainSize);
  2548     // Honor "page-break-after", if we're multi-line and have more children:
  2549     if (!isSingleLine && childFrame->GetNextSibling() &&
  2550         childFrame->StyleDisplay()->mBreakAfter) {
  2551       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
  2553     itemIdxInContainer++;
  2556   return NS_OK;
  2559 // Retrieves the content-box main-size of our flex container from the
  2560 // reflow state (specifically, the main-size of *this continuation* of the
  2561 // flex container).
  2562 nscoord
  2563 nsFlexContainerFrame::GetMainSizeFromReflowState(
  2564   const nsHTMLReflowState& aReflowState,
  2565   const FlexboxAxisTracker& aAxisTracker)
  2567   if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
  2568     // Horizontal case is easy -- our main size is our computed width
  2569     // (which is already resolved).
  2570     return aReflowState.ComputedWidth();
  2573   return GetEffectiveComputedHeight(aReflowState);
  2576 // Returns the largest outer hypothetical main-size of any line in |aLines|.
  2577 // (i.e. the hypothetical main-size of the largest line)
  2578 static nscoord
  2579 GetLargestLineMainSize(const FlexLine* aFirstLine)
  2581   nscoord largestLineOuterSize = 0;
  2582   for (const FlexLine* line = aFirstLine; line; line = line->getNext()) {
  2583     largestLineOuterSize = std::max(largestLineOuterSize,
  2584                                     line->GetTotalOuterHypotheticalMainSize());
  2586   return largestLineOuterSize;
  2589 // Returns the content-box main-size of our flex container, based on the
  2590 // available height (if appropriate) and the main-sizes of the flex items.
  2591 static nscoord
  2592 ClampFlexContainerMainSize(const nsHTMLReflowState& aReflowState,
  2593                            const FlexboxAxisTracker& aAxisTracker,
  2594                            nscoord aUnclampedMainSize,
  2595                            nscoord aAvailableHeightForContent,
  2596                            const FlexLine* aFirstLine,
  2597                            nsReflowStatus& aStatus)
  2599   MOZ_ASSERT(aFirstLine, "null first line pointer");
  2601   if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
  2602     // Horizontal case is easy -- our main size should already be resolved
  2603     // before we get a call to Reflow. We don't have to worry about doing
  2604     // page-breaking or shrinkwrapping in the horizontal axis.
  2605     return aUnclampedMainSize;
  2608   if (aUnclampedMainSize != NS_INTRINSICSIZE) {
  2609     // Vertical case, with fixed height:
  2610     if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE ||
  2611         aUnclampedMainSize < aAvailableHeightForContent) {
  2612       // Not in a fragmenting context, OR no need to fragment because we have
  2613       // more available height than we need. Either way, just use our fixed
  2614       // height.  (Note that the reflow state has already done the appropriate
  2615       // min/max-height clamping.)
  2616       return aUnclampedMainSize;
  2619     // Fragmenting *and* our fixed height is too tall for available height:
  2620     // Mark incomplete so we get a next-in-flow, and take up all of the
  2621     // available height (or the amount of height required by our children, if
  2622     // that's larger; but of course not more than our own computed height).
  2623     // XXXdholbert For now, we don't support pushing children to our next
  2624     // continuation or splitting children, so "amount of height required by
  2625     // our children" is just the main-size (height) of our longest flex line.
  2626     NS_FRAME_SET_INCOMPLETE(aStatus);
  2627     nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
  2629     if (largestLineOuterSize <= aAvailableHeightForContent) {
  2630       return aAvailableHeightForContent;
  2632     return std::min(aUnclampedMainSize, largestLineOuterSize);
  2635   // Vertical case, with auto-height:
  2636   // Resolve auto-height to the largest FlexLine-length, clamped to our
  2637   // computed min/max main-size properties (min-height & max-height).
  2638   // XXXdholbert Handle constrained-aAvailableHeightForContent case here.
  2639   nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine);
  2640   return NS_CSS_MINMAX(largestLineOuterSize,
  2641                        aReflowState.ComputedMinHeight(),
  2642                        aReflowState.ComputedMaxHeight());
  2645 nscoord
  2646 nsFlexContainerFrame::ComputeCrossSize(const nsHTMLReflowState& aReflowState,
  2647                                        const FlexboxAxisTracker& aAxisTracker,
  2648                                        nscoord aSumLineCrossSizes,
  2649                                        nscoord aAvailableHeightForContent,
  2650                                        bool* aIsDefinite,
  2651                                        nsReflowStatus& aStatus)
  2653   MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null");
  2655   if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
  2656     // Cross axis is horizontal: our cross size is our computed width
  2657     // (which is already resolved).
  2658     *aIsDefinite = true;
  2659     return aReflowState.ComputedWidth();
  2662   nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState);
  2663   if (effectiveComputedHeight != NS_INTRINSICSIZE) {
  2664     // Cross-axis is vertical, and we have a fixed height:
  2665     *aIsDefinite = true;
  2666     if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE ||
  2667         effectiveComputedHeight < aAvailableHeightForContent) {
  2668       // Not in a fragmenting context, OR no need to fragment because we have
  2669       // more available height than we need. Either way, just use our fixed
  2670       // height.  (Note that the reflow state has already done the appropriate
  2671       // min/max-height clamping.)
  2672       return effectiveComputedHeight;
  2675     // Fragmenting *and* our fixed height is too tall for available height:
  2676     // Mark incomplete so we get a next-in-flow, and take up all of the
  2677     // available height (or the amount of height required by our children, if
  2678     // that's larger; but of course not more than our own computed height).
  2679     // XXXdholbert For now, we don't support pushing children to our next
  2680     // continuation or splitting children, so "amount of height required by
  2681     // our children" is just our line-height.
  2682     NS_FRAME_SET_INCOMPLETE(aStatus);
  2683     if (aSumLineCrossSizes <= aAvailableHeightForContent) {
  2684       return aAvailableHeightForContent;
  2686     return std::min(effectiveComputedHeight, aSumLineCrossSizes);
  2689   // Cross axis is vertical and we have auto-height: shrink-wrap our line(s),
  2690   // subject to our min-size / max-size constraints in that (vertical) axis.
  2691   // XXXdholbert Handle constrained-aAvailableHeightForContent case here.
  2692   *aIsDefinite = false;
  2693   return NS_CSS_MINMAX(aSumLineCrossSizes,
  2694                        aReflowState.ComputedMinHeight(),
  2695                        aReflowState.ComputedMaxHeight());
  2698 void
  2699 FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent,
  2700                                   nscoord aContentBoxMainSize,
  2701                                   const FlexboxAxisTracker& aAxisTracker)
  2703   MainAxisPositionTracker mainAxisPosnTracker(aAxisTracker, this,
  2704                                               aJustifyContent,
  2705                                               aContentBoxMainSize);
  2706   for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  2707     nscoord itemMainBorderBoxSize =
  2708       item->GetMainSize() +
  2709       item->GetBorderPaddingSizeInAxis(mainAxisPosnTracker.GetAxis());
  2711     // Resolve any main-axis 'auto' margins on aChild to an actual value.
  2712     mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item);
  2714     // Advance our position tracker to child's upper-left content-box corner,
  2715     // and use that as its position in the main axis.
  2716     mainAxisPosnTracker.EnterMargin(item->GetMargin());
  2717     mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize);
  2719     item->SetMainPosition(mainAxisPosnTracker.GetPosition());
  2721     mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize);
  2722     mainAxisPosnTracker.ExitMargin(item->GetMargin());
  2723     mainAxisPosnTracker.TraversePackingSpace();
  2727 // Helper method to take care of children who ASK_FOR_BASELINE, when
  2728 // we need their baseline.
  2729 static void
  2730 ResolveReflowedChildAscent(nsIFrame* aFrame,
  2731                            nsHTMLReflowMetrics& aChildDesiredSize)
  2733   if (aChildDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
  2734     // Use GetFirstLineBaseline(), or just GetBaseline() if that fails.
  2735     nscoord ascent;
  2736     if (nsLayoutUtils::GetFirstLineBaseline(aFrame, &ascent)) {
  2737       aChildDesiredSize.SetTopAscent(ascent);
  2738     } else {
  2739       aChildDesiredSize.SetTopAscent(aFrame->GetBaseline());
  2744 /**
  2745  * Given the flex container's "logical ascent" (i.e. distance from the
  2746  * flex container's content-box cross-start edge to its baseline), returns
  2747  * its actual physical ascent value (the distance from the *border-box* top
  2748  * edge to its baseline).
  2749  */
  2750 static nscoord
  2751 ComputePhysicalAscentFromLogicalAscent(nscoord aLogicalAscent,
  2752                                        nscoord aContentBoxCrossSize,
  2753                                        const nsHTMLReflowState& aReflowState,
  2754                                        const FlexboxAxisTracker& aAxisTracker)
  2756   return aReflowState.ComputedPhysicalBorderPadding().top +
  2757     PhysicalPosFromLogicalPos(aLogicalAscent, aContentBoxCrossSize,
  2758                               aAxisTracker.GetCrossAxis());
  2761 nsresult
  2762 nsFlexContainerFrame::SizeItemInCrossAxis(
  2763   nsPresContext* aPresContext,
  2764   const FlexboxAxisTracker& aAxisTracker,
  2765   nsHTMLReflowState& aChildReflowState,
  2766   FlexItem& aItem)
  2768   // In vertical flexbox (with horizontal cross-axis), we can just trust the
  2769   // reflow state's computed-width as our cross-size. We also don't need to
  2770   // record the baseline because we'll have converted any "align-self:baseline"
  2771   // items to be "align-self:flex-start" in the FlexItem constructor.
  2772   // FIXME: Once we support writing-mode (vertical text), we will be able to
  2773   // have baseline-aligned items in a vertical flexbox, and we'll need to
  2774   // record baseline information here.
  2775   if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
  2776     MOZ_ASSERT(aItem.GetAlignSelf() != NS_STYLE_ALIGN_ITEMS_BASELINE,
  2777                "In vert flex container, we depend on FlexItem constructor to "
  2778                "convert 'align-self: baseline' to 'align-self: flex-start'");
  2779     aItem.SetCrossSize(aChildReflowState.ComputedWidth());
  2780     return NS_OK;
  2783   MOZ_ASSERT(!aItem.HadMeasuringReflow(),
  2784              "We shouldn't need more than one measuring reflow");
  2786   if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH) {
  2787     // This item's got "align-self: stretch", so we probably imposed a
  2788     // stretched computed height on it during its previous reflow. We're
  2789     // not imposing that height for *this* measuring reflow, so we need to
  2790     // tell it to treat this reflow as a vertical resize (regardless of
  2791     // whether any of its ancestors are being resized).
  2792     aChildReflowState.mFlags.mVResize = true;
  2794   nsHTMLReflowMetrics childDesiredSize(aChildReflowState);
  2795   nsReflowStatus childReflowStatus;
  2796   const uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
  2797   nsresult rv = ReflowChild(aItem.Frame(), aPresContext,
  2798                             childDesiredSize, aChildReflowState,
  2799                             0, 0, flags, childReflowStatus);
  2800   aItem.SetHadMeasuringReflow();
  2801   NS_ENSURE_SUCCESS(rv, rv);
  2803   // XXXdholbert Once we do pagination / splitting, we'll need to actually
  2804   // handle incomplete childReflowStatuses. But for now, we give our kids
  2805   // unconstrained available height, which means they should always complete.
  2806   MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
  2807              "We gave flex item unconstrained available height, so it "
  2808              "should be complete");
  2810   // Tell the child we're done with its initial reflow.
  2811   // (Necessary for e.g. GetBaseline() to work below w/out asserting)
  2812   rv = FinishReflowChild(aItem.Frame(), aPresContext,
  2813                          childDesiredSize, &aChildReflowState, 0, 0, flags);
  2814   NS_ENSURE_SUCCESS(rv, rv);
  2816   // Save the sizing info that we learned from this reflow
  2817   // -----------------------------------------------------
  2819   // Tentatively store the child's desired content-box cross-size.
  2820   // Note that childDesiredSize is the border-box size, so we have to
  2821   // subtract border & padding to get the content-box size.
  2822   // (Note that at this point in the code, we know our cross axis is vertical,
  2823   // so we don't bother with making aAxisTracker pick the cross-axis component
  2824   // for us.)
  2825   nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom();
  2826   if (childDesiredSize.Height() < crossAxisBorderPadding) {
  2827     // Child's requested size isn't large enough for its border/padding!
  2828     // This is OK for the trivial nsFrame::Reflow() impl, but other frame
  2829     // classes should know better. So, if we get here, the child had better be
  2830     // an instance of nsFrame (i.e. it should return null from GetType()).
  2831     // XXXdholbert Once we've fixed bug 765861, we should upgrade this to an
  2832     // assertion that trivially passes if bug 765861's flag has been flipped.
  2833     NS_WARN_IF_FALSE(!aItem.Frame()->GetType(),
  2834                      "Child should at least request space for border/padding");
  2835     aItem.SetCrossSize(0);
  2836   } else {
  2837     // (normal case)
  2838     aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding);
  2841   // If we need to do baseline-alignment, store the child's ascent.
  2842   if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE) {
  2843     ResolveReflowedChildAscent(aItem.Frame(), childDesiredSize);
  2844     aItem.SetAscent(childDesiredSize.TopAscent());
  2847   return NS_OK;
  2850 void
  2851 FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition,
  2852                                    const FlexboxAxisTracker& aAxisTracker)
  2854   SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker);
  2856   for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
  2857     // First, stretch the item's cross size (if appropriate), and resolve any
  2858     // auto margins in this axis.
  2859     item->ResolveStretchedCrossSize(mLineCrossSize, aAxisTracker);
  2860     lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, *item);
  2862     // Compute the cross-axis position of this item
  2863     nscoord itemCrossBorderBoxSize =
  2864       item->GetCrossSize() +
  2865       item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis());
  2866     lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker);
  2867     lineCrossAxisPosnTracker.EnterMargin(item->GetMargin());
  2868     lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize);
  2870     item->SetCrossPosition(aLineStartPosition +
  2871                            lineCrossAxisPosnTracker.GetPosition());
  2873     // Back out to cross-axis edge of the line.
  2874     lineCrossAxisPosnTracker.ResetPosition();
  2878 nsresult
  2879 nsFlexContainerFrame::Reflow(nsPresContext*           aPresContext,
  2880                              nsHTMLReflowMetrics&     aDesiredSize,
  2881                              const nsHTMLReflowState& aReflowState,
  2882                              nsReflowStatus&          aStatus)
  2884   DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame");
  2885   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
  2886   PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG,
  2887          ("Reflow() for nsFlexContainerFrame %p\n", this));
  2889   if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) {
  2890     return NS_OK;
  2893   // We (and our children) can only depend on our ancestor's height if we have
  2894   // a percent-height, or if we're positioned and we have "top" and "bottom"
  2895   // set and have height:auto.  (There are actually other cases, too -- e.g. if
  2896   // our parent is itself a vertical flex container and we're flexible -- but
  2897   // we'll let our ancestors handle those sorts of cases.)
  2898   const nsStylePosition* stylePos = StylePosition();
  2899   if (stylePos->mHeight.HasPercent() ||
  2900       (StyleDisplay()->IsAbsolutelyPositionedStyle() &&
  2901        eStyleUnit_Auto == stylePos->mHeight.GetUnit() &&
  2902        eStyleUnit_Auto != stylePos->mOffset.GetTopUnit() &&
  2903        eStyleUnit_Auto != stylePos->mOffset.GetBottomUnit())) {
  2904     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
  2907 #ifdef DEBUG
  2908   SanityCheckAnonymousFlexItems();
  2909 #endif // DEBUG
  2911   // If we've never reordered our children, then we can trust that they're
  2912   // already in DOM-order, and we only need to consider their "order" property
  2913   // when checking them for sortedness & sorting them.
  2914   //
  2915   // After we actually sort them, though, we can't trust that they're in DOM
  2916   // order anymore.  So, from that point on, our sort & sorted-order-checking
  2917   // operations need to use a fancier LEQ function that also takes DOM order
  2918   // into account, so that we can honor the spec's requirement that frames w/
  2919   // equal "order" values are laid out in DOM order.
  2921   if (!HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED)) {
  2922     if (SortChildrenIfNeeded<IsOrderLEQ>()) {
  2923       AddStateBits(NS_STATE_FLEX_CHILDREN_REORDERED);
  2925   } else {
  2926     SortChildrenIfNeeded<IsOrderLEQWithDOMFallback>();
  2929   const FlexboxAxisTracker axisTracker(this);
  2931   // If we're being fragmented into a constrained height, subtract off
  2932   // borderpadding-top from it, to get the available height for our
  2933   // content box. (Don't subtract if we're skipping top border/padding,
  2934   // though.)
  2935   nscoord availableHeightForContent = aReflowState.AvailableHeight();
  2936   if (availableHeightForContent != NS_UNCONSTRAINEDSIZE &&
  2937       !(GetSkipSides() & (1 << NS_SIDE_TOP))) {
  2938     availableHeightForContent -= aReflowState.ComputedPhysicalBorderPadding().top;
  2939     // (Don't let that push availableHeightForContent below zero, though):
  2940     availableHeightForContent = std::max(availableHeightForContent, 0);
  2943   nscoord contentBoxMainSize = GetMainSizeFromReflowState(aReflowState,
  2944                                                           axisTracker);
  2946   nsAutoTArray<StrutInfo, 1> struts;
  2947   nsresult rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus,
  2948                              contentBoxMainSize, availableHeightForContent,
  2949                              struts, axisTracker);
  2951   if (NS_SUCCEEDED(rv) && !struts.IsEmpty()) {
  2952     // We're restarting flex layout, with new knowledge of collapsed items.
  2953     rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus,
  2954                       contentBoxMainSize, availableHeightForContent,
  2955                       struts, axisTracker);
  2958   return rv;
  2961 // RAII class to clean up a list of FlexLines.
  2962 // Specifically, this removes each line from the list, deletes all the
  2963 // FlexItems in its list, and deletes the FlexLine.
  2964 class MOZ_STACK_CLASS AutoFlexLineListClearer
  2966 public:
  2967   AutoFlexLineListClearer(LinkedList<FlexLine>& aLines
  2968                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
  2969   : mLines(aLines)
  2971     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
  2974   ~AutoFlexLineListClearer()
  2976     while (FlexLine* line = mLines.popFirst()) {
  2977       while (FlexItem* item = line->mItems.popFirst()) {
  2978         delete item;
  2980       delete line;
  2984 private:
  2985   LinkedList<FlexLine>& mLines;
  2986   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
  2987 };
  2989 nsresult
  2990 nsFlexContainerFrame::DoFlexLayout(nsPresContext*           aPresContext,
  2991                                    nsHTMLReflowMetrics&     aDesiredSize,
  2992                                    const nsHTMLReflowState& aReflowState,
  2993                                    nsReflowStatus&          aStatus,
  2994                                    nscoord aContentBoxMainSize,
  2995                                    nscoord aAvailableHeightForContent,
  2996                                    nsTArray<StrutInfo>& aStruts,
  2997                                    const FlexboxAxisTracker& aAxisTracker)
  2999   aStatus = NS_FRAME_COMPLETE;
  3001   LinkedList<FlexLine> lines;
  3002   AutoFlexLineListClearer cleanupLines(lines);
  3004   nsresult rv = GenerateFlexLines(aPresContext, aReflowState,
  3005                                   aContentBoxMainSize,
  3006                                   aAvailableHeightForContent,
  3007                                   aStruts, aAxisTracker, lines);
  3008   NS_ENSURE_SUCCESS(rv, rv);
  3010   aContentBoxMainSize =
  3011     ClampFlexContainerMainSize(aReflowState, aAxisTracker,
  3012                                aContentBoxMainSize, aAvailableHeightForContent,
  3013                                lines.getFirst(), aStatus);
  3015   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
  3016     line->ResolveFlexibleLengths(aContentBoxMainSize);
  3019   // Cross Size Determination - Flexbox spec section 9.4
  3020   // ===================================================
  3021   // Calculate the hypothetical cross size of each item:
  3022   nscoord sumLineCrossSizes = 0;
  3023   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
  3024     for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) {
  3025       // (If the item's already been stretched, or it's a strut, then it
  3026       // already knows its cross size.  Don't bother trying to recalculate it.)
  3027       if (!item->IsStretched() && !item->IsStrut()) {
  3028         nsHTMLReflowState childReflowState(aPresContext, aReflowState,
  3029                                            item->Frame(),
  3030                                            nsSize(aReflowState.ComputedWidth(),
  3031                                                   NS_UNCONSTRAINEDSIZE));
  3032         // Override computed main-size
  3033         if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
  3034           childReflowState.SetComputedWidth(item->GetMainSize());
  3035         } else {
  3036           childReflowState.SetComputedHeight(item->GetMainSize());
  3039         nsresult rv = SizeItemInCrossAxis(aPresContext, aAxisTracker,
  3040                                           childReflowState, *item);
  3041         NS_ENSURE_SUCCESS(rv, rv);
  3044     // Now that we've finished with this line's items, size the line itself:
  3045     line->ComputeCrossSizeAndBaseline(aAxisTracker);
  3046     sumLineCrossSizes += line->GetLineCrossSize();
  3049   bool isCrossSizeDefinite;
  3050   const nscoord contentBoxCrossSize =
  3051     ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes,
  3052                      aAvailableHeightForContent, &isCrossSizeDefinite, aStatus);
  3054   // Set up state for cross-axis alignment, at a high level (outside the
  3055   // scope of a particular flex line)
  3056   CrossAxisPositionTracker
  3057     crossAxisPosnTracker(lines.getFirst(),
  3058                          aReflowState.mStylePosition->mAlignContent,
  3059                          contentBoxCrossSize, isCrossSizeDefinite,
  3060                          aAxisTracker);
  3062   // Now that we know the cross size of each line (including
  3063   // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
  3064   // constructor), we can create struts for any flex items with
  3065   // "visibility: collapse" (and restart flex layout).
  3066   if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
  3067     BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
  3068     if (!aStruts.IsEmpty()) {
  3069       // Restart flex layout, using our struts.
  3070       return NS_OK;
  3074   // If the container should derive its baseline from the first FlexLine,
  3075   // do that here (while crossAxisPosnTracker is conveniently pointing
  3076   // at the cross-start edge of that line, which the line's baseline offset is
  3077   // measured from):
  3078   nscoord flexContainerAscent;
  3079   if (!aAxisTracker.AreAxesInternallyReversed()) {
  3080     nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset();
  3081     if (firstLineBaselineOffset == nscoord_MIN) {
  3082       // No baseline-aligned items in line. Use sentinel value to prompt us to
  3083       // get baseline from the first FlexItem after we've reflowed it.
  3084       flexContainerAscent = nscoord_MIN;
  3085     } else  {
  3086       flexContainerAscent =
  3087         ComputePhysicalAscentFromLogicalAscent(
  3088           crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
  3089           contentBoxCrossSize, aReflowState, aAxisTracker);
  3093   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
  3095     // Main-Axis Alignment - Flexbox spec section 9.5
  3096     // ==============================================
  3097     line->PositionItemsInMainAxis(aReflowState.mStylePosition->mJustifyContent,
  3098                                   aContentBoxMainSize,
  3099                                   aAxisTracker);
  3101     // Cross-Axis Alignment - Flexbox spec section 9.6
  3102     // ===============================================
  3103     line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(),
  3104                                    aAxisTracker);
  3105     crossAxisPosnTracker.TraverseLine(*line);
  3106     crossAxisPosnTracker.TraversePackingSpace();
  3109   // If the container should derive its baseline from the last FlexLine,
  3110   // do that here (while crossAxisPosnTracker is conveniently pointing
  3111   // at the cross-end edge of that line, which the line's baseline offset is
  3112   // measured from):
  3113   if (aAxisTracker.AreAxesInternallyReversed()) {
  3114     nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset();
  3115     if (lastLineBaselineOffset == nscoord_MIN) {
  3116       // No baseline-aligned items in line. Use sentinel value to prompt us to
  3117       // get baseline from the last FlexItem after we've reflowed it.
  3118       flexContainerAscent = nscoord_MIN;
  3119     } else {
  3120       flexContainerAscent =
  3121         ComputePhysicalAscentFromLogicalAscent(
  3122           crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
  3123           contentBoxCrossSize, aReflowState, aAxisTracker);
  3127   // Before giving each child a final reflow, calculate the origin of the
  3128   // flex container's content box (with respect to its border-box), so that
  3129   // we can compute our flex item's final positions.
  3130   nsMargin containerBorderPadding(aReflowState.ComputedPhysicalBorderPadding());
  3131   ApplySkipSides(containerBorderPadding, &aReflowState);
  3132   const nsPoint containerContentBoxOrigin(containerBorderPadding.left,
  3133                                           containerBorderPadding.top);
  3135   // FINAL REFLOW: Give each child frame another chance to reflow, now that
  3136   // we know its final size and position.
  3137   for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
  3138     for (const FlexItem* item = line->GetFirstItem(); item;
  3139          item = item->getNext()) {
  3140       nsPoint physicalPosn = aAxisTracker.PhysicalPointFromLogicalPoint(
  3141                                item->GetMainPosition(),
  3142                                item->GetCrossPosition(),
  3143                                aContentBoxMainSize,
  3144                                contentBoxCrossSize);
  3145       // Adjust physicalPosn to be relative to the container's border-box
  3146       // (i.e. its frame rect), instead of the container's content-box:
  3147       physicalPosn += containerContentBoxOrigin;
  3149       nsHTMLReflowState childReflowState(aPresContext, aReflowState,
  3150                                          item->Frame(),
  3151                                          nsSize(aReflowState.ComputedWidth(),
  3152                                                 NS_UNCONSTRAINEDSIZE));
  3154       // Keep track of whether we've overriden the child's computed height
  3155       // and/or width, so we can set its resize flags accordingly.
  3156       bool didOverrideComputedWidth = false;
  3157       bool didOverrideComputedHeight = false;
  3159       // Override computed main-size
  3160       if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) {
  3161         childReflowState.SetComputedWidth(item->GetMainSize());
  3162         didOverrideComputedWidth = true;
  3163       } else {
  3164         childReflowState.SetComputedHeight(item->GetMainSize());
  3165         didOverrideComputedHeight = true;
  3168       // Override reflow state's computed cross-size, for stretched items.
  3169       if (item->IsStretched()) {
  3170         MOZ_ASSERT(item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH,
  3171                    "stretched item w/o 'align-self: stretch'?");
  3172         if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) {
  3173           childReflowState.SetComputedWidth(item->GetCrossSize());
  3174           didOverrideComputedWidth = true;
  3175         } else {
  3176           // If this item's height is stretched, it's a relative height.
  3177           item->Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
  3178           childReflowState.SetComputedHeight(item->GetCrossSize());
  3179           didOverrideComputedHeight = true;
  3183       // XXXdholbert Might need to actually set the correct margins in the
  3184       // reflow state at some point, so that they can be saved on the frame for
  3185       // UsedMarginProperty().  Maybe doesn't matter though...?
  3187       // If we're overriding the computed width or height, *and* we had an
  3188       // earlier "measuring" reflow, then this upcoming reflow needs to be
  3189       // treated as a resize.
  3190       if (item->HadMeasuringReflow()) {
  3191         if (didOverrideComputedWidth) {
  3192           // (This is somewhat redundant, since the reflow state already
  3193           // sets mHResize whenever our computed width has changed since the
  3194           // previous reflow. Still, it's nice for symmetry, and it may become
  3195           // necessary once we support orthogonal flows.)
  3196           childReflowState.mFlags.mHResize = true;
  3198         if (didOverrideComputedHeight) {
  3199           childReflowState.mFlags.mVResize = true;
  3202       // NOTE: Be very careful about doing anything else with childReflowState
  3203       // after this point, because some of its methods (e.g. SetComputedWidth)
  3204       // internally call InitResizeFlags and stomp on mVResize & mHResize.
  3206       nsHTMLReflowMetrics childDesiredSize(childReflowState);
  3207       nsReflowStatus childReflowStatus;
  3208       nsresult rv = ReflowChild(item->Frame(), aPresContext,
  3209                                 childDesiredSize, childReflowState,
  3210                                 physicalPosn.x, physicalPosn.y,
  3211                                 0, childReflowStatus);
  3212       NS_ENSURE_SUCCESS(rv, rv);
  3214       // XXXdholbert Once we do pagination / splitting, we'll need to actually
  3215       // handle incomplete childReflowStatuses. But for now, we give our kids
  3216       // unconstrained available height, which means they should always
  3217       // complete.
  3218       MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus),
  3219                  "We gave flex item unconstrained available height, so it "
  3220                  "should be complete");
  3222       childReflowState.ApplyRelativePositioning(&physicalPosn);
  3224       rv = FinishReflowChild(item->Frame(), aPresContext,
  3225                              childDesiredSize, &childReflowState,
  3226                              physicalPosn.x, physicalPosn.y, 0);
  3227       NS_ENSURE_SUCCESS(rv, rv);
  3229       // If this is our first child and we haven't established a baseline for
  3230       // the container yet (i.e. if we don't have 'align-self: baseline' on any
  3231       // children), then use this child's baseline as the container's baseline.
  3232       if (item->Frame() == mFrames.FirstChild() &&
  3233           flexContainerAscent == nscoord_MIN) {
  3234         ResolveReflowedChildAscent(item->Frame(), childDesiredSize);
  3236         // (We use GetNormalPosition() instead of physicalPosn because we don't
  3237         // want relative positioning on the child to affect the baseline that we
  3238         // read from it).
  3239         flexContainerAscent = item->Frame()->GetNormalPosition().y +
  3240           childDesiredSize.TopAscent();
  3245   nsSize desiredContentBoxSize =
  3246     aAxisTracker.PhysicalSizeFromLogicalSizes(aContentBoxMainSize,
  3247                                               contentBoxCrossSize);
  3249   aDesiredSize.Width() = desiredContentBoxSize.width +
  3250     containerBorderPadding.LeftRight();
  3251   // Does *NOT* include bottom border/padding yet (we add that a bit lower down)
  3252   aDesiredSize.Height() = desiredContentBoxSize.height +
  3253     containerBorderPadding.top;
  3255   if (flexContainerAscent == nscoord_MIN) {
  3256     // Still don't have our baseline set -- this happens if we have no
  3257     // children (or if our children are huge enough that they have nscoord_MIN
  3258     // as their baseline... in which case, we'll use the wrong baseline, but no
  3259     // big deal)
  3260     NS_WARN_IF_FALSE(lines.getFirst()->IsEmpty(),
  3261                      "Have flex items but didn't get an ascent - that's odd "
  3262                      "(or there are just gigantic sizes involved)");
  3263     // Per spec, just use the bottom of content-box.
  3264     flexContainerAscent = aDesiredSize.Height();
  3266   aDesiredSize.SetTopAscent(flexContainerAscent);
  3268   // Now: If we're complete, add bottom border/padding to desired height
  3269   // (unless that pushes us over available height, in which case we become
  3270   // incomplete (unless we already weren't asking for any height, in which case
  3271   // we stay complete to avoid looping forever)).
  3272   // NOTE: If we're auto-height, we allow our bottom border/padding to push us
  3273   // over the available height without requesting a continuation, for
  3274   // consistency with the behavior of "display:block" elements.
  3275   if (NS_FRAME_IS_COMPLETE(aStatus)) {
  3276     // NOTE: We can't use containerBorderPadding.bottom for this, because if
  3277     // we're auto-height, ApplySkipSides will have zeroed it (because it
  3278     // assumed we might get a continuation). We have the correct value in
  3279     // aReflowState.ComputedPhyiscalBorderPadding().bottom, though, so we use that.
  3280     nscoord desiredHeightWithBottomBP =
  3281       aDesiredSize.Height() + aReflowState.ComputedPhysicalBorderPadding().bottom;
  3283     if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE ||
  3284         aDesiredSize.Height() == 0 ||
  3285         desiredHeightWithBottomBP <= aReflowState.AvailableHeight() ||
  3286         aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
  3287       // Update desired height to include bottom border/padding
  3288       aDesiredSize.Height() = desiredHeightWithBottomBP;
  3289     } else {
  3290       // We couldn't fit bottom border/padding, so we'll need a continuation.
  3291       NS_FRAME_SET_INCOMPLETE(aStatus);
  3295   // Overflow area = union(my overflow area, kids' overflow areas)
  3296   aDesiredSize.SetOverflowAreasToDesiredBounds();
  3297   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
  3298     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, e.get());
  3301   FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
  3302                                  aReflowState, aStatus);
  3304   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize)
  3305   return NS_OK;
  3308 /* virtual */ nscoord
  3309 nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
  3311   nscoord minWidth = 0;
  3312   DISPLAY_MIN_WIDTH(this, minWidth);
  3314   FlexboxAxisTracker axisTracker(this);
  3316   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
  3317     nscoord childMinWidth =
  3318       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),
  3319                                            nsLayoutUtils::MIN_WIDTH);
  3320     // For a horizontal single-line flex container, the intrinsic min width is
  3321     // the sum of its items' min widths.
  3322     // For a vertical flex container, or for a multi-line horizontal flex
  3323     // container, the intrinsic min width is the max of its items' min widths.
  3324     if (IsAxisHorizontal(axisTracker.GetMainAxis()) &&
  3325         NS_STYLE_FLEX_WRAP_NOWRAP == StylePosition()->mFlexWrap) {
  3326       minWidth += childMinWidth;
  3327     } else {
  3328       minWidth = std::max(minWidth, childMinWidth);
  3331   return minWidth;
  3334 /* virtual */ nscoord
  3335 nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
  3337   nscoord prefWidth = 0;
  3338   DISPLAY_PREF_WIDTH(this, prefWidth);
  3340   // XXXdholbert Optimization: We could cache our intrinsic widths like
  3341   // nsBlockFrame does (and return it early from this function if it's set).
  3342   // Whenever anything happens that might change it, set it to
  3343   // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicWidthsDirty
  3344   // does)
  3345   FlexboxAxisTracker axisTracker(this);
  3347   for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
  3348     nscoord childPrefWidth =
  3349       nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(),
  3350                                            nsLayoutUtils::PREF_WIDTH);
  3351     if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
  3352       prefWidth += childPrefWidth;
  3353     } else {
  3354       prefWidth = std::max(prefWidth, childPrefWidth);
  3357   return prefWidth;

mercurial