Bug 1300369 part 3 - [css-grid] Implement margin-box min-size clamping during track sizing. r=dholbert
authorMats Palmgren <mats@mozilla.com>
Sat, 05 Nov 2016 02:57:06 +0100
changeset 351294 84cfc7a757b9c3e632d6fd339b8479aa08a203ef
parent 351293 a89255f21073637c9c2588bc9fec6ec5de02a60f
child 351295 c9ee55e916c6b7661c294a57759cac1381e4a758
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert
bugs1300369
milestone52.0a1
Bug 1300369 part 3 - [css-grid] Implement margin-box min-size clamping during track sizing. r=dholbert
layout/generic/nsGridContainerFrame.cpp
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -38,16 +38,17 @@ using namespace mozilla;
 
 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
 typedef nsGridContainerFrame::TrackSize TrackSize;
 const uint32_t nsGridContainerFrame::kTranslatedMaxLine =
   uint32_t(nsStyleGridLine::kMaxLine - nsStyleGridLine::kMinLine);
 const uint32_t nsGridContainerFrame::kAutoLine = kTranslatedMaxLine + 3457U;
 typedef nsTHashtable< nsPtrHashKey<nsIFrame> > FrameHashtable;
 typedef mozilla::CSSAlignUtils::AlignJustifyFlags AlignJustifyFlags;
+typedef nsLayoutUtils::IntrinsicISizeType IntrinsicISizeType;
 
 // https://drafts.csswg.org/css-sizing/#constraints
 enum class SizingConstraint
 {
   eMinContent,  // sizing under min-content constraint
   eMaxContent,  // sizing under max-content constraint
   eNoConstraint // no constraint, used during Reflow
 };
@@ -219,16 +220,20 @@ struct nsGridContainerFrame::TrackSize
   void Dump() const;
 #endif
 
   static bool IsMinContent(const nsStyleCoord& aCoord)
   {
     return aCoord.GetUnit() == eStyleUnit_Enumerated &&
       aCoord.GetIntValue() == NS_STYLE_GRID_TRACK_BREADTH_MIN_CONTENT;
   }
+  static bool IsDefiniteMaxSizing(StateBits aStateBits)
+  {
+    return (aStateBits & (eIntrinsicMaxSizing | eFlexMaxSizing)) == 0;
+  }
 
   nscoord mBase;
   nscoord mLimit;
   nscoord mPosition;  // zero until we apply 'align/justify-content'
   // mBaselineSubtreeSize is the size of a baseline-aligned subtree within
   // this track.  One subtree per baseline-sharing group (per track).
   nscoord mBaselineSubtreeSize[2];
   StateBits mState;
@@ -839,16 +844,40 @@ struct nsGridContainerFrame::GridItemInf
     if (!(mState[aAxis] & eSelfBaseline)) {
       return aAlign == NS_STYLE_ALIGN_BASELINE ? NS_STYLE_ALIGN_SELF_START
                                                : NS_STYLE_ALIGN_SELF_END;
     }
     *aBaselineOffset = mBaselineOffset[aAxis];
     return aAlign;
   }
 
+  // Return true if we should we clamp this item's Automatic Minimum Size.
+  // https://drafts.csswg.org/css-grid/#min-size-auto
+  bool ShouldClampMinSize(WritingMode aContainerWM,
+                          LogicalAxis aContainerAxis,
+                          nscoord aPercentageBasis) const
+  {
+    const auto pos = mFrame->StylePosition();
+    const auto& size = aContainerAxis == eLogicalAxisInline ?
+      pos->ISize(aContainerWM) : pos->BSize(aContainerWM);
+    // NOTE: if we have a definite or 'max-content' size then our automatic
+    // minimum size can't affect our size.  Excluding these simplifies applying
+    // the clamping in the right cases later.
+    if (size.GetUnit() == eStyleUnit_Auto ||
+        ::IsPercentOfIndefiniteSize(size, aPercentageBasis) || // same as 'auto'
+        (size.GetUnit() == eStyleUnit_Enumerated &&
+         size.GetIntValue() != NS_STYLE_WIDTH_MAX_CONTENT)) {
+      const auto& minSize = aContainerAxis == eLogicalAxisInline ?
+        pos->MinISize(aContainerWM) : pos->MinBSize(aContainerWM);
+      return minSize.GetUnit() == eStyleUnit_Auto &&
+             mFrame->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE;
+    }
+    return false;
+  }
+
 #ifdef DEBUG
   void Dump() const;
 #endif
 
   static bool IsStartRowLessThan(const GridItemInfo* a, const GridItemInfo* b)
   {
     return a->mArea.mRows.mStart < b->mArea.mRows.mStart;
   }
