Bug 1105111 part 3: Add support for 'flex-basis:content' in layout. r=mats draft
authorDaniel Holbert <dholbert@cs.stanford.edu>
Fri, 30 Mar 2018 16:50:49 -0700
changeset 775396 da3e5c8f6a75c48f47ac833f07c66672d7afe42a
parent 775395 fa00ad1c6ad7c5639033b1590942a811c6294749
child 775397 2726bc6830412067c6787d0e880b2f4e0a766206
child 775399 f49727b133c7fb4ecefcd1ff8bdae0ffb340870a
push id104709
push userdholbert@mozilla.com
push dateFri, 30 Mar 2018 23:51:10 +0000
reviewersmats
bugs1105111
milestone61.0a1
Bug 1105111 part 3: Add support for 'flex-basis:content' in layout. r=mats BACKGROUND: Early in flex layout, we have to resolve the 'flex-basis' value to produce the "flex base size" (basically, the flex-basis resolved to an absolute length). This resolution happens in two "phases" (which both happen within nsFlexContainer::GenerateFlexItemForChild()): First phase: we try to resolve the flex-basis by creating a ReflowInput for the flex item (which gets us some other things as well). Under the hood, we use the flex-basis when resolving this ReflowInput's main-axis size. The code for this lives in nsFrame::ComputeSize (and in nsFrame::ComputeSizeWithIntrinsicDimensions, via some frame classes' overrides of ComputeSize). Second phase: If the first phase didn't get us a definite size, then that means we have to do reflow to measure the content size & produce a resolved flex base size, which we do via ResolveAutoFlexBasisAndMinSize(). NOTES ON THIS PATCH: To add 'flex-basis:content' support to layout, this patch only needs to modify the first phase discussed above. If it turns out we also have some second-phase work to do (i.e. if we need to do reflow to resolve 'flex-basis:content'), this patch causes that reflow to happen by simply making us use eStyleUnit_Auto in the main axis's nsStyleCoord in the first phase. (And then, if that 'auto' nsStyleCoord really does require reflow, then that first phase will end up producing an unconstrained main-size in the flex item's ReflowInput, which will automatically trigger the second phase.) MozReview-Commit-ID: 2nH4Fh78C81
layout/generic/nsContainerFrame.cpp
layout/generic/nsFrame.cpp
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -8,16 +8,17 @@
 
 #include "nsContainerFrame.h"
 
 #include "mozilla/ComputedStyle.h"
 #include "mozilla/dom/HTMLSummaryElement.h"
 #include "nsAbsoluteContainingBlock.h"
 #include "nsAttrValue.h"
 #include "nsAttrValueInlines.h"
+#include "nsFlexContainerFrame.h"
 #include "nsIDocument.h"
 #include "nsPresContext.h"
 #include "nsRect.h"
 #include "nsPoint.h"
 #include "nsStyleConsts.h"
 #include "nsView.h"
 #include "nsIPresShell.h"
 #include "nsCOMPtr.h"
@@ -841,18 +842,28 @@ nsContainerFrame::ComputeAutoSize(gfxCon
                                   const LogicalSize&  aPadding,
                                   ComputeSizeFlags    aFlags)
 {
   LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
   nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
                        aBorder.ISize(aWM) - aPadding.ISize(aWM);
   // replaced elements always shrink-wrap
   if ((aFlags & ComputeSizeFlags::eShrinkWrap) || IsFrameOfType(eReplaced)) {
-    // don't bother setting it if the result won't be used
-    if (StylePosition()->ISize(aWM).GetUnit() == eStyleUnit_Auto) {
+    // Only bother computing our 'auto' ISize if the result will be used.
+    // It'll be used under two scenarios:
+    // - If our ISize property is itself 'auto'.
+    // - If we're using flex-basis in place of our ISize property (i.e. we're a
+    // flex item with our inline axis being the main axis), AND we have
+    // flex-basis:content.
+    const nsStylePosition* pos = StylePosition();
+    if (pos->ISize(aWM).GetUnit() == eStyleUnit_Auto ||
+        (pos->mFlexBasis.GetUnit() == eStyleUnit_Enumerated &&
+         pos->mFlexBasis.GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT &&
+         IsFlexItem() &&
+         nsFlexContainerFrame::IsItemInlineAxisMainAxis(this))) {
       result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
     }
   } else {
     result.ISize(aWM) = availBased;
   }
 
   if (IsTableCaption()) {
     // If we're a container for font size inflation, then shrink
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5668,19 +5668,32 @@ nsFrame::ComputeSize(gfxContext*        
     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this) ?
       eLogicalAxisInline : eLogicalAxisBlock;
 
     // NOTE: The logic here should match the similar chunk for determining
     // inlineStyleCoord and blockStyleCoord in
     // nsFrame::ComputeSizeWithIntrinsicDimensions().
     const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
     if (flexBasis->GetUnit() != eStyleUnit_Auto) {
-      // Override whichever styleCoord is in flex container's main axis:
-      (flexMainAxis == eLogicalAxisInline ?
-       inlineStyleCoord : blockStyleCoord) = flexBasis;
+      // Replace our main-axis styleCoord pointer with a different one,
+      // depending on our flex-basis value.
+      auto& mainAxisCoord = (flexMainAxis == eLogicalAxisInline
+                             ? inlineStyleCoord : blockStyleCoord);
+      if (flexBasis->GetUnit() == eStyleUnit_Enumerated &&
+          flexBasis->GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT) {
+        // We have 'flex-basis: content', which is equivalent to
+        // 'flex-basis:auto; {main-size}: auto'. So, just swap in a dummy
+        // 'auto' value to use for the main size property:
+        static const nsStyleCoord autoStyleCoord(eStyleUnit_Auto);
+        mainAxisCoord = &autoStyleCoord;
+      } else {
+        // For all other flex-basis values, we just swap in the flex-basis
+        // itself for the main-size property here:
+        mainAxisCoord = flexBasis;
+      }
     }
   }
 
   // Compute inline-axis size
   if (inlineStyleCoord->GetUnit() != eStyleUnit_Auto) {
     result.ISize(aWM) =
       ComputeISizeValue(aRenderingContext, aCBSize.ISize(aWM),
                         boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
@@ -5910,19 +5923,33 @@ nsFrame::ComputeSizeWithIntrinsicDimensi
       // Flex items use their "flex-basis" property in place of their main-size
       // property (e.g. "width") for sizing purposes, *unless* they have
       // "flex-basis:auto", in which case they use their main-size property
       // after all.
       // NOTE: The logic here should match the similar chunk for determining
       // inlineStyleCoord and blockStyleCoord in nsFrame::ComputeSize().
       const nsStyleCoord* flexBasis = &(stylePos->mFlexBasis);
       if (flexBasis->GetUnit() != eStyleUnit_Auto) {
-        // Override whichever styleCoord is in flex container's main axis:
-        (flexMainAxis == eLogicalAxisInline ?
-         inlineStyleCoord : blockStyleCoord) = flexBasis;
+        // Replace our main-axis styleCoord pointer with a different one,
+        // depending on our flex-basis value.
+        auto& mainAxisCoord = (flexMainAxis == eLogicalAxisInline
+                               ? inlineStyleCoord : blockStyleCoord);
+
+        if (flexBasis->GetUnit() == eStyleUnit_Enumerated &&
+            flexBasis->GetIntValue() == NS_STYLE_FLEX_BASIS_CONTENT) {
+          // We have 'flex-basis: content', which is equivalent to
+          // 'flex-basis:auto; {main-size}: auto'. So, just swap in a dummy
+          // 'auto' value to use for the main size property:
+          static const nsStyleCoord autoStyleCoord(eStyleUnit_Auto);
+          mainAxisCoord = &autoStyleCoord;
+        } else {
+          // For all other flex-basis values, we just swap in the flex-basis
+          // itself for the main-size property here:
+          mainAxisCoord = flexBasis;
+        }
       }
     }
   }
 
   // Handle intrinsic sizes and their interaction with
   // {min-,max-,}{width,height} according to the rules in
   // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths