diff options
Diffstat (limited to 'layout/generic/nsFlexContainerFrame.cpp')
-rw-r--r-- | layout/generic/nsFlexContainerFrame.cpp | 216 |
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, |