Bug 1398963 part 6: Make "visibility:collapse" cause flex items to be 0-sized, in emulated -moz-{inline-}box. r=mats
authorDaniel Holbert <dholbert@cs.stanford.edu>
Mon, 18 Dec 2017 12:17:11 -0600
changeset 448512 16e76fada893040809d0f9f672b53bd99ffabfe4
parent 448511 9d1204867a515fb03fd91690fc39241d66987a6c
child 448513 4f4836d4a0841f27ac88dfa05d248309e8d601d1
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmats
bugs1398963
milestone59.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 1398963 part 6: Make "visibility:collapse" cause flex items to be 0-sized, in emulated -moz-{inline-}box. r=mats In modern flexbox and in "display:-webkit-box", children with "visibility:collapse" currently generate "struts" which have 0 main-size but nonzero cross-size. But XUL/-moz-box treats these children differently -- it makes them 0-sized in both axes. So we need to add a custom behavior to modern flexbox in order to emulate that. Specifically, this patch makes us: - Ignore these children when computing the flex container's intrinsic sizes. - Take a simpler codepath with 0-sized struts for collapsed elements when laying out a -moz-box (rather than the typical 2-pass layout, with strut cross-size being established in the 1st pass). MozReview-Commit-ID: IpkADpFFBMx
layout/generic/nsFlexContainerFrame.cpp
layout/generic/nsFlexContainerFrame.h
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3604,16 +3604,49 @@ AddNewFlexLineToList(LinkedList<FlexLine
   if (aShouldInsertAtFront) {
     aLines.insertFront(newLine);
   } else {
     aLines.insertBack(newLine);
   }
   return newLine;
 }
 
