Bug 1627125 Part 2 - Add a helper to compute available size for flex items, and pass the information to ReflowChildren. r=dholbert
authorTing-Yu Lin <tlin@mozilla.com>
Thu, 16 Apr 2020 05:37:47 +0000
changeset 524470 a8e29df7db26ff1b8c7f0dfca8e944e0bfaf4ad9
parent 524469 36907dee3e51d41337bc7d4e7dc27c28178dfdfe
child 524471 c15c24dcc029e28ba1294da3a574439698bc373e
push id37321
push userdluca@mozilla.com
push dateFri, 17 Apr 2020 09:38:52 +0000
treeherdermozilla-central@24537fed53e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1627125
milestone77.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1627125 Part 2 - Add a helper to compute available size for flex items, and pass the information to ReflowChildren. r=dholbert Differential Revision: https://phabricator.services.mozilla.com/D69470
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFlexContainerFrame.h
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -4037,16 +4037,45 @@ nscoord nsFlexContainerFrame::ComputeCro
   // Shrink-wrap our line(s), subject to our min-size / max-size
   // constraints in that (block) axis.
   // XXXdholbert Handle constrained-aAvailableBSizeForContent case here.
   *aIsDefinite = false;
   return NS_CSS_MINMAX(aSumLineCrossSizes, aReflowInput.ComputedMinBSize(),
                        aReflowInput.ComputedMaxBSize());
 }
 
