summaryrefslogtreecommitdiffstats
path: root/layout/generic/nsFlexContainerFrame.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'layout/generic/nsFlexContainerFrame.cpp')
-rw-r--r--layout/generic/nsFlexContainerFrame.cpp216
1 files changed, 175 insertions, 41 deletions
diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp
index 66f1e79609..4604106a4a 100644
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -312,7 +312,7 @@ class MOZ_STACK_CLASS nsFlexContainerFrame::FlexboxAxisTracker {
return StyleAlignFlags::START;
}
- MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == eAxisHorizontal,
+ MOZ_ASSERT(wm.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal,
"Vertical column-oriented flex container's main axis should "
"be parallel to physical left <-> right axis!");
// Map 'left' or 'right' to 'start' or 'end', depending on its block flow
@@ -2021,7 +2021,7 @@ const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem(
// CachedFlexItemData is stored in item's writing mode, so we pass
// aChildReflowInput into ReflowOutput's constructor.
ReflowOutput childReflowOutput(aChildReflowInput);
- nsReflowStatus childReflowStatus;
+ nsReflowStatus childStatus;
const ReflowChildFlags flags = ReflowChildFlags::NoMoveFrame;
const WritingMode outerWM = GetWritingMode();
@@ -2032,12 +2032,12 @@ const CachedBAxisMeasurement& nsFlexContainerFrame::MeasureBSizeForFlexItem(
// unimportant.
ReflowChild(aItem.Frame(), PresContext(), childReflowOutput,
aChildReflowInput, outerWM, dummyPosition, dummyContainerSize,
- flags, childReflowStatus);
+ flags, childStatus);
aItem.SetHadMeasuringReflow();
// We always use unconstrained available block-size to measure flex items,
// which means they should always complete.
- MOZ_ASSERT(childReflowStatus.IsComplete(),
+ MOZ_ASSERT(childStatus.IsComplete(),
"We gave flex item unconstrained available block-size, so it "
"should be complete");
@@ -2227,7 +2227,7 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow,
// (We'll resolve them later; until then, we want to treat them as 0-sized.)
#ifdef DEBUG
{
- for (const auto side : AllLogicalSides()) {
+ for (const auto side : LogicalSides::All) {
if (styleMargin->mMargin.Get(mCBWM, side).IsAuto()) {
MOZ_ASSERT(GetMarginComponentForSide(side) == 0,
"Someone else tried to resolve our auto margin");
@@ -2257,7 +2257,7 @@ FlexItem::FlexItem(ReflowInput& aFlexItemReflowInput, float aFlexGrow,
// (Note: this is *not* the "flex-start" side; rather, it's the *logical*
// i.e. WM-relative block-start or inline-start side.)
mozilla::Side containerStartSideInCrossAxis = mCBWM.PhysicalSide(
- MakeLogicalSide(aAxisTracker.CrossAxis(), eLogicalEdgeStart));
+ MakeLogicalSide(aAxisTracker.CrossAxis(), LogicalEdge::Start));
// We already know these two Sides (the item's block-start and the
// container's 'logical start' side for its cross axis) are in the same
@@ -2354,7 +2354,7 @@ nscoord FlexItem::BaselineOffsetFromOuterCrossEdge(
// column-oriented flex container. We need to synthesize the item's baseline
// from its border-box edge.
const bool isMainAxisHorizontal =
- mCBWM.PhysicalAxis(MainAxis()) == mozilla::eAxisHorizontal;
+ mCBWM.PhysicalAxis(MainAxis()) == PhysicalAxis::Horizontal;
// When the main axis is horizontal, the synthesized baseline is the bottom
// edge of the item's border-box. Otherwise, when the main axis is vertical,
@@ -2448,7 +2448,7 @@ void FlexItem::ResolveFlexBaseSizeFromAspectRatio(
uint32_t FlexItem::NumAutoMarginsInAxis(LogicalAxis aAxis) const {
uint32_t numAutoMargins = 0;
const auto& styleMargin = mFrame->StyleMargin()->mMargin;
- for (const auto edge : {eLogicalEdgeStart, eLogicalEdgeEnd}) {
+ for (const auto edge : {LogicalEdge::Start, LogicalEdge::End}) {
const auto side = MakeLogicalSide(aAxis, edge);
if (styleMargin.Get(mCBWM, side).IsAuto()) {
numAutoMargins++;
@@ -2708,12 +2708,12 @@ class MOZ_STACK_CLASS PositionTracker {
inline LogicalSide StartSide() {
return MakeLogicalSide(
- mAxis, mIsAxisReversed ? eLogicalEdgeEnd : eLogicalEdgeStart);
+ mAxis, mIsAxisReversed ? LogicalEdge::End : LogicalEdge::Start);
}
inline LogicalSide EndSide() {
return MakeLogicalSide(
- mAxis, mIsAxisReversed ? eLogicalEdgeStart : eLogicalEdgeEnd);
+ mAxis, mIsAxisReversed ? LogicalEdge::Start : LogicalEdge::End);
}
// Advances our position across the start edge of the given margin, in the
@@ -4111,12 +4111,13 @@ FlexboxAxisTracker::FlexboxAxisTracker(
LogicalSide FlexboxAxisTracker::MainAxisStartSide() const {
return MakeLogicalSide(
- MainAxis(), IsMainAxisReversed() ? eLogicalEdgeEnd : eLogicalEdgeStart);
+ MainAxis(), IsMainAxisReversed() ? LogicalEdge::End : LogicalEdge::Start);
}
LogicalSide FlexboxAxisTracker::CrossAxisStartSide() const {
- return MakeLogicalSide(
- CrossAxis(), IsCrossAxisReversed() ? eLogicalEdgeEnd : eLogicalEdgeStart);
+ return MakeLogicalSide(CrossAxis(), IsCrossAxisReversed()
+ ? LogicalEdge::End
+ : LogicalEdge::Start);
}
void nsFlexContainerFrame::GenerateFlexLines(
@@ -4885,8 +4886,14 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow(
bool anyScrolledContentItem = false;
// Union of normal-positioned margin boxes for all the items.
nsRect itemMarginBoxes;
- // Union of relative-positioned margin boxes for the relpos items only.
- nsRect relPosItemMarginBoxes;
+ // Overflow areas containing the union of relative-positioned and
+ // stick-positioned margin boxes of relpos items.
+ //
+ // Note for sticky-positioned margin boxes, we only union it with the ink
+ // overflow to avoid circular dependencies with the scroll container. (The
+ // scroll position and the scroll container's size impact the sticky position,
+ // so we don't want the sticky position to impact them.)
+ OverflowAreas relPosItemMarginBoxes;
const bool useMozBoxCollapseBehavior =
StyleVisibility()->UseLegacyCollapseBehavior();
for (nsIFrame* f : mFrames) {
@@ -4905,8 +4912,13 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow(
const nsRect marginRect = f->GetMarginRectRelativeToSelf();
itemMarginBoxes =
itemMarginBoxes.Union(marginRect + f->GetNormalPosition());
- relPosItemMarginBoxes =
- relPosItemMarginBoxes.Union(marginRect + f->GetPosition());
+ if (f->IsRelativelyPositioned()) {
+ relPosItemMarginBoxes.UnionAllWith(marginRect + f->GetPosition());
+ } else {
+ MOZ_ASSERT(f->IsStickyPositioned());
+ relPosItemMarginBoxes.UnionWith(
+ OverflowAreas(marginRect + f->GetPosition(), nsRect()));
+ }
} else {
itemMarginBoxes = itemMarginBoxes.Union(f->GetMarginRect());
}
@@ -4915,7 +4927,7 @@ void nsFlexContainerFrame::UnionInFlowChildOverflow(
if (anyScrolledContentItem) {
itemMarginBoxes.Inflate(GetUsedPadding());
aOverflowAreas.UnionAllWith(itemMarginBoxes);
- aOverflowAreas.UnionAllWith(relPosItemMarginBoxes);
+ aOverflowAreas.UnionWith(relPosItemMarginBoxes);
}
}
@@ -5515,14 +5527,19 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
const bool isSingleLine =
StyleFlexWrap::Nowrap == aReflowInput.mStylePosition->mFlexWrap;
-
- // FINAL REFLOW: Give each child frame another chance to reflow, now that
- // we know its final size and position.
const FlexLine& startmostLine = StartmostLine(aFlr.mLines, aAxisTracker);
+ const FlexLine& endmostLine = EndmostLine(aFlr.mLines, aAxisTracker);
const FlexItem* startmostItem =
startmostLine.IsEmpty() ? nullptr
: &startmostLine.StartmostItem(aAxisTracker);
+ const FlexItem* endmostItem =
+ endmostLine.IsEmpty() ? nullptr : &endmostLine.EndmostItem(aAxisTracker);
+
+ bool endmostItemOrLineHasBreakAfter = false;
+ // If true, push all remaining flex items to the container's next-in-flow.
+ bool shouldPushRemainingItems = false;
+ // FINAL REFLOW: Give each child frame another chance to reflow.
const size_t numLines = aFlr.mLines.Length();
for (size_t lineIdx = 0; lineIdx < numLines; ++lineIdx) {
// Iterate flex lines from the startmost to endmost (relative to flex
@@ -5533,6 +5550,11 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
MOZ_ASSERT(lineIdx != 0 || &line == &startmostLine,
"Logic for finding startmost line should be consistent!");
+ // These two variables can be set when we are a row-oriented flex container
+ // during fragmentation.
+ bool lineHasBreakBefore = false;
+ bool lineHasBreakAfter = false;
+
const size_t numItems = line.Items().Length();
for (size_t itemIdx = 0; itemIdx < numItems; ++itemIdx) {
// Iterate flex items from the startmost to endmost (relative to flex
@@ -5631,15 +5653,22 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
// (i.e. its frame rect), instead of the container's content-box:
framePos += containerContentBoxOrigin;
- // Check if we actually need to reflow the item -- if the item's position
- // is below the available space's block-end, push it to our next-in-flow;
- // if it does need a reflow, and we already reflowed it with the right
- // content-box size.
- const bool childBPosExceedAvailableSpaceBEnd =
- availableBSizeForItem != NS_UNCONSTRAINEDSIZE &&
- availableBSizeForItem <= 0;
+ // Check if we can skip reflowing the item because it will be pushed to
+ // our next-in-flow -- i.e. if there was a forced break before it, or its
+ // position is beyond the available space's block-end.
bool itemInPushedItems = false;
- if (childBPosExceedAvailableSpaceBEnd) {
+ if (shouldPushRemainingItems) {
+ FLEX_ITEM_LOG(
+ item.Frame(),
+ "[frag] Item needed to be pushed to container's next-in-flow due "
+ "to a forced break before it");
+ pushedItems.Insert(item.Frame());
+ itemInPushedItems = true;
+ } else if (availableBSizeForItem != NS_UNCONSTRAINEDSIZE &&
+ availableBSizeForItem <= 0) {
+ // The item's position is beyond the available space, so we have to push
+ // it.
+ //
// Note: Even if all of our items are beyond the available space & get
// pushed here, we'll be guaranteed to place at least one of them (and
// make progress) in one of the flex container's *next* fragment. It's
@@ -5662,17 +5691,50 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
availableBSizeForItem)
.ConvertTo(itemWM, flexWM);
- const nsReflowStatus childReflowStatus =
+ const bool isAdjacentWithBStart =
+ framePos.B(flexWM) == containerContentBoxOrigin.B(flexWM);
+ const nsReflowStatus childStatus =
ReflowFlexItem(aAxisTracker, aReflowInput, item, framePos,
- availableSize, aContainerSize);
+ isAdjacentWithBStart, availableSize, aContainerSize);
+
+ if (aReflowInput.IsInFragmentedContext()) {
+ const bool itemHasBreakBefore =
+ item.Frame()->ShouldBreakBefore(aReflowInput.mBreakType) ||
+ childStatus.IsInlineBreakBefore();
+ if (itemHasBreakBefore) {
+ if (aAxisTracker.IsRowOriented()) {
+ lineHasBreakBefore = true;
+ } else if (isSingleLine) {
+ if (&item == startmostItem) {
+ if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) {
+ // If we are first-in-flow and not at top-of-page, early
+ // return here to propagate forced break-before from the
+ // startmost item to the flex container.
+ nsReflowStatus childrenStatus;
+ childrenStatus.SetInlineLineBreakBeforeAndReset();
+ return {0, childrenStatus};
+ }
+ } else {
+ shouldPushRemainingItems = true;
+ }
+ } else {
+ // Bug 1806717: We haven't implemented fragmentation for
+ // multi-line column-oriented flex container, so we just ignore
+ // forced breaks for now.
+ }
+ }
+ }
const bool shouldPushItem = [&]() {
+ if (shouldPushRemainingItems) {
+ return true;
+ }
if (availableBSizeForItem == NS_UNCONSTRAINEDSIZE) {
// If the available block-size is unconstrained, then we're not
// fragmenting and we don't want to push the item.
return false;
}
- if (framePos.B(flexWM) == containerContentBoxOrigin.B(flexWM)) {
+ if (isAdjacentWithBStart) {
// The flex item is adjacent with block-start of the container's
// content-box. Don't push it, or we'll trap in an infinite loop.
return false;
@@ -5691,15 +5753,38 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
FLEX_ITEM_LOG(
item.Frame(),
"[frag] Item needed to be pushed to container's next-in-flow "
- "because its block-size is larger than the available space");
+ "because it encounters a forced break before it, or its "
+ "block-size is larger than the available space");
pushedItems.Insert(item.Frame());
itemInPushedItems = true;
- } else if (childReflowStatus.IsIncomplete()) {
+ } else if (childStatus.IsIncomplete()) {
incompleteItems.Insert(item.Frame());
- } else if (childReflowStatus.IsOverflowIncomplete()) {
+ } else if (childStatus.IsOverflowIncomplete()) {
overflowIncompleteItems.Insert(item.Frame());
}
+
+ if (aReflowInput.IsInFragmentedContext()) {
+ const bool itemHasBreakAfter =
+ item.Frame()->ShouldBreakAfter(aReflowInput.mBreakType) ||
+ childStatus.IsInlineBreakAfter();
+ if (itemHasBreakAfter) {
+ if (aAxisTracker.IsRowOriented()) {
+ lineHasBreakAfter = true;
+ } else if (isSingleLine) {
+ shouldPushRemainingItems = true;
+ if (&item == endmostItem) {
+ endmostItemOrLineHasBreakAfter = true;
+ }
+ } else {
+ // Bug 1806717: We haven't implemented fragmentation for
+ // multi-line column-oriented flex container, so we just ignore
+ // forced breaks for now.
+ }
+ }
+ }
} else {
+ // We already reflowed the item with the right content-box size, so we
+ // can simply move it into place.
MoveFlexItemToFinalPosition(item, framePos, aContainerSize);
}
@@ -5787,6 +5872,37 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
}
}
+ if (aReflowInput.IsInFragmentedContext() && aAxisTracker.IsRowOriented()) {
+ // Propagate forced break values from the flex items to its flex line.
+ if (lineHasBreakBefore) {
+ if (&line == &startmostLine) {
+ if (!GetPrevInFlow() && !aReflowInput.mFlags.mIsTopOfPage) {
+ // If we are first-in-flow and not at top-of-page, early return here
+ // to propagate forced break-before from the startmost line to the
+ // flex container.
+ nsReflowStatus childrenStatus;
+ childrenStatus.SetInlineLineBreakBeforeAndReset();
+ return {0, childrenStatus};
+ }
+ } else {
+ // Current non-startmost line has forced break-before, so push all the
+ // items in this line.
+ for (const FlexItem& item : line.Items()) {
+ pushedItems.Insert(item.Frame());
+ incompleteItems.Remove(item.Frame());
+ overflowIncompleteItems.Remove(item.Frame());
+ }
+ shouldPushRemainingItems = true;
+ }
+ }
+ if (lineHasBreakAfter) {
+ shouldPushRemainingItems = true;
+ if (&line == &endmostLine) {
+ endmostItemOrLineHasBreakAfter = true;
+ }
+ }
+ }
+
// Now we've finished processing all the items in the startmost line.
// Determine the amount by which the startmost line's block-end edge has
// shifted, so we can apply the same shift for the remaining lines.
@@ -5808,6 +5924,8 @@ std::tuple<nscoord, nsReflowStatus> nsFlexContainerFrame::ReflowChildren(
childrenStatus.SetIncomplete();
} else if (!overflowIncompleteItems.IsEmpty()) {
childrenStatus.SetOverflowIncomplete();
+ } else if (endmostItemOrLineHasBreakAfter) {
+ childrenStatus.SetInlineLineBreakAfter();
}
PushIncompleteChildren(pushedItems, incompleteItems, overflowIncompleteItems);
@@ -5952,6 +6070,14 @@ void nsFlexContainerFrame::PopulateReflowOutput(
return;
}
+ // Propagate forced break values from flex items or flex lines.
+ if (aChildrenStatus.IsInlineBreakBefore()) {
+ aStatus.SetInlineLineBreakBeforeAndReset();
+ }
+ if (aChildrenStatus.IsInlineBreakAfter()) {
+ aStatus.SetInlineLineBreakAfter();
+ }
+
// If we haven't established a baseline for the container yet, i.e. if we
// don't have any flex item in the startmost flex line that participates in
// baseline alignment, then use the startmost flex item to derive the
@@ -6067,7 +6193,8 @@ void nsFlexContainerFrame::MoveFlexItemToFinalPosition(
nsReflowStatus nsFlexContainerFrame::ReflowFlexItem(
const FlexboxAxisTracker& aAxisTracker, const ReflowInput& aReflowInput,
const FlexItem& aItem, const LogicalPoint& aFramePos,
- const LogicalSize& aAvailableSize, const nsSize& aContainerSize) {
+ const bool aIsAdjacentWithBStart, const LogicalSize& aAvailableSize,
+ const nsSize& aContainerSize) {
FLEX_ITEM_LOG(aItem.Frame(), "Doing final reflow");
// Returns true if we should use 'auto' in block axis's StyleSizeOverrides to
@@ -6206,6 +6333,13 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem(
aItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
}
+ if (!aIsAdjacentWithBStart) {
+ // mIsTopOfPage bit in childReflowInput is carried over from aReflowInput.
+ // However, if this item's position is not adjacent with the flex
+ // container's content-box block-start edge, we should clear it.
+ childReflowInput.mFlags.mIsTopOfPage = false;
+ }
+
// NOTE: Be very careful about doing anything else with childReflowInput
// after this point, because some of its methods (e.g. SetComputedWidth)
// internally call InitResizeFlags and stomp on mVResize & mHResize.
@@ -6216,11 +6350,11 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem(
// CachedFlexItemData is stored in item's writing mode, so we pass
// aChildReflowInput into ReflowOutput's constructor.
ReflowOutput childReflowOutput(childReflowInput);
- nsReflowStatus childReflowStatus;
+ nsReflowStatus childStatus;
WritingMode outerWM = aReflowInput.GetWritingMode();
ReflowChild(aItem.Frame(), PresContext(), childReflowOutput, childReflowInput,
outerWM, aFramePos, aContainerSize, ReflowChildFlags::Default,
- childReflowStatus);
+ childStatus);
// XXXdholbert Perhaps we should call CheckForInterrupt here; see bug 1495532.
@@ -6240,7 +6374,7 @@ nsReflowStatus nsFlexContainerFrame::ReflowFlexItem(
aItem.Frame()->SetProperty(CachedFlexItemData::Prop(), cached);
}
- return childReflowStatus;
+ return childStatus;
}
void nsFlexContainerFrame::ReflowPlaceholders(
@@ -6260,10 +6394,10 @@ void nsFlexContainerFrame::ReflowPlaceholders(
// No need to set the -webkit-line-clamp related flags when reflowing
// a placeholder.
ReflowOutput childReflowOutput(outerWM);
- nsReflowStatus childReflowStatus;
+ nsReflowStatus childStatus;
ReflowChild(placeholder, PresContext(), childReflowOutput, childReflowInput,
outerWM, aContentBoxOrigin, aContainerSize,
- ReflowChildFlags::Default, childReflowStatus);
+ ReflowChildFlags::Default, childStatus);
FinishReflowChild(placeholder, PresContext(), childReflowOutput,
&childReflowInput, outerWM, aContentBoxOrigin,