Bug 851607: Make nsFlexContainerFrame::Reflow reflow its children unconditionally. r=dbaron
authorDaniel Holbert <dholbert@cs.stanford.edu>
Fri, 15 Mar 2013 22:40:57 -0700
changeset 125005 3a1ab4d115134c4789a54856079af73bb03e16c5
parent 125004 d4c1d68f2d9f9bfbbc7e4f4b22e2b2680b324da9
child 125006 46585b03426a268600caaaf2f5896023393be6ca
push id24734
push userdholbert@mozilla.com
push dateSat, 16 Mar 2013 05:47:59 +0000
treeherdermozilla-inbound@3a1ab4d11513 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs851607
milestone22.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 851607: Make nsFlexContainerFrame::Reflow reflow its children unconditionally. r=dbaron
layout/generic/nsFlexContainerFrame.cpp
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -2082,23 +2082,16 @@ nsFlexContainerFrame::Reflow(nsPresConte
        eStyleUnit_Auto != stylePos->mOffset.GetBottomUnit())) {
     AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
   }
 
 #ifdef DEBUG
   SanityCheckAnonymousFlexItems();
 #endif // DEBUG
 
-  // If our subtree is dirty (i.e. some of our descendants have changed), we
-  // reflow _all_ of our children.  We have to do this -- we can't just reflow
-  // select children, as we would in other frame classes.  This is because flex
-  // items' sizes (in both axes) are highly dependent on their siblings' sizes.
-  bool shouldReflowChildren =
-    NS_SUBTREE_DIRTY(this) || aReflowState.ShouldReflowAllKids();
-
   // If we've never reordered our children, then we can trust that they're
   // already in DOM-order, and we only need to consider their "order" property
   // when checking them for sortedness & sorting them.
   //
   // After we actually sort them, though, we can't trust that they're in DOM
   // order anymore.  So, from that point on, our sort & sorted-order-checking
   // operations need to use a fancier LEQ function that also takes DOM order
   // into account, so that we can honor the spec's requirement that frames w/