+LogicalSize nsFlexContainerFrame::ComputeAvailableSizeForItems(
+    const ReflowInput& aReflowInput,
+    const mozilla::LogicalMargin& aBorderPadding) const {
+  const WritingMode wm = GetWritingMode();
+  nscoord availableBSize = aReflowInput.AvailableBSize();
+
+  if (availableBSize != NS_UNCONSTRAINEDSIZE) {
+    // Available block-size is constrained. Subtract block-start border and
+    // padding from it.
+    availableBSize -= aBorderPadding.BStart(wm);
+
+    if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
+        StyleBoxDecorationBreak::Clone) {
+      // We have box-decoration-break:clone. Subtract block-end border and
+      // padding from the available block-size as well.
+      availableBSize -= aBorderPadding.BEnd(wm);
+    }
+
+    // Available block-size can became negative after subtracting block-axis
+    // border and padding. Per spec, to guarantee progress, fragmentainers are
+    // assumed to have a minimum block size of 1px regardless of their used
+    // size. https://drafts.csswg.org/css-break/#breaking-rules
+    availableBSize =
+        std::max(nsPresContext::CSSPixelsToAppUnits(1), availableBSize);
+  }
+
+  return LogicalSize(wm, aReflowInput.ComputedISize(), availableBSize);
+}
+
 void FlexLine::PositionItemsInMainAxis(
     const StyleContentDistribution& aJustifyContent,
     nscoord aContentBoxMainSize, const FlexboxAxisTracker& aAxisTracker) {
   MainAxisPositionTracker mainAxisPosnTracker(
       aAxisTracker, this, aJustifyContent, aContentBoxMainSize);
   for (FlexItem& item : Items()) {
     nscoord itemMainBorderBoxSize =
         item.MainSize() + item.BorderPaddingSizeInMainAxis();
@@ -4186,28 +4215,26 @@ void nsFlexContainerFrame::Reflow(nsPres
   RemoveStateBits(NS_STATE_FLEX_HAS_LINE_CLAMP_ELLIPSIS);
 
   const FlexboxAxisTracker axisTracker(this, aReflowInput.GetWritingMode());
 
   // Check to see if we need to create a computed info structure, to
   // be filled out for use by devtools.
   ComputedFlexContainerInfo* containerInfo = CreateOrClearFlexContainerInfo();
 
-  // If we're being fragmented into a constrained BSize, then subtract off
-  // borderpadding BStart from that constrained BSize, to get the available
-  // BSize for our content box. (No need to subtract the borderpadding BStart
-  // if we're already skipping it via GetLogicalSkipSides, though.)
-  nscoord availableBSizeForContent = aReflowInput.AvailableBSize();
-  if (availableBSizeForContent != NS_UNCONSTRAINEDSIZE &&
-      !(GetLogicalSkipSides(&aReflowInput).BStart())) {
-    availableBSizeForContent -=
-        aReflowInput.ComputedLogicalBorderPadding().BStart(wm);
-    // (Don't let that push availableBSizeForContent below zero, though):
-    availableBSizeForContent = std::max(availableBSizeForContent, 0);
-  }
+  // We assume we are the last fragment by using
+  // PreReflowBlockLevelLogicalSkipSides(). We will skip block-end
+  // border/padding when we know our content-box size after DoFlexLayout.
+  LogicalMargin borderPadding =
+      aReflowInput.ComputedLogicalBorderPadding().ApplySkipSides(
+          PreReflowBlockLevelLogicalSkipSides());
+
+  const LogicalSize availableSizeForItems =
+      ComputeAvailableSizeForItems(aReflowInput, borderPadding);
+  const nscoord availableBSizeForContent = availableSizeForItems.BSize(wm);
 
   nscoord contentBoxMainSize =
       GetMainSizeFromReflowInput(aReflowInput, axisTracker);
   nscoord contentBoxCrossSize;
   nscoord flexContainerAscent;
 
   // Calculate gap size for main and cross axis
   nscoord mainGapSize;
@@ -4242,17 +4269,36 @@ void nsFlexContainerFrame::Reflow(nsPres
     lines.Clear();
     placeholders.Clear();
     DoFlexLayout(aReflowInput, aStatus, contentBoxMainSize, contentBoxCrossSize,
                  flexContainerAscent, availableBSizeForContent, lines, struts,
                  placeholders, axisTracker, mainGapSize, crossGapSize,
                  hasLineClampEllipsis, containerInfo);
   }
 
+  const LogicalSize contentBoxSize =
+      axisTracker.LogicalSizeFromFlexRelativeSizes(contentBoxMainSize,
+                                                   contentBoxCrossSize);
+  const nscoord consumedBSize = ConsumedBSize(wm);
+  const nscoord effectiveContentBSize =
+      contentBoxSize.BSize(wm) - consumedBSize;
+
+  // Check if we may need a next-in-flow. If so, we'll need to skip block-end
+  // border and padding.
+  const bool mayNeedNextInFlow =
+      effectiveContentBSize > availableSizeForItems.BSize(wm);
+  if (mayNeedNextInFlow) {
+    if (aReflowInput.mStyleBorder->mBoxDecorationBreak ==
+        StyleBoxDecorationBreak::Slice) {
+      borderPadding.BEnd(wm) = 0;
+    }
+  }
+
   ReflowChildren(aReflowInput, contentBoxMainSize, contentBoxCrossSize,
+                 availableSizeForItems, borderPadding, consumedBSize,
                  flexContainerAscent, lines, placeholders, axisTracker,
                  hasLineClampEllipsis);
 
   ComputeFinalSize(aReflowOutput, aReflowInput, aStatus, contentBoxMainSize,
                    contentBoxCrossSize, flexContainerAscent, lines,
                    axisTracker);
 
   // Finally update our line and item measurements in our containerInfo.
@@ -4795,41 +4841,33 @@ void nsFlexContainerFrame::DoFlexLayout(
           crossAxisPosnTracker.Position() - lastLineBaselineOffset,
           aContentBoxCrossSize, aReflowInput, aAxisTracker);
     }
   }
 }
 
 void nsFlexContainerFrame::ReflowChildren(
     const ReflowInput& aReflowInput, const nscoord aContentBoxMainSize,
-    const nscoord aContentBoxCrossSize, nscoord& aFlexContainerAscent,
-    nsTArray<FlexLine>& aLines, nsTArray<nsIFrame*>& aPlaceholders,
-    const FlexboxAxisTracker& aAxisTracker, bool aHasLineClampEllipsis) {
+    const nscoord aContentBoxCrossSize,
+    const LogicalSize& aAvailableSizeForItems,
+    const LogicalMargin& aBorderPadding, const nscoord aConsumedBSize,
+    nscoord& aFlexContainerAscent, nsTArray<FlexLine>& aLines,
+    nsTArray<nsIFrame*>& aPlaceholders, const FlexboxAxisTracker& aAxisTracker,
+    bool aHasLineClampEllipsis) {
   // Before giving each child a final reflow, calculate the origin of the
   // flex container's content box (with respect to its border-box), so that
   // we can compute our flex item's final positions.
   WritingMode flexWM = aReflowInput.GetWritingMode();
-  LogicalMargin containerBP = aReflowInput.ComputedLogicalBorderPadding();
-
-  // Unconditionally skip block-end border & padding for now, regardless of
-  // writing-mode/GetLogicalSkipSides.  We add it lower down, after we've
-  // established baseline and decided whether bottom border-padding fits (if
-  // we're fragmented).
-  const LogicalSides skipSides =
-      GetLogicalSkipSides(&aReflowInput) | LogicalSides(eLogicalSideBitsBEnd);
-  containerBP.ApplySkipSides(skipSides);
-
   const LogicalPoint containerContentBoxOrigin(
-      flexWM, containerBP.IStart(flexWM), containerBP.BStart(flexWM));
+      flexWM, aBorderPadding.IStart(flexWM), aBorderPadding.BStart(flexWM));
 
   // Determine flex container's border-box size (used in positioning children):
   LogicalSize logSize = aAxisTracker.LogicalSizeFromFlexRelativeSizes(
       aContentBoxMainSize, aContentBoxCrossSize);