+bool
+nsFlexContainerFrame::ShouldUseMozBoxCollapseBehavior(
+  const nsStyleDisplay* aThisStyleDisp)
+{
+  MOZ_ASSERT(StyleDisplay() == aThisStyleDisp, "wrong StyleDisplay passed in");
+
+  // Quick filter to screen out *actual* (not-coopted-for-emulation)
+  // flex containers, using state bit:
+  if (!IsLegacyBox(this)) {
+    return false;
+  }
+
+  // Check our own display value:
+  if (aThisStyleDisp->mDisplay == mozilla::StyleDisplay::MozBox ||
+      aThisStyleDisp->mDisplay == mozilla::StyleDisplay::MozInlineBox) {
+    return true;
+  }
+
+  // Check our parent's display value, if we're an anonymous box (with a
+  // potentially-untrustworthy display value):
+  auto pseudoType = StyleContext()->GetPseudo();
+  if (pseudoType == nsCSSAnonBoxes::scrolledContent ||
+      pseudoType == nsCSSAnonBoxes::buttonContent) {
+    const nsStyleDisplay* disp = GetParent()->StyleDisplay();
+    if (disp->mDisplay == mozilla::StyleDisplay::MozBox ||
+        disp->mDisplay == mozilla::StyleDisplay::MozInlineBox) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void
 nsFlexContainerFrame::GenerateFlexLines(
   nsPresContext* aPresContext,
   const ReflowInput& aReflowInput,
   nscoord aContentBoxMainSize,
   nscoord aAvailableBSizeForContent,
   const nsTArray<StrutInfo>& aStruts,
   const FlexboxAxisTracker& aAxisTracker,
@@ -3680,34 +3713,42 @@ nsFlexContainerFrame::GenerateFlexLines(
                                   OrderingPropertyForIter(this));
 
   if (iter.ItemsAreAlreadyInOrder()) {
     AddStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   } else {
     RemoveStateBits(NS_STATE_FLEX_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   }
 
+  const bool useMozBoxCollapseBehavior =
+    ShouldUseMozBoxCollapseBehavior(aReflowInput.mStyleDisplay);
+
   for (; !iter.AtEnd(); iter.Next()) {
     nsIFrame* childFrame = *iter;
     // Don't create flex items / lines for placeholder frames:
     if (childFrame->IsPlaceholderFrame()) {
       aPlaceholders.AppendElement(childFrame);
       continue;
     }
 
     // Honor "page-break-before", if we're multi-line and this line isn't empty:
     if (!isSingleLine && !curLine->IsEmpty() &&
         childFrame->StyleDisplay()->mBreakBefore) {
       curLine = AddNewFlexLineToList(aLines, shouldInsertAtFront);
     }
 
     UniquePtr<FlexItem> item;
-    if (nextStrutIdx < aStruts.Length() &&
-        aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
-
+    if (useMozBoxCollapseBehavior &&
+        (NS_STYLE_VISIBILITY_COLLAPSE ==
+         childFrame->StyleVisibility()->mVisible)) {
+      // Legacy visibility:collapse behavior: make a 0-sized strut. (No need to
+      // bother with aStruts and remembering cross size.)
+      item = MakeUnique<FlexItem>(childFrame, 0, aReflowInput.GetWritingMode());
+    } else if (nextStrutIdx < aStruts.Length() &&
+               aStruts[nextStrutIdx].mItemIdx == itemIdxInContainer) {
       // Use the simplified "strut" FlexItem constructor:
       item = MakeUnique<FlexItem>(childFrame, aStruts[nextStrutIdx].mStrutCrossSize,
                                   aReflowInput.GetWritingMode());
       nextStrutIdx++;
     } else {
       item = GenerateFlexItemForChild(aPresContext, childFrame,
                                       aReflowInput, aAxisTracker);
     }
@@ -4469,17 +4510,18 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
     crossAxisPosnTracker(lines.getFirst(),
                          aReflowInput, contentBoxCrossSize,
                          isCrossSizeDefinite, aAxisTracker);
 
   // Now that we know the cross size of each line (including
   // "align-content:stretch" adjustments, from the CrossAxisPositionTracker
   // constructor), we can create struts for any flex items with
   // "visibility: collapse" (and restart flex layout).
-  if (aStruts.IsEmpty()) { // (Don't make struts if we already did)
+  if (aStruts.IsEmpty() && // (Don't make struts if we already did)
+      !ShouldUseMozBoxCollapseBehavior(aReflowInput.mStyleDisplay)) {
     BuildStrutInfoFromCollapsedItems(lines.getFirst(), aStruts);
     if (!aStruts.IsEmpty()) {
       // Restart flex layout, using our struts.
       return;
     }
   }
 
   // If the container should derive its baseline from the first FlexLine,
@@ -4915,30 +4957,39 @@ nsFlexContainerFrame::GetMinISize(gfxCon
   nscoord minISize = 0;
   DISPLAY_MIN_WIDTH(this, minISize);
 
   RenumberList();
 
   const nsStylePosition* stylePos = StylePosition();
   const FlexboxAxisTracker axisTracker(this, GetWritingMode());
 
+  const bool useMozBoxCollapseBehavior =
+    ShouldUseMozBoxCollapseBehavior(StyleDisplay());
+
   for (nsIFrame* childFrame : mFrames) {
-    nscoord childMinISize =
-      nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
-                                           nsLayoutUtils::MIN_ISIZE);
-    // For a horizontal single-line flex container, the intrinsic min
-    // isize is the sum of its items' min isizes.
-    // For a column-oriented flex container, or for a multi-line row-
-    // oriented flex container, the intrinsic min isize is the max of
-    // its items' min isizes.
-    if (axisTracker.IsRowOriented() &&
-        NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) {
-      minISize += childMinISize;
-    } else {
-      minISize = std::max(minISize, childMinISize);
+    // If we're using legacy "visibility:collapse" behavior, then we don't
+    // care about the sizes of any collapsed children.
+    if (!useMozBoxCollapseBehavior ||
+        (NS_STYLE_VISIBILITY_COLLAPSE !=
+         childFrame->StyleVisibility()->mVisible)) {
+      nscoord childMinISize =
+        nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+                                             nsLayoutUtils::MIN_ISIZE);
+      // For a horizontal single-line flex container, the intrinsic min
+      // isize is the sum of its items' min isizes.
+      // For a column-oriented flex container, or for a multi-line row-
+      // oriented flex container, the intrinsic min isize is the max of
+      // its items' min isizes.
+      if (axisTracker.IsRowOriented() &&
+          NS_STYLE_FLEX_WRAP_NOWRAP == stylePos->mFlexWrap) {
+        minISize += childMinISize;
+      } else {
+        minISize = std::max(minISize, childMinISize);
+      }
     }
   }
   return minISize;
 }
 
 /* virtual */ nscoord
 nsFlexContainerFrame::GetPrefISize(gfxContext* aRenderingContext)
 {
@@ -4949,20 +5000,29 @@ nsFlexContainerFrame::GetPrefISize(gfxCo
 
   // XXXdholbert Optimization: We could cache our intrinsic widths like
   // nsBlockFrame does (and return it early from this function if it's set).
   // Whenever anything happens that might change it, set it to
   // NS_INTRINSIC_WIDTH_UNKNOWN (like nsBlockFrame::MarkIntrinsicISizesDirty
   // does)
   const FlexboxAxisTracker axisTracker(this, GetWritingMode());
 
+  const bool useMozBoxCollapseBehavior =
+    ShouldUseMozBoxCollapseBehavior(StyleDisplay());
+
   for (nsIFrame* childFrame : mFrames) {
-    nscoord childPrefISize =
-      nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
-                                           nsLayoutUtils::PREF_ISIZE);
-    if (axisTracker.IsRowOriented()) {
-      prefISize += childPrefISize;
-    } else {
-      prefISize = std::max(prefISize, childPrefISize);
+    // If we're using legacy "visibility:collapse" behavior, then we don't
+    // care about the sizes of any collapsed children.
+    if (!useMozBoxCollapseBehavior ||
+        (NS_STYLE_VISIBILITY_COLLAPSE !=
+         childFrame->StyleVisibility()->mVisible)) {
+      nscoord childPrefISize =
+        nsLayoutUtils::IntrinsicForContainer(aRenderingContext, childFrame,
+                                             nsLayoutUtils::PREF_ISIZE);
+      if (axisTracker.IsRowOriented()) {
+        prefISize += childPrefISize;
+      } else {
+        prefISize = std::max(prefISize, childPrefISize);
+      }
     }
   }
   return prefISize;
 }
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -295,16 +295,27 @@ protected:
    * (Helper for GenerateFlexItemForChild().)
    */
   void ResolveAutoFlexBasisAndMinSize(nsPresContext* aPresContext,
                                       FlexItem& aFlexItem,
                                       const ReflowInput& aItemReflowInput,
                                       const FlexboxAxisTracker& aAxisTracker);
 
   /**
+   * Returns true if "this" is the nsFlexContainerFrame for a -moz-box or
+   * a -moz-inline-box -- these boxes have special behavior for flex items with
+   * "visibility:collapse".
+   *
+   * @param aFlexStyleDisp This frame's StyleDisplay(). (Just an optimization to
+   *                       avoid repeated lookup; some callers already have it.)
+   * @return true if "this" is the nsFlexContainerFrame for a -moz-{inline}box.
+   */
+  bool ShouldUseMozBoxCollapseBehavior(const nsStyleDisplay* aFlexStyleDisp);
+
+  /**
    * This method:
    *  - Creates FlexItems for all of our child frames (except placeholders).
    *  - Groups those FlexItems into FlexLines.
    *  - Returns those FlexLines in the outparam |aLines|.
    *
    * For any child frames which are placeholders, this method will instead just
    * append that child to the outparam |aPlaceholders| for separate handling.
    * (Absolutely positioned children of a flex container are *not* flex items.)