--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -451,27 +451,32 @@ public:
nscoord GetCrossMinSize() const { return mCrossMinSize; }
nscoord GetCrossMaxSize() const { return mCrossMaxSize; }
// Note: These return the cross-axis position and size of our *content box*.
nscoord GetCrossSize() const { return mCrossSize; }
nscoord GetCrossPosition() const { return mCrossPosn; }
- nscoord ResolvedAscent() const {
+ nscoord ResolvedAscent(bool aUseFirstBaseline) const {
if (mAscent == ReflowOutput::ASK_FOR_BASELINE) {
// XXXdholbert We should probably be using the *container's* writing-mode
// here, instead of the item's -- though it doesn't much matter right
// now, because all of the baseline-handling code here essentially
// assumes that the container & items have the same writing-mode. This
// will matter more (& can be expanded/tested) once we officially support
// logical directions & vertical writing-modes in flexbox, in bug 1079155
// or a dependency.
- // Use GetFirstLineBaseline(), or just GetBaseline() if that fails.
- if (!nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent)) {
+ // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
+ // or just GetLogicalBaseline() if that fails.
+ bool found = aUseFirstBaseline ?
+ nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
+ nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
+
+ if (!found) {
mAscent = mFrame->GetLogicalBaseline(mWM);
}
}
return mAscent;
}
// Convenience methods to compute the main & cross size of our *margin-box*.
// The caller is responsible for telling us the right axis, so that we can
@@ -487,17 +492,18 @@ public:
}
// Returns the distance between this FlexItem's baseline and the cross-start
// edge of its margin-box. Used in baseline alignment.
// (This function needs to be told which edge we're measuring the baseline
// from, so that it can look up the appropriate components from mMargin.)
nscoord GetBaselineOffsetFromOuterCrossEdge(
AxisEdgeType aEdge,
- const FlexboxAxisTracker& aAxisTracker) const;
+ const FlexboxAxisTracker& aAxisTracker,
+ bool aUseFirstLineBaseline) const;
float GetShareOfWeightSoFar() const { return mShareOfWeightSoFar; }
bool IsFrozen() const { return mIsFrozen; }
bool HadMinViolation() const { return mHadMinViolation; }
bool HadMaxViolation() const { return mHadMaxViolation; }
@@ -821,17 +827,18 @@ class nsFlexContainerFrame::FlexLine : p
{
public:
FlexLine()
: mNumItems(0),
mNumFrozenItems(0),
mTotalInnerHypotheticalMainSize(0),
mTotalOuterHypotheticalMainSize(0),
mLineCrossSize(0),
- mBaselineOffset(nscoord_MIN)
+ mFirstBaselineOffset(nscoord_MIN),
+ mLastBaselineOffset(nscoord_MIN)
{}
// Returns the sum of our FlexItems' outer hypothetical main sizes.
// ("outer" = margin-box, and "hypothetical" = before flexing)
nscoord GetTotalOuterHypotheticalMainSize() const {
return mTotalOuterHypotheticalMainSize;
}
@@ -920,18 +927,32 @@ public:
* Returns the offset within this line where any baseline-aligned FlexItems
* should place their baseline. Usually, this represents a distance from the
* line's cross-start edge, but if we're internally reversing the axes (see
* AreAxesInternallyReversed()), this instead represents the distance from
* its cross-end edge.
*
* If there are no baseline-aligned FlexItems, returns nscoord_MIN.
*/
- nscoord GetBaselineOffset() const {
- return mBaselineOffset;
+ nscoord GetFirstBaselineOffset() const {
+ return mFirstBaselineOffset;
+ }
+
+ /**
+ * Returns the offset within this line where any last baseline-aligned
+ * FlexItems should place their baseline. Opposite the case of the first
+ * baseline offset, this represents a distance from the line's cross-end
+ * edge (since last baseline-aligned items are flush to the cross-end edge).
+ * If we're internally reversing the axes, this instead represents the
+ * distance from the line's cross-start edge.
+ *
+ * If there are no last baseline-aligned FlexItems, returns nscoord_MIN.
+ */
+ nscoord GetLastBaselineOffset() const {
+ return mLastBaselineOffset;
}
// Runs the "Resolving Flexible Lengths" algorithm from section 9.7 of the
// CSS flexbox spec to distribute aFlexContainerMainSize among our flex items.
void ResolveFlexibleLengths(nscoord aFlexContainerMainSize);
void PositionItemsInMainAxis(uint8_t aJustifyContent,
nscoord aContentBoxMainSize,
@@ -960,17 +981,18 @@ private:
// Number of *frozen* FlexItems in this line, based on FlexItem::IsFrozen().
// Mostly used for optimization purposes, e.g. to bail out early from loops
// when we can tell they have nothing left to do.
uint32_t mNumFrozenItems;
nscoord mTotalInnerHypotheticalMainSize;
nscoord mTotalOuterHypotheticalMainSize;
nscoord mLineCrossSize;
- nscoord mBaselineOffset;
+ nscoord mFirstBaselineOffset;
+ nscoord mLastBaselineOffset;
};
// Information about a strut left behind by a FlexItem that's been collapsed
// using "visibility:collapse".
struct nsFlexContainerFrame::StrutInfo {
StrutInfo(uint32_t aItemIdx, nscoord aStrutCrossSize)
: mItemIdx(aItemIdx),
mStrutCrossSize(aStrutCrossSize)
@@ -1891,20 +1913,24 @@ FlexItem::FlexItem(ReflowInput& aFlexIte
}
#endif // DEBUG
// Map align-self 'baseline' value to 'start' when baseline alignment
// is not possible because the FlexItem's writing mode is orthogonal to
// the main axis of the container. If that's the case, we just directly
// convert our align-self value here, so that we don't have to handle this
// with special cases elsewhere.
+ // We are treating this case as one where it is appropriate to use the
+ // fallback values defined at https://www.w3.org/TR/css-align-3/#baseline
if (aAxisTracker.IsRowOriented() ==
aAxisTracker.GetWritingMode().IsOrthogonalTo(mWM)) {
if (mAlignSelf == NS_STYLE_ALIGN_BASELINE) {
mAlignSelf = NS_STYLE_ALIGN_FLEX_START;
+ } else if (mAlignSelf == NS_STYLE_ALIGN_LAST_BASELINE) {
+ mAlignSelf = NS_STYLE_ALIGN_FLEX_END;
}
}
}
// Simplified constructor for creating a special "strut" FlexItem, for a child
// with visibility:collapse. The strut has 0 main-size, and it only exists to
// impose a minimum cross size on whichever FlexLine it ends up in.
FlexItem::FlexItem(nsIFrame* aChildFrame, nscoord aCrossSize,
@@ -1968,31 +1994,33 @@ FlexItem::CheckForMinSizeAuto(const Refl
mNeedsMinSizeAutoResolution = (minSize.GetUnit() == eStyleUnit_Auto &&
overflowVal == NS_STYLE_OVERFLOW_VISIBLE);
}
nscoord
FlexItem::GetBaselineOffsetFromOuterCrossEdge(
AxisEdgeType aEdge,
- const FlexboxAxisTracker& aAxisTracker) const
+ const FlexboxAxisTracker& aAxisTracker,
+ bool aUseFirstLineBaseline) const
{
// NOTE: Currently, 'mAscent' (taken from reflow) is an inherently vertical
// measurement -- it's the distance from the border-top edge of this FlexItem
// to its baseline. So, we can really only do baseline alignment when the
// cross axis is vertical. (The FlexItem constructor enforces this when
// resolving the item's "mAlignSelf" value).
MOZ_ASSERT(!aAxisTracker.IsCrossAxisHorizontal(),
"Only expecting to be doing baseline computations when the "
"cross axis is vertical");
AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
mozilla::Side sideToMeasureFrom = kAxisOrientationToSidesMap[crossAxis][aEdge];
- nscoord marginTopToBaseline = ResolvedAscent() + mMargin.top;
+ nscoord marginTopToBaseline = ResolvedAscent(aUseFirstLineBaseline) +
+ mMargin.top;
if (sideToMeasureFrom == eSideTop) {
// Measuring from top (normal case): the distance from the margin-box top
// edge to the baseline is just ascent + margin-top.
return marginTopToBaseline;
}
MOZ_ASSERT(sideToMeasureFrom == eSideBottom,
@@ -3081,25 +3109,29 @@ SingleLineCrossAxisPositionTracker::
: PositionTracker(aAxisTracker.GetCrossAxis(),
aAxisTracker.IsCrossAxisReversed())
{
}
void
FlexLine::ComputeCrossSizeAndBaseline(const FlexboxAxisTracker& aAxisTracker)
{
- nscoord crossStartToFurthestBaseline = nscoord_MIN;
- nscoord crossEndToFurthestBaseline = nscoord_MIN;
+ nscoord crossStartToFurthestFirstBaseline = nscoord_MIN;
+ nscoord crossEndToFurthestFirstBaseline = nscoord_MIN;
+ nscoord crossStartToFurthestLastBaseline = nscoord_MIN;
+ nscoord crossEndToFurthestLastBaseline = nscoord_MIN;
nscoord largestOuterCrossSize = 0;
for (const FlexItem* item = mItems.getFirst(); item; item = item->getNext()) {
nscoord curOuterCrossSize =
item->GetOuterCrossSize(aAxisTracker.GetCrossAxis());
- if (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE &&
+ if ((item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE ||
+ item->GetAlignSelf() == NS_STYLE_ALIGN_LAST_BASELINE) &&
item->GetNumAutoMarginsInAxis(aAxisTracker.GetCrossAxis()) == 0) {
+ const bool useFirst = (item->GetAlignSelf() == NS_STYLE_ALIGN_BASELINE);
// FIXME: Once we support "writing-mode", we'll have to do baseline
// alignment in vertical flex containers here (w/ horizontal cross-axes).
// Find distance from our item's cross-start and cross-end margin-box
// edges to its baseline.
//
// Here's a diagram of a flex-item that we might be doing this on.
// "mmm" is the margin-box, "bbb" is the border-box. The bottom of
@@ -3121,46 +3153,61 @@ FlexLine::ComputeCrossSizeAndBaseline(co
//
// We already have the curOuterCrossSize, margin-start, and the ascent.
// * We can get crossStartToBaseline by adding margin-start + ascent.
// * If we subtract that from the curOuterCrossSize, we get
// crossEndToBaseline.
nscoord crossStartToBaseline =
item->GetBaselineOffsetFromOuterCrossEdge(eAxisEdge_Start,
- aAxisTracker);
+ aAxisTracker,
+ useFirst);
nscoord crossEndToBaseline = curOuterCrossSize - crossStartToBaseline;
// Now, update our "largest" values for these (across all the flex items
// in this flex line), so we can use them in computing the line's cross
// size below:
- crossStartToFurthestBaseline = std::max(crossStartToFurthestBaseline,
- crossStartToBaseline);
- crossEndToFurthestBaseline = std::max(crossEndToFurthestBaseline,
- crossEndToBaseline);
+ if (useFirst) {
+ crossStartToFurthestFirstBaseline =
+ std::max(crossStartToFurthestFirstBaseline, crossStartToBaseline);
+ crossEndToFurthestFirstBaseline =
+ std::max(crossEndToFurthestFirstBaseline, crossEndToBaseline);
+ } else {
+ crossStartToFurthestLastBaseline =
+ std::max(crossStartToFurthestLastBaseline, crossStartToBaseline);
+ crossEndToFurthestLastBaseline =
+ std::max(crossEndToFurthestLastBaseline, crossEndToBaseline);
+ }
} else {
largestOuterCrossSize = std::max(largestOuterCrossSize, curOuterCrossSize);
}
}
// The line's baseline offset is the distance from the line's edge (start or
// end, depending on whether we've flipped the axes) to the furthest
// item-baseline. The item(s) with that baseline will be exactly aligned with
// the line's edge.
- mBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
- crossEndToFurthestBaseline : crossStartToFurthestBaseline;
+ mFirstBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
+ crossEndToFurthestFirstBaseline : crossStartToFurthestFirstBaseline;
+
+ mLastBaselineOffset = aAxisTracker.AreAxesInternallyReversed() ?
+ crossStartToFurthestLastBaseline : crossEndToFurthestLastBaseline;
// The line's cross-size is the larger of:
// (a) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
// all baseline-aligned items with no cross-axis auto margins...
// and
- // (b) largest cross-size of all other children.
- mLineCrossSize = std::max(crossStartToFurthestBaseline +
- crossEndToFurthestBaseline,
- largestOuterCrossSize);
+ // (b) [largest cross-start-to-baseline + largest baseline-to-cross-end] of
+ // all last baseline-aligned items with no cross-axis auto margins...
+ // and
+ // (c) largest cross-size of all other children.
+ mLineCrossSize = std::max(
+ std::max(crossStartToFurthestFirstBaseline + crossEndToFurthestFirstBaseline,
+ crossStartToFurthestLastBaseline + crossEndToFurthestLastBaseline),
+ largestOuterCrossSize);
}
void
FlexItem::ResolveStretchedCrossSize(nscoord aLineCrossSize,
const FlexboxAxisTracker& aAxisTracker)
{
AxisOrientationType crossAxis = aAxisTracker.GetCrossAxis();
// We stretch IFF we are align-self:stretch, have no auto margins in
@@ -3280,52 +3327,57 @@ SingleLineCrossAxisPositionTracker::
} else if (alignSelf == NS_STYLE_ALIGN_FLEX_END) {
alignSelf = NS_STYLE_ALIGN_FLEX_START;
}
}
switch (alignSelf) {
case NS_STYLE_ALIGN_SELF_START:
case NS_STYLE_ALIGN_SELF_END:
- case NS_STYLE_ALIGN_LAST_BASELINE:
- NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end/last baseline");
+ NS_WARNING("NYI: align-items/align-self:left/right/self-start/self-end");
MOZ_FALLTHROUGH;
case NS_STYLE_ALIGN_FLEX_START:
// No space to skip over -- we're done.
break;
case NS_STYLE_ALIGN_FLEX_END:
mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
break;
case NS_STYLE_ALIGN_CENTER:
// Note: If cross-size is odd, the "after" space will get the extra unit.
mPosition +=
(aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis)) / 2;
break;
- case NS_STYLE_ALIGN_BASELINE: {
+ case NS_STYLE_ALIGN_BASELINE:
+ case NS_STYLE_ALIGN_LAST_BASELINE: {
+ const bool useFirst = (alignSelf == NS_STYLE_ALIGN_BASELINE);
+
// Normally, baseline-aligned items are collectively aligned with the
// line's cross-start edge; however, if our cross axis is (internally)
// reversed, we instead align them with the cross-end edge.
+ // A similar logic holds for last baseline-aligned items, but in reverse.
AxisEdgeType baselineAlignEdge =
- aAxisTracker.AreAxesInternallyReversed() ?
+ aAxisTracker.AreAxesInternallyReversed() == useFirst ?
eAxisEdge_End : eAxisEdge_Start;
nscoord itemBaselineOffset =
aItem.GetBaselineOffsetFromOuterCrossEdge(baselineAlignEdge,
- aAxisTracker);
-
- nscoord lineBaselineOffset = aLine.GetBaselineOffset();
+ aAxisTracker,
+ useFirst);
+
+ nscoord lineBaselineOffset = useFirst ? aLine.GetFirstBaselineOffset()
+ : aLine.GetLastBaselineOffset();
NS_ASSERTION(lineBaselineOffset >= itemBaselineOffset,
"failed at finding largest baseline offset");
// How much do we need to adjust our position (from the line edge),
// to get the item's baseline to hit the line's baseline offset:
nscoord baselineDiff = lineBaselineOffset - itemBaselineOffset;
- if (aAxisTracker.AreAxesInternallyReversed()) {
+ if (aAxisTracker.AreAxesInternallyReversed() == useFirst) {
// Advance to align item w/ line's flex-end edge (as in FLEX_END case):
mPosition += aLine.GetLineCrossSize() - aItem.GetOuterCrossSize(mAxis);
// ...and step *back* by the baseline adjustment:
mPosition -= baselineDiff;
} else {
// mPosition is already at line's flex-start edge.
// From there, we step *forward* by the baseline adjustment:
mPosition += baselineDiff;
@@ -4268,17 +4320,17 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
}
// If the container should derive its baseline from the first FlexLine,
// do that here (while crossAxisPosnTracker is conveniently pointing
// at the cross-start edge of that line, which the line's baseline offset is
// measured from):
nscoord flexContainerAscent;
if (!aAxisTracker.AreAxesInternallyReversed()) {
- nscoord firstLineBaselineOffset = lines.getFirst()->GetBaselineOffset();
+ nscoord firstLineBaselineOffset = lines.getFirst()->GetFirstBaselineOffset();
if (firstLineBaselineOffset == nscoord_MIN) {
// No baseline-aligned items in line. Use sentinel value to prompt us to
// get baseline from the first FlexItem after we've reflowed it.
flexContainerAscent = nscoord_MIN;
} else {
flexContainerAscent =
ComputePhysicalAscentFromFlexRelativeAscent(
crossAxisPosnTracker.GetPosition() + firstLineBaselineOffset,
@@ -4305,17 +4357,17 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
crossAxisPosnTracker.TraversePackingSpace();
}
// If the container should derive its baseline from the last FlexLine,
// do that here (while crossAxisPosnTracker is conveniently pointing
// at the cross-end edge of that line, which the line's baseline offset is
// measured from):
if (aAxisTracker.AreAxesInternallyReversed()) {
- nscoord lastLineBaselineOffset = lines.getLast()->GetBaselineOffset();
+ nscoord lastLineBaselineOffset = lines.getLast()->GetFirstBaselineOffset();
if (lastLineBaselineOffset == nscoord_MIN) {
// No baseline-aligned items in line. Use sentinel value to prompt us to
// get baseline from the last FlexItem after we've reflowed it.
flexContainerAscent = nscoord_MIN;
} else {
flexContainerAscent =
ComputePhysicalAscentFromFlexRelativeAscent(
crossAxisPosnTracker.GetPosition() - lastLineBaselineOffset,
@@ -4404,20 +4456,21 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
}
if (itemNeedsReflow) {
ReflowFlexItem(aPresContext, aAxisTracker, aReflowInput,
*item, framePos, containerSize);
}
// If this is our first item and we haven't established a baseline for
// the container yet (i.e. if we don't have 'align-self: baseline' on any
- // children), then use this child's baseline as the container's baseline.
+ // children), then use this child's first baseline as the container's
+ // baseline.
if (item == firstItem &&
flexContainerAscent == nscoord_MIN) {
- flexContainerAscent = itemNormalBPos + item->ResolvedAscent();
+ flexContainerAscent = itemNormalBPos + item->ResolvedAscent(true);
}
}
}
if (!placeholderKids.IsEmpty()) {
ReflowPlaceholders(aPresContext, aReflowInput,
placeholderKids, containerContentBoxOrigin,
containerSize);