-  // XXXTYLin: Should we add containerBP.Size(flexWM) to logSize instead?
-  logSize += aReflowInput.ComputedLogicalBorderPadding().Size(flexWM);
+  logSize += aBorderPadding.Size(flexWM);
   nsSize containerSize = logSize.GetPhysicalSize(flexWM);
 
   // If the flex container has no baseline-aligned items, it will use this item
   // (the first item, discounting any under-the-hood reversing that we've done)
   // to determine its baseline:
   const FlexItem* firstItem =
       aAxisTracker.AreAxesInternallyReversed()
           ? (aLines.LastElement().IsEmpty() ? nullptr
@@ -4842,37 +4880,47 @@ void nsFlexContainerFrame::ReflowChildre
     for (const FlexItem& item : line.Items()) {
       LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint(
           item.MainPosition(), item.CrossPosition(), aContentBoxMainSize,
           aContentBoxCrossSize);
       // Adjust framePos to be relative to the container's border-box
       // (i.e. its frame rect), instead of the container's content-box:
       framePos += containerContentBoxOrigin;
 
+      // XXX: We need to subtract aConsumedBSize from framePos.B(flewm) after we
+      // support flex item fragmentation.
+
       // (Intentionally snapshotting this before ApplyRelativePositioning, to
       // maybe use for setting the flex container's baseline.)
       const nscoord itemNormalBPos = framePos.B(flexWM);
 
       // Check if we actually need to reflow the item -- if we already reflowed
       // it with the right content-box size, and there is no need to do a reflow
       // to clear out a -webkit-line-clamp ellipsis, we can just reposition it
       // as-needed.
       if (item.NeedsFinalReflow()) {
         // The available size must be in item's writing-mode.
+        // XXX: The correct available block-size is from the position where the
+        // flex item is placed to the end of the available block-size.
         const WritingMode itemWM = item.GetWritingMode();
-        LogicalSize availableSize = aReflowInput.ComputedSize(itemWM);
+        LogicalSize availableSize =
+            aAvailableSizeForItems.ConvertTo(itemWM, flexWM);
 
         // XXX: Unconditionally give our children unconstrained block-size until
         // we support flex item fragmentation.
         availableSize.BSize(itemWM) = NS_UNCONSTRAINEDSIZE;
 
         const nsReflowStatus childReflowStatus =
             ReflowFlexItem(aAxisTracker, aReflowInput, item, framePos,
                            availableSize, containerSize, aHasLineClampEllipsis);
 
+        // XXX: Silence the unused childReflowStatus warning in opt build for
+        // now.
+        Unused << childReflowStatus;
+
         // XXXdholbert Once we do pagination / splitting, we'll need to actually
         // handle incomplete childReflowStatuses. But for now, we give our kids
         // unconstrained available height, which means they should always
         // complete.
         MOZ_ASSERT(childReflowStatus.IsComplete(),
                    "We gave flex item unconstrained available height, so it "
                    "should be complete");
       } else {
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -427,16 +427,32 @@ class nsFlexContainerFrame final : publi
                           nsReflowStatus& aStatus) const;
 
   nscoord ComputeCrossSize(const ReflowInput& aReflowInput,
                            const FlexboxAxisTracker& aAxisTracker,
                            nscoord aSumLineCrossSizes,
                            nscoord aAvailableBSizeForContent, bool* aIsDefinite,
                            nsReflowStatus& aStatus) const;
 
+  /**
+   * Compute the size of the available space that we'll give to our children to
+   * reflow into. In particular, compute the available size that we would give
+   * to a hypothetical child placed at the IStart/BStart corner of this flex
+   * container's content-box.
+   *
+   * @param aReflowInput the flex container's reflow input.
+   * @param aBorderPadding the border and padding of this frame with the
+   *                       assumption that this is the last fragment.
+   *
+   * @return the size of the available space for our children to reflow into.
+   */
+  mozilla::LogicalSize ComputeAvailableSizeForItems(
+      const ReflowInput& aReflowInput,
+      const mozilla::LogicalMargin& aBorderPadding) const;
+
   void SizeItemInCrossAxis(ReflowInput& aChildReflowInput, FlexItem& aItem);
 
   /**
    * This method computes flex container's final size and baseline.
    *
    * @param aContentBoxMainSize the final content-box main-size of the flex
    *                            container.
    * @param aContentBoxCrossSize the final content-box cross-size of the flex
@@ -454,26 +470,36 @@ class nsFlexContainerFrame final : publi
 
   /**
    * Perform a final Reflow for our child frames.
    *
    * @param aContentBoxMainSize the final content-box main-size of the flex
    *                            container.
    * @param aContentBoxCrossSize the final content-box cross-size of the flex
    *                             container.
+   * @param aAvailableSizeForItems the size of the available space for our
+   *                               children to reflow into.
+   * @param aBorderPadding the border and padding for this frame (possibly with
+   *                       some sides skipped as-appropriate, if we're in a
+   *                       continuation chain).
+   * @param aConsumedBSize the sum of our content block-size consumed by our
+   *                       prev-in-flows.
    * @param aFlexContainerAscent [in/out] initially, the "tentative" flex
    *                             container ascent computed in DoFlexLayout; or,
    *                             nscoord_MIN if the ascent hasn't been
    *                             established yet. If the latter, this will be
    *                             updated with an ascent derived from the first
    *                             flex item (if there are any flex items).
    */
   void ReflowChildren(const ReflowInput& aReflowInput,
                       const nscoord aContentBoxMainSize,
                       const nscoord aContentBoxCrossSize,
+                      const mozilla::LogicalSize& aAvailableSizeForItems,
+                      const mozilla::LogicalMargin& aBorderPadding,
+                      const nscoord aConsumedBSize,
                       nscoord& aFlexContainerAscent, nsTArray<FlexLine>& aLines,
                       nsTArray<nsIFrame*>& aPlaceholders,
                       const FlexboxAxisTracker& aAxisTracker,
                       bool aHasLineClampEllipsis);
 
   /**
    * Moves the given flex item's frame to the given LogicalPosition (modulo any
    * relative positioning).