1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsFlexContainerFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,3358 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=2 et sw=2 tw=80: */ 1.6 + 1.7 +/* This Source Code is subject to the terms of the Mozilla Public License 1.8 + * version 2.0 (the "License"). You can obtain a copy of the License at 1.9 + * http://mozilla.org/MPL/2.0/. */ 1.10 + 1.11 +/* rendering object for CSS "display: flex" */ 1.12 + 1.13 +#include "nsFlexContainerFrame.h" 1.14 +#include "nsContentUtils.h" 1.15 +#include "nsCSSAnonBoxes.h" 1.16 +#include "nsDisplayList.h" 1.17 +#include "nsIFrameInlines.h" 1.18 +#include "nsLayoutUtils.h" 1.19 +#include "nsPlaceholderFrame.h" 1.20 +#include "nsPresContext.h" 1.21 +#include "nsStyleContext.h" 1.22 +#include "prlog.h" 1.23 +#include <algorithm> 1.24 +#include "mozilla/LinkedList.h" 1.25 + 1.26 +using namespace mozilla; 1.27 +using namespace mozilla::css; 1.28 +using namespace mozilla::layout; 1.29 + 1.30 +// Convenience typedefs for helper classes that we forward-declare in .h file 1.31 +// (so that nsFlexContainerFrame methods can use them as parameters): 1.32 +typedef nsFlexContainerFrame::FlexItem FlexItem; 1.33 +typedef nsFlexContainerFrame::FlexLine FlexLine; 1.34 +typedef nsFlexContainerFrame::FlexboxAxisTracker FlexboxAxisTracker; 1.35 +typedef nsFlexContainerFrame::StrutInfo StrutInfo; 1.36 + 1.37 +#ifdef PR_LOGGING 1.38 +static PRLogModuleInfo* 1.39 +GetFlexContainerLog() 1.40 +{ 1.41 + static PRLogModuleInfo *sLog; 1.42 + if (!sLog) 1.43 + sLog = PR_NewLogModule("nsFlexContainerFrame"); 1.44 + return sLog; 1.45 +} 1.46 +#endif /* PR_LOGGING */ 1.47 + 1.48 +// XXXdholbert Some of this helper-stuff should be separated out into a general 1.49 +// "LogicalAxisUtils.h" helper. Should that be a class, or a namespace (under 1.50 +// what super-namespace?), or what? 1.51 + 1.52 +// Helper enums 1.53 +// ============ 1.54 + 1.55 +// Represents a physical orientation for an axis. 1.56 +// The directional suffix indicates the direction in which the axis *grows*. 1.57 +// So e.g. eAxis_LR means a horizontal left-to-right axis, whereas eAxis_BT 1.58 +// means a vertical bottom-to-top axis. 1.59 +// NOTE: The order here is important -- these values are used as indices into 1.60 +// the static array 'kAxisOrientationToSidesMap', defined below. 1.61 +enum AxisOrientationType { 1.62 + eAxis_LR, 1.63 + eAxis_RL, 1.64 + eAxis_TB, 1.65 + eAxis_BT, 1.66 + eNumAxisOrientationTypes // For sizing arrays that use these values as indices 1.67 +}; 1.68 + 1.69 +// Represents one or the other extreme of an axis (e.g. for the main axis, the 1.70 +// main-start vs. main-end edge. 1.71 +// NOTE: The order here is important -- these values are used as indices into 1.72 +// the sub-arrays in 'kAxisOrientationToSidesMap', defined below. 1.73 +enum AxisEdgeType { 1.74 + eAxisEdge_Start, 1.75 + eAxisEdge_End, 1.76 + eNumAxisEdges // For sizing arrays that use these values as indices 1.77 +}; 1.78 + 1.79 +// This array maps each axis orientation to a pair of corresponding 1.80 +// [start, end] physical mozilla::css::Side values. 1.81 +static const Side 1.82 +kAxisOrientationToSidesMap[eNumAxisOrientationTypes][eNumAxisEdges] = { 1.83 + { eSideLeft, eSideRight }, // eAxis_LR 1.84 + { eSideRight, eSideLeft }, // eAxis_RL 1.85 + { eSideTop, eSideBottom }, // eAxis_TB 1.86 + { eSideBottom, eSideTop } // eAxis_BT 1.87 +}; 1.88 + 1.89 +// Helper structs / classes / methods 1.90 +// ================================== 1.91 + 1.92 +// Indicates whether advancing along the given axis is equivalent to 1.93 +// increasing our X or Y position (as opposed to decreasing it). 1.94 +static inline bool 1.95 +AxisGrowsInPositiveDirection(AxisOrientationType aAxis) 1.96 +{ 1.97 + return eAxis_LR == aAxis || eAxis_TB == aAxis; 1.98 +} 1.99 + 1.100 +// Indicates whether the given axis is horizontal. 1.101 +static inline bool 1.102 +IsAxisHorizontal(AxisOrientationType aAxis) 1.103 +{ 1.104 + return eAxis_LR == aAxis || eAxis_RL == aAxis; 1.105 +} 1.106 + 1.107 +// Given an AxisOrientationType, returns the "reverse" AxisOrientationType 1.108 +// (in the same dimension, but the opposite direction) 1.109 +static inline AxisOrientationType 1.110 +GetReverseAxis(AxisOrientationType aAxis) 1.111 +{ 1.112 + AxisOrientationType reversedAxis; 1.113 + 1.114 + if (aAxis % 2 == 0) { 1.115 + // even enum value. Add 1 to reverse. 1.116 + reversedAxis = AxisOrientationType(aAxis + 1); 1.117 + } else { 1.118 + // odd enum value. Subtract 1 to reverse. 1.119 + reversedAxis = AxisOrientationType(aAxis - 1); 1.120 + } 1.121 + 1.122 + // Check that we're still in the enum's valid range 1.123 + MOZ_ASSERT(reversedAxis >= eAxis_LR && 1.124 + reversedAxis <= eAxis_BT); 1.125 + 1.126 + return reversedAxis; 1.127 +} 1.128 + 1.129 +// Returns aFrame's computed value for 'height' or 'width' -- whichever is in 1.130 +// the same dimension as aAxis. 1.131 +static inline const nsStyleCoord& 1.132 +GetSizePropertyForAxis(const nsIFrame* aFrame, AxisOrientationType aAxis) 1.133 +{ 1.134 + const nsStylePosition* stylePos = aFrame->StylePosition(); 1.135 + 1.136 + return IsAxisHorizontal(aAxis) ? 1.137 + stylePos->mWidth : 1.138 + stylePos->mHeight; 1.139 +} 1.140 + 1.141 +/** 1.142 + * Converts a logical position in a given axis into a position in the 1.143 + * corresponding physical (x or y) axis. If the logical axis already maps 1.144 + * directly onto one of our physical axes (i.e. LTR or TTB), then the logical 1.145 + * and physical positions are equal; otherwise, we subtract the logical 1.146 + * position from the container-size in that axis, to flip the polarity. 1.147 + * (so e.g. a logical position of 2px in a RTL 20px-wide container 1.148 + * would correspond to a physical position of 18px.) 1.149 + */ 1.150 +static nscoord 1.151 +PhysicalPosFromLogicalPos(nscoord aLogicalPosn, 1.152 + nscoord aLogicalContainerSize, 1.153 + AxisOrientationType aAxis) { 1.154 + if (AxisGrowsInPositiveDirection(aAxis)) { 1.155 + return aLogicalPosn; 1.156 + } 1.157 + return aLogicalContainerSize - aLogicalPosn; 1.158 +} 1.159 + 1.160 +static nscoord 1.161 +MarginComponentForSide(const nsMargin& aMargin, Side aSide) 1.162 +{ 1.163 + switch (aSide) { 1.164 + case eSideLeft: 1.165 + return aMargin.left; 1.166 + case eSideRight: 1.167 + return aMargin.right; 1.168 + case eSideTop: 1.169 + return aMargin.top; 1.170 + case eSideBottom: 1.171 + return aMargin.bottom; 1.172 + } 1.173 + 1.174 + NS_NOTREACHED("unexpected Side enum"); 1.175 + return aMargin.left; // have to return something 1.176 + // (but something's busted if we got here) 1.177 +} 1.178 + 1.179 +static nscoord& 1.180 +MarginComponentForSide(nsMargin& aMargin, Side aSide) 1.181 +{ 1.182 + switch (aSide) { 1.183 + case eSideLeft: 1.184 + return aMargin.left; 1.185 + case eSideRight: 1.186 + return aMargin.right; 1.187 + case eSideTop: 1.188 + return aMargin.top; 1.189 + case eSideBottom: 1.190 + return aMargin.bottom; 1.191 + } 1.192 + 1.193 + NS_NOTREACHED("unexpected Side enum"); 1.194 + return aMargin.left; // have to return something 1.195 + // (but something's busted if we got here) 1.196 +} 1.197 + 1.198 +// Helper-macro to let us pick one of two expressions to evaluate 1.199 +// (a width expression vs. a height expression), to get a main-axis or 1.200 +// cross-axis component. 1.201 +// For code that has e.g. a nsSize object, FlexboxAxisTracker::GetMainComponent 1.202 +// and GetCrossComponent are cleaner; but in cases where we simply have 1.203 +// two separate expressions for width and height (which may be expensive to 1.204 +// evaluate), these macros will ensure that only the expression for the correct 1.205 +// axis gets evaluated. 1.206 +#define GET_MAIN_COMPONENT(axisTracker_, width_, height_) \ 1.207 + IsAxisHorizontal((axisTracker_).GetMainAxis()) ? (width_) : (height_) 1.208 + 1.209 +#define GET_CROSS_COMPONENT(axisTracker_, width_, height_) \ 1.210 + IsAxisHorizontal((axisTracker_).GetCrossAxis()) ? (width_) : (height_) 1.211 + 1.212 +// Encapsulates our flex container's main & cross axes. 1.213 +class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker { 1.214 +public: 1.215 + FlexboxAxisTracker(nsFlexContainerFrame* aFlexContainerFrame); 1.216 + 1.217 + // Accessors: 1.218 + AxisOrientationType GetMainAxis() const { return mMainAxis; } 1.219 + AxisOrientationType GetCrossAxis() const { return mCrossAxis; } 1.220 + 1.221 + nscoord GetMainComponent(const nsSize& aSize) const { 1.222 + return GET_MAIN_COMPONENT(*this, aSize.width, aSize.height); 1.223 + } 1.224 + int32_t GetMainComponent(const nsIntSize& aIntSize) const { 1.225 + return GET_MAIN_COMPONENT(*this, aIntSize.width, aIntSize.height); 1.226 + } 1.227 + 1.228 + nscoord GetCrossComponent(const nsSize& aSize) const { 1.229 + return GET_CROSS_COMPONENT(*this, aSize.width, aSize.height); 1.230 + } 1.231 + int32_t GetCrossComponent(const nsIntSize& aIntSize) const { 1.232 + return GET_CROSS_COMPONENT(*this, aIntSize.width, aIntSize.height); 1.233 + } 1.234 + 1.235 + nscoord GetMarginSizeInMainAxis(const nsMargin& aMargin) const { 1.236 + return IsAxisHorizontal(mMainAxis) ? 1.237 + aMargin.LeftRight() : 1.238 + aMargin.TopBottom(); 1.239 + } 1.240 + nscoord GetMarginSizeInCrossAxis(const nsMargin& aMargin) const { 1.241 + return IsAxisHorizontal(mCrossAxis) ? 1.242 + aMargin.LeftRight() : 1.243 + aMargin.TopBottom(); 1.244 + } 1.245 + 1.246 + /** 1.247 + * Converts a logical point into a "physical" x,y point. 1.248 + * 1.249 + * In the simplest case where the main-axis is left-to-right and the 1.250 + * cross-axis is top-to-bottom, this just returns 1.251 + * nsPoint(aMainPosn, aCrossPosn). 1.252 + * 1.253 + * @arg aMainPosn The main-axis position -- i.e an offset from the 1.254 + * main-start edge of the container's content box. 1.255 + * @arg aCrossPosn The cross-axis position -- i.e an offset from the 1.256 + * cross-start edge of the container's content box. 1.257 + * @return A nsPoint representing the same position (in coordinates 1.258 + * relative to the container's content box). 1.259 + */ 1.260 + nsPoint PhysicalPointFromLogicalPoint(nscoord aMainPosn, 1.261 + nscoord aCrossPosn, 1.262 + nscoord aContainerMainSize, 1.263 + nscoord aContainerCrossSize) const { 1.264 + nscoord physicalPosnInMainAxis = 1.265 + PhysicalPosFromLogicalPos(aMainPosn, aContainerMainSize, mMainAxis); 1.266 + nscoord physicalPosnInCrossAxis = 1.267 + PhysicalPosFromLogicalPos(aCrossPosn, aContainerCrossSize, mCrossAxis); 1.268 + 1.269 + return IsAxisHorizontal(mMainAxis) ? 1.270 + nsPoint(physicalPosnInMainAxis, physicalPosnInCrossAxis) : 1.271 + nsPoint(physicalPosnInCrossAxis, physicalPosnInMainAxis); 1.272 + } 1.273 + nsSize PhysicalSizeFromLogicalSizes(nscoord aMainSize, 1.274 + nscoord aCrossSize) const { 1.275 + return IsAxisHorizontal(mMainAxis) ? 1.276 + nsSize(aMainSize, aCrossSize) : 1.277 + nsSize(aCrossSize, aMainSize); 1.278 + } 1.279 + 1.280 + // Are my axes reversed with respect to what the author asked for? 1.281 + // (We may reverse the axes in the FlexboxAxisTracker constructor and set 1.282 + // this flag, to avoid reflowing our children in bottom-to-top order.) 1.283 + bool AreAxesInternallyReversed() const 1.284 + { 1.285 + return mAreAxesInternallyReversed; 1.286 + } 1.287 + 1.288 +private: 1.289 + AxisOrientationType mMainAxis; 1.290 + AxisOrientationType mCrossAxis; 1.291 + bool mAreAxesInternallyReversed; 1.292 +}; 1.293 + 1.294 +/** 1.295 + * Represents a flex item. 1.296 + * Includes the various pieces of input that the Flexbox Layout Algorithm uses 1.297 + * to resolve a flexible width. 1.298 + */ 1.299 +class nsFlexContainerFrame::FlexItem : public LinkedListElement<FlexItem> 1.300 +{ 1.301 +public: 1.302 + // Normal constructor: 1.303 + FlexItem(nsIFrame* aChildFrame, 1.304 + float aFlexGrow, float aFlexShrink, nscoord aMainBaseSize, 1.305 + nscoord aMainMinSize, nscoord aMainMaxSize, 1.306 + nscoord aCrossMinSize, nscoord aCrossMaxSize, 1.307 + nsMargin aMargin, nsMargin aBorderPadding, 1.308 + const FlexboxAxisTracker& aAxisTracker); 1.309 + 1.310 + // Simplified constructor, to be used only for generating "struts": 1.311 + FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize); 1.312 + 1.313 + // Accessors 1.314 + nsIFrame* Frame() const { return mFrame; } 1.315 + nscoord GetFlexBaseSize() const { return mFlexBaseSize; } 1.316 + 1.317 + nscoord GetMainMinSize() const { return mMainMinSize; } 1.318 + nscoord GetMainMaxSize() const { return mMainMaxSize; } 1.319 + 1.320 + // Note: These return the main-axis position and size of our *content box*. 1.321 + nscoord GetMainSize() const { return mMainSize; } 1.322 + nscoord GetMainPosition() const { return mMainPosn; } 1.323 + 1.324 + nscoord GetCrossMinSize() const { return mCrossMinSize; } 1.325 + nscoord GetCrossMaxSize() const { return mCrossMaxSize; } 1.326 + 1.327 + // Note: These return the cross-axis position and size of our *content box*. 1.328 + nscoord GetCrossSize() const { return mCrossSize; } 1.329 + nscoord GetCrossPosition() const { return mCrossPosn; } 1.330 + 1.331 + // Convenience methods to compute the main & cross size of our *margin-box*. 1.332 + // The caller is responsible for telling us the right axis, so that we can 1.333 + // pull out the appropriate components of our margin/border/padding structs. 1.334 + nscoord GetOuterMainSize(AxisOrientationType aMainAxis) const 1.335 + { 1.336 + return mMainSize + GetMarginBorderPaddingSizeInAxis(aMainAxis); 1.337 + } 1.338 + 1.339 + nscoord GetOuterCrossSize(AxisOrientationType aCrossAxis) const 1.340 + { 1.341 + return mCrossSize + GetMarginBorderPaddingSizeInAxis(aCrossAxis); 1.342 + } 1.343 + 1.344 + // Returns the distance between this FlexItem's baseline and the cross-start 1.345 + // edge of its margin-box. Used in baseline alignment. 1.346 + // (This function needs to be told what cross axis is & which edge we're 1.347 + // measuring the baseline from, so that it can look up the appropriate 1.348 + // components from mMargin.) 1.349 + nscoord GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis, 1.350 + AxisEdgeType aEdge) const; 1.351 + 1.352 + float GetShareOfFlexWeightSoFar() const { return mShareOfFlexWeightSoFar; } 1.353 + 1.354 + bool IsFrozen() const { return mIsFrozen; } 1.355 + 1.356 + bool HadMinViolation() const { return mHadMinViolation; } 1.357 + bool HadMaxViolation() const { return mHadMaxViolation; } 1.358 + 1.359 + // Indicates whether this item received a preliminary "measuring" reflow 1.360 + // before its actual reflow. 1.361 + bool HadMeasuringReflow() const { return mHadMeasuringReflow; } 1.362 + 1.363 + // Indicates whether this item's cross-size has been stretched (from having 1.364 + // "align-self: stretch" with an auto cross-size and no auto margins in the 1.365 + // cross axis). 1.366 + bool IsStretched() const { return mIsStretched; } 1.367 + 1.368 + // Indicates whether this item is a "strut" left behind by an element with 1.369 + // visibility:collapse. 1.370 + bool IsStrut() const { return mIsStrut; } 1.371 + 1.372 + uint8_t GetAlignSelf() const { return mAlignSelf; } 1.373 + 1.374 + // Returns the flex weight that we should use in the "resolving flexible 1.375 + // lengths" algorithm. If we're using flex grow, we just return that; 1.376 + // otherwise, we use the "scaled flex shrink weight" (scaled by our flex 1.377 + // base size, so that when both large and small items are shrinking, 1.378 + // the large items shrink more). 1.379 + float GetFlexWeightToUse(bool aIsUsingFlexGrow) 1.380 + { 1.381 + if (IsFrozen()) { 1.382 + return 0.0f; 1.383 + } 1.384 + 1.385 + if (aIsUsingFlexGrow) { 1.386 + return mFlexGrow; 1.387 + } 1.388 + 1.389 + // We're using flex-shrink --> return mFlexShrink * mFlexBaseSize 1.390 + if (mFlexBaseSize == 0) { 1.391 + // Special-case for mFlexBaseSize == 0 -- we have no room to shrink, so 1.392 + // regardless of mFlexShrink, we should just return 0. 1.393 + // (This is really a special-case for when mFlexShrink is infinity, to 1.394 + // avoid performing mFlexShrink * mFlexBaseSize = inf * 0 = undefined.) 1.395 + return 0.0f; 1.396 + } 1.397 + return mFlexShrink * mFlexBaseSize; 1.398 + } 1.399 + 1.400 + // Getters for margin: 1.401 + // =================== 1.402 + const nsMargin& GetMargin() const { return mMargin; } 1.403 + 1.404 + // Returns the margin component for a given mozilla::css::Side 1.405 + nscoord GetMarginComponentForSide(Side aSide) const 1.406 + { return MarginComponentForSide(mMargin, aSide); } 1.407 + 1.408 + // Returns the total space occupied by this item's margins in the given axis 1.409 + nscoord GetMarginSizeInAxis(AxisOrientationType aAxis) const 1.410 + { 1.411 + Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start]; 1.412 + Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End]; 1.413 + return GetMarginComponentForSide(startSide) + 1.414 + GetMarginComponentForSide(endSide); 1.415 + } 1.416 + 1.417 + // Getters for border/padding 1.418 + // ========================== 1.419 + const nsMargin& GetBorderPadding() const { return mBorderPadding; } 1.420 + 1.421 + // Returns the border+padding component for a given mozilla::css::Side 1.422 + nscoord GetBorderPaddingComponentForSide(Side aSide) const 1.423 + { return MarginComponentForSide(mBorderPadding, aSide); } 1.424 + 1.425 + // Returns the total space occupied by this item's borders and padding in 1.426 + // the given axis 1.427 + nscoord GetBorderPaddingSizeInAxis(AxisOrientationType aAxis) const 1.428 + { 1.429 + Side startSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_Start]; 1.430 + Side endSide = kAxisOrientationToSidesMap[aAxis][eAxisEdge_End]; 1.431 + return GetBorderPaddingComponentForSide(startSide) + 1.432 + GetBorderPaddingComponentForSide(endSide); 1.433 + } 1.434 + 1.435 + // Getter for combined margin/border/padding 1.436 + // ========================================= 1.437 + // Returns the total space occupied by this item's margins, borders and 1.438 + // padding in the given axis 1.439 + nscoord GetMarginBorderPaddingSizeInAxis(AxisOrientationType aAxis) const 1.440 + { 1.441 + return GetMarginSizeInAxis(aAxis) + GetBorderPaddingSizeInAxis(aAxis); 1.442 + } 1.443 + 1.444 + // Setters 1.445 + // ======= 1.446 + 1.447 + // This sets our flex base size, and then updates the main size to the 1.448 + // base size clamped to our main-axis [min,max] constraints. 1.449 + void SetFlexBaseSizeAndMainSize(nscoord aNewFlexBaseSize) 1.450 + { 1.451 + MOZ_ASSERT(!mIsFrozen || mFlexBaseSize == NS_INTRINSICSIZE, 1.452 + "flex base size shouldn't change after we're frozen " 1.453 + "(unless we're just resolving an intrinsic size)"); 1.454 + mFlexBaseSize = aNewFlexBaseSize; 1.455 + 1.456 + // Before we've resolved flexible lengths, we keep mMainSize set to 1.457 + // the 'hypothetical main size', which is the flex base size, clamped 1.458 + // to the [min,max] range: 1.459 + mMainSize = NS_CSS_MINMAX(mFlexBaseSize, mMainMinSize, mMainMaxSize); 1.460 + } 1.461 + 1.462 + // Setters used while we're resolving flexible lengths 1.463 + // --------------------------------------------------- 1.464 + 1.465 + // Sets the main-size of our flex item's content-box. 1.466 + void SetMainSize(nscoord aNewMainSize) 1.467 + { 1.468 + MOZ_ASSERT(!mIsFrozen, "main size shouldn't change after we're frozen"); 1.469 + mMainSize = aNewMainSize; 1.470 + } 1.471 + 1.472 + void SetShareOfFlexWeightSoFar(float aNewShare) 1.473 + { 1.474 + MOZ_ASSERT(!mIsFrozen || aNewShare == 0.0f, 1.475 + "shouldn't be giving this item any share of the weight " 1.476 + "after it's frozen"); 1.477 + mShareOfFlexWeightSoFar = aNewShare; 1.478 + } 1.479 + 1.480 + void Freeze() { mIsFrozen = true; } 1.481 + 1.482 + void SetHadMinViolation() 1.483 + { 1.484 + MOZ_ASSERT(!mIsFrozen, 1.485 + "shouldn't be changing main size & having violations " 1.486 + "after we're frozen"); 1.487 + mHadMinViolation = true; 1.488 + } 1.489 + void SetHadMaxViolation() 1.490 + { 1.491 + MOZ_ASSERT(!mIsFrozen, 1.492 + "shouldn't be changing main size & having violations " 1.493 + "after we're frozen"); 1.494 + mHadMaxViolation = true; 1.495 + } 1.496 + void ClearViolationFlags() 1.497 + { mHadMinViolation = mHadMaxViolation = false; } 1.498 + 1.499 + // Setters for values that are determined after we've resolved our main size 1.500 + // ------------------------------------------------------------------------- 1.501 + 1.502 + // Sets the main-axis position of our flex item's content-box. 1.503 + // (This is the distance between the main-start edge of the flex container 1.504 + // and the main-start edge of the flex item's content-box.) 1.505 + void SetMainPosition(nscoord aPosn) { 1.506 + MOZ_ASSERT(mIsFrozen, "main size should be resolved before this"); 1.507 + mMainPosn = aPosn; 1.508 + } 1.509 + 1.510 + // Sets the cross-size of our flex item's content-box. 1.511 + void SetCrossSize(nscoord aCrossSize) { 1.512 + MOZ_ASSERT(!mIsStretched, 1.513 + "Cross size shouldn't be modified after it's been stretched"); 1.514 + mCrossSize = aCrossSize; 1.515 + } 1.516 + 1.517 + // Sets the cross-axis position of our flex item's content-box. 1.518 + // (This is the distance between the cross-start edge of the flex container 1.519 + // and the cross-start edge of the flex item.) 1.520 + void SetCrossPosition(nscoord aPosn) { 1.521 + MOZ_ASSERT(mIsFrozen, "main size should be resolved before this"); 1.522 + mCrossPosn = aPosn; 1.523 + } 1.524 + 1.525 + void SetAscent(nscoord aAscent) { 1.526 + mAscent = aAscent; 1.527 + } 1.528 + 1.529 + void SetHadMeasuringReflow() { 1.530 + mHadMeasuringReflow = true; 1.531 + } 1.532 + 1.533 + void SetIsStretched() { 1.534 + MOZ_ASSERT(mIsFrozen, "main size should be resolved before this"); 1.535 + mIsStretched = true; 1.536 + } 1.537 + 1.538 + // Setter for margin components (for resolving "auto" margins) 1.539 + void SetMarginComponentForSide(Side aSide, nscoord aLength) 1.540 + { 1.541 + MOZ_ASSERT(mIsFrozen, "main size should be resolved before this"); 1.542 + MarginComponentForSide(mMargin, aSide) = aLength; 1.543 + } 1.544 + 1.545 + void ResolveStretchedCrossSize(nscoord aLineCrossSize, 1.546 + const FlexboxAxisTracker& aAxisTracker); 1.547 + 1.548 + uint32_t GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const; 1.549 + 1.550 +protected: 1.551 + // Our frame: 1.552 + nsIFrame* const mFrame; 1.553 + 1.554 + // Values that we already know in constructor: (and are hence mostly 'const') 1.555 + const float mFlexGrow; 1.556 + const float mFlexShrink; 1.557 + 1.558 + const nsMargin mBorderPadding; 1.559 + nsMargin mMargin; // non-const because we need to resolve auto margins 1.560 + 1.561 + nscoord mFlexBaseSize; 1.562 + 1.563 + const nscoord mMainMinSize; 1.564 + const nscoord mMainMaxSize; 1.565 + const nscoord mCrossMinSize; 1.566 + const nscoord mCrossMaxSize; 1.567 + 1.568 + // Values that we compute after constructor: 1.569 + nscoord mMainSize; 1.570 + nscoord mMainPosn; 1.571 + nscoord mCrossSize; 1.572 + nscoord mCrossPosn; 1.573 + nscoord mAscent; 1.574 + 1.575 + // Temporary state, while we're resolving flexible widths (for our main size) 1.576 + // XXXdholbert To save space, we could use a union to make these variables 1.577 + // overlay the same memory as some other member vars that aren't touched 1.578 + // until after main-size has been resolved. In particular, these could share 1.579 + // memory with mMainPosn through mAscent, and mIsStretched. 1.580 + float mShareOfFlexWeightSoFar; 1.581 + bool mIsFrozen; 1.582 + bool mHadMinViolation; 1.583 + bool mHadMaxViolation; 1.584 + 1.585 + // Misc: 1.586 + bool mHadMeasuringReflow; // Did this item get a preliminary reflow, 1.587 + // to measure its desired height? 1.588 + bool mIsStretched; // See IsStretched() documentation 1.589 + bool mIsStrut; // Is this item a "strut" left behind by an element 1.590 + // with visibility:collapse? 1.591 + uint8_t mAlignSelf; // My "align-self" computed value (with "auto" 1.592 + // swapped out for parent"s "align-items" value, 1.593 + // in our constructor). 1.594 +}; 1.595 + 1.596 +/** 1.597 + * Represents a single flex line in a flex container. 1.598 + * Manages a linked list of the FlexItems that are in the line. 1.599 + */ 1.600 +class nsFlexContainerFrame::FlexLine : public LinkedListElement<FlexLine> 1.601 +{ 1.602 +public: 1.603 + FlexLine() 1.604 + : mNumItems(0), 1.605 + mTotalInnerHypotheticalMainSize(0), 1.606 + mTotalOuterHypotheticalMainSize(0), 1.607 + mLineCrossSize(0), 1.608 + mBaselineOffset(nscoord_MIN) 1.609 + {} 1.610 + 1.611 + // Returns the sum of our FlexItems' outer hypothetical main sizes. 1.612 + // ("outer" = margin-box, and "hypothetical" = before flexing) 1.613 + nscoord GetTotalOuterHypotheticalMainSize() const { 1.614 + return mTotalOuterHypotheticalMainSize; 1.615 + } 1.616 + 1.617 + // Accessors for our FlexItems & information about them: 1.618 + FlexItem* GetFirstItem() 1.619 + { 1.620 + MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0), 1.621 + "mNumItems bookkeeping is off"); 1.622 + return mItems.getFirst(); 1.623 + } 1.624 + 1.625 + const FlexItem* GetFirstItem() const 1.626 + { 1.627 + MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0), 1.628 + "mNumItems bookkeeping is off"); 1.629 + return mItems.getFirst(); 1.630 + } 1.631 + 1.632 + bool IsEmpty() const 1.633 + { 1.634 + MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0), 1.635 + "mNumItems bookkeeping is off"); 1.636 + return mItems.isEmpty(); 1.637 + } 1.638 + 1.639 + uint32_t NumItems() const 1.640 + { 1.641 + MOZ_ASSERT(mItems.isEmpty() == (mNumItems == 0), 1.642 + "mNumItems bookkeeping is off"); 1.643 + return mNumItems; 1.644 + } 1.645 + 1.646 + // Adds the given FlexItem to our list of items (at the front or back 1.647 + // depending on aShouldInsertAtFront), and adds its hypothetical 1.648 + // outer & inner main sizes to our totals. Use this method instead of 1.649 + // directly modifying the item list, so that our bookkeeping remains correct. 1.650 + void AddItem(FlexItem* aItem, 1.651 + bool aShouldInsertAtFront, 1.652 + nscoord aItemInnerHypotheticalMainSize, 1.653 + nscoord aItemOuterHypotheticalMainSize) 1.654 + { 1.655 + if (aShouldInsertAtFront) { 1.656 + mItems.insertFront(aItem); 1.657 + } else { 1.658 + mItems.insertBack(aItem); 1.659 + } 1.660 + mNumItems++; 1.661 + mTotalInnerHypotheticalMainSize += aItemInnerHypotheticalMainSize; 1.662 + mTotalOuterHypotheticalMainSize += aItemOuterHypotheticalMainSize; 1.663 + } 1.664 + 1.665 + // Computes the cross-size and baseline position of this FlexLine, based on 1.666 + // its FlexItems. 1.667 + void ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker); 1.668 + 1.669 + // Returns the cross-size of this line. 1.670 + nscoord GetLineCrossSize() const { return mLineCrossSize; } 1.671 + 1.672 + // Setter for line cross-size -- needed for cases where the flex container 1.673 + // imposes a cross-size on the line. (e.g. for single-line flexbox, or for 1.674 + // multi-line flexbox with 'align-content: stretch') 1.675 + void SetLineCrossSize(nscoord aLineCrossSize) { 1.676 + mLineCrossSize = aLineCrossSize; 1.677 + } 1.678 + 1.679 + /** 1.680 + * Returns the offset within this line where any baseline-aligned FlexItems 1.681 + * should place their baseline. Usually, this represents a distance from the 1.682 + * line's cross-start edge, but if we're internally reversing the axes (see 1.683 + * AreAxesInternallyReversed()), this instead represents the distance from 1.684 + * its cross-end edge. 1.685 + * 1.686 + * If there are no baseline-aligned FlexItems, returns nscoord_MIN. 1.687 + */ 1.688 + nscoord GetBaselineOffset() const { 1.689 + return mBaselineOffset; 1.690 + } 1.691 + 1.692 + // Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the 1.693 + // CSS flexbox spec to distribute aFlexContainerMainSize among our flex items. 1.694 + void ResolveFlexibleLengths(nscoord aFlexContainerMainSize); 1.695 + 1.696 + void PositionItemsInMainAxis(uint8_t aJustifyContent, 1.697 + nscoord aContentBoxMainSize, 1.698 + const FlexboxAxisTracker& aAxisTracker); 1.699 + 1.700 + void PositionItemsInCrossAxis(nscoord aLineStartPosition, 1.701 + const FlexboxAxisTracker& aAxisTracker); 1.702 + 1.703 + friend class AutoFlexLineListClearer; // (needs access to mItems) 1.704 + 1.705 +private: 1.706 + // Helper for ResolveFlexibleLengths(): 1.707 + void FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation, 1.708 + bool aIsFinalIteration); 1.709 + 1.710 + LinkedList<FlexItem> mItems; // Linked list of this line's flex items. 1.711 + 1.712 + uint32_t mNumItems; // Number of FlexItems in this line (in |mItems|). 1.713 + // (Shouldn't change after GenerateFlexLines finishes 1.714 + // with this line -- at least, not until we add support 1.715 + // for splitting lines across continuations. Then we can 1.716 + // update this count carefully.) 1.717 + 1.718 + nscoord mTotalInnerHypotheticalMainSize; 1.719 + nscoord mTotalOuterHypotheticalMainSize; 1.720 + nscoord mLineCrossSize; 1.721 + nscoord mBaselineOffset; 1.722 +}; 1.723 + 1.724 +// Information about a strut left behind by a FlexItem that's been collapsed 1.725 +// using "visibility:collapse". 1.726 +struct nsFlexContainerFrame::StrutInfo { 1.727 + StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize) 1.728 + : mItemIdx(aItemIdx), 1.729 + mStrutCrossSize(aStrutCrossSize) 1.730 + { 1.731 + } 1.732 + 1.733 + uint32_t mItemIdx; // Index in the child list. 1.734 + nscoord mStrutCrossSize; // The cross-size of this strut. 1.735 +}; 1.736 + 1.737 +static void 1.738 +BuildStrutInfoFromCollapsedItems(const FlexLine* aFirstLine, 1.739 + nsTArray<StrutInfo>& aStruts) 1.740 +{ 1.741 + MOZ_ASSERT(aFirstLine, "null first line pointer"); 1.742 + MOZ_ASSERT(aStruts.IsEmpty(), 1.743 + "We should only build up StrutInfo once per reflow, so " 1.744 + "aStruts should be empty when this is called"); 1.745 + 1.746 + uint32_t itemIdxInContainer = 0; 1.747 + for (const FlexLine* line = aFirstLine; line; line = line->getNext()) { 1.748 + for (const FlexItem* item = line->GetFirstItem(); item; 1.749 + item = item->getNext()) { 1.750 + if (NS_STYLE_VISIBILITY_COLLAPSE == 1.751 + item->Frame()->StyleVisibility()->mVisible) { 1.752 + // Note the cross size of the line as the item's strut size. 1.753 + aStruts.AppendElement(StrutInfo(itemIdxInContainer, 1.754 + line->GetLineCrossSize())); 1.755 + } 1.756 + itemIdxInContainer++; 1.757 + } 1.758 + } 1.759 +} 1.760 + 1.761 +// Helper-function to find the first non-anonymous-box descendent of aFrame. 1.762 +static nsIFrame* 1.763 +GetFirstNonAnonBoxDescendant(nsIFrame* aFrame) 1.764 +{ 1.765 + while (aFrame) { 1.766 + nsIAtom* pseudoTag = aFrame->StyleContext()->GetPseudo(); 1.767 + 1.768 + // If aFrame isn't an anonymous container, then it'll do. 1.769 + if (!pseudoTag || // No pseudotag. 1.770 + !nsCSSAnonBoxes::IsAnonBox(pseudoTag) || // Pseudotag isn't anon. 1.771 + pseudoTag == nsCSSAnonBoxes::mozNonElement) { // Text, not a container. 1.772 + break; 1.773 + } 1.774 + 1.775 + // Otherwise, descend to its first child and repeat. 1.776 + 1.777 + // SPECIAL CASE: if we're dealing with an anonymous table, then it might 1.778 + // be wrapping something non-anonymous in its caption or col-group lists 1.779 + // (instead of its principal child list), so we have to look there. 1.780 + // (Note: For anonymous tables that have a non-anon cell *and* a non-anon 1.781 + // column, we'll always return the column. This is fine; we're really just 1.782 + // looking for a handle to *anything* with a meaningful content node inside 1.783 + // the table, for use in DOM comparisons to things outside of the table.) 1.784 + if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableOuterFrame)) { 1.785 + nsIFrame* captionDescendant = 1.786 + GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kCaptionList)); 1.787 + if (captionDescendant) { 1.788 + return captionDescendant; 1.789 + } 1.790 + } else if (MOZ_UNLIKELY(aFrame->GetType() == nsGkAtoms::tableFrame)) { 1.791 + nsIFrame* colgroupDescendant = 1.792 + GetFirstNonAnonBoxDescendant(aFrame->GetFirstChild(kColGroupList)); 1.793 + if (colgroupDescendant) { 1.794 + return colgroupDescendant; 1.795 + } 1.796 + } 1.797 + 1.798 + // USUAL CASE: Descend to the first child in principal list. 1.799 + aFrame = aFrame->GetFirstPrincipalChild(); 1.800 + } 1.801 + return aFrame; 1.802 +} 1.803 + 1.804 +/** 1.805 + * Sorting helper-function that compares two frames' "order" property-values, 1.806 + * and if they're equal, compares the DOM positions of their corresponding 1.807 + * content nodes. Returns true if aFrame1 is "less than or equal to" aFrame2 1.808 + * according to this comparison. 1.809 + * 1.810 + * Note: This can't be a static function, because we need to pass it as a 1.811 + * template argument. (Only functions with external linkage can be passed as 1.812 + * template arguments.) 1.813 + * 1.814 + * @return true if the computed "order" property of aFrame1 is less than that 1.815 + * of aFrame2, or if the computed "order" values are equal and aFrame1's 1.816 + * corresponding DOM node is earlier than aFrame2's in the DOM tree. 1.817 + * Otherwise, returns false. 1.818 + */ 1.819 +bool 1.820 +IsOrderLEQWithDOMFallback(nsIFrame* aFrame1, 1.821 + nsIFrame* aFrame2) 1.822 +{ 1.823 + MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(), 1.824 + "this method only intended for comparing flex items"); 1.825 + 1.826 + if (aFrame1 == aFrame2) { 1.827 + // Anything is trivially LEQ itself, so we return "true" here... but it's 1.828 + // probably bad if we end up actually needing this, so let's assert. 1.829 + NS_ERROR("Why are we checking if a frame is LEQ itself?"); 1.830 + return true; 1.831 + } 1.832 + 1.833 + // If we've got a placeholder frame, use its out-of-flow frame's 'order' val. 1.834 + { 1.835 + nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1); 1.836 + nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2); 1.837 + 1.838 + int32_t order1 = aRealFrame1->StylePosition()->mOrder; 1.839 + int32_t order2 = aRealFrame2->StylePosition()->mOrder; 1.840 + 1.841 + if (order1 != order2) { 1.842 + return order1 < order2; 1.843 + } 1.844 + } 1.845 + 1.846 + // The "order" values are equal, so we need to fall back on DOM comparison. 1.847 + // For that, we need to dig through any anonymous box wrapper frames to find 1.848 + // the actual frame that corresponds to our child content. 1.849 + aFrame1 = GetFirstNonAnonBoxDescendant(aFrame1); 1.850 + aFrame2 = GetFirstNonAnonBoxDescendant(aFrame2); 1.851 + MOZ_ASSERT(aFrame1 && aFrame2, 1.852 + "why do we have an anonymous box without any " 1.853 + "non-anonymous descendants?"); 1.854 + 1.855 + 1.856 + // Special case: 1.857 + // If either frame is for generated content from ::before or ::after, then 1.858 + // we can't use nsContentUtils::PositionIsBefore(), since that method won't 1.859 + // recognize generated content as being an actual sibling of other nodes. 1.860 + // We know where ::before and ::after nodes *effectively* insert in the DOM 1.861 + // tree, though (at the beginning & end), so we can just special-case them. 1.862 + nsIAtom* pseudo1 = aFrame1->StyleContext()->GetPseudo(); 1.863 + nsIAtom* pseudo2 = aFrame2->StyleContext()->GetPseudo(); 1.864 + if (pseudo1 == nsCSSPseudoElements::before || 1.865 + pseudo2 == nsCSSPseudoElements::after) { 1.866 + // frame1 is ::before and/or frame2 is ::after => frame1 is LEQ frame2. 1.867 + return true; 1.868 + } 1.869 + if (pseudo1 == nsCSSPseudoElements::after || 1.870 + pseudo2 == nsCSSPseudoElements::before) { 1.871 + // frame1 is ::after and/or frame2 is ::before => frame1 is not LEQ frame2. 1.872 + return false; 1.873 + } 1.874 + 1.875 + // Usual case: Compare DOM position. 1.876 + nsIContent* content1 = aFrame1->GetContent(); 1.877 + nsIContent* content2 = aFrame2->GetContent(); 1.878 + MOZ_ASSERT(content1 != content2, 1.879 + "Two different flex items are using the same nsIContent node for " 1.880 + "comparison, so we may be sorting them in an arbitrary order"); 1.881 + 1.882 + return nsContentUtils::PositionIsBefore(content1, content2); 1.883 +} 1.884 + 1.885 +/** 1.886 + * Sorting helper-function that compares two frames' "order" property-values. 1.887 + * Returns true if aFrame1 is "less than or equal to" aFrame2 according to this 1.888 + * comparison. 1.889 + * 1.890 + * Note: This can't be a static function, because we need to pass it as a 1.891 + * template argument. (Only functions with external linkage can be passed as 1.892 + * template arguments.) 1.893 + * 1.894 + * @return true if the computed "order" property of aFrame1 is less than or 1.895 + * equal to that of aFrame2. Otherwise, returns false. 1.896 + */ 1.897 +bool 1.898 +IsOrderLEQ(nsIFrame* aFrame1, 1.899 + nsIFrame* aFrame2) 1.900 +{ 1.901 + MOZ_ASSERT(aFrame1->IsFlexItem() && aFrame2->IsFlexItem(), 1.902 + "this method only intended for comparing flex items"); 1.903 + 1.904 + // If we've got a placeholder frame, use its out-of-flow frame's 'order' val. 1.905 + nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1); 1.906 + nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2); 1.907 + 1.908 + int32_t order1 = aRealFrame1->StylePosition()->mOrder; 1.909 + int32_t order2 = aRealFrame2->StylePosition()->mOrder; 1.910 + 1.911 + return order1 <= order2; 1.912 +} 1.913 + 1.914 +bool 1.915 +nsFlexContainerFrame::IsHorizontal() 1.916 +{ 1.917 + const FlexboxAxisTracker axisTracker(this); 1.918 + return IsAxisHorizontal(axisTracker.GetMainAxis()); 1.919 +} 1.920 + 1.921 +FlexItem* 1.922 +nsFlexContainerFrame::GenerateFlexItemForChild( 1.923 + nsPresContext* aPresContext, 1.924 + nsIFrame* aChildFrame, 1.925 + const nsHTMLReflowState& aParentReflowState, 1.926 + const FlexboxAxisTracker& aAxisTracker) 1.927 +{ 1.928 + // Create temporary reflow state just for sizing -- to get hypothetical 1.929 + // main-size and the computed values of min / max main-size property. 1.930 + // (This reflow state will _not_ be used for reflow.) 1.931 + nsHTMLReflowState childRS(aPresContext, aParentReflowState, aChildFrame, 1.932 + nsSize(aParentReflowState.ComputedWidth(), 1.933 + aParentReflowState.ComputedHeight())); 1.934 + 1.935 + // FLEX GROW & SHRINK WEIGHTS 1.936 + // -------------------------- 1.937 + const nsStylePosition* stylePos = aChildFrame->StylePosition(); 1.938 + float flexGrow = stylePos->mFlexGrow; 1.939 + float flexShrink = stylePos->mFlexShrink; 1.940 + 1.941 + // MAIN SIZES (flex base size, min/max size) 1.942 + // ----------------------------------------- 1.943 + nscoord flexBaseSize = GET_MAIN_COMPONENT(aAxisTracker, 1.944 + childRS.ComputedWidth(), 1.945 + childRS.ComputedHeight()); 1.946 + nscoord mainMinSize = GET_MAIN_COMPONENT(aAxisTracker, 1.947 + childRS.ComputedMinWidth(), 1.948 + childRS.ComputedMinHeight()); 1.949 + nscoord mainMaxSize = GET_MAIN_COMPONENT(aAxisTracker, 1.950 + childRS.ComputedMaxWidth(), 1.951 + childRS.ComputedMaxHeight()); 1.952 + // This is enforced by the nsHTMLReflowState where these values come from: 1.953 + MOZ_ASSERT(mainMinSize <= mainMaxSize, "min size is larger than max size"); 1.954 + 1.955 + // CROSS MIN/MAX SIZE 1.956 + // ------------------ 1.957 + 1.958 + nscoord crossMinSize = GET_CROSS_COMPONENT(aAxisTracker, 1.959 + childRS.ComputedMinWidth(), 1.960 + childRS.ComputedMinHeight()); 1.961 + nscoord crossMaxSize = GET_CROSS_COMPONENT(aAxisTracker, 1.962 + childRS.ComputedMaxWidth(), 1.963 + childRS.ComputedMaxHeight()); 1.964 + 1.965 + // SPECIAL-CASE FOR WIDGET-IMPOSED SIZES 1.966 + // Check if we're a themed widget, in which case we might have a minimum 1.967 + // main & cross size imposed by our widget (which we can't go below), or 1.968 + // (more severe) our widget might have only a single valid size. 1.969 + bool isFixedSizeWidget = false; 1.970 + const nsStyleDisplay* disp = aChildFrame->StyleDisplay(); 1.971 + if (aChildFrame->IsThemed(disp)) { 1.972 + nsIntSize widgetMinSize(0, 0); 1.973 + bool canOverride = true; 1.974 + aPresContext->GetTheme()-> 1.975 + GetMinimumWidgetSize(childRS.rendContext, aChildFrame, 1.976 + disp->mAppearance, 1.977 + &widgetMinSize, &canOverride); 1.978 + 1.979 + nscoord widgetMainMinSize = 1.980 + aPresContext->DevPixelsToAppUnits( 1.981 + aAxisTracker.GetMainComponent(widgetMinSize)); 1.982 + nscoord widgetCrossMinSize = 1.983 + aPresContext->DevPixelsToAppUnits( 1.984 + aAxisTracker.GetCrossComponent(widgetMinSize)); 1.985 + 1.986 + // GMWS() returns border-box. We need content-box, so subtract 1.987 + // borderPadding (but don't let that push our min sizes below 0). 1.988 + nsMargin& bp = childRS.ComputedPhysicalBorderPadding(); 1.989 + widgetMainMinSize = std::max(widgetMainMinSize - 1.990 + aAxisTracker.GetMarginSizeInMainAxis(bp), 0); 1.991 + widgetCrossMinSize = std::max(widgetCrossMinSize - 1.992 + aAxisTracker.GetMarginSizeInCrossAxis(bp), 0); 1.993 + 1.994 + if (!canOverride) { 1.995 + // Fixed-size widget: freeze our main-size at the widget's mandated size. 1.996 + // (Set min and max main-sizes to that size, too, to keep us from 1.997 + // clamping to any other size later on.) 1.998 + flexBaseSize = mainMinSize = mainMaxSize = widgetMainMinSize; 1.999 + crossMinSize = crossMaxSize = widgetCrossMinSize; 1.1000 + isFixedSizeWidget = true; 1.1001 + } else { 1.1002 + // Variable-size widget: ensure our min/max sizes are at least as large 1.1003 + // as the widget's mandated minimum size, so we don't flex below that. 1.1004 + mainMinSize = std::max(mainMinSize, widgetMainMinSize); 1.1005 + mainMaxSize = std::max(mainMaxSize, widgetMainMinSize); 1.1006 + 1.1007 + crossMinSize = std::max(crossMinSize, widgetCrossMinSize); 1.1008 + crossMaxSize = std::max(crossMaxSize, widgetCrossMinSize); 1.1009 + } 1.1010 + } 1.1011 + 1.1012 + // Construct the flex item! 1.1013 + FlexItem* item = new FlexItem(aChildFrame, 1.1014 + flexGrow, flexShrink, flexBaseSize, 1.1015 + mainMinSize, mainMaxSize, 1.1016 + crossMinSize, crossMaxSize, 1.1017 + childRS.ComputedPhysicalMargin(), 1.1018 + childRS.ComputedPhysicalBorderPadding(), 1.1019 + aAxisTracker); 1.1020 + 1.1021 + // If we're inflexible, we can just freeze to our hypothetical main-size 1.1022 + // up-front. Similarly, if we're a fixed-size widget, we only have one 1.1023 + // valid size, so we freeze to keep ourselves from flexing. 1.1024 + if (isFixedSizeWidget || (flexGrow == 0.0f && flexShrink == 0.0f)) { 1.1025 + item->Freeze(); 1.1026 + } 1.1027 + 1.1028 + return item; 1.1029 +} 1.1030 + 1.1031 +nsresult 1.1032 +nsFlexContainerFrame:: 1.1033 + ResolveFlexItemMaxContentSizing(nsPresContext* aPresContext, 1.1034 + FlexItem& aFlexItem, 1.1035 + const nsHTMLReflowState& aParentReflowState, 1.1036 + const FlexboxAxisTracker& aAxisTracker) 1.1037 +{ 1.1038 + if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { 1.1039 + // Nothing to do -- this function is only for measuring flex items 1.1040 + // in a vertical flex container. 1.1041 + return NS_OK; 1.1042 + } 1.1043 + 1.1044 + if (NS_AUTOHEIGHT != aFlexItem.GetFlexBaseSize()) { 1.1045 + // Nothing to do; this function's only relevant for flex items 1.1046 + // with a base size of "auto" (or equivalent). 1.1047 + // XXXdholbert If & when we handle "min-height: min-content" for flex items, 1.1048 + // we'll want to resolve that in this function, too. 1.1049 + return NS_OK; 1.1050 + } 1.1051 + 1.1052 + // If we get here, we're vertical and our main size ended up being 1.1053 + // unconstrained. We need to use our "max-content" height, which is what we 1.1054 + // get from reflowing into our available width. 1.1055 + // Note: This has to come *after* we construct the FlexItem, since we 1.1056 + // invoke at least one convenience method (ResolveStretchedCrossSize) which 1.1057 + // requires a FlexItem. 1.1058 + 1.1059 + // Give the item a special reflow with "mIsFlexContainerMeasuringHeight" 1.1060 + // set. This tells it to behave as if it had "height: auto", regardless 1.1061 + // of what the "height" property is actually set to. 1.1062 + nsHTMLReflowState 1.1063 + childRSForMeasuringHeight(aPresContext, aParentReflowState, 1.1064 + aFlexItem.Frame(), 1.1065 + nsSize(aParentReflowState.ComputedWidth(), 1.1066 + NS_UNCONSTRAINEDSIZE), 1.1067 + -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); 1.1068 + childRSForMeasuringHeight.mFlags.mIsFlexContainerMeasuringHeight = true; 1.1069 + childRSForMeasuringHeight.Init(aPresContext); 1.1070 + 1.1071 + aFlexItem.ResolveStretchedCrossSize(aParentReflowState.ComputedWidth(), 1.1072 + aAxisTracker); 1.1073 + if (aFlexItem.IsStretched()) { 1.1074 + childRSForMeasuringHeight.SetComputedWidth(aFlexItem.GetCrossSize()); 1.1075 + childRSForMeasuringHeight.mFlags.mHResize = true; 1.1076 + } 1.1077 + 1.1078 + // If this item is flexible (vertically), then we assume that the 1.1079 + // computed-height we're reflowing with now could be different 1.1080 + // from the one we'll use for this flex item's "actual" reflow later on. 1.1081 + // In that case, we need to be sure the flex item treats this as a 1.1082 + // vertical resize, even though none of its ancestors are necessarily 1.1083 + // being vertically resized. 1.1084 + // (Note: We don't have to do this for width, because InitResizeFlags 1.1085 + // will always turn on mHResize on when it sees that the computed width 1.1086 + // is different from current width, and that's all we need.) 1.1087 + if (!aFlexItem.IsFrozen()) { // Are we flexible? 1.1088 + childRSForMeasuringHeight.mFlags.mVResize = true; 1.1089 + } 1.1090 + 1.1091 + nsHTMLReflowMetrics childDesiredSize(childRSForMeasuringHeight); 1.1092 + nsReflowStatus childReflowStatus; 1.1093 + const uint32_t flags = NS_FRAME_NO_MOVE_FRAME; 1.1094 + nsresult rv = ReflowChild(aFlexItem.Frame(), aPresContext, 1.1095 + childDesiredSize, childRSForMeasuringHeight, 1.1096 + 0, 0, flags, childReflowStatus); 1.1097 + NS_ENSURE_SUCCESS(rv, rv); 1.1098 + 1.1099 + MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), 1.1100 + "We gave flex item unconstrained available height, so it " 1.1101 + "should be complete"); 1.1102 + 1.1103 + rv = FinishReflowChild(aFlexItem.Frame(), aPresContext, 1.1104 + childDesiredSize, &childRSForMeasuringHeight, 1.1105 + 0, 0, flags); 1.1106 + NS_ENSURE_SUCCESS(rv, rv); 1.1107 + 1.1108 + // Subtract border/padding in vertical axis, to get _just_ 1.1109 + // the effective computed value of the "height" property. 1.1110 + nscoord childDesiredHeight = childDesiredSize.Height() - 1.1111 + childRSForMeasuringHeight.ComputedPhysicalBorderPadding().TopBottom(); 1.1112 + childDesiredHeight = std::max(0, childDesiredHeight); 1.1113 + 1.1114 + aFlexItem.SetFlexBaseSizeAndMainSize(childDesiredHeight); 1.1115 + aFlexItem.SetHadMeasuringReflow(); 1.1116 + 1.1117 + return NS_OK; 1.1118 +} 1.1119 + 1.1120 +FlexItem::FlexItem(nsIFrame* aChildFrame, 1.1121 + float aFlexGrow, float aFlexShrink, nscoord aFlexBaseSize, 1.1122 + nscoord aMainMinSize, nscoord aMainMaxSize, 1.1123 + nscoord aCrossMinSize, nscoord aCrossMaxSize, 1.1124 + nsMargin aMargin, nsMargin aBorderPadding, 1.1125 + const FlexboxAxisTracker& aAxisTracker) 1.1126 + : mFrame(aChildFrame), 1.1127 + mFlexGrow(aFlexGrow), 1.1128 + mFlexShrink(aFlexShrink), 1.1129 + mBorderPadding(aBorderPadding), 1.1130 + mMargin(aMargin), 1.1131 + mMainMinSize(aMainMinSize), 1.1132 + mMainMaxSize(aMainMaxSize), 1.1133 + mCrossMinSize(aCrossMinSize), 1.1134 + mCrossMaxSize(aCrossMaxSize), 1.1135 + mMainPosn(0), 1.1136 + mCrossSize(0), 1.1137 + mCrossPosn(0), 1.1138 + mAscent(0), 1.1139 + mShareOfFlexWeightSoFar(0.0f), 1.1140 + mIsFrozen(false), 1.1141 + mHadMinViolation(false), 1.1142 + mHadMaxViolation(false), 1.1143 + mHadMeasuringReflow(false), 1.1144 + mIsStretched(false), 1.1145 + mIsStrut(false), 1.1146 + mAlignSelf(aChildFrame->StylePosition()->mAlignSelf) 1.1147 +{ 1.1148 + MOZ_ASSERT(mFrame, "expecting a non-null child frame"); 1.1149 + MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame, 1.1150 + "placeholder frames should not be treated as flex items"); 1.1151 + MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW), 1.1152 + "out-of-flow frames should not be treated as flex items"); 1.1153 + 1.1154 + SetFlexBaseSizeAndMainSize(aFlexBaseSize); 1.1155 + 1.1156 + // Assert that any "auto" margin components are set to 0. 1.1157 + // (We'll resolve them later; until then, we want to treat them as 0-sized.) 1.1158 +#ifdef DEBUG 1.1159 + { 1.1160 + const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin; 1.1161 + NS_FOR_CSS_SIDES(side) { 1.1162 + if (styleMargin.GetUnit(side) == eStyleUnit_Auto) { 1.1163 + MOZ_ASSERT(GetMarginComponentForSide(side) == 0, 1.1164 + "Someone else tried to resolve our auto margin"); 1.1165 + } 1.1166 + } 1.1167 + } 1.1168 +#endif // DEBUG 1.1169 + 1.1170 + // Resolve "align-self: auto" to parent's "align-items" value. 1.1171 + if (mAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) { 1.1172 + mAlignSelf = 1.1173 + mFrame->StyleContext()->GetParent()->StylePosition()->mAlignItems; 1.1174 + } 1.1175 + 1.1176 + // If the flex item's inline axis is the same as the cross axis, then 1.1177 + // 'align-self:baseline' is identical to 'flex-start'. If that's the case, we 1.1178 + // just directly convert our align-self value here, so that we don't have to 1.1179 + // handle this with special cases elsewhere. 1.1180 + // Moreover: for the time being (until we support writing-modes), 1.1181 + // all inline axes are horizontal -- so we can just check if the cross axis 1.1182 + // is horizontal. 1.1183 + // FIXME: Once we support writing-mode (vertical text), this IsAxisHorizontal 1.1184 + // check won't be sufficient anymore -- we'll actually need to compare our 1.1185 + // inline axis vs. the cross axis. 1.1186 + if (mAlignSelf == NS_STYLE_ALIGN_ITEMS_BASELINE && 1.1187 + IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { 1.1188 + mAlignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START; 1.1189 + } 1.1190 +} 1.1191 + 1.1192 +// Simplified constructor for creating a special "strut" FlexItem, for a child 1.1193 +// with visibility:collapse. The strut has 0 main-size, and it only exists to 1.1194 +// impose a minimum cross size on whichever FlexLine it ends up in. 1.1195 +FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize) 1.1196 + : mFrame(aChildFrame), 1.1197 + mFlexGrow(0.0f), 1.1198 + mFlexShrink(0.0f), 1.1199 + // mBorderPadding uses default constructor, 1.1200 + // mMargin uses default constructor, 1.1201 + mFlexBaseSize(0), 1.1202 + mMainMinSize(0), 1.1203 + mMainMaxSize(0), 1.1204 + mCrossMinSize(0), 1.1205 + mCrossMaxSize(0), 1.1206 + mMainSize(0), 1.1207 + mMainPosn(0), 1.1208 + mCrossSize(aCrossSize), 1.1209 + mCrossPosn(0), 1.1210 + mAscent(0), 1.1211 + mShareOfFlexWeightSoFar(0.0f), 1.1212 + mIsFrozen(true), 1.1213 + mHadMinViolation(false), 1.1214 + mHadMaxViolation(false), 1.1215 + mHadMeasuringReflow(false), 1.1216 + mIsStretched(false), 1.1217 + mIsStrut(true), // (this is the constructor for making struts, after all) 1.1218 + mAlignSelf(NS_STYLE_ALIGN_ITEMS_FLEX_START) 1.1219 +{ 1.1220 + MOZ_ASSERT(mFrame, "expecting a non-null child frame"); 1.1221 + MOZ_ASSERT(NS_STYLE_VISIBILITY_COLLAPSE == 1.1222 + mFrame->StyleVisibility()->mVisible, 1.1223 + "Should only make struts for children with 'visibility:collapse'"); 1.1224 + MOZ_ASSERT(mFrame->GetType() != nsGkAtoms::placeholderFrame, 1.1225 + "placeholder frames should not be treated as flex items"); 1.1226 + MOZ_ASSERT(!(mFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW), 1.1227 + "out-of-flow frames should not be treated as flex items"); 1.1228 +} 1.1229 + 1.1230 +nscoord 1.1231 +FlexItem::GetBaselineOffsetFromOuterCrossEdge(AxisOrientationType aCrossAxis, 1.1232 + AxisEdgeType aEdge) const 1.1233 +{ 1.1234 + // NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical 1.1235 + // measurement -- it's the distance from the border-top edge of this FlexItem 1.1236 + // to its baseline. So, we can really only do baseline alignment when the 1.1237 + // cross axis is vertical. (The FlexItem constructor enforces this when 1.1238 + // resolving the item's "mAlignSelf" value). 1.1239 + MOZ_ASSERT(!IsAxisHorizontal(aCrossAxis), 1.1240 + "Only expecting to be doing baseline computations when the " 1.1241 + "cross axis is vertical"); 1.1242 + 1.1243 + Side sideToMeasureFrom = kAxisOrientationToSidesMap[aCrossAxis][aEdge]; 1.1244 + 1.1245 + nscoord marginTopToBaseline = mAscent + mMargin.top; 1.1246 + 1.1247 + if (sideToMeasureFrom == eSideTop) { 1.1248 + // Measuring from top (normal case): the distance from the margin-box top 1.1249 + // edge to the baseline is just ascent + margin-top. 1.1250 + return marginTopToBaseline; 1.1251 + } 1.1252 + 1.1253 + MOZ_ASSERT(sideToMeasureFrom == eSideBottom, 1.1254 + "We already checked that we're dealing with a vertical axis, and " 1.1255 + "we're not using the top side, so that only leaves the bottom..."); 1.1256 + 1.1257 + // Measuring from bottom: The distance from the margin-box bottom edge to the 1.1258 + // baseline is just the margin-box cross size (i.e. outer cross size), minus 1.1259 + // the already-computed distance from margin-top to baseline. 1.1260 + return GetOuterCrossSize(aCrossAxis) - marginTopToBaseline; 1.1261 +} 1.1262 + 1.1263 +uint32_t 1.1264 +FlexItem::GetNumAutoMarginsInAxis(AxisOrientationType aAxis) const 1.1265 +{ 1.1266 + uint32_t numAutoMargins = 0; 1.1267 + const nsStyleSides& styleMargin = mFrame->StyleMargin()->mMargin; 1.1268 + for (uint32_t i = 0; i < eNumAxisEdges; i++) { 1.1269 + Side side = kAxisOrientationToSidesMap[aAxis][i]; 1.1270 + if (styleMargin.GetUnit(side) == eStyleUnit_Auto) { 1.1271 + numAutoMargins++; 1.1272 + } 1.1273 + } 1.1274 + 1.1275 + // Mostly for clarity: 1.1276 + MOZ_ASSERT(numAutoMargins <= 2, 1.1277 + "We're just looking at one item along one dimension, so we " 1.1278 + "should only have examined 2 margins"); 1.1279 + 1.1280 + return numAutoMargins; 1.1281 +} 1.1282 + 1.1283 +// Keeps track of our position along a particular axis (where a '0' position 1.1284 +// corresponds to the 'start' edge of that axis). 1.1285 +// This class shouldn't be instantiated directly -- rather, it should only be 1.1286 +// instantiated via its subclasses defined below. 1.1287 +class MOZ_STACK_CLASS PositionTracker { 1.1288 +public: 1.1289 + // Accessor for the current value of the position that we're tracking. 1.1290 + inline nscoord GetPosition() const { return mPosition; } 1.1291 + inline AxisOrientationType GetAxis() const { return mAxis; } 1.1292 + 1.1293 + // Advances our position across the start edge of the given margin, in the 1.1294 + // axis we're tracking. 1.1295 + void EnterMargin(const nsMargin& aMargin) 1.1296 + { 1.1297 + Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_Start]; 1.1298 + mPosition += MarginComponentForSide(aMargin, side); 1.1299 + } 1.1300 + 1.1301 + // Advances our position across the end edge of the given margin, in the axis 1.1302 + // we're tracking. 1.1303 + void ExitMargin(const nsMargin& aMargin) 1.1304 + { 1.1305 + Side side = kAxisOrientationToSidesMap[mAxis][eAxisEdge_End]; 1.1306 + mPosition += MarginComponentForSide(aMargin, side); 1.1307 + } 1.1308 + 1.1309 + // Advances our current position from the start side of a child frame's 1.1310 + // border-box to the frame's upper or left edge (depending on our axis). 1.1311 + // (Note that this is a no-op if our axis grows in positive direction.) 1.1312 + void EnterChildFrame(nscoord aChildFrameSize) 1.1313 + { 1.1314 + if (!AxisGrowsInPositiveDirection(mAxis)) 1.1315 + mPosition += aChildFrameSize; 1.1316 + } 1.1317 + 1.1318 + // Advances our current position from a frame's upper or left border-box edge 1.1319 + // (whichever is in the axis we're tracking) to the 'end' side of the frame 1.1320 + // in the axis that we're tracking. (Note that this is a no-op if our axis 1.1321 + // grows in the negative direction.) 1.1322 + void ExitChildFrame(nscoord aChildFrameSize) 1.1323 + { 1.1324 + if (AxisGrowsInPositiveDirection(mAxis)) 1.1325 + mPosition += aChildFrameSize; 1.1326 + } 1.1327 + 1.1328 +protected: 1.1329 + // Protected constructor, to be sure we're only instantiated via a subclass. 1.1330 + PositionTracker(AxisOrientationType aAxis) 1.1331 + : mPosition(0), 1.1332 + mAxis(aAxis) 1.1333 + {} 1.1334 + 1.1335 +private: 1.1336 + // Private copy-constructor, since we don't want any instances of our 1.1337 + // subclasses to be accidentally copied. 1.1338 + PositionTracker(const PositionTracker& aOther) 1.1339 + : mPosition(aOther.mPosition), 1.1340 + mAxis(aOther.mAxis) 1.1341 + {} 1.1342 + 1.1343 +protected: 1.1344 + // Member data: 1.1345 + nscoord mPosition; // The position we're tracking 1.1346 + const AxisOrientationType mAxis; // The axis along which we're moving 1.1347 +}; 1.1348 + 1.1349 +// Tracks our position in the main axis, when we're laying out flex items. 1.1350 +// The "0" position represents the main-start edge of the flex container's 1.1351 +// content-box. 1.1352 +class MOZ_STACK_CLASS MainAxisPositionTracker : public PositionTracker { 1.1353 +public: 1.1354 + MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker, 1.1355 + const FlexLine* aLine, 1.1356 + uint8_t aJustifyContent, 1.1357 + nscoord aContentBoxMainSize); 1.1358 + 1.1359 + ~MainAxisPositionTracker() { 1.1360 + MOZ_ASSERT(mNumPackingSpacesRemaining == 0, 1.1361 + "miscounted the number of packing spaces"); 1.1362 + MOZ_ASSERT(mNumAutoMarginsInMainAxis == 0, 1.1363 + "miscounted the number of auto margins"); 1.1364 + } 1.1365 + 1.1366 + // Advances past the packing space (if any) between two flex items 1.1367 + void TraversePackingSpace(); 1.1368 + 1.1369 + // If aItem has any 'auto' margins in the main axis, this method updates the 1.1370 + // corresponding values in its margin. 1.1371 + void ResolveAutoMarginsInMainAxis(FlexItem& aItem); 1.1372 + 1.1373 +private: 1.1374 + nscoord mPackingSpaceRemaining; 1.1375 + uint32_t mNumAutoMarginsInMainAxis; 1.1376 + uint32_t mNumPackingSpacesRemaining; 1.1377 + uint8_t mJustifyContent; 1.1378 +}; 1.1379 + 1.1380 +// Utility class for managing our position along the cross axis along 1.1381 +// the whole flex container (at a higher level than a single line). 1.1382 +// The "0" position represents the cross-start edge of the flex container's 1.1383 +// content-box. 1.1384 +class MOZ_STACK_CLASS CrossAxisPositionTracker : public PositionTracker { 1.1385 +public: 1.1386 + CrossAxisPositionTracker(FlexLine* aFirstLine, 1.1387 + uint8_t aAlignContent, 1.1388 + nscoord aContentBoxCrossSize, 1.1389 + bool aIsCrossSizeDefinite, 1.1390 + const FlexboxAxisTracker& aAxisTracker); 1.1391 + 1.1392 + // Advances past the packing space (if any) between two flex lines 1.1393 + void TraversePackingSpace(); 1.1394 + 1.1395 + // Advances past the given FlexLine 1.1396 + void TraverseLine(FlexLine& aLine) { mPosition += aLine.GetLineCrossSize(); } 1.1397 + 1.1398 +private: 1.1399 + // Redeclare the frame-related methods from PositionTracker as private with 1.1400 + // MOZ_DELETE, to be sure (at compile time) that no client code can invoke 1.1401 + // them. (Unlike the other PositionTracker derived classes, this class here 1.1402 + // deals with FlexLines, not with individual FlexItems or frames.) 1.1403 + void EnterMargin(const nsMargin& aMargin) MOZ_DELETE; 1.1404 + void ExitMargin(const nsMargin& aMargin) MOZ_DELETE; 1.1405 + void EnterChildFrame(nscoord aChildFrameSize) MOZ_DELETE; 1.1406 + void ExitChildFrame(nscoord aChildFrameSize) MOZ_DELETE; 1.1407 + 1.1408 + nscoord mPackingSpaceRemaining; 1.1409 + uint32_t mNumPackingSpacesRemaining; 1.1410 + uint8_t mAlignContent; 1.1411 +}; 1.1412 + 1.1413 +// Utility class for managing our position along the cross axis, *within* a 1.1414 +// single flex line. 1.1415 +class MOZ_STACK_CLASS SingleLineCrossAxisPositionTracker : public PositionTracker { 1.1416 +public: 1.1417 + SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker); 1.1418 + 1.1419 + void ResolveAutoMarginsInCrossAxis(const FlexLine& aLine, 1.1420 + FlexItem& aItem); 1.1421 + 1.1422 + void EnterAlignPackingSpace(const FlexLine& aLine, 1.1423 + const FlexItem& aItem, 1.1424 + const FlexboxAxisTracker& aAxisTracker); 1.1425 + 1.1426 + // Resets our position to the cross-start edge of this line. 1.1427 + inline void ResetPosition() { mPosition = 0; } 1.1428 +}; 1.1429 + 1.1430 +//---------------------------------------------------------------------- 1.1431 + 1.1432 +// Frame class boilerplate 1.1433 +// ======================= 1.1434 + 1.1435 +NS_QUERYFRAME_HEAD(nsFlexContainerFrame) 1.1436 + NS_QUERYFRAME_ENTRY(nsFlexContainerFrame) 1.1437 +NS_QUERYFRAME_TAIL_INHERITING(nsFlexContainerFrameSuper) 1.1438 + 1.1439 +NS_IMPL_FRAMEARENA_HELPERS(nsFlexContainerFrame) 1.1440 + 1.1441 +nsIFrame* 1.1442 +NS_NewFlexContainerFrame(nsIPresShell* aPresShell, 1.1443 + nsStyleContext* aContext) 1.1444 +{ 1.1445 + return new (aPresShell) nsFlexContainerFrame(aContext); 1.1446 +} 1.1447 + 1.1448 +//---------------------------------------------------------------------- 1.1449 + 1.1450 +// nsFlexContainerFrame Method Implementations 1.1451 +// =========================================== 1.1452 + 1.1453 +/* virtual */ 1.1454 +nsFlexContainerFrame::~nsFlexContainerFrame() 1.1455 +{ 1.1456 +} 1.1457 + 1.1458 +template<bool IsLessThanOrEqual(nsIFrame*, nsIFrame*)> 1.1459 +/* static */ bool 1.1460 +nsFlexContainerFrame::SortChildrenIfNeeded() 1.1461 +{ 1.1462 + if (nsIFrame::IsFrameListSorted<IsLessThanOrEqual>(mFrames)) { 1.1463 + return false; 1.1464 + } 1.1465 + 1.1466 + nsIFrame::SortFrameList<IsLessThanOrEqual>(mFrames); 1.1467 + return true; 1.1468 +} 1.1469 + 1.1470 +/* virtual */ 1.1471 +nsIAtom* 1.1472 +nsFlexContainerFrame::GetType() const 1.1473 +{ 1.1474 + return nsGkAtoms::flexContainerFrame; 1.1475 +} 1.1476 + 1.1477 +#ifdef DEBUG_FRAME_DUMP 1.1478 +nsresult 1.1479 +nsFlexContainerFrame::GetFrameName(nsAString& aResult) const 1.1480 +{ 1.1481 + return MakeFrameName(NS_LITERAL_STRING("FlexContainer"), aResult); 1.1482 +} 1.1483 +#endif 1.1484 + 1.1485 +// Helper for BuildDisplayList, to implement this special-case for flex items 1.1486 +// from the spec: 1.1487 +// Flex items paint exactly the same as block-level elements in the 1.1488 +// normal flow, except that 'z-index' values other than 'auto' create 1.1489 +// a stacking context even if 'position' is 'static'. 1.1490 +// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#painting 1.1491 +uint32_t 1.1492 +GetDisplayFlagsForFlexItem(nsIFrame* aFrame) 1.1493 +{ 1.1494 + MOZ_ASSERT(aFrame->IsFlexItem(), "Should only be called on flex items"); 1.1495 + 1.1496 + const nsStylePosition* pos = aFrame->StylePosition(); 1.1497 + if (pos->mZIndex.GetUnit() == eStyleUnit_Integer) { 1.1498 + return nsIFrame::DISPLAY_CHILD_FORCE_STACKING_CONTEXT; 1.1499 + } 1.1500 + return nsIFrame::DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT; 1.1501 +} 1.1502 + 1.1503 +void 1.1504 +nsFlexContainerFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.1505 + const nsRect& aDirtyRect, 1.1506 + const nsDisplayListSet& aLists) 1.1507 +{ 1.1508 + NS_ASSERTION( 1.1509 + nsIFrame::IsFrameListSorted<IsOrderLEQWithDOMFallback>(mFrames), 1.1510 + "Child frames aren't sorted correctly"); 1.1511 + 1.1512 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.1513 + 1.1514 + // Our children are all block-level, so their borders/backgrounds all go on 1.1515 + // the BlockBorderBackgrounds list. 1.1516 + nsDisplayListSet childLists(aLists, aLists.BlockBorderBackgrounds()); 1.1517 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.1518 + BuildDisplayListForChild(aBuilder, e.get(), aDirtyRect, childLists, 1.1519 + GetDisplayFlagsForFlexItem(e.get())); 1.1520 + } 1.1521 +} 1.1522 + 1.1523 +#ifdef DEBUG 1.1524 +// helper for the debugging method below 1.1525 +bool 1.1526 +FrameWantsToBeInAnonymousFlexItem(nsIFrame* aFrame) 1.1527 +{ 1.1528 + // Note: This needs to match the logic in 1.1529 + // nsCSSFrameConstructor::FrameConstructionItem::NeedsAnonFlexItem() 1.1530 + return (aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || 1.1531 + nsGkAtoms::placeholderFrame == aFrame->GetType()); 1.1532 +} 1.1533 + 1.1534 +// Debugging method, to let us assert that our anonymous flex items are 1.1535 +// set up correctly -- in particular, we assert: 1.1536 +// (1) we don't have any inline non-replaced children 1.1537 +// (2) we don't have any consecutive anonymous flex items 1.1538 +// (3) we don't have any empty anonymous flex items 1.1539 +// 1.1540 +// XXXdholbert This matches what nsCSSFrameConstructor currently does, and what 1.1541 +// the spec used to say. However, the spec has now changed regarding what 1.1542 +// types of content get wrapped in an anonymous flexbox item. The patch that 1.1543 +// implements those changes (in nsCSSFrameConstructor) will need to change 1.1544 +// this method as well. 1.1545 +void 1.1546 +nsFlexContainerFrame::SanityCheckAnonymousFlexItems() const 1.1547 +{ 1.1548 + bool prevChildWasAnonFlexItem = false; 1.1549 + for (nsIFrame* child = mFrames.FirstChild(); child; 1.1550 + child = child->GetNextSibling()) { 1.1551 + MOZ_ASSERT(!FrameWantsToBeInAnonymousFlexItem(child), 1.1552 + "frame wants to be inside an anonymous flex item, " 1.1553 + "but it isn't"); 1.1554 + if (child->StyleContext()->GetPseudo() == 1.1555 + nsCSSAnonBoxes::anonymousFlexItem) { 1.1556 + MOZ_ASSERT(!prevChildWasAnonFlexItem || 1.1557 + HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED), 1.1558 + "two anon flex items in a row (shouldn't happen, unless our " 1.1559 + "children have been reordered with the 'order' property)"); 1.1560 + 1.1561 + nsIFrame* firstWrappedChild = child->GetFirstPrincipalChild(); 1.1562 + MOZ_ASSERT(firstWrappedChild, 1.1563 + "anonymous flex item is empty (shouldn't happen)"); 1.1564 + prevChildWasAnonFlexItem = true; 1.1565 + } else { 1.1566 + prevChildWasAnonFlexItem = false; 1.1567 + } 1.1568 + } 1.1569 +} 1.1570 +#endif // DEBUG 1.1571 + 1.1572 +// Based on the sign of aTotalViolation, this function freezes a subset of our 1.1573 +// flexible sizes, and restores the remaining ones to their initial pref sizes. 1.1574 +void 1.1575 +FlexLine::FreezeOrRestoreEachFlexibleSize(const nscoord aTotalViolation, 1.1576 + bool aIsFinalIteration) 1.1577 +{ 1.1578 + enum FreezeType { 1.1579 + eFreezeEverything, 1.1580 + eFreezeMinViolations, 1.1581 + eFreezeMaxViolations 1.1582 + }; 1.1583 + 1.1584 + FreezeType freezeType; 1.1585 + if (aTotalViolation == 0) { 1.1586 + freezeType = eFreezeEverything; 1.1587 + } else if (aTotalViolation > 0) { 1.1588 + freezeType = eFreezeMinViolations; 1.1589 + } else { // aTotalViolation < 0 1.1590 + freezeType = eFreezeMaxViolations; 1.1591 + } 1.1592 + 1.1593 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.1594 + MOZ_ASSERT(!item->HadMinViolation() || !item->HadMaxViolation(), 1.1595 + "Can have either min or max violation, but not both"); 1.1596 + 1.1597 + if (!item->IsFrozen()) { 1.1598 + if (eFreezeEverything == freezeType || 1.1599 + (eFreezeMinViolations == freezeType && item->HadMinViolation()) || 1.1600 + (eFreezeMaxViolations == freezeType && item->HadMaxViolation())) { 1.1601 + 1.1602 + MOZ_ASSERT(item->GetMainSize() >= item->GetMainMinSize(), 1.1603 + "Freezing item at a size below its minimum"); 1.1604 + MOZ_ASSERT(item->GetMainSize() <= item->GetMainMaxSize(), 1.1605 + "Freezing item at a size above its maximum"); 1.1606 + 1.1607 + item->Freeze(); 1.1608 + } else if (MOZ_UNLIKELY(aIsFinalIteration)) { 1.1609 + // XXXdholbert If & when bug 765861 is fixed, we should upgrade this 1.1610 + // assertion to be fatal except in documents with enormous lengths. 1.1611 + NS_ERROR("Final iteration still has unfrozen items, this shouldn't" 1.1612 + " happen unless there was nscoord under/overflow."); 1.1613 + item->Freeze(); 1.1614 + } // else, we'll reset this item's main size to its flex base size on the 1.1615 + // next iteration of this algorithm. 1.1616 + 1.1617 + // Clear this item's violation(s), now that we've dealt with them 1.1618 + item->ClearViolationFlags(); 1.1619 + } 1.1620 + } 1.1621 +} 1.1622 + 1.1623 +void 1.1624 +FlexLine::ResolveFlexibleLengths(nscoord aFlexContainerMainSize) 1.1625 +{ 1.1626 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, ("ResolveFlexibleLengths\n")); 1.1627 + if (IsEmpty()) { 1.1628 + return; 1.1629 + } 1.1630 + 1.1631 + // Subtract space occupied by our items' margins/borders/padding, so we can 1.1632 + // just be dealing with the space available for our flex items' content 1.1633 + // boxes. 1.1634 + nscoord spaceReservedForMarginBorderPadding = 1.1635 + mTotalOuterHypotheticalMainSize - mTotalInnerHypotheticalMainSize; 1.1636 + 1.1637 + nscoord spaceAvailableForFlexItemsContentBoxes = 1.1638 + aFlexContainerMainSize - spaceReservedForMarginBorderPadding; 1.1639 + 1.1640 + // Determine whether we're going to be growing or shrinking items. 1.1641 + const bool isUsingFlexGrow = 1.1642 + (mTotalOuterHypotheticalMainSize < aFlexContainerMainSize); 1.1643 + 1.1644 + // NOTE: I claim that this chunk of the algorithm (the looping part) needs to 1.1645 + // run the loop at MOST mNumItems times. This claim should hold up 1.1646 + // because we'll freeze at least one item on each loop iteration, and once 1.1647 + // we've run out of items to freeze, there's nothing left to do. However, 1.1648 + // in most cases, we'll break out of this loop long before we hit that many 1.1649 + // iterations. 1.1650 + for (uint32_t iterationCounter = 0; 1.1651 + iterationCounter < mNumItems; iterationCounter++) { 1.1652 + // Set every not-yet-frozen item's used main size to its 1.1653 + // flex base size, and subtract all the used main sizes from our 1.1654 + // total amount of space to determine the 'available free space' 1.1655 + // (positive or negative) to be distributed among our flexible items. 1.1656 + nscoord availableFreeSpace = spaceAvailableForFlexItemsContentBoxes; 1.1657 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.1658 + if (!item->IsFrozen()) { 1.1659 + item->SetMainSize(item->GetFlexBaseSize()); 1.1660 + } 1.1661 + availableFreeSpace -= item->GetMainSize(); 1.1662 + } 1.1663 + 1.1664 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.1665 + (" available free space = %d\n", availableFreeSpace)); 1.1666 + 1.1667 + // If sign of free space matches the type of flexing that we're doing, give 1.1668 + // each flexible item a portion of availableFreeSpace. 1.1669 + if ((availableFreeSpace > 0 && isUsingFlexGrow) || 1.1670 + (availableFreeSpace < 0 && !isUsingFlexGrow)) { 1.1671 + 1.1672 + // STRATEGY: On each item, we compute & store its "share" of the total 1.1673 + // flex weight that we've seen so far: 1.1674 + // curFlexWeight / runningFlexWeightSum 1.1675 + // 1.1676 + // Then, when we go to actually distribute the space (in the next loop), 1.1677 + // we can simply walk backwards through the elements and give each item 1.1678 + // its "share" multiplied by the remaining available space. 1.1679 + // 1.1680 + // SPECIAL CASE: If the sum of the flex weights is larger than the 1.1681 + // maximum representable float (overflowing to infinity), then we can't 1.1682 + // sensibly divide out proportional shares anymore. In that case, we 1.1683 + // simply treat the flex item(s) with the largest flex weights as if 1.1684 + // their weights were infinite (dwarfing all the others), and we 1.1685 + // distribute all of the available space among them. 1.1686 + float runningFlexWeightSum = 0.0f; 1.1687 + float largestFlexWeight = 0.0f; 1.1688 + uint32_t numItemsWithLargestFlexWeight = 0; 1.1689 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.1690 + float curFlexWeight = item->GetFlexWeightToUse(isUsingFlexGrow); 1.1691 + MOZ_ASSERT(curFlexWeight >= 0.0f, "weights are non-negative"); 1.1692 + 1.1693 + runningFlexWeightSum += curFlexWeight; 1.1694 + if (NS_finite(runningFlexWeightSum)) { 1.1695 + if (curFlexWeight == 0.0f) { 1.1696 + item->SetShareOfFlexWeightSoFar(0.0f); 1.1697 + } else { 1.1698 + item->SetShareOfFlexWeightSoFar(curFlexWeight / 1.1699 + runningFlexWeightSum); 1.1700 + } 1.1701 + } // else, the sum of weights overflows to infinity, in which 1.1702 + // case we don't bother with "SetShareOfFlexWeightSoFar" since 1.1703 + // we know we won't use it. (instead, we'll just give every 1.1704 + // item with the largest flex weight an equal share of space.) 1.1705 + 1.1706 + // Update our largest-flex-weight tracking vars 1.1707 + if (curFlexWeight > largestFlexWeight) { 1.1708 + largestFlexWeight = curFlexWeight; 1.1709 + numItemsWithLargestFlexWeight = 1; 1.1710 + } else if (curFlexWeight == largestFlexWeight) { 1.1711 + numItemsWithLargestFlexWeight++; 1.1712 + } 1.1713 + } 1.1714 + 1.1715 + if (runningFlexWeightSum != 0.0f) { // no distribution if no flexibility 1.1716 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.1717 + (" Distributing available space:")); 1.1718 + // NOTE: It's important that we traverse our items in *reverse* order 1.1719 + // here, for correct width distribution according to the items' 1.1720 + // "ShareOfFlexWeightSoFar" progressively-calculated values. 1.1721 + for (FlexItem* item = mItems.getLast(); item; 1.1722 + item = item->getPrevious()) { 1.1723 + 1.1724 + if (!item->IsFrozen()) { 1.1725 + // To avoid rounding issues, we compute the change in size for this 1.1726 + // item, and then subtract it from the remaining available space. 1.1727 + nscoord sizeDelta = 0; 1.1728 + if (NS_finite(runningFlexWeightSum)) { 1.1729 + float myShareOfRemainingSpace = 1.1730 + item->GetShareOfFlexWeightSoFar(); 1.1731 + 1.1732 + MOZ_ASSERT(myShareOfRemainingSpace >= 0.0f && 1.1733 + myShareOfRemainingSpace <= 1.0f, 1.1734 + "my share should be nonnegative fractional amount"); 1.1735 + 1.1736 + if (myShareOfRemainingSpace == 1.0f) { 1.1737 + // (We special-case 1.0f to avoid float error from converting 1.1738 + // availableFreeSpace from integer*1.0f --> float --> integer) 1.1739 + sizeDelta = availableFreeSpace; 1.1740 + } else if (myShareOfRemainingSpace > 0.0f) { 1.1741 + sizeDelta = NSToCoordRound(availableFreeSpace * 1.1742 + myShareOfRemainingSpace); 1.1743 + } 1.1744 + } else if (item->GetFlexWeightToUse(isUsingFlexGrow) == 1.1745 + largestFlexWeight) { 1.1746 + // Total flexibility is infinite, so we're just distributing 1.1747 + // the available space equally among the items that are tied for 1.1748 + // having the largest weight (and this is one of those items). 1.1749 + sizeDelta = 1.1750 + NSToCoordRound(availableFreeSpace / 1.1751 + float(numItemsWithLargestFlexWeight)); 1.1752 + numItemsWithLargestFlexWeight--; 1.1753 + } 1.1754 + 1.1755 + availableFreeSpace -= sizeDelta; 1.1756 + 1.1757 + item->SetMainSize(item->GetMainSize() + sizeDelta); 1.1758 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.1759 + (" child %p receives %d, for a total of %d\n", 1.1760 + item, sizeDelta, item->GetMainSize())); 1.1761 + } 1.1762 + } 1.1763 + } 1.1764 + } 1.1765 + 1.1766 + // Fix min/max violations: 1.1767 + nscoord totalViolation = 0; // keeps track of adjustments for min/max 1.1768 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.1769 + (" Checking for violations:")); 1.1770 + 1.1771 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.1772 + if (!item->IsFrozen()) { 1.1773 + if (item->GetMainSize() < item->GetMainMinSize()) { 1.1774 + // min violation 1.1775 + totalViolation += item->GetMainMinSize() - item->GetMainSize(); 1.1776 + item->SetMainSize(item->GetMainMinSize()); 1.1777 + item->SetHadMinViolation(); 1.1778 + } else if (item->GetMainSize() > item->GetMainMaxSize()) { 1.1779 + // max violation 1.1780 + totalViolation += item->GetMainMaxSize() - item->GetMainSize(); 1.1781 + item->SetMainSize(item->GetMainMaxSize()); 1.1782 + item->SetHadMaxViolation(); 1.1783 + } 1.1784 + } 1.1785 + } 1.1786 + 1.1787 + FreezeOrRestoreEachFlexibleSize(totalViolation, 1.1788 + iterationCounter + 1 == mNumItems); 1.1789 + 1.1790 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.1791 + (" Total violation: %d\n", totalViolation)); 1.1792 + 1.1793 + if (totalViolation == 0) { 1.1794 + break; 1.1795 + } 1.1796 + } 1.1797 + 1.1798 + // Post-condition: all lengths should've been frozen. 1.1799 +#ifdef DEBUG 1.1800 + for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.1801 + MOZ_ASSERT(item->IsFrozen(), 1.1802 + "All flexible lengths should've been resolved"); 1.1803 + } 1.1804 +#endif // DEBUG 1.1805 +} 1.1806 + 1.1807 +MainAxisPositionTracker:: 1.1808 + MainAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker, 1.1809 + const FlexLine* aLine, 1.1810 + uint8_t aJustifyContent, 1.1811 + nscoord aContentBoxMainSize) 1.1812 + : PositionTracker(aAxisTracker.GetMainAxis()), 1.1813 + mPackingSpaceRemaining(aContentBoxMainSize), // we chip away at this below 1.1814 + mNumAutoMarginsInMainAxis(0), 1.1815 + mNumPackingSpacesRemaining(0), 1.1816 + mJustifyContent(aJustifyContent) 1.1817 +{ 1.1818 + // mPackingSpaceRemaining is initialized to the container's main size. Now 1.1819 + // we'll subtract out the main sizes of our flex items, so that it ends up 1.1820 + // with the *actual* amount of packing space. 1.1821 + for (const FlexItem* item = aLine->GetFirstItem(); item; 1.1822 + item = item->getNext()) { 1.1823 + mPackingSpaceRemaining -= item->GetOuterMainSize(mAxis); 1.1824 + mNumAutoMarginsInMainAxis += item->GetNumAutoMarginsInAxis(mAxis); 1.1825 + } 1.1826 + 1.1827 + if (mPackingSpaceRemaining <= 0) { 1.1828 + // No available packing space to use for resolving auto margins. 1.1829 + mNumAutoMarginsInMainAxis = 0; 1.1830 + } 1.1831 + 1.1832 + // If packing space is negative, 'space-between' behaves like 'flex-start', 1.1833 + // and 'space-around' behaves like 'center'. In those cases, it's simplest to 1.1834 + // just pretend we have a different 'justify-content' value and share code. 1.1835 + if (mPackingSpaceRemaining < 0) { 1.1836 + if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN) { 1.1837 + mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START; 1.1838 + } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND) { 1.1839 + mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_CENTER; 1.1840 + } 1.1841 + } 1.1842 + 1.1843 + // If our main axis is (internally) reversed, swap the justify-content 1.1844 + // "flex-start" and "flex-end" behaviors: 1.1845 + if (aAxisTracker.AreAxesInternallyReversed()) { 1.1846 + if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_START) { 1.1847 + mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_END; 1.1848 + } else if (mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_FLEX_END) { 1.1849 + mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START; 1.1850 + } 1.1851 + } 1.1852 + 1.1853 + // Figure out how much space we'll set aside for auto margins or 1.1854 + // packing spaces, and advance past any leading packing-space. 1.1855 + if (mNumAutoMarginsInMainAxis == 0 && 1.1856 + mPackingSpaceRemaining != 0 && 1.1857 + !aLine->IsEmpty()) { 1.1858 + switch (mJustifyContent) { 1.1859 + case NS_STYLE_JUSTIFY_CONTENT_FLEX_START: 1.1860 + // All packing space should go at the end --> nothing to do here. 1.1861 + break; 1.1862 + case NS_STYLE_JUSTIFY_CONTENT_FLEX_END: 1.1863 + // All packing space goes at the beginning 1.1864 + mPosition += mPackingSpaceRemaining; 1.1865 + break; 1.1866 + case NS_STYLE_JUSTIFY_CONTENT_CENTER: 1.1867 + // Half the packing space goes at the beginning 1.1868 + mPosition += mPackingSpaceRemaining / 2; 1.1869 + break; 1.1870 + case NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN: 1.1871 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.1872 + "negative packing space should make us use 'flex-start' " 1.1873 + "instead of 'space-between'"); 1.1874 + // 1 packing space between each flex item, no packing space at ends. 1.1875 + mNumPackingSpacesRemaining = aLine->NumItems() - 1; 1.1876 + break; 1.1877 + case NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND: 1.1878 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.1879 + "negative packing space should make us use 'center' " 1.1880 + "instead of 'space-around'"); 1.1881 + // 1 packing space between each flex item, plus half a packing space 1.1882 + // at beginning & end. So our number of full packing-spaces is equal 1.1883 + // to the number of flex items. 1.1884 + mNumPackingSpacesRemaining = aLine->NumItems(); 1.1885 + if (mNumPackingSpacesRemaining > 0) { 1.1886 + // The edges (start/end) share one full packing space 1.1887 + nscoord totalEdgePackingSpace = 1.1888 + mPackingSpaceRemaining / mNumPackingSpacesRemaining; 1.1889 + 1.1890 + // ...and we'll use half of that right now, at the start 1.1891 + mPosition += totalEdgePackingSpace / 2; 1.1892 + // ...but we need to subtract all of it right away, so that we won't 1.1893 + // hand out any of it to intermediate packing spaces. 1.1894 + mPackingSpaceRemaining -= totalEdgePackingSpace; 1.1895 + mNumPackingSpacesRemaining--; 1.1896 + } 1.1897 + break; 1.1898 + default: 1.1899 + MOZ_CRASH("Unexpected justify-content value"); 1.1900 + } 1.1901 + } 1.1902 + 1.1903 + MOZ_ASSERT(mNumPackingSpacesRemaining == 0 || 1.1904 + mNumAutoMarginsInMainAxis == 0, 1.1905 + "extra space should either go to packing space or to " 1.1906 + "auto margins, but not to both"); 1.1907 +} 1.1908 + 1.1909 +void 1.1910 +MainAxisPositionTracker::ResolveAutoMarginsInMainAxis(FlexItem& aItem) 1.1911 +{ 1.1912 + if (mNumAutoMarginsInMainAxis) { 1.1913 + const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin; 1.1914 + for (uint32_t i = 0; i < eNumAxisEdges; i++) { 1.1915 + Side side = kAxisOrientationToSidesMap[mAxis][i]; 1.1916 + if (styleMargin.GetUnit(side) == eStyleUnit_Auto) { 1.1917 + // NOTE: This integer math will skew the distribution of remainder 1.1918 + // app-units towards the end, which is fine. 1.1919 + nscoord curAutoMarginSize = 1.1920 + mPackingSpaceRemaining / mNumAutoMarginsInMainAxis; 1.1921 + 1.1922 + MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0, 1.1923 + "Expecting auto margins to have value '0' before we " 1.1924 + "resolve them"); 1.1925 + aItem.SetMarginComponentForSide(side, curAutoMarginSize); 1.1926 + 1.1927 + mNumAutoMarginsInMainAxis--; 1.1928 + mPackingSpaceRemaining -= curAutoMarginSize; 1.1929 + } 1.1930 + } 1.1931 + } 1.1932 +} 1.1933 + 1.1934 +void 1.1935 +MainAxisPositionTracker::TraversePackingSpace() 1.1936 +{ 1.1937 + if (mNumPackingSpacesRemaining) { 1.1938 + MOZ_ASSERT(mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_BETWEEN || 1.1939 + mJustifyContent == NS_STYLE_JUSTIFY_CONTENT_SPACE_AROUND, 1.1940 + "mNumPackingSpacesRemaining only applies for " 1.1941 + "space-between/space-around"); 1.1942 + 1.1943 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.1944 + "ran out of packing space earlier than we expected"); 1.1945 + 1.1946 + // NOTE: This integer math will skew the distribution of remainder 1.1947 + // app-units towards the end, which is fine. 1.1948 + nscoord curPackingSpace = 1.1949 + mPackingSpaceRemaining / mNumPackingSpacesRemaining; 1.1950 + 1.1951 + mPosition += curPackingSpace; 1.1952 + mNumPackingSpacesRemaining--; 1.1953 + mPackingSpaceRemaining -= curPackingSpace; 1.1954 + } 1.1955 +} 1.1956 + 1.1957 +CrossAxisPositionTracker:: 1.1958 + CrossAxisPositionTracker(FlexLine* aFirstLine, 1.1959 + uint8_t aAlignContent, 1.1960 + nscoord aContentBoxCrossSize, 1.1961 + bool aIsCrossSizeDefinite, 1.1962 + const FlexboxAxisTracker& aAxisTracker) 1.1963 + : PositionTracker(aAxisTracker.GetCrossAxis()), 1.1964 + mPackingSpaceRemaining(0), 1.1965 + mNumPackingSpacesRemaining(0), 1.1966 + mAlignContent(aAlignContent) 1.1967 +{ 1.1968 + MOZ_ASSERT(aFirstLine, "null first line pointer"); 1.1969 + 1.1970 + if (aIsCrossSizeDefinite && !aFirstLine->getNext()) { 1.1971 + // "If the flex container has only a single line (even if it's a 1.1972 + // multi-line flex container) and has a definite cross size, the cross 1.1973 + // size of the flex line is the flex container's inner cross size." 1.1974 + // SOURCE: http://dev.w3.org/csswg/css-flexbox/#algo-line-break 1.1975 + // NOTE: This means (by definition) that there's no packing space, which 1.1976 + // means we don't need to be concerned with "align-conent" at all and we 1.1977 + // can return early. This is handy, because this is the usual case (for 1.1978 + // single-line flexbox). 1.1979 + aFirstLine->SetLineCrossSize(aContentBoxCrossSize); 1.1980 + return; 1.1981 + } 1.1982 + 1.1983 + // NOTE: The rest of this function should essentially match 1.1984 + // MainAxisPositionTracker's constructor, though with FlexLines instead of 1.1985 + // FlexItems, and with the additional value "stretch" (and of course with 1.1986 + // cross sizes instead of main sizes.) 1.1987 + 1.1988 + // Figure out how much packing space we have (container's cross size minus 1.1989 + // all the lines' cross sizes). Also, share this loop to count how many 1.1990 + // lines we have. (We need that count in some cases below.) 1.1991 + mPackingSpaceRemaining = aContentBoxCrossSize; 1.1992 + uint32_t numLines = 0; 1.1993 + for (FlexLine* line = aFirstLine; line; line = line->getNext()) { 1.1994 + mPackingSpaceRemaining -= line->GetLineCrossSize(); 1.1995 + numLines++; 1.1996 + } 1.1997 + 1.1998 + // If packing space is negative, 'space-between' and 'stretch' behave like 1.1999 + // 'flex-start', and 'space-around' behaves like 'center'. In those cases, 1.2000 + // it's simplest to just pretend we have a different 'align-content' value 1.2001 + // and share code. 1.2002 + if (mPackingSpaceRemaining < 0) { 1.2003 + if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN || 1.2004 + mAlignContent == NS_STYLE_ALIGN_CONTENT_STRETCH) { 1.2005 + mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START; 1.2006 + } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND) { 1.2007 + mAlignContent = NS_STYLE_ALIGN_CONTENT_CENTER; 1.2008 + } 1.2009 + } 1.2010 + 1.2011 + // If our cross axis is (internally) reversed, swap the align-content 1.2012 + // "flex-start" and "flex-end" behaviors: 1.2013 + if (aAxisTracker.AreAxesInternallyReversed()) { 1.2014 + if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_START) { 1.2015 + mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_END; 1.2016 + } else if (mAlignContent == NS_STYLE_ALIGN_CONTENT_FLEX_END) { 1.2017 + mAlignContent = NS_STYLE_ALIGN_CONTENT_FLEX_START; 1.2018 + } 1.2019 + } 1.2020 + 1.2021 + // Figure out how much space we'll set aside for packing spaces, and advance 1.2022 + // past any leading packing-space. 1.2023 + if (mPackingSpaceRemaining != 0) { 1.2024 + switch (mAlignContent) { 1.2025 + case NS_STYLE_ALIGN_CONTENT_FLEX_START: 1.2026 + // All packing space should go at the end --> nothing to do here. 1.2027 + break; 1.2028 + case NS_STYLE_ALIGN_CONTENT_FLEX_END: 1.2029 + // All packing space goes at the beginning 1.2030 + mPosition += mPackingSpaceRemaining; 1.2031 + break; 1.2032 + case NS_STYLE_ALIGN_CONTENT_CENTER: 1.2033 + // Half the packing space goes at the beginning 1.2034 + mPosition += mPackingSpaceRemaining / 2; 1.2035 + break; 1.2036 + case NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN: 1.2037 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.2038 + "negative packing space should make us use 'flex-start' " 1.2039 + "instead of 'space-between'"); 1.2040 + // 1 packing space between each flex line, no packing space at ends. 1.2041 + mNumPackingSpacesRemaining = numLines - 1; 1.2042 + break; 1.2043 + case NS_STYLE_ALIGN_CONTENT_SPACE_AROUND: { 1.2044 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.2045 + "negative packing space should make us use 'center' " 1.2046 + "instead of 'space-around'"); 1.2047 + // 1 packing space between each flex line, plus half a packing space 1.2048 + // at beginning & end. So our number of full packing-spaces is equal 1.2049 + // to the number of flex lines. 1.2050 + mNumPackingSpacesRemaining = numLines; 1.2051 + // The edges (start/end) share one full packing space 1.2052 + nscoord totalEdgePackingSpace = 1.2053 + mPackingSpaceRemaining / mNumPackingSpacesRemaining; 1.2054 + 1.2055 + // ...and we'll use half of that right now, at the start 1.2056 + mPosition += totalEdgePackingSpace / 2; 1.2057 + // ...but we need to subtract all of it right away, so that we won't 1.2058 + // hand out any of it to intermediate packing spaces. 1.2059 + mPackingSpaceRemaining -= totalEdgePackingSpace; 1.2060 + mNumPackingSpacesRemaining--; 1.2061 + break; 1.2062 + } 1.2063 + case NS_STYLE_ALIGN_CONTENT_STRETCH: { 1.2064 + // Split space equally between the lines: 1.2065 + MOZ_ASSERT(mPackingSpaceRemaining > 0, 1.2066 + "negative packing space should make us use 'flex-start' " 1.2067 + "instead of 'stretch' (and we shouldn't bother with this " 1.2068 + "code if we have 0 packing space)"); 1.2069 + 1.2070 + uint32_t numLinesLeft = numLines; 1.2071 + for (FlexLine* line = aFirstLine; line; line = line->getNext()) { 1.2072 + // Our share is the amount of space remaining, divided by the number 1.2073 + // of lines remainig. 1.2074 + MOZ_ASSERT(numLinesLeft > 0, "miscalculated num lines"); 1.2075 + nscoord shareOfExtraSpace = mPackingSpaceRemaining / numLinesLeft; 1.2076 + nscoord newSize = line->GetLineCrossSize() + shareOfExtraSpace; 1.2077 + line->SetLineCrossSize(newSize); 1.2078 + 1.2079 + mPackingSpaceRemaining -= shareOfExtraSpace; 1.2080 + numLinesLeft--; 1.2081 + } 1.2082 + MOZ_ASSERT(numLinesLeft == 0, "miscalculated num lines"); 1.2083 + break; 1.2084 + } 1.2085 + default: 1.2086 + MOZ_CRASH("Unexpected align-content value"); 1.2087 + } 1.2088 + } 1.2089 +} 1.2090 + 1.2091 +void 1.2092 +CrossAxisPositionTracker::TraversePackingSpace() 1.2093 +{ 1.2094 + if (mNumPackingSpacesRemaining) { 1.2095 + MOZ_ASSERT(mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_BETWEEN || 1.2096 + mAlignContent == NS_STYLE_ALIGN_CONTENT_SPACE_AROUND, 1.2097 + "mNumPackingSpacesRemaining only applies for " 1.2098 + "space-between/space-around"); 1.2099 + 1.2100 + MOZ_ASSERT(mPackingSpaceRemaining >= 0, 1.2101 + "ran out of packing space earlier than we expected"); 1.2102 + 1.2103 + // NOTE: This integer math will skew the distribution of remainder 1.2104 + // app-units towards the end, which is fine. 1.2105 + nscoord curPackingSpace = 1.2106 + mPackingSpaceRemaining / mNumPackingSpacesRemaining; 1.2107 + 1.2108 + mPosition += curPackingSpace; 1.2109 + mNumPackingSpacesRemaining--; 1.2110 + mPackingSpaceRemaining -= curPackingSpace; 1.2111 + } 1.2112 +} 1.2113 + 1.2114 +SingleLineCrossAxisPositionTracker:: 1.2115 + SingleLineCrossAxisPositionTracker(const FlexboxAxisTracker& aAxisTracker) 1.2116 + : PositionTracker(aAxisTracker.GetCrossAxis()) 1.2117 +{ 1.2118 +} 1.2119 + 1.2120 +void 1.2121 +FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker) 1.2122 +{ 1.2123 + nscoord crossStartToFurthestBaseline = nscoord_MIN; 1.2124 + nscoord crossEndToFurthestBaseline = nscoord_MIN; 1.2125 + nscoord largestOuterCrossSize = 0; 1.2126 + for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.2127 + nscoord curOuterCrossSize = 1.2128 + item->GetOuterCrossSize(aAxisTracker.GetCrossAxis()); 1.2129 + 1.2130 + if (item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE && 1.2131 + item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) { 1.2132 + // FIXME: Once we support "writing-mode", we'll have to do baseline 1.2133 + // alignment in vertical flex containers here (w/ horizontal cross-axes). 1.2134 + 1.2135 + // Find distance from our item's cross-start and cross-end margin-box 1.2136 + // edges to its baseline. 1.2137 + // 1.2138 + // Here's a diagram of a flex-item that we might be doing this on. 1.2139 + // "mmm" is the margin-box, "bbb" is the border-box. The bottom of 1.2140 + // the text "BASE" is the baseline. 1.2141 + // 1.2142 + // ---(cross-start)--- 1.2143 + // ___ ___ ___ 1.2144 + // mmmmmmmmmmmm | |margin-start | 1.2145 + // m m | _|_ ___ | 1.2146 + // m bbbbbbbb m |curOuterCrossSize | |crossStartToBaseline 1.2147 + // m b b m | |ascent | 1.2148 + // m b BASE b m | _|_ _|_ 1.2149 + // m b b m | | 1.2150 + // m bbbbbbbb m | |crossEndToBaseline 1.2151 + // m m | | 1.2152 + // mmmmmmmmmmmm _|_ _|_ 1.2153 + // 1.2154 + // ---(cross-end)--- 1.2155 + // 1.2156 + // We already have the curOuterCrossSize, margin-start, and the ascent. 1.2157 + // * We can get crossStartToBaseline by adding margin-start + ascent. 1.2158 + // * If we subtract that from the curOuterCrossSize, we get 1.2159 + // crossEndToBaseline. 1.2160 + 1.2161 + nscoord crossStartToBaseline = 1.2162 + item->GetBaselineOffsetFromOuterCrossEdge(aAxisTracker.GetCrossAxis(), 1.2163 + eAxisEdge_Start); 1.2164 + nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline; 1.2165 + 1.2166 + // Now, update our "largest" values for these (across all the flex items 1.2167 + // in this flex line), so we can use them in computing the line's cross 1.2168 + // size below: 1.2169 + crossStartToFurthestBaseline = std::max(crossStartToFurthestBaseline, 1.2170 + crossStartToBaseline); 1.2171 + crossEndToFurthestBaseline = std::max(crossEndToFurthestBaseline, 1.2172 + crossEndToBaseline); 1.2173 + } else { 1.2174 + largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize); 1.2175 + } 1.2176 + } 1.2177 + 1.2178 + // The line's baseline offset is the distance from the line's edge (start or 1.2179 + // end, depending on whether we've flipped the axes) to the furthest 1.2180 + // item-baseline. The item(s) with that baseline will be exactly aligned with 1.2181 + // the line's edge. 1.2182 + mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ? 1.2183 + crossEndToFurthestBaseline : crossStartToFurthestBaseline; 1.2184 + 1.2185 + // The line's cross-size is the larger of: 1.2186 + // (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of 1.2187 + // all baseline-aligned items with no cross-axis auto margins... 1.2188 + // and 1.2189 + // (b) largest cross-size of all other children. 1.2190 + mLineCrossSize = std::max(crossStartToFurthestBaseline + 1.2191 + crossEndToFurthestBaseline, 1.2192 + largestOuterCrossSize); 1.2193 +} 1.2194 + 1.2195 +void 1.2196 +FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize, 1.2197 + const FlexboxAxisTracker& aAxisTracker) 1.2198 +{ 1.2199 + AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis(); 1.2200 + // We stretch IFF we are align-self:stretch, have no auto margins in 1.2201 + // cross axis, and have cross-axis size property == "auto". If any of those 1.2202 + // conditions don't hold up, we won't stretch. 1.2203 + if (mAlignSelf != NS_STYLE_ALIGN_ITEMS_STRETCH || 1.2204 + GetNumAutoMarginsInAxis(crossAxis) != 0 || 1.2205 + eStyleUnit_Auto != GetSizePropertyForAxis(mFrame, crossAxis).GetUnit()) { 1.2206 + return; 1.2207 + } 1.2208 + 1.2209 + // If we've already been stretched, we can bail out early, too. 1.2210 + // No need to redo the calculation. 1.2211 + if (mIsStretched) { 1.2212 + return; 1.2213 + } 1.2214 + 1.2215 + // Reserve space for margins & border & padding, and then use whatever 1.2216 + // remains as our item's cross-size (clamped to its min/max range). 1.2217 + nscoord stretchedSize = aLineCrossSize - 1.2218 + GetMarginBorderPaddingSizeInAxis(crossAxis); 1.2219 + 1.2220 + stretchedSize = NS_CSS_MINMAX(stretchedSize, mCrossMinSize, mCrossMaxSize); 1.2221 + 1.2222 + // Update the cross-size & make a note that it's stretched, so we know to 1.2223 + // override the reflow state's computed cross-size in our final reflow. 1.2224 + SetCrossSize(stretchedSize); 1.2225 + mIsStretched = true; 1.2226 +} 1.2227 + 1.2228 +void 1.2229 +SingleLineCrossAxisPositionTracker:: 1.2230 + ResolveAutoMarginsInCrossAxis(const FlexLine& aLine, 1.2231 + FlexItem& aItem) 1.2232 +{ 1.2233 + // Subtract the space that our item is already occupying, to see how much 1.2234 + // space (if any) is available for its auto margins. 1.2235 + nscoord spaceForAutoMargins = aLine.GetLineCrossSize() - 1.2236 + aItem.GetOuterCrossSize(mAxis); 1.2237 + 1.2238 + if (spaceForAutoMargins <= 0) { 1.2239 + return; // No available space --> nothing to do 1.2240 + } 1.2241 + 1.2242 + uint32_t numAutoMargins = aItem.GetNumAutoMarginsInAxis(mAxis); 1.2243 + if (numAutoMargins == 0) { 1.2244 + return; // No auto margins --> nothing to do. 1.2245 + } 1.2246 + 1.2247 + // OK, we have at least one auto margin and we have some available space. 1.2248 + // Give each auto margin a share of the space. 1.2249 + const nsStyleSides& styleMargin = aItem.Frame()->StyleMargin()->mMargin; 1.2250 + for (uint32_t i = 0; i < eNumAxisEdges; i++) { 1.2251 + Side side = kAxisOrientationToSidesMap[mAxis][i]; 1.2252 + if (styleMargin.GetUnit(side) == eStyleUnit_Auto) { 1.2253 + MOZ_ASSERT(aItem.GetMarginComponentForSide(side) == 0, 1.2254 + "Expecting auto margins to have value '0' before we " 1.2255 + "update them"); 1.2256 + 1.2257 + // NOTE: integer divison is fine here; numAutoMargins is either 1 or 2. 1.2258 + // If it's 2 & spaceForAutoMargins is odd, 1st margin gets smaller half. 1.2259 + nscoord curAutoMarginSize = spaceForAutoMargins / numAutoMargins; 1.2260 + aItem.SetMarginComponentForSide(side, curAutoMarginSize); 1.2261 + numAutoMargins--; 1.2262 + spaceForAutoMargins -= curAutoMarginSize; 1.2263 + } 1.2264 + } 1.2265 +} 1.2266 + 1.2267 +void 1.2268 +SingleLineCrossAxisPositionTracker:: 1.2269 + EnterAlignPackingSpace(const FlexLine& aLine, 1.2270 + const FlexItem& aItem, 1.2271 + const FlexboxAxisTracker& aAxisTracker) 1.2272 +{ 1.2273 + // We don't do align-self alignment on items that have auto margins 1.2274 + // in the cross axis. 1.2275 + if (aItem.GetNumAutoMarginsInAxis(mAxis)) { 1.2276 + return; 1.2277 + } 1.2278 + 1.2279 + uint8_t alignSelf = aItem.GetAlignSelf(); 1.2280 + // NOTE: 'stretch' behaves like 'flex-start' once we've stretched any 1.2281 + // auto-sized items (which we've already done). 1.2282 + if (alignSelf == NS_STYLE_ALIGN_ITEMS_STRETCH) { 1.2283 + alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START; 1.2284 + } 1.2285 + 1.2286 + // If our cross axis is (internally) reversed, swap the align-self 1.2287 + // "flex-start" and "flex-end" behaviors: 1.2288 + if (aAxisTracker.AreAxesInternallyReversed()) { 1.2289 + if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_START) { 1.2290 + alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_END; 1.2291 + } else if (alignSelf == NS_STYLE_ALIGN_ITEMS_FLEX_END) { 1.2292 + alignSelf = NS_STYLE_ALIGN_ITEMS_FLEX_START; 1.2293 + } 1.2294 + } 1.2295 + 1.2296 + switch (alignSelf) { 1.2297 + case NS_STYLE_ALIGN_ITEMS_FLEX_START: 1.2298 + // No space to skip over -- we're done. 1.2299 + break; 1.2300 + case NS_STYLE_ALIGN_ITEMS_FLEX_END: 1.2301 + mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis); 1.2302 + break; 1.2303 + case NS_STYLE_ALIGN_ITEMS_CENTER: 1.2304 + // Note: If cross-size is odd, the "after" space will get the extra unit. 1.2305 + mPosition += 1.2306 + (aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2; 1.2307 + break; 1.2308 + case NS_STYLE_ALIGN_ITEMS_BASELINE: { 1.2309 + // Normally, baseline-aligned items are collectively aligned with the 1.2310 + // line's cross-start edge; however, if our cross axis is (internally) 1.2311 + // reversed, we instead align them with the cross-end edge. 1.2312 + nscoord itemBaselineOffset = 1.2313 + aItem.GetBaselineOffsetFromOuterCrossEdge(mAxis, 1.2314 + aAxisTracker.AreAxesInternallyReversed() ? 1.2315 + eAxisEdge_End : eAxisEdge_Start); 1.2316 + 1.2317 + nscoord lineBaselineOffset = aLine.GetBaselineOffset(); 1.2318 + 1.2319 + NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset, 1.2320 + "failed at finding largest baseline offset"); 1.2321 + 1.2322 + // How much do we need to adjust our position (from the line edge), 1.2323 + // to get the item's baseline to hit the line's baseline offset: 1.2324 + nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset; 1.2325 + 1.2326 + if (aAxisTracker.AreAxesInternallyReversed()) { 1.2327 + // Advance to align item w/ line's flex-end edge (as in FLEX_END case): 1.2328 + mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis); 1.2329 + // ...and step *back* by the baseline adjustment: 1.2330 + mPosition -= baselineDiff; 1.2331 + } else { 1.2332 + // mPosition is already at line's flex-start edge. 1.2333 + // From there, we step *forward* by the baseline adjustment: 1.2334 + mPosition += baselineDiff; 1.2335 + } 1.2336 + break; 1.2337 + } 1.2338 + default: 1.2339 + NS_NOTREACHED("Unexpected align-self value"); 1.2340 + break; 1.2341 + } 1.2342 +} 1.2343 + 1.2344 +FlexboxAxisTracker::FlexboxAxisTracker( 1.2345 + nsFlexContainerFrame* aFlexContainerFrame) 1.2346 + : mAreAxesInternallyReversed(false) 1.2347 +{ 1.2348 + const nsStylePosition* pos = aFlexContainerFrame->StylePosition(); 1.2349 + uint32_t flexDirection = pos->mFlexDirection; 1.2350 + uint32_t cssDirection = 1.2351 + aFlexContainerFrame->StyleVisibility()->mDirection; 1.2352 + 1.2353 + MOZ_ASSERT(cssDirection == NS_STYLE_DIRECTION_LTR || 1.2354 + cssDirection == NS_STYLE_DIRECTION_RTL, 1.2355 + "Unexpected computed value for 'direction' property"); 1.2356 + // (Not asserting for flexDirection here; it's checked by the switch below.) 1.2357 + 1.2358 + // These are defined according to writing-modes' definitions of 1.2359 + // start/end (for the inline dimension) and before/after (for the block 1.2360 + // dimension), here: 1.2361 + // http://www.w3.org/TR/css3-writing-modes/#logical-directions 1.2362 + // (NOTE: I'm intentionally not calling this "inlineAxis"/"blockAxis", since 1.2363 + // those terms have explicit definition in the writing-modes spec, which are 1.2364 + // the opposite of how I'd be using them here.) 1.2365 + // XXXdholbert Once we support the 'writing-mode' property, use its value 1.2366 + // here to further customize inlineDimension & blockDimension. 1.2367 + 1.2368 + // Inline dimension ("start-to-end"): 1.2369 + AxisOrientationType inlineDimension = 1.2370 + cssDirection == NS_STYLE_DIRECTION_RTL ? eAxis_RL : eAxis_LR; 1.2371 + 1.2372 + // Block dimension ("before-to-after"): 1.2373 + AxisOrientationType blockDimension = eAxis_TB; 1.2374 + 1.2375 + // Determine main axis: 1.2376 + switch (flexDirection) { 1.2377 + case NS_STYLE_FLEX_DIRECTION_ROW: 1.2378 + mMainAxis = inlineDimension; 1.2379 + break; 1.2380 + case NS_STYLE_FLEX_DIRECTION_ROW_REVERSE: 1.2381 + mMainAxis = GetReverseAxis(inlineDimension); 1.2382 + break; 1.2383 + case NS_STYLE_FLEX_DIRECTION_COLUMN: 1.2384 + mMainAxis = blockDimension; 1.2385 + break; 1.2386 + case NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE: 1.2387 + mMainAxis = GetReverseAxis(blockDimension); 1.2388 + break; 1.2389 + default: 1.2390 + MOZ_CRASH("Unexpected computed value for 'flex-flow' property"); 1.2391 + } 1.2392 + 1.2393 + // Determine cross axis: 1.2394 + // (This is set up so that a bogus |flexDirection| value will 1.2395 + // give us blockDimension. 1.2396 + if (flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN || 1.2397 + flexDirection == NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE) { 1.2398 + mCrossAxis = inlineDimension; 1.2399 + } else { 1.2400 + mCrossAxis = blockDimension; 1.2401 + } 1.2402 + 1.2403 + // "flex-wrap: wrap-reverse" reverses our cross axis. 1.2404 + if (pos->mFlexWrap == NS_STYLE_FLEX_WRAP_WRAP_REVERSE) { 1.2405 + mCrossAxis = GetReverseAxis(mCrossAxis); 1.2406 + } 1.2407 + 1.2408 + // Master switch to enable/disable bug 983427's code for reversing our axes 1.2409 + // and reversing some logic, to avoid reflowing children in bottom-to-top 1.2410 + // order. (This switch can be removed eventually, but for now, it allows 1.2411 + // this special-case code path to be compared against the normal code path.) 1.2412 + static bool sPreventBottomToTopChildOrdering = true; 1.2413 + 1.2414 + if (sPreventBottomToTopChildOrdering) { 1.2415 + // If either axis is bottom-to-top, we flip both axes (and set a flag 1.2416 + // so that we can flip some logic to make the reversal transparent). 1.2417 + if (eAxis_BT == mMainAxis || eAxis_BT == mCrossAxis) { 1.2418 + mMainAxis = GetReverseAxis(mMainAxis); 1.2419 + mCrossAxis = GetReverseAxis(mCrossAxis); 1.2420 + mAreAxesInternallyReversed = true; 1.2421 + } 1.2422 + } 1.2423 + 1.2424 + MOZ_ASSERT(IsAxisHorizontal(mMainAxis) != IsAxisHorizontal(mCrossAxis), 1.2425 + "main & cross axes should be in different dimensions"); 1.2426 +} 1.2427 + 1.2428 +// Allocates a new FlexLine, adds it to the given LinkedList (at the front or 1.2429 +// back depending on aShouldInsertAtFront), and returns a pointer to it. 1.2430 +static FlexLine* 1.2431 +AddNewFlexLineToList(LinkedList<FlexLine>& aLines, 1.2432 + bool aShouldInsertAtFront) 1.2433 +{ 1.2434 + FlexLine* newLine = new FlexLine(); 1.2435 + if (aShouldInsertAtFront) { 1.2436 + aLines.insertFront(newLine); 1.2437 + } else { 1.2438 + aLines.insertBack(newLine); 1.2439 + } 1.2440 + return newLine; 1.2441 +} 1.2442 + 1.2443 +nsresult 1.2444 +nsFlexContainerFrame::GenerateFlexLines( 1.2445 + nsPresContext* aPresContext, 1.2446 + const nsHTMLReflowState& aReflowState, 1.2447 + nscoord aContentBoxMainSize, 1.2448 + nscoord aAvailableHeightForContent, 1.2449 + const nsTArray<StrutInfo>& aStruts, 1.2450 + const FlexboxAxisTracker& aAxisTracker, 1.2451 + LinkedList<FlexLine>& aLines) 1.2452 +{ 1.2453 + MOZ_ASSERT(aLines.isEmpty(), "Expecting outparam to start out empty"); 1.2454 + 1.2455 + const bool isSingleLine = 1.2456 + NS_STYLE_FLEX_WRAP_NOWRAP == aReflowState.mStylePosition->mFlexWrap; 1.2457 + 1.2458 + // If we're transparently reversing axes, then we'll need to link up our 1.2459 + // FlexItems and FlexLines in the reverse order, so that the rest of flex 1.2460 + // layout (with flipped axes) will still produce the correct result. 1.2461 + // Here, we declare a convenience bool that we'll pass when adding a new 1.2462 + // FlexLine or FlexItem, to make us insert it at the beginning of its list 1.2463 + // (so the list ends up reversed). 1.2464 + const bool shouldInsertAtFront = aAxisTracker.AreAxesInternallyReversed(); 1.2465 + 1.2466 + // We have at least one FlexLine. Even an empty flex container has a single 1.2467 + // (empty) flex line. 1.2468 + FlexLine* curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront); 1.2469 + 1.2470 + nscoord wrapThreshold; 1.2471 + if (isSingleLine) { 1.2472 + // Not wrapping. Set threshold to sentinel value that tells us not to wrap. 1.2473 + wrapThreshold = NS_UNCONSTRAINEDSIZE; 1.2474 + } else { 1.2475 + // Wrapping! Set wrap threshold to flex container's content-box main-size. 1.2476 + wrapThreshold = aContentBoxMainSize; 1.2477 + 1.2478 + // If the flex container doesn't have a definite content-box main-size 1.2479 + // (e.g. if we're 'height:auto'), make sure we at least wrap when we hit 1.2480 + // its max main-size. 1.2481 + if (wrapThreshold == NS_UNCONSTRAINEDSIZE) { 1.2482 + const nscoord flexContainerMaxMainSize = 1.2483 + GET_MAIN_COMPONENT(aAxisTracker, 1.2484 + aReflowState.ComputedMaxWidth(), 1.2485 + aReflowState.ComputedMaxHeight()); 1.2486 + 1.2487 + wrapThreshold = flexContainerMaxMainSize; 1.2488 + } 1.2489 + 1.2490 + // Also: if we're vertical and paginating, we may need to wrap sooner 1.2491 + // (before we run off the end of the page) 1.2492 + if (!IsAxisHorizontal(aAxisTracker.GetMainAxis()) && 1.2493 + aAvailableHeightForContent != NS_UNCONSTRAINEDSIZE) { 1.2494 + wrapThreshold = std::min(wrapThreshold, aAvailableHeightForContent); 1.2495 + } 1.2496 + } 1.2497 + 1.2498 + // Tracks the index of the next strut, in aStruts (and when this hits 1.2499 + // aStruts.Length(), that means there are no more struts): 1.2500 + uint32_t nextStrutIdx = 0; 1.2501 + 1.2502 + // Overall index of the current flex item in the flex container. (This gets 1.2503 + // checked against entries in aStruts.) 1.2504 + uint32_t itemIdxInContainer = 0; 1.2505 + 1.2506 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.2507 + nsIFrame* childFrame = e.get(); 1.2508 + 1.2509 + // Honor "page-break-before", if we're multi-line and this line isn't empty: 1.2510 + if (!isSingleLine && !curLine->IsEmpty() && 1.2511 + childFrame->StyleDisplay()->mBreakBefore) { 1.2512 + curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront); 1.2513 + } 1.2514 + 1.2515 + nsAutoPtr<FlexItem> item; 1.2516 + if (nextStrutIdx < aStruts.Length() && 1.2517 + aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) { 1.2518 + 1.2519 + // Use the simplified "strut" FlexItem constructor: 1.2520 + item = new FlexItem(childFrame, aStruts[nextStrutIdx].mStrutCrossSize); 1.2521 + nextStrutIdx++; 1.2522 + } else { 1.2523 + item = GenerateFlexItemForChild(aPresContext, childFrame, 1.2524 + aReflowState, aAxisTracker); 1.2525 + 1.2526 + nsresult rv = ResolveFlexItemMaxContentSizing(aPresContext, *item, 1.2527 + aReflowState, aAxisTracker); 1.2528 + NS_ENSURE_SUCCESS(rv,rv); 1.2529 + } 1.2530 + 1.2531 + nscoord itemInnerHypotheticalMainSize = item->GetMainSize(); 1.2532 + nscoord itemOuterHypotheticalMainSize = 1.2533 + item->GetOuterMainSize(aAxisTracker.GetMainAxis()); 1.2534 + 1.2535 + // Check if we need to wrap |item| to a new line 1.2536 + // (i.e. check if its outer hypothetical main size pushes our line over 1.2537 + // the threshold) 1.2538 + if (wrapThreshold != NS_UNCONSTRAINEDSIZE && 1.2539 + !curLine->IsEmpty() && // No need to wrap at start of a line. 1.2540 + wrapThreshold < (curLine->GetTotalOuterHypotheticalMainSize() + 1.2541 + itemOuterHypotheticalMainSize)) { 1.2542 + curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront); 1.2543 + } 1.2544 + 1.2545 + // Add item to current flex line (and update the line's bookkeeping about 1.2546 + // how large its items collectively are). 1.2547 + curLine->AddItem(item.forget(), shouldInsertAtFront, 1.2548 + itemInnerHypotheticalMainSize, 1.2549 + itemOuterHypotheticalMainSize); 1.2550 + 1.2551 + // Honor "page-break-after", if we're multi-line and have more children: 1.2552 + if (!isSingleLine && childFrame->GetNextSibling() && 1.2553 + childFrame->StyleDisplay()->mBreakAfter) { 1.2554 + curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront); 1.2555 + } 1.2556 + itemIdxInContainer++; 1.2557 + } 1.2558 + 1.2559 + return NS_OK; 1.2560 +} 1.2561 + 1.2562 +// Retrieves the content-box main-size of our flex container from the 1.2563 +// reflow state (specifically, the main-size of *this continuation* of the 1.2564 +// flex container). 1.2565 +nscoord 1.2566 +nsFlexContainerFrame::GetMainSizeFromReflowState( 1.2567 + const nsHTMLReflowState& aReflowState, 1.2568 + const FlexboxAxisTracker& aAxisTracker) 1.2569 +{ 1.2570 + if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { 1.2571 + // Horizontal case is easy -- our main size is our computed width 1.2572 + // (which is already resolved). 1.2573 + return aReflowState.ComputedWidth(); 1.2574 + } 1.2575 + 1.2576 + return GetEffectiveComputedHeight(aReflowState); 1.2577 +} 1.2578 + 1.2579 +// Returns the largest outer hypothetical main-size of any line in |aLines|. 1.2580 +// (i.e. the hypothetical main-size of the largest line) 1.2581 +static nscoord 1.2582 +GetLargestLineMainSize(const FlexLine* aFirstLine) 1.2583 +{ 1.2584 + nscoord largestLineOuterSize = 0; 1.2585 + for (const FlexLine* line = aFirstLine; line; line = line->getNext()) { 1.2586 + largestLineOuterSize = std::max(largestLineOuterSize, 1.2587 + line->GetTotalOuterHypotheticalMainSize()); 1.2588 + } 1.2589 + return largestLineOuterSize; 1.2590 +} 1.2591 + 1.2592 +// Returns the content-box main-size of our flex container, based on the 1.2593 +// available height (if appropriate) and the main-sizes of the flex items. 1.2594 +static nscoord 1.2595 +ClampFlexContainerMainSize(const nsHTMLReflowState& aReflowState, 1.2596 + const FlexboxAxisTracker& aAxisTracker, 1.2597 + nscoord aUnclampedMainSize, 1.2598 + nscoord aAvailableHeightForContent, 1.2599 + const FlexLine* aFirstLine, 1.2600 + nsReflowStatus& aStatus) 1.2601 +{ 1.2602 + MOZ_ASSERT(aFirstLine, "null first line pointer"); 1.2603 + 1.2604 + if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { 1.2605 + // Horizontal case is easy -- our main size should already be resolved 1.2606 + // before we get a call to Reflow. We don't have to worry about doing 1.2607 + // page-breaking or shrinkwrapping in the horizontal axis. 1.2608 + return aUnclampedMainSize; 1.2609 + } 1.2610 + 1.2611 + if (aUnclampedMainSize != NS_INTRINSICSIZE) { 1.2612 + // Vertical case, with fixed height: 1.2613 + if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE || 1.2614 + aUnclampedMainSize < aAvailableHeightForContent) { 1.2615 + // Not in a fragmenting context, OR no need to fragment because we have 1.2616 + // more available height than we need. Either way, just use our fixed 1.2617 + // height. (Note that the reflow state has already done the appropriate 1.2618 + // min/max-height clamping.) 1.2619 + return aUnclampedMainSize; 1.2620 + } 1.2621 + 1.2622 + // Fragmenting *and* our fixed height is too tall for available height: 1.2623 + // Mark incomplete so we get a next-in-flow, and take up all of the 1.2624 + // available height (or the amount of height required by our children, if 1.2625 + // that's larger; but of course not more than our own computed height). 1.2626 + // XXXdholbert For now, we don't support pushing children to our next 1.2627 + // continuation or splitting children, so "amount of height required by 1.2628 + // our children" is just the main-size (height) of our longest flex line. 1.2629 + NS_FRAME_SET_INCOMPLETE(aStatus); 1.2630 + nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine); 1.2631 + 1.2632 + if (largestLineOuterSize <= aAvailableHeightForContent) { 1.2633 + return aAvailableHeightForContent; 1.2634 + } 1.2635 + return std::min(aUnclampedMainSize, largestLineOuterSize); 1.2636 + } 1.2637 + 1.2638 + // Vertical case, with auto-height: 1.2639 + // Resolve auto-height to the largest FlexLine-length, clamped to our 1.2640 + // computed min/max main-size properties (min-height & max-height). 1.2641 + // XXXdholbert Handle constrained-aAvailableHeightForContent case here. 1.2642 + nscoord largestLineOuterSize = GetLargestLineMainSize(aFirstLine); 1.2643 + return NS_CSS_MINMAX(largestLineOuterSize, 1.2644 + aReflowState.ComputedMinHeight(), 1.2645 + aReflowState.ComputedMaxHeight()); 1.2646 +} 1.2647 + 1.2648 +nscoord 1.2649 +nsFlexContainerFrame::ComputeCrossSize(const nsHTMLReflowState& aReflowState, 1.2650 + const FlexboxAxisTracker& aAxisTracker, 1.2651 + nscoord aSumLineCrossSizes, 1.2652 + nscoord aAvailableHeightForContent, 1.2653 + bool* aIsDefinite, 1.2654 + nsReflowStatus& aStatus) 1.2655 +{ 1.2656 + MOZ_ASSERT(aIsDefinite, "outparam pointer must be non-null"); 1.2657 + 1.2658 + if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { 1.2659 + // Cross axis is horizontal: our cross size is our computed width 1.2660 + // (which is already resolved). 1.2661 + *aIsDefinite = true; 1.2662 + return aReflowState.ComputedWidth(); 1.2663 + } 1.2664 + 1.2665 + nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState); 1.2666 + if (effectiveComputedHeight != NS_INTRINSICSIZE) { 1.2667 + // Cross-axis is vertical, and we have a fixed height: 1.2668 + *aIsDefinite = true; 1.2669 + if (aAvailableHeightForContent == NS_UNCONSTRAINEDSIZE || 1.2670 + effectiveComputedHeight < aAvailableHeightForContent) { 1.2671 + // Not in a fragmenting context, OR no need to fragment because we have 1.2672 + // more available height than we need. Either way, just use our fixed 1.2673 + // height. (Note that the reflow state has already done the appropriate 1.2674 + // min/max-height clamping.) 1.2675 + return effectiveComputedHeight; 1.2676 + } 1.2677 + 1.2678 + // Fragmenting *and* our fixed height is too tall for available height: 1.2679 + // Mark incomplete so we get a next-in-flow, and take up all of the 1.2680 + // available height (or the amount of height required by our children, if 1.2681 + // that's larger; but of course not more than our own computed height). 1.2682 + // XXXdholbert For now, we don't support pushing children to our next 1.2683 + // continuation or splitting children, so "amount of height required by 1.2684 + // our children" is just our line-height. 1.2685 + NS_FRAME_SET_INCOMPLETE(aStatus); 1.2686 + if (aSumLineCrossSizes <= aAvailableHeightForContent) { 1.2687 + return aAvailableHeightForContent; 1.2688 + } 1.2689 + return std::min(effectiveComputedHeight, aSumLineCrossSizes); 1.2690 + } 1.2691 + 1.2692 + // Cross axis is vertical and we have auto-height: shrink-wrap our line(s), 1.2693 + // subject to our min-size / max-size constraints in that (vertical) axis. 1.2694 + // XXXdholbert Handle constrained-aAvailableHeightForContent case here. 1.2695 + *aIsDefinite = false; 1.2696 + return NS_CSS_MINMAX(aSumLineCrossSizes, 1.2697 + aReflowState.ComputedMinHeight(), 1.2698 + aReflowState.ComputedMaxHeight()); 1.2699 +} 1.2700 + 1.2701 +void 1.2702 +FlexLine::PositionItemsInMainAxis(uint8_t aJustifyContent, 1.2703 + nscoord aContentBoxMainSize, 1.2704 + const FlexboxAxisTracker& aAxisTracker) 1.2705 +{ 1.2706 + MainAxisPositionTracker mainAxisPosnTracker(aAxisTracker, this, 1.2707 + aJustifyContent, 1.2708 + aContentBoxMainSize); 1.2709 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.2710 + nscoord itemMainBorderBoxSize = 1.2711 + item->GetMainSize() + 1.2712 + item->GetBorderPaddingSizeInAxis(mainAxisPosnTracker.GetAxis()); 1.2713 + 1.2714 + // Resolve any main-axis 'auto' margins on aChild to an actual value. 1.2715 + mainAxisPosnTracker.ResolveAutoMarginsInMainAxis(*item); 1.2716 + 1.2717 + // Advance our position tracker to child's upper-left content-box corner, 1.2718 + // and use that as its position in the main axis. 1.2719 + mainAxisPosnTracker.EnterMargin(item->GetMargin()); 1.2720 + mainAxisPosnTracker.EnterChildFrame(itemMainBorderBoxSize); 1.2721 + 1.2722 + item->SetMainPosition(mainAxisPosnTracker.GetPosition()); 1.2723 + 1.2724 + mainAxisPosnTracker.ExitChildFrame(itemMainBorderBoxSize); 1.2725 + mainAxisPosnTracker.ExitMargin(item->GetMargin()); 1.2726 + mainAxisPosnTracker.TraversePackingSpace(); 1.2727 + } 1.2728 +} 1.2729 + 1.2730 +// Helper method to take care of children who ASK_FOR_BASELINE, when 1.2731 +// we need their baseline. 1.2732 +static void 1.2733 +ResolveReflowedChildAscent(nsIFrame* aFrame, 1.2734 + nsHTMLReflowMetrics& aChildDesiredSize) 1.2735 +{ 1.2736 + if (aChildDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { 1.2737 + // Use GetFirstLineBaseline(), or just GetBaseline() if that fails. 1.2738 + nscoord ascent; 1.2739 + if (nsLayoutUtils::GetFirstLineBaseline(aFrame, &ascent)) { 1.2740 + aChildDesiredSize.SetTopAscent(ascent); 1.2741 + } else { 1.2742 + aChildDesiredSize.SetTopAscent(aFrame->GetBaseline()); 1.2743 + } 1.2744 + } 1.2745 +} 1.2746 + 1.2747 +/** 1.2748 + * Given the flex container's "logical ascent" (i.e. distance from the 1.2749 + * flex container's content-box cross-start edge to its baseline), returns 1.2750 + * its actual physical ascent value (the distance from the *border-box* top 1.2751 + * edge to its baseline). 1.2752 + */ 1.2753 +static nscoord 1.2754 +ComputePhysicalAscentFromLogicalAscent(nscoord aLogicalAscent, 1.2755 + nscoord aContentBoxCrossSize, 1.2756 + const nsHTMLReflowState& aReflowState, 1.2757 + const FlexboxAxisTracker& aAxisTracker) 1.2758 +{ 1.2759 + return aReflowState.ComputedPhysicalBorderPadding().top + 1.2760 + PhysicalPosFromLogicalPos(aLogicalAscent, aContentBoxCrossSize, 1.2761 + aAxisTracker.GetCrossAxis()); 1.2762 +} 1.2763 + 1.2764 +nsresult 1.2765 +nsFlexContainerFrame::SizeItemInCrossAxis( 1.2766 + nsPresContext* aPresContext, 1.2767 + const FlexboxAxisTracker& aAxisTracker, 1.2768 + nsHTMLReflowState& aChildReflowState, 1.2769 + FlexItem& aItem) 1.2770 +{ 1.2771 + // In vertical flexbox (with horizontal cross-axis), we can just trust the 1.2772 + // reflow state's computed-width as our cross-size. We also don't need to 1.2773 + // record the baseline because we'll have converted any "align-self:baseline" 1.2774 + // items to be "align-self:flex-start" in the FlexItem constructor. 1.2775 + // FIXME: Once we support writing-mode (vertical text), we will be able to 1.2776 + // have baseline-aligned items in a vertical flexbox, and we'll need to 1.2777 + // record baseline information here. 1.2778 + if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { 1.2779 + MOZ_ASSERT(aItem.GetAlignSelf() != NS_STYLE_ALIGN_ITEMS_BASELINE, 1.2780 + "In vert flex container, we depend on FlexItem constructor to " 1.2781 + "convert 'align-self: baseline' to 'align-self: flex-start'"); 1.2782 + aItem.SetCrossSize(aChildReflowState.ComputedWidth()); 1.2783 + return NS_OK; 1.2784 + } 1.2785 + 1.2786 + MOZ_ASSERT(!aItem.HadMeasuringReflow(), 1.2787 + "We shouldn't need more than one measuring reflow"); 1.2788 + 1.2789 + if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH) { 1.2790 + // This item's got "align-self: stretch", so we probably imposed a 1.2791 + // stretched computed height on it during its previous reflow. We're 1.2792 + // not imposing that height for *this* measuring reflow, so we need to 1.2793 + // tell it to treat this reflow as a vertical resize (regardless of 1.2794 + // whether any of its ancestors are being resized). 1.2795 + aChildReflowState.mFlags.mVResize = true; 1.2796 + } 1.2797 + nsHTMLReflowMetrics childDesiredSize(aChildReflowState); 1.2798 + nsReflowStatus childReflowStatus; 1.2799 + const uint32_t flags = NS_FRAME_NO_MOVE_FRAME; 1.2800 + nsresult rv = ReflowChild(aItem.Frame(), aPresContext, 1.2801 + childDesiredSize, aChildReflowState, 1.2802 + 0, 0, flags, childReflowStatus); 1.2803 + aItem.SetHadMeasuringReflow(); 1.2804 + NS_ENSURE_SUCCESS(rv, rv); 1.2805 + 1.2806 + // XXXdholbert Once we do pagination / splitting, we'll need to actually 1.2807 + // handle incomplete childReflowStatuses. But for now, we give our kids 1.2808 + // unconstrained available height, which means they should always complete. 1.2809 + MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), 1.2810 + "We gave flex item unconstrained available height, so it " 1.2811 + "should be complete"); 1.2812 + 1.2813 + // Tell the child we're done with its initial reflow. 1.2814 + // (Necessary for e.g. GetBaseline() to work below w/out asserting) 1.2815 + rv = FinishReflowChild(aItem.Frame(), aPresContext, 1.2816 + childDesiredSize, &aChildReflowState, 0, 0, flags); 1.2817 + NS_ENSURE_SUCCESS(rv, rv); 1.2818 + 1.2819 + // Save the sizing info that we learned from this reflow 1.2820 + // ----------------------------------------------------- 1.2821 + 1.2822 + // Tentatively store the child's desired content-box cross-size. 1.2823 + // Note that childDesiredSize is the border-box size, so we have to 1.2824 + // subtract border & padding to get the content-box size. 1.2825 + // (Note that at this point in the code, we know our cross axis is vertical, 1.2826 + // so we don't bother with making aAxisTracker pick the cross-axis component 1.2827 + // for us.) 1.2828 + nscoord crossAxisBorderPadding = aItem.GetBorderPadding().TopBottom(); 1.2829 + if (childDesiredSize.Height() < crossAxisBorderPadding) { 1.2830 + // Child's requested size isn't large enough for its border/padding! 1.2831 + // This is OK for the trivial nsFrame::Reflow() impl, but other frame 1.2832 + // classes should know better. So, if we get here, the child had better be 1.2833 + // an instance of nsFrame (i.e. it should return null from GetType()). 1.2834 + // XXXdholbert Once we've fixed bug 765861, we should upgrade this to an 1.2835 + // assertion that trivially passes if bug 765861's flag has been flipped. 1.2836 + NS_WARN_IF_FALSE(!aItem.Frame()->GetType(), 1.2837 + "Child should at least request space for border/padding"); 1.2838 + aItem.SetCrossSize(0); 1.2839 + } else { 1.2840 + // (normal case) 1.2841 + aItem.SetCrossSize(childDesiredSize.Height() - crossAxisBorderPadding); 1.2842 + } 1.2843 + 1.2844 + // If we need to do baseline-alignment, store the child's ascent. 1.2845 + if (aItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_BASELINE) { 1.2846 + ResolveReflowedChildAscent(aItem.Frame(), childDesiredSize); 1.2847 + aItem.SetAscent(childDesiredSize.TopAscent()); 1.2848 + } 1.2849 + 1.2850 + return NS_OK; 1.2851 +} 1.2852 + 1.2853 +void 1.2854 +FlexLine::PositionItemsInCrossAxis(nscoord aLineStartPosition, 1.2855 + const FlexboxAxisTracker& aAxisTracker) 1.2856 +{ 1.2857 + SingleLineCrossAxisPositionTracker lineCrossAxisPosnTracker(aAxisTracker); 1.2858 + 1.2859 + for (FlexItem* item = mItems.getFirst(); item; item = item->getNext()) { 1.2860 + // First, stretch the item's cross size (if appropriate), and resolve any 1.2861 + // auto margins in this axis. 1.2862 + item->ResolveStretchedCrossSize(mLineCrossSize, aAxisTracker); 1.2863 + lineCrossAxisPosnTracker.ResolveAutoMarginsInCrossAxis(*this, *item); 1.2864 + 1.2865 + // Compute the cross-axis position of this item 1.2866 + nscoord itemCrossBorderBoxSize = 1.2867 + item->GetCrossSize() + 1.2868 + item->GetBorderPaddingSizeInAxis(aAxisTracker.GetCrossAxis()); 1.2869 + lineCrossAxisPosnTracker.EnterAlignPackingSpace(*this, *item, aAxisTracker); 1.2870 + lineCrossAxisPosnTracker.EnterMargin(item->GetMargin()); 1.2871 + lineCrossAxisPosnTracker.EnterChildFrame(itemCrossBorderBoxSize); 1.2872 + 1.2873 + item->SetCrossPosition(aLineStartPosition + 1.2874 + lineCrossAxisPosnTracker.GetPosition()); 1.2875 + 1.2876 + // Back out to cross-axis edge of the line. 1.2877 + lineCrossAxisPosnTracker.ResetPosition(); 1.2878 + } 1.2879 +} 1.2880 + 1.2881 +nsresult 1.2882 +nsFlexContainerFrame::Reflow(nsPresContext* aPresContext, 1.2883 + nsHTMLReflowMetrics& aDesiredSize, 1.2884 + const nsHTMLReflowState& aReflowState, 1.2885 + nsReflowStatus& aStatus) 1.2886 +{ 1.2887 + DO_GLOBAL_REFLOW_COUNT("nsFlexContainerFrame"); 1.2888 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.2889 + PR_LOG(GetFlexContainerLog(), PR_LOG_DEBUG, 1.2890 + ("Reflow() for nsFlexContainerFrame %p\n", this)); 1.2891 + 1.2892 + if (IsFrameTreeTooDeep(aReflowState, aDesiredSize, aStatus)) { 1.2893 + return NS_OK; 1.2894 + } 1.2895 + 1.2896 + // We (and our children) can only depend on our ancestor's height if we have 1.2897 + // a percent-height, or if we're positioned and we have "top" and "bottom" 1.2898 + // set and have height:auto. (There are actually other cases, too -- e.g. if 1.2899 + // our parent is itself a vertical flex container and we're flexible -- but 1.2900 + // we'll let our ancestors handle those sorts of cases.) 1.2901 + const nsStylePosition* stylePos = StylePosition(); 1.2902 + if (stylePos->mHeight.HasPercent() || 1.2903 + (StyleDisplay()->IsAbsolutelyPositionedStyle() && 1.2904 + eStyleUnit_Auto == stylePos->mHeight.GetUnit() && 1.2905 + eStyleUnit_Auto != stylePos->mOffset.GetTopUnit() && 1.2906 + eStyleUnit_Auto != stylePos->mOffset.GetBottomUnit())) { 1.2907 + AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.2908 + } 1.2909 + 1.2910 +#ifdef DEBUG 1.2911 + SanityCheckAnonymousFlexItems(); 1.2912 +#endif // DEBUG 1.2913 + 1.2914 + // If we've never reordered our children, then we can trust that they're 1.2915 + // already in DOM-order, and we only need to consider their "order" property 1.2916 + // when checking them for sortedness & sorting them. 1.2917 + // 1.2918 + // After we actually sort them, though, we can't trust that they're in DOM 1.2919 + // order anymore. So, from that point on, our sort & sorted-order-checking 1.2920 + // operations need to use a fancier LEQ function that also takes DOM order 1.2921 + // into account, so that we can honor the spec's requirement that frames w/ 1.2922 + // equal "order" values are laid out in DOM order. 1.2923 + 1.2924 + if (!HasAnyStateBits(NS_STATE_FLEX_CHILDREN_REORDERED)) { 1.2925 + if (SortChildrenIfNeeded<IsOrderLEQ>()) { 1.2926 + AddStateBits(NS_STATE_FLEX_CHILDREN_REORDERED); 1.2927 + } 1.2928 + } else { 1.2929 + SortChildrenIfNeeded<IsOrderLEQWithDOMFallback>(); 1.2930 + } 1.2931 + 1.2932 + const FlexboxAxisTracker axisTracker(this); 1.2933 + 1.2934 + // If we're being fragmented into a constrained height, subtract off 1.2935 + // borderpadding-top from it, to get the available height for our 1.2936 + // content box. (Don't subtract if we're skipping top border/padding, 1.2937 + // though.) 1.2938 + nscoord availableHeightForContent = aReflowState.AvailableHeight(); 1.2939 + if (availableHeightForContent != NS_UNCONSTRAINEDSIZE && 1.2940 + !(GetSkipSides() & (1 << NS_SIDE_TOP))) { 1.2941 + availableHeightForContent -= aReflowState.ComputedPhysicalBorderPadding().top; 1.2942 + // (Don't let that push availableHeightForContent below zero, though): 1.2943 + availableHeightForContent = std::max(availableHeightForContent, 0); 1.2944 + } 1.2945 + 1.2946 + nscoord contentBoxMainSize = GetMainSizeFromReflowState(aReflowState, 1.2947 + axisTracker); 1.2948 + 1.2949 + nsAutoTArray<StrutInfo, 1> struts; 1.2950 + nsresult rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus, 1.2951 + contentBoxMainSize, availableHeightForContent, 1.2952 + struts, axisTracker); 1.2953 + 1.2954 + if (NS_SUCCEEDED(rv) && !struts.IsEmpty()) { 1.2955 + // We're restarting flex layout, with new knowledge of collapsed items. 1.2956 + rv = DoFlexLayout(aPresContext, aDesiredSize, aReflowState, aStatus, 1.2957 + contentBoxMainSize, availableHeightForContent, 1.2958 + struts, axisTracker); 1.2959 + } 1.2960 + 1.2961 + return rv; 1.2962 +} 1.2963 + 1.2964 +// RAII class to clean up a list of FlexLines. 1.2965 +// Specifically, this removes each line from the list, deletes all the 1.2966 +// FlexItems in its list, and deletes the FlexLine. 1.2967 +class MOZ_STACK_CLASS AutoFlexLineListClearer 1.2968 +{ 1.2969 +public: 1.2970 + AutoFlexLineListClearer(LinkedList<FlexLine>& aLines 1.2971 + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) 1.2972 + : mLines(aLines) 1.2973 + { 1.2974 + MOZ_GUARD_OBJECT_NOTIFIER_INIT; 1.2975 + } 1.2976 + 1.2977 + ~AutoFlexLineListClearer() 1.2978 + { 1.2979 + while (FlexLine* line = mLines.popFirst()) { 1.2980 + while (FlexItem* item = line->mItems.popFirst()) { 1.2981 + delete item; 1.2982 + } 1.2983 + delete line; 1.2984 + } 1.2985 + } 1.2986 + 1.2987 +private: 1.2988 + LinkedList<FlexLine>& mLines; 1.2989 + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER 1.2990 +}; 1.2991 + 1.2992 +nsresult 1.2993 +nsFlexContainerFrame::DoFlexLayout(nsPresContext* aPresContext, 1.2994 + nsHTMLReflowMetrics& aDesiredSize, 1.2995 + const nsHTMLReflowState& aReflowState, 1.2996 + nsReflowStatus& aStatus, 1.2997 + nscoord aContentBoxMainSize, 1.2998 + nscoord aAvailableHeightForContent, 1.2999 + nsTArray<StrutInfo>& aStruts, 1.3000 + const FlexboxAxisTracker& aAxisTracker) 1.3001 +{ 1.3002 + aStatus = NS_FRAME_COMPLETE; 1.3003 + 1.3004 + LinkedList<FlexLine> lines; 1.3005 + AutoFlexLineListClearer cleanupLines(lines); 1.3006 + 1.3007 + nsresult rv = GenerateFlexLines(aPresContext, aReflowState, 1.3008 + aContentBoxMainSize, 1.3009 + aAvailableHeightForContent, 1.3010 + aStruts, aAxisTracker, lines); 1.3011 + NS_ENSURE_SUCCESS(rv, rv); 1.3012 + 1.3013 + aContentBoxMainSize = 1.3014 + ClampFlexContainerMainSize(aReflowState, aAxisTracker, 1.3015 + aContentBoxMainSize, aAvailableHeightForContent, 1.3016 + lines.getFirst(), aStatus); 1.3017 + 1.3018 + for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) { 1.3019 + line->ResolveFlexibleLengths(aContentBoxMainSize); 1.3020 + } 1.3021 + 1.3022 + // Cross Size Determination - Flexbox spec section 9.4 1.3023 + // =================================================== 1.3024 + // Calculate the hypothetical cross size of each item: 1.3025 + nscoord sumLineCrossSizes = 0; 1.3026 + for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) { 1.3027 + for (FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) { 1.3028 + // (If the item's already been stretched, or it's a strut, then it 1.3029 + // already knows its cross size. Don't bother trying to recalculate it.) 1.3030 + if (!item->IsStretched() && !item->IsStrut()) { 1.3031 + nsHTMLReflowState childReflowState(aPresContext, aReflowState, 1.3032 + item->Frame(), 1.3033 + nsSize(aReflowState.ComputedWidth(), 1.3034 + NS_UNCONSTRAINEDSIZE)); 1.3035 + // Override computed main-size 1.3036 + if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { 1.3037 + childReflowState.SetComputedWidth(item->GetMainSize()); 1.3038 + } else { 1.3039 + childReflowState.SetComputedHeight(item->GetMainSize()); 1.3040 + } 1.3041 + 1.3042 + nsresult rv = SizeItemInCrossAxis(aPresContext, aAxisTracker, 1.3043 + childReflowState, *item); 1.3044 + NS_ENSURE_SUCCESS(rv, rv); 1.3045 + } 1.3046 + } 1.3047 + // Now that we've finished with this line's items, size the line itself: 1.3048 + line->ComputeCrossSizeAndBaseline(aAxisTracker); 1.3049 + sumLineCrossSizes += line->GetLineCrossSize(); 1.3050 + } 1.3051 + 1.3052 + bool isCrossSizeDefinite; 1.3053 + const nscoord contentBoxCrossSize = 1.3054 + ComputeCrossSize(aReflowState, aAxisTracker, sumLineCrossSizes, 1.3055 + aAvailableHeightForContent, &isCrossSizeDefinite, aStatus); 1.3056 + 1.3057 + // Set up state for cross-axis alignment, at a high level (outside the 1.3058 + // scope of a particular flex line) 1.3059 + CrossAxisPositionTracker 1.3060 + crossAxisPosnTracker(lines.getFirst(), 1.3061 + aReflowState.mStylePosition->mAlignContent, 1.3062 + contentBoxCrossSize, isCrossSizeDefinite, 1.3063 + aAxisTracker); 1.3064 + 1.3065 + // Now that we know the cross size of each line (including 1.3066 + // "align-content:stretch" adjustments, from the CrossAxisPositionTracker 1.3067 + // constructor), we can create struts for any flex items with 1.3068 + // "visibility: collapse" (and restart flex layout). 1.3069 + if (aStruts.IsEmpty()) { // (Don't make struts if we already did) 1.3070 + BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts); 1.3071 + if (!aStruts.IsEmpty()) { 1.3072 + // Restart flex layout, using our struts. 1.3073 + return NS_OK; 1.3074 + } 1.3075 + } 1.3076 + 1.3077 + // If the container should derive its baseline from the first FlexLine, 1.3078 + // do that here (while crossAxisPosnTracker is conveniently pointing 1.3079 + // at the cross-start edge of that line, which the line's baseline offset is 1.3080 + // measured from): 1.3081 + nscoord flexContainerAscent; 1.3082 + if (!aAxisTracker.AreAxesInternallyReversed()) { 1.3083 + nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset(); 1.3084 + if (firstLineBaselineOffset == nscoord_MIN) { 1.3085 + // No baseline-aligned items in line. Use sentinel value to prompt us to 1.3086 + // get baseline from the first FlexItem after we've reflowed it. 1.3087 + flexContainerAscent = nscoord_MIN; 1.3088 + } else { 1.3089 + flexContainerAscent = 1.3090 + ComputePhysicalAscentFromLogicalAscent( 1.3091 + crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset, 1.3092 + contentBoxCrossSize, aReflowState, aAxisTracker); 1.3093 + } 1.3094 + } 1.3095 + 1.3096 + for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) { 1.3097 + 1.3098 + // Main-Axis Alignment - Flexbox spec section 9.5 1.3099 + // ============================================== 1.3100 + line->PositionItemsInMainAxis(aReflowState.mStylePosition->mJustifyContent, 1.3101 + aContentBoxMainSize, 1.3102 + aAxisTracker); 1.3103 + 1.3104 + // Cross-Axis Alignment - Flexbox spec section 9.6 1.3105 + // =============================================== 1.3106 + line->PositionItemsInCrossAxis(crossAxisPosnTracker.GetPosition(), 1.3107 + aAxisTracker); 1.3108 + crossAxisPosnTracker.TraverseLine(*line); 1.3109 + crossAxisPosnTracker.TraversePackingSpace(); 1.3110 + } 1.3111 + 1.3112 + // If the container should derive its baseline from the last FlexLine, 1.3113 + // do that here (while crossAxisPosnTracker is conveniently pointing 1.3114 + // at the cross-end edge of that line, which the line's baseline offset is 1.3115 + // measured from): 1.3116 + if (aAxisTracker.AreAxesInternallyReversed()) { 1.3117 + nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset(); 1.3118 + if (lastLineBaselineOffset == nscoord_MIN) { 1.3119 + // No baseline-aligned items in line. Use sentinel value to prompt us to 1.3120 + // get baseline from the last FlexItem after we've reflowed it. 1.3121 + flexContainerAscent = nscoord_MIN; 1.3122 + } else { 1.3123 + flexContainerAscent = 1.3124 + ComputePhysicalAscentFromLogicalAscent( 1.3125 + crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset, 1.3126 + contentBoxCrossSize, aReflowState, aAxisTracker); 1.3127 + } 1.3128 + } 1.3129 + 1.3130 + // Before giving each child a final reflow, calculate the origin of the 1.3131 + // flex container's content box (with respect to its border-box), so that 1.3132 + // we can compute our flex item's final positions. 1.3133 + nsMargin containerBorderPadding(aReflowState.ComputedPhysicalBorderPadding()); 1.3134 + ApplySkipSides(containerBorderPadding, &aReflowState); 1.3135 + const nsPoint containerContentBoxOrigin(containerBorderPadding.left, 1.3136 + containerBorderPadding.top); 1.3137 + 1.3138 + // FINAL REFLOW: Give each child frame another chance to reflow, now that 1.3139 + // we know its final size and position. 1.3140 + for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) { 1.3141 + for (const FlexItem* item = line->GetFirstItem(); item; 1.3142 + item = item->getNext()) { 1.3143 + nsPoint physicalPosn = aAxisTracker.PhysicalPointFromLogicalPoint( 1.3144 + item->GetMainPosition(), 1.3145 + item->GetCrossPosition(), 1.3146 + aContentBoxMainSize, 1.3147 + contentBoxCrossSize); 1.3148 + // Adjust physicalPosn to be relative to the container's border-box 1.3149 + // (i.e. its frame rect), instead of the container's content-box: 1.3150 + physicalPosn += containerContentBoxOrigin; 1.3151 + 1.3152 + nsHTMLReflowState childReflowState(aPresContext, aReflowState, 1.3153 + item->Frame(), 1.3154 + nsSize(aReflowState.ComputedWidth(), 1.3155 + NS_UNCONSTRAINEDSIZE)); 1.3156 + 1.3157 + // Keep track of whether we've overriden the child's computed height 1.3158 + // and/or width, so we can set its resize flags accordingly. 1.3159 + bool didOverrideComputedWidth = false; 1.3160 + bool didOverrideComputedHeight = false; 1.3161 + 1.3162 + // Override computed main-size 1.3163 + if (IsAxisHorizontal(aAxisTracker.GetMainAxis())) { 1.3164 + childReflowState.SetComputedWidth(item->GetMainSize()); 1.3165 + didOverrideComputedWidth = true; 1.3166 + } else { 1.3167 + childReflowState.SetComputedHeight(item->GetMainSize()); 1.3168 + didOverrideComputedHeight = true; 1.3169 + } 1.3170 + 1.3171 + // Override reflow state's computed cross-size, for stretched items. 1.3172 + if (item->IsStretched()) { 1.3173 + MOZ_ASSERT(item->GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH, 1.3174 + "stretched item w/o 'align-self: stretch'?"); 1.3175 + if (IsAxisHorizontal(aAxisTracker.GetCrossAxis())) { 1.3176 + childReflowState.SetComputedWidth(item->GetCrossSize()); 1.3177 + didOverrideComputedWidth = true; 1.3178 + } else { 1.3179 + // If this item's height is stretched, it's a relative height. 1.3180 + item->Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); 1.3181 + childReflowState.SetComputedHeight(item->GetCrossSize()); 1.3182 + didOverrideComputedHeight = true; 1.3183 + } 1.3184 + } 1.3185 + 1.3186 + // XXXdholbert Might need to actually set the correct margins in the 1.3187 + // reflow state at some point, so that they can be saved on the frame for 1.3188 + // UsedMarginProperty(). Maybe doesn't matter though...? 1.3189 + 1.3190 + // If we're overriding the computed width or height, *and* we had an 1.3191 + // earlier "measuring" reflow, then this upcoming reflow needs to be 1.3192 + // treated as a resize. 1.3193 + if (item->HadMeasuringReflow()) { 1.3194 + if (didOverrideComputedWidth) { 1.3195 + // (This is somewhat redundant, since the reflow state already 1.3196 + // sets mHResize whenever our computed width has changed since the 1.3197 + // previous reflow. Still, it's nice for symmetry, and it may become 1.3198 + // necessary once we support orthogonal flows.) 1.3199 + childReflowState.mFlags.mHResize = true; 1.3200 + } 1.3201 + if (didOverrideComputedHeight) { 1.3202 + childReflowState.mFlags.mVResize = true; 1.3203 + } 1.3204 + } 1.3205 + // NOTE: Be very careful about doing anything else with childReflowState 1.3206 + // after this point, because some of its methods (e.g. SetComputedWidth) 1.3207 + // internally call InitResizeFlags and stomp on mVResize & mHResize. 1.3208 + 1.3209 + nsHTMLReflowMetrics childDesiredSize(childReflowState); 1.3210 + nsReflowStatus childReflowStatus; 1.3211 + nsresult rv = ReflowChild(item->Frame(), aPresContext, 1.3212 + childDesiredSize, childReflowState, 1.3213 + physicalPosn.x, physicalPosn.y, 1.3214 + 0, childReflowStatus); 1.3215 + NS_ENSURE_SUCCESS(rv, rv); 1.3216 + 1.3217 + // XXXdholbert Once we do pagination / splitting, we'll need to actually 1.3218 + // handle incomplete childReflowStatuses. But for now, we give our kids 1.3219 + // unconstrained available height, which means they should always 1.3220 + // complete. 1.3221 + MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), 1.3222 + "We gave flex item unconstrained available height, so it " 1.3223 + "should be complete"); 1.3224 + 1.3225 + childReflowState.ApplyRelativePositioning(&physicalPosn); 1.3226 + 1.3227 + rv = FinishReflowChild(item->Frame(), aPresContext, 1.3228 + childDesiredSize, &childReflowState, 1.3229 + physicalPosn.x, physicalPosn.y, 0); 1.3230 + NS_ENSURE_SUCCESS(rv, rv); 1.3231 + 1.3232 + // If this is our first child and we haven't established a baseline for 1.3233 + // the container yet (i.e. if we don't have 'align-self: baseline' on any 1.3234 + // children), then use this child's baseline as the container's baseline. 1.3235 + if (item->Frame() == mFrames.FirstChild() && 1.3236 + flexContainerAscent == nscoord_MIN) { 1.3237 + ResolveReflowedChildAscent(item->Frame(), childDesiredSize); 1.3238 + 1.3239 + // (We use GetNormalPosition() instead of physicalPosn because we don't 1.3240 + // want relative positioning on the child to affect the baseline that we 1.3241 + // read from it). 1.3242 + flexContainerAscent = item->Frame()->GetNormalPosition().y + 1.3243 + childDesiredSize.TopAscent(); 1.3244 + } 1.3245 + } 1.3246 + } 1.3247 + 1.3248 + nsSize desiredContentBoxSize = 1.3249 + aAxisTracker.PhysicalSizeFromLogicalSizes(aContentBoxMainSize, 1.3250 + contentBoxCrossSize); 1.3251 + 1.3252 + aDesiredSize.Width() = desiredContentBoxSize.width + 1.3253 + containerBorderPadding.LeftRight(); 1.3254 + // Does *NOT* include bottom border/padding yet (we add that a bit lower down) 1.3255 + aDesiredSize.Height() = desiredContentBoxSize.height + 1.3256 + containerBorderPadding.top; 1.3257 + 1.3258 + if (flexContainerAscent == nscoord_MIN) { 1.3259 + // Still don't have our baseline set -- this happens if we have no 1.3260 + // children (or if our children are huge enough that they have nscoord_MIN 1.3261 + // as their baseline... in which case, we'll use the wrong baseline, but no 1.3262 + // big deal) 1.3263 + NS_WARN_IF_FALSE(lines.getFirst()->IsEmpty(), 1.3264 + "Have flex items but didn't get an ascent - that's odd " 1.3265 + "(or there are just gigantic sizes involved)"); 1.3266 + // Per spec, just use the bottom of content-box. 1.3267 + flexContainerAscent = aDesiredSize.Height(); 1.3268 + } 1.3269 + aDesiredSize.SetTopAscent(flexContainerAscent); 1.3270 + 1.3271 + // Now: If we're complete, add bottom border/padding to desired height 1.3272 + // (unless that pushes us over available height, in which case we become 1.3273 + // incomplete (unless we already weren't asking for any height, in which case 1.3274 + // we stay complete to avoid looping forever)). 1.3275 + // NOTE: If we're auto-height, we allow our bottom border/padding to push us 1.3276 + // over the available height without requesting a continuation, for 1.3277 + // consistency with the behavior of "display:block" elements. 1.3278 + if (NS_FRAME_IS_COMPLETE(aStatus)) { 1.3279 + // NOTE: We can't use containerBorderPadding.bottom for this, because if 1.3280 + // we're auto-height, ApplySkipSides will have zeroed it (because it 1.3281 + // assumed we might get a continuation). We have the correct value in 1.3282 + // aReflowState.ComputedPhyiscalBorderPadding().bottom, though, so we use that. 1.3283 + nscoord desiredHeightWithBottomBP = 1.3284 + aDesiredSize.Height() + aReflowState.ComputedPhysicalBorderPadding().bottom; 1.3285 + 1.3286 + if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE || 1.3287 + aDesiredSize.Height() == 0 || 1.3288 + desiredHeightWithBottomBP <= aReflowState.AvailableHeight() || 1.3289 + aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { 1.3290 + // Update desired height to include bottom border/padding 1.3291 + aDesiredSize.Height() = desiredHeightWithBottomBP; 1.3292 + } else { 1.3293 + // We couldn't fit bottom border/padding, so we'll need a continuation. 1.3294 + NS_FRAME_SET_INCOMPLETE(aStatus); 1.3295 + } 1.3296 + } 1.3297 + 1.3298 + // Overflow area = union(my overflow area, kids' overflow areas) 1.3299 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.3300 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.3301 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, e.get()); 1.3302 + } 1.3303 + 1.3304 + FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, 1.3305 + aReflowState, aStatus); 1.3306 + 1.3307 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize) 1.3308 + return NS_OK; 1.3309 +} 1.3310 + 1.3311 +/* virtual */ nscoord 1.3312 +nsFlexContainerFrame::GetMinWidth(nsRenderingContext* aRenderingContext) 1.3313 +{ 1.3314 + nscoord minWidth = 0; 1.3315 + DISPLAY_MIN_WIDTH(this, minWidth); 1.3316 + 1.3317 + FlexboxAxisTracker axisTracker(this); 1.3318 + 1.3319 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.3320 + nscoord childMinWidth = 1.3321 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(), 1.3322 + nsLayoutUtils::MIN_WIDTH); 1.3323 + // For a horizontal single-line flex container, the intrinsic min width is 1.3324 + // the sum of its items' min widths. 1.3325 + // For a vertical flex container, or for a multi-line horizontal flex 1.3326 + // container, the intrinsic min width is the max of its items' min widths. 1.3327 + if (IsAxisHorizontal(axisTracker.GetMainAxis()) && 1.3328 + NS_STYLE_FLEX_WRAP_NOWRAP == StylePosition()->mFlexWrap) { 1.3329 + minWidth += childMinWidth; 1.3330 + } else { 1.3331 + minWidth = std::max(minWidth, childMinWidth); 1.3332 + } 1.3333 + } 1.3334 + return minWidth; 1.3335 +} 1.3336 + 1.3337 +/* virtual */ nscoord 1.3338 +nsFlexContainerFrame::GetPrefWidth(nsRenderingContext* aRenderingContext) 1.3339 +{ 1.3340 + nscoord prefWidth = 0; 1.3341 + DISPLAY_PREF_WIDTH(this, prefWidth); 1.3342 + 1.3343 + // XXXdholbert Optimization: We could cache our intrinsic widths like 1.3344 + // nsBlockFrame does (and return it early from this function if it's set). 1.3345 + // Whenever anything happens that might change it, set it to 1.3346 + // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicWidthsDirty 1.3347 + // does) 1.3348 + FlexboxAxisTracker axisTracker(this); 1.3349 + 1.3350 + for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) { 1.3351 + nscoord childPrefWidth = 1.3352 + nsLayoutUtils::IntrinsicForContainer(aRenderingContext, e.get(), 1.3353 + nsLayoutUtils::PREF_WIDTH); 1.3354 + if (IsAxisHorizontal(axisTracker.GetMainAxis())) { 1.3355 + prefWidth += childPrefWidth; 1.3356 + } else { 1.3357 + prefWidth = std::max(prefWidth, childPrefWidth); 1.3358 + } 1.3359 + } 1.3360 + return prefWidth; 1.3361 +}