@@ -3693,29 +3722,31 @@ MeasuringReflow(nsIFrame*               
   return childSize.BSize(wm);
 }
 
 /**
  * Return the [min|max]-content contribution of aChild to its parent (i.e.
  * the child's margin-box) in aAxis.
  */
 static nscoord
-ContentContribution(const GridItemInfo&               aGridItem,
-                    const GridReflowInput&            aState,
-                    nsRenderingContext*               aRC,
-                    WritingMode                       aCBWM,
-                    LogicalAxis                       aAxis,
-                    nsLayoutUtils::IntrinsicISizeType aConstraint,
-                    uint32_t                          aFlags = 0)
+ContentContribution(const GridItemInfo&    aGridItem,
+                    const GridReflowInput& aState,
+                    nsRenderingContext*    aRC,
+                    WritingMode            aCBWM,
+                    LogicalAxis            aAxis,
+                    IntrinsicISizeType     aConstraint,
+                    nscoord                aMinSizeClamp = NS_MAXSIZE,
+                    uint32_t               aFlags = 0)
 {
   nsIFrame* child = aGridItem.mFrame;
   PhysicalAxis axis(aCBWM.PhysicalAxis(aAxis));
   nscoord size = nsLayoutUtils::IntrinsicForAxis(axis, aRC, child, aConstraint,
                    aFlags | nsLayoutUtils::BAIL_IF_REFLOW_NEEDED |
-                            nsLayoutUtils::ADD_PERCENTS);
+                            nsLayoutUtils::ADD_PERCENTS,
+                   aMinSizeClamp);
   if (size == NS_INTRINSIC_WIDTH_UNKNOWN) {
     // We need to reflow the child to find its BSize contribution.
     // XXX this will give mostly correct results for now (until bug 1174569).
     nscoord cbISize = INFINITE_ISIZE_COORD;
     nscoord cbBSize = NS_UNCONSTRAINEDSIZE;
     auto childWM = child->GetWritingMode();
     if (aState.mCols.mCanResolveLineRangeSize) {
       nscoord sz = aState.mCols.ResolveSize(aGridItem.mArea.mCols);
@@ -3732,63 +3763,79 @@ ContentContribution(const GridItemInfo& 
     auto percent = offsets.hPctMargin;
     if (!aState.mReflowInput) {
       // We always want to add in percent padding too, but during Reflow we
       // always have a definite percentage basis (the grid area) so any percent
       // padding is already resolved and baked in to 'size' at this point.
       percent += offsets.hPctPadding;
     }
     size = nsLayoutUtils::AddPercents(size, percent);
+    nscoord overflow = size - aMinSizeClamp;
+    if (MOZ_UNLIKELY(overflow > 0)) {
+      nscoord contentSize = child->ContentBSize(childWM);
+      nscoord newContentSize = std::max(nscoord(0), contentSize - overflow);
+      // XXXmats deal with percentages better, see bug 1300369 comment 27.
+      size -= contentSize - newContentSize;
+    }
   }
   MOZ_ASSERT(aGridItem.mBaselineOffset[aAxis] >= 0,
              "baseline offset should be non-negative at this point");
   MOZ_ASSERT((aGridItem.mState[aAxis] & ItemState::eIsBaselineAligned) ||
              aGridItem.mBaselineOffset[aAxis] == nscoord(0),
              "baseline offset should be zero when not baseline-aligned");
   size += aGridItem.mBaselineOffset[aAxis];
   return std::max(size, 0);
 }
 
 struct CachedIntrinsicSizes
 {
   Maybe<nscoord> mMinSize;
   Maybe<nscoord> mMinContentContribution;
   Maybe<nscoord> mMaxContentContribution;
+  // "if the grid item spans only grid tracks that have a fixed max track
+  // sizing function, its automatic minimum size in that dimension is
+  // further clamped to less than or equal to the size necessary to fit its
+  // margin box within the resulting grid area (flooring at zero)"
+  // https://drafts.csswg.org/css-grid/#min-size-auto
+  // This is the clamp value to use for that:
+  nscoord mMinSizeClamp = NS_MAXSIZE;
 };
 
 static nscoord
 MinContentContribution(const GridItemInfo&    aGridItem,
                        const GridReflowInput& aState,
                        nsRenderingContext*    aRC,
                        WritingMode            aCBWM,
                        LogicalAxis            aAxis,
                        CachedIntrinsicSizes*  aCache)
 {
   if (aCache->mMinContentContribution.isSome()) {
     return aCache->mMinContentContribution.value();
   }
   nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
-                                  nsLayoutUtils::MIN_ISIZE);
+                                  nsLayoutUtils::MIN_ISIZE,
+                                  aCache->mMinSizeClamp);
   aCache->mMinContentContribution.emplace(s);
   return s;
 }
 
 static nscoord
 MaxContentContribution(const GridItemInfo&    aGridItem,
                        const GridReflowInput& aState,
                        nsRenderingContext*    aRC,
                        WritingMode            aCBWM,
                        LogicalAxis            aAxis,
                        CachedIntrinsicSizes*  aCache)
 {
   if (aCache->mMaxContentContribution.isSome()) {
     return aCache->mMaxContentContribution.value();
   }
   nscoord s = ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
-                                  nsLayoutUtils::PREF_ISIZE);
+                                  nsLayoutUtils::PREF_ISIZE,
+                                  aCache->mMinSizeClamp);
   aCache->mMaxContentContribution.emplace(s);
   return s;
 }
 
 // Computes the min-size contribution for a grid item, as defined at
 // https://drafts.csswg.org/css-grid/#min-size-contributions
 static nscoord
 MinSize(const GridItemInfo&    aGridItem,
@@ -3832,16 +3879,17 @@ MinSize(const GridItemInfo&    aGridItem
   auto unit = style.GetUnit();
   if (unit == eStyleUnit_Enumerated ||
       (unit == eStyleUnit_Auto &&
        child->StyleDisplay()->mOverflowX == NS_STYLE_OVERFLOW_VISIBLE)) {
     // Now calculate the "content size" part and return whichever is smaller.
     MOZ_ASSERT(unit != eStyleUnit_Enumerated || sz == NS_UNCONSTRAINEDSIZE);
     sz = std::min(sz, ContentContribution(aGridItem, aState, aRC, aCBWM, aAxis,
                                           nsLayoutUtils::MIN_ISIZE,
+                                          aCache->mMinSizeClamp,
                                           nsLayoutUtils::MIN_INTRINSIC_ISIZE));
   }
   aCache->mMinSize.emplace(sz);
   return sz;
 }
 
 void
 nsGridContainerFrame::Tracks::CalculateSizes(
@@ -3902,16 +3950,23 @@ nsGridContainerFrame::Tracks::ResolveInt
   const LineRange&            aRange,
   const GridItemInfo&         aGridItem)
 {
   CachedIntrinsicSizes cache;
   // min sizing
   TrackSize& sz = mSizes[aRange.mStart];
   WritingMode wm = aState.mWM;
   nsRenderingContext* rc = &aState.mRenderingContext;
+  if ((sz.mState & TrackSize::eIntrinsicMinSizing) &&
+      TrackSize::IsDefiniteMaxSizing(sz.mState) &&
+      aGridItem.ShouldClampMinSize(wm, mAxis)) {
+    auto maxCoord = aFunctions.MaxSizingFor(aRange.mStart);
+    cache.mMinSizeClamp =
+      nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
+  }
   if (sz.mState & TrackSize::eAutoMinSizing) {
     nscoord s;
     if (aConstraint == SizingConstraint::eMinContent) {
       s = MinContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
     } else if (aConstraint == SizingConstraint::eMaxContent) {
       s = MaxContentContribution(aGridItem, aState, rc, wm, mAxis, &cache);
     } else {
       MOZ_ASSERT(aConstraint == SizingConstraint::eNoConstraint);
@@ -4310,16 +4365,28 @@ nsGridContainerFrame::Tracks::ResolveInt
           uint32_t len = 2 * span;
           stateBitsPerSpan.SetCapacity(len);
           for (uint32_t i = stateBitsPerSpan.Length(); i < len; ++i) {
             stateBitsPerSpan.AppendElement(TrackSize::StateBits(0));
           }
         }
         stateBitsPerSpan[span] |= state;
         CachedIntrinsicSizes cache;
+        if ((state & TrackSize::eIntrinsicMinSizing) &&
+            TrackSize::IsDefiniteMaxSizing(state) &&
+            gridItem.ShouldClampMinSize(wm, mAxis)) {
+          nscoord minSizeClamp = 0;
+          for (auto i = lineRange.mStart, end = lineRange.mEnd; i < end; ++i) {
+            auto maxCoord = aFunctions.MaxSizingFor(i);
+            minSizeClamp +=
+              nsRuleNode::ComputeCoordPercentCalc(maxCoord, aPercentageBasis);
+          }
+          minSizeClamp += mGridGap * (span - 1);
+          cache.mMinSizeClamp = minSizeClamp;
+        }
         nscoord minSize = 0;
         if (state & (TrackSize::eIntrinsicMinSizing |   // for 2.1
                      TrackSize::eIntrinsicMaxSizing)) { // for 2.5
           minSize = MinSize(gridItem, aState, rc, wm, mAxis, &cache);
         }
         nscoord minContent = 0;
         if (state & contentBasedMinSelector) { // for 2.2
           minContent = MinContentContribution(gridItem, aState,