@@ -2129,227 +2122,207 @@ nsFlexContainerFrame::Reflow(nsPresConte
   ResolveFlexibleLengths(axisTracker, flexContainerMainSize, items);
 
   // Our frame's main-size is the content-box size plus border and padding.
   nscoord frameMainSize = flexContainerMainSize +
     axisTracker.GetMarginSizeInMainAxis(aReflowState.mComputedBorderPadding);
 
   nscoord frameCrossSize;
 
-  if (!shouldReflowChildren) {
-    // So far, it looks like none of our flex items need a reflow.
-    // HOWEVER: if we already gave any of them a measuring reflow, then we
-    // should consider it dirty -- it'll need a "real" reflow to undo the
-    // effects of our measuring reflow.
-    for (uint32_t i = 0; i < items.Length(); ++i) {
-      if (items[i].HadMeasuringReflow()) {
-        shouldReflowChildren = true;
-        break;
-      }
-    }
-  }
+  MainAxisPositionTracker mainAxisPosnTracker(this, axisTracker,
+                                              aReflowState, items);
 
-  if (!shouldReflowChildren) {
-    // Children don't need reflow --> assume our content-box size is the same
-    // since our last reflow.
-    frameCrossSize = mCachedContentBoxCrossSize +
-      axisTracker.GetMarginSizeInCrossAxis(aReflowState.mComputedBorderPadding);
-  } else {
-    MainAxisPositionTracker mainAxisPosnTracker(this, axisTracker,
-                                                aReflowState, items);
+  // First loop: Compute main axis position & cross-axis size of each item
+  for (uint32_t i = 0; i < items.Length(); ++i) {
+    FlexItem& curItem = items[i];
 
-    // First loop: Compute main axis position & cross-axis size of each item
-    for (uint32_t i = 0; i < items.Length(); ++i) {
-      FlexItem& curItem = items[i];
-
-      nsHTMLReflowState childReflowState(aPresContext, aReflowState,
-                                         curItem.Frame(),
-                                         nsSize(aReflowState.ComputedWidth(),
-                                                NS_UNCONSTRAINEDSIZE));
-      // Override computed main-size
-      if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
-        childReflowState.SetComputedWidth(curItem.GetMainSize());
-      } else {
-        childReflowState.SetComputedHeight(curItem.GetMainSize());
-      }
-
-      PositionItemInMainAxis(mainAxisPosnTracker, curItem);
-
-      nsresult rv =
-        SizeItemInCrossAxis(aPresContext, axisTracker,
-                            childReflowState, curItem);
-      NS_ENSURE_SUCCESS(rv, rv);
+    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
+                                       curItem.Frame(),
+                                       nsSize(aReflowState.ComputedWidth(),
+                                              NS_UNCONSTRAINEDSIZE));
+    // Override computed main-size
+    if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
+      childReflowState.SetComputedWidth(curItem.GetMainSize());
+    } else {
+      childReflowState.SetComputedHeight(curItem.GetMainSize());
     }
 
-    // SIZE & POSITION THE FLEX LINE (IN CROSS AXIS)
-    // Set up state for cross-axis alignment, at a high level (outside the
-    // scope of a particular flex line)
-    CrossAxisPositionTracker
-      crossAxisPosnTracker(this, axisTracker, aReflowState);
+    PositionItemInMainAxis(mainAxisPosnTracker, curItem);
 
-    // Set up state for cross-axis-positioning of children _within_ a single
-    // flex line.
-    SingleLineCrossAxisPositionTracker
-      lineCrossAxisPosnTracker(this, axisTracker, items);
+    nsresult rv =
+      SizeItemInCrossAxis(aPresContext, axisTracker,
+                          childReflowState, curItem);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // SIZE & POSITION THE FLEX LINE (IN CROSS AXIS)
+  // Set up state for cross-axis alignment, at a high level (outside the
+  // scope of a particular flex line)
+  CrossAxisPositionTracker
+    crossAxisPosnTracker(this, axisTracker, aReflowState);
 
-    lineCrossAxisPosnTracker.ComputeLineCrossSize(items);
-    // XXXdholbert Once we've got multi-line flexbox support: here, after we've
-    // computed the cross size of all lines, we need to check if if
-    // 'align-content' is 'stretch' -- if it is, we need to give each line an
-    // additional share of our flex container's desired cross-size. (if it's
-    // not NS_AUTOHEIGHT and there's any cross-size left over to distribute)
+  // Set up state for cross-axis-positioning of children _within_ a single
+  // flex line.
+  SingleLineCrossAxisPositionTracker
+    lineCrossAxisPosnTracker(this, axisTracker, items);
 
-    // Figure out our flex container's cross size
-    mCachedContentBoxCrossSize =
-      axisTracker.GetCrossComponent(nsSize(aReflowState.ComputedWidth(),
-                                           aReflowState.ComputedHeight()));
+  lineCrossAxisPosnTracker.ComputeLineCrossSize(items);
+  // XXXdholbert Once we've got multi-line flexbox support: here, after we've
+  // computed the cross size of all lines, we need to check if if
+  // 'align-content' is 'stretch' -- if it is, we need to give each line an
+  // additional share of our flex container's desired cross-size. (if it's
+  // not NS_AUTOHEIGHT and there's any cross-size left over to distribute)
+
+  // Figure out our flex container's cross size
+  mCachedContentBoxCrossSize =
+    axisTracker.GetCrossComponent(nsSize(aReflowState.ComputedWidth(),
+                                         aReflowState.ComputedHeight()));
 
-    if (mCachedContentBoxCrossSize == NS_AUTOHEIGHT) {
-      // Unconstrained 'auto' cross-size: shrink-wrap our line(s), subject
-      // to our min-size / max-size constraints in that axis.
-      nscoord minCrossSize =
-        axisTracker.GetCrossComponent(nsSize(aReflowState.mComputedMinWidth,
-                                             aReflowState.mComputedMinHeight));
-      nscoord maxCrossSize =
-        axisTracker.GetCrossComponent(nsSize(aReflowState.mComputedMaxWidth,
-                                             aReflowState.mComputedMaxHeight));
-      mCachedContentBoxCrossSize =
-        NS_CSS_MINMAX(lineCrossAxisPosnTracker.GetLineCrossSize(),
-                      minCrossSize, maxCrossSize);
-    }
-    if (lineCrossAxisPosnTracker.GetLineCrossSize() !=
-        mCachedContentBoxCrossSize) {
-      // XXXdholbert When we support multi-line flex containers, we should
-      // distribute any extra space among or between our lines here according
-      // to 'align-content'. For now, we do the single-line special behavior:
-      // "If the flex container has only a single line (even if it's a
-      // multi-line flex container), the cross size of the flex line is the
-      // flex container's inner cross size."
-      lineCrossAxisPosnTracker.SetLineCrossSize(mCachedContentBoxCrossSize);
-    }
-    frameCrossSize = mCachedContentBoxCrossSize +
-      axisTracker.GetMarginSizeInCrossAxis(aReflowState.mComputedBorderPadding);
+  if (mCachedContentBoxCrossSize == NS_AUTOHEIGHT) {
+    // Unconstrained 'auto' cross-size: shrink-wrap our line(s), subject
+    // to our min-size / max-size constraints in that axis.
+    nscoord minCrossSize =
+      axisTracker.GetCrossComponent(nsSize(aReflowState.mComputedMinWidth,
+                                           aReflowState.mComputedMinHeight));
+    nscoord maxCrossSize =
+      axisTracker.GetCrossComponent(nsSize(aReflowState.mComputedMaxWidth,
+                                           aReflowState.mComputedMaxHeight));
+    mCachedContentBoxCrossSize =
+      NS_CSS_MINMAX(lineCrossAxisPosnTracker.GetLineCrossSize(),
+                    minCrossSize, maxCrossSize);
+  }
+  if (lineCrossAxisPosnTracker.GetLineCrossSize() !=
+      mCachedContentBoxCrossSize) {
+    // XXXdholbert When we support multi-line flex containers, we should
+    // distribute any extra space among or between our lines here according
+    // to 'align-content'. For now, we do the single-line special behavior:
+    // "If the flex container has only a single line (even if it's a
+    // multi-line flex container), the cross size of the flex line is the
+    // flex container's inner cross size."
+    lineCrossAxisPosnTracker.SetLineCrossSize(mCachedContentBoxCrossSize);
+  }
+  frameCrossSize = mCachedContentBoxCrossSize +
+    axisTracker.GetMarginSizeInCrossAxis(aReflowState.mComputedBorderPadding);
 
-    // XXXdholbert FOLLOW ACTUAL RULES FOR FLEX CONTAINER BASELINE
-    // If we have any baseline-aligned items on first line, use their baseline.
-    // ...ELSE if we have at least one flex item and our first flex item's
-    //         baseline is parallel to main axis, then use that baseline.
-    // ...ELSE use "after" edge of content box.
-    // Default baseline: the "after" edge of content box. (Note: if we have any
-    // flex items, they'll override this.)
-    mCachedAscent = mCachedContentBoxCrossSize +
-      aReflowState.mComputedBorderPadding.top;
+  // XXXdholbert FOLLOW ACTUAL RULES FOR FLEX CONTAINER BASELINE
+  // If we have any baseline-aligned items on first line, use their baseline.
+  // ...ELSE if we have at least one flex item and our first flex item's
+  //         baseline is parallel to main axis, then use that baseline.
+  // ...ELSE use "after" edge of content box.
+  // Default baseline: the "after" edge of content box. (Note: if we have any
+  // flex items, they'll override this.)
+  mCachedAscent = mCachedContentBoxCrossSize +
+    aReflowState.mComputedBorderPadding.top;
+
+  // Position the items in cross axis, within their line
+  for (uint32_t i = 0; i < items.Length(); ++i) {
+    PositionItemInCrossAxis(crossAxisPosnTracker.GetPosition(),
+                            lineCrossAxisPosnTracker, items[i]);
+  }
 
-    // Position the items in cross axis, within their line
-    for (uint32_t i = 0; i < items.Length(); ++i) {
-      PositionItemInCrossAxis(crossAxisPosnTracker.GetPosition(),
-                              lineCrossAxisPosnTracker, items[i]);
+  // FINAL REFLOW: Give each child frame another chance to reflow, now that
+  // we know its final size and position.
+  for (uint32_t i = 0; i < items.Length(); ++i) {
+    FlexItem& curItem = items[i];
+    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
+                                       curItem.Frame(),
+                                       nsSize(aReflowState.ComputedWidth(),
+                                              NS_UNCONSTRAINEDSIZE));
+
+    // Keep track of whether we've overriden the child's computed height
+    // and/or width, so we can set its resize flags accordingly.
+    bool didOverrideComputedWidth = false;
+    bool didOverrideComputedHeight = false;
+
+    // Override computed main-size
+    if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
+      childReflowState.SetComputedWidth(curItem.GetMainSize());
+      didOverrideComputedWidth = true;
+    } else {
+      childReflowState.SetComputedHeight(curItem.GetMainSize());
+      didOverrideComputedHeight = true;
     }
 
-    // FINAL REFLOW: Give each child frame another chance to reflow, now that
-    // we know its final size and position.
-    for (uint32_t i = 0; i < items.Length(); ++i) {
-      FlexItem& curItem = items[i];
-      nsHTMLReflowState childReflowState(aPresContext, aReflowState,
-                                         curItem.Frame(),
-                                         nsSize(aReflowState.ComputedWidth(),
-                                                NS_UNCONSTRAINEDSIZE));
-
-      // Keep track of whether we've overriden the child's computed height
-      // and/or width, so we can set its resize flags accordingly.
-      bool didOverrideComputedWidth = false;
-      bool didOverrideComputedHeight = false;
-
-      // Override computed main-size
-      if (IsAxisHorizontal(axisTracker.GetMainAxis())) {
-        childReflowState.SetComputedWidth(curItem.GetMainSize());
+    // Override reflow state's computed cross-size, for stretched items.
+    if (curItem.IsStretched()) {
+      MOZ_ASSERT(curItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH,
+                 "stretched item w/o 'align-self: stretch'?");
+      if (IsAxisHorizontal(axisTracker.GetCrossAxis())) {
+        childReflowState.SetComputedWidth(curItem.GetCrossSize());
         didOverrideComputedWidth = true;
       } else {
-        childReflowState.SetComputedHeight(curItem.GetMainSize());
+        // If this item's height is stretched, it's a relative height.
+        curItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
+        childReflowState.SetComputedHeight(curItem.GetCrossSize());
         didOverrideComputedHeight = true;
       }
+    }
 
-      // Override reflow state's computed cross-size, for stretched items.
-      if (curItem.IsStretched()) {
-        MOZ_ASSERT(curItem.GetAlignSelf() == NS_STYLE_ALIGN_ITEMS_STRETCH,
-                   "stretched item w/o 'align-self: stretch'?");
-        if (IsAxisHorizontal(axisTracker.GetCrossAxis())) {
-          childReflowState.SetComputedWidth(curItem.GetCrossSize());
-          didOverrideComputedWidth = true;
-        } else {
-          // If this item's height is stretched, it's a relative height.
-          curItem.Frame()->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
-          childReflowState.SetComputedHeight(curItem.GetCrossSize());
-          didOverrideComputedHeight = true;
-        }
-      }
+    // XXXdholbert Might need to actually set the correct margins in the
+    // reflow state at some point, so that they can be saved on the frame for
+    // UsedMarginProperty().  Maybe doesn't matter though...?
 
-      // XXXdholbert Might need to actually set the correct margins in the
-      // reflow state at some point, so that they can be saved on the frame for
-      // UsedMarginProperty().  Maybe doesn't matter though...?
-
-      // If we're overriding the computed width or height, *and* we had an
-      // earlier "measuring" reflow, then this upcoming reflow needs to be
-      // treated as a resize.
-      if (curItem.HadMeasuringReflow()) {
-        if (didOverrideComputedWidth) {
-          // (This is somewhat redundant, since the reflow state already
-          // sets mHResize whenever our computed width has changed since the
-          // previous reflow. Still, it's nice for symmetry, and it may become
-          // necessary once we support orthogonal flows.)
-          childReflowState.mFlags.mHResize = true;
-        }
-        if (didOverrideComputedHeight) {
-          childReflowState.mFlags.mVResize = true;
-        }
+    // If we're overriding the computed width or height, *and* we had an
+    // earlier "measuring" reflow, then this upcoming reflow needs to be
+    // treated as a resize.
+    if (curItem.HadMeasuringReflow()) {
+      if (didOverrideComputedWidth) {
+        // (This is somewhat redundant, since the reflow state already
+        // sets mHResize whenever our computed width has changed since the
+        // previous reflow. Still, it's nice for symmetry, and it may become
+        // necessary once we support orthogonal flows.)
+        childReflowState.mFlags.mHResize = true;
       }
-      // NOTE: Be very careful about doing anything else with childReflowState
-      // after this point, because some of its methods (e.g. SetComputedWidth)
-      // internally call InitResizeFlags and stomp on mVResize & mHResize.
+      if (didOverrideComputedHeight) {
+        childReflowState.mFlags.mVResize = true;
+      }
+    }
+    // NOTE: Be very careful about doing anything else with childReflowState
+    // after this point, because some of its methods (e.g. SetComputedWidth)
+    // internally call InitResizeFlags and stomp on mVResize & mHResize.
 
-      nscoord mainPosn = curItem.GetMainPosition();
-      nscoord crossPosn = curItem.GetCrossPosition();
-      if (!AxisGrowsInPositiveDirection(axisTracker.GetMainAxis())) {
-        mainPosn = frameMainSize - mainPosn;
-      }
-      if (!AxisGrowsInPositiveDirection(axisTracker.GetCrossAxis())) {
-        crossPosn = frameCrossSize - crossPosn;
-      }
+    nscoord mainPosn = curItem.GetMainPosition();
+    nscoord crossPosn = curItem.GetCrossPosition();
+    if (!AxisGrowsInPositiveDirection(axisTracker.GetMainAxis())) {
+      mainPosn = frameMainSize - mainPosn;
+    }
+    if (!AxisGrowsInPositiveDirection(axisTracker.GetCrossAxis())) {
+      crossPosn = frameCrossSize - crossPosn;
+    }
 
-      nsPoint physicalPosn =
-        axisTracker.PhysicalPositionFromLogicalPosition(mainPosn, crossPosn);
+    nsPoint physicalPosn =
+      axisTracker.PhysicalPositionFromLogicalPosition(mainPosn, crossPosn);
 
-      nsHTMLReflowMetrics childDesiredSize;
-      nsReflowStatus childReflowStatus;
-      nsresult rv = ReflowChild(curItem.Frame(), aPresContext,
-                                childDesiredSize, childReflowState,
-                                physicalPosn.x, physicalPosn.y,
-                                0, childReflowStatus);
-      NS_ENSURE_SUCCESS(rv, rv);
+    nsHTMLReflowMetrics childDesiredSize;
+    nsReflowStatus childReflowStatus;
+    nsresult rv = ReflowChild(curItem.Frame(), aPresContext,
+                              childDesiredSize, childReflowState,
+                              physicalPosn.x, physicalPosn.y,
+                              0, childReflowStatus);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-      // 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(NS_FRAME_IS_COMPLETE(childReflowStatus),
-                 "We gave flex item unconstrained available height, so it "
-                 "should be complete");
+    // 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(NS_FRAME_IS_COMPLETE(childReflowStatus),
+               "We gave flex item unconstrained available height, so it "
+               "should be complete");
 
-      // Apply CSS relative positioning
-      const nsStyleDisplay* styleDisp = curItem.Frame()->StyleDisplay();
-      if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) {
-        physicalPosn.x += childReflowState.mComputedOffsets.left;
-        physicalPosn.y += childReflowState.mComputedOffsets.top;
-      }
+    // Apply CSS relative positioning
+    const nsStyleDisplay* styleDisp = curItem.Frame()->StyleDisplay();
+    if (NS_STYLE_POSITION_RELATIVE == styleDisp->mPosition) {
+      physicalPosn.x += childReflowState.mComputedOffsets.left;
+      physicalPosn.y += childReflowState.mComputedOffsets.top;
+    }
 
-      rv = FinishReflowChild(curItem.Frame(), aPresContext,
-                             &childReflowState, childDesiredSize,
-                             physicalPosn.x, physicalPosn.y, 0);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
+    rv = FinishReflowChild(curItem.Frame(), aPresContext,
+                           &childReflowState, childDesiredSize,
+                           physicalPosn.x, physicalPosn.y, 0);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // XXXdholbert This could be more elegant
   aDesiredSize.width =
     IsAxisHorizontal(axisTracker.GetMainAxis()) ?
     frameMainSize : frameCrossSize;
   aDesiredSize.height =
     IsAxisHorizontal(axisTracker.GetCrossAxis()) ?