Bug 743402, Part 2: Pull GetEffectiveComputedHeight() into nsSplittableFrame and refactor it to utilize consumed height for paginated content. [r=roc]
☠☠ backed out by 91356879fbfd ☠ ☠
authorScott Johnson <sjohnson@mozilla.com>
Wed, 24 Jul 2013 12:47:06 -0500
changeset 152148 c7907c54187f551b6a5e605985771b33733155ad
parent 152147 2edbbf6440c4d33150ef1788d408ad5d5ae1ccfe
child 152149 ada93e976dca5a5a1eaa6e28ec03031720cca21e
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs743402
milestone25.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 743402, Part 2: Pull GetEffectiveComputedHeight() into nsSplittableFrame and refactor it to utilize consumed height for paginated content. [r=roc]
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsSplittableFrame.cpp
layout/generic/nsSplittableFrame.h
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -902,32 +902,35 @@ nsBlockFrame::Reflow(nsPresContext*     
   int32_t ctc = 0;        // We only use these if they are set (gLameReflowMetrics).
   if (gLameReflowMetrics) {
     start = PR_Now();
     ctc = nsLineBox::GetCtorCount();
   }
 #endif
 
   const nsHTMLReflowState *reflowState = &aReflowState;
+  nscoord consumedHeight = GetConsumedHeight();
+  nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState,
+                                                               consumedHeight);
   Maybe<nsHTMLReflowState> mutableReflowState;
   // If we have non-auto height, we're clipping our kids and we fit,
   // make sure our kids fit too.
   if (aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE &&
       aReflowState.ComputedHeight() != NS_AUTOHEIGHT &&
       ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) {
     nsMargin heightExtras = aReflowState.mComputedBorderPadding;
     if (GetSkipSides() & NS_SIDE_TOP) {
       heightExtras.top = 0;
     } else {
       // Bottom margin never causes us to create continuations, so we
       // don't need to worry about whether it fits in its entirety.
       heightExtras.top += aReflowState.mComputedMargin.top;
     }
 
-    if (GetEffectiveComputedHeight(aReflowState) + heightExtras.TopBottom() <=
+    if (effectiveComputedHeight + heightExtras.TopBottom() <=
         aReflowState.availableHeight) {
       mutableReflowState.construct(aReflowState);
       mutableReflowState.ref().availableHeight = NS_UNCONSTRAINEDSIZE;
       reflowState = mutableReflowState.addr();
     }
   }
 
   // See comment below about oldSize. Use *only* for the
@@ -952,18 +955,22 @@ nsBlockFrame::Reflow(nsPresContext*     
   ClearLineCursor();
 
   if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) {
     return NS_OK;
   }
 
   bool topMarginRoot, bottomMarginRoot;
   IsMarginRoot(&topMarginRoot, &bottomMarginRoot);
+
+  // Cache the consumed height in the block reflow state so that we don't have
+  // to continually recompute it.
   nsBlockReflowState state(*reflowState, aPresContext, this,
-                           topMarginRoot, bottomMarginRoot, needFloatManager);
+                           topMarginRoot, bottomMarginRoot, needFloatManager,
+                           consumedHeight);
 
 #ifdef IBMBIDI
   if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)
     static_cast<nsBlockFrame*>(GetFirstContinuation())->ResolveBidi();
 #endif // IBMBIDI
 
   if (RenumberLists(aPresContext)) {
     AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
@@ -7073,36 +7080,16 @@ nsBlockFrame::GetNearestAncestorBlock(ns
     }
     // Not a block. Check its parent next.
     aCandidate = aCandidate->GetParent();
   }
   NS_NOTREACHED("Fell off frame tree looking for ancestor block!");
   return nullptr;
 }
 
-nscoord
-nsBlockFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState) const
-{
-  nscoord height = aReflowState.ComputedHeight();
-  NS_ABORT_IF_FALSE(height != NS_UNCONSTRAINEDSIZE, "Don't call me!");
-
-  if (GetPrevInFlow()) {
-    // Reduce the height by the computed height of prev-in-flows.
-    for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
-      height -= prev->GetRect().height;
-    }
-    // We just subtracted our top-border padding, since it was included in the
-    // first frame's height. Add it back to get the content height.
-    height += aReflowState.mComputedBorderPadding.top;
-    // We may have stretched the frame beyond its computed height. Oh well.
-    height = std::max(0, height);
-  }
-  return height;
-}
-
 #ifdef IBMBIDI
 nsresult
 nsBlockFrame::ResolveBidi()
 {
   NS_ASSERTION(!GetPrevInFlow(),
                "ResolveBidi called on non-first continuation");
 
   nsPresContext* presContext = PresContext();
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -791,23 +791,16 @@ public:
     return 0 != (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES);
   }
   FrameLines* GetOverflowLines() const;
 protected:
   FrameLines* RemoveOverflowLines();
   void SetOverflowLines(FrameLines* aOverflowLines);
   void DestroyOverflowLines();
 
-  // Determine the computed height that's in effect for this block
-  // frame (that is, our computed height minus the heights of our
-  // previous in-flows).
-  // XXXbz this clearly makes laying out a block with N in-flows
-  // O(N^2)!  Good thing the constant is tiny.
-  nscoord GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState) const;
-
   /**
    * This class is useful for efficiently modifying the out of flow
    * overflow list. It gives the client direct writable access to
    * the frame list temporarily but ensures that property is only
    * written back if absolutely necessary.
    */
   struct nsAutoOOFFrameList {
     nsFrameList mList;
--- a/layout/generic/nsSplittableFrame.cpp
+++ b/layout/generic/nsSplittableFrame.cpp
@@ -7,16 +7,17 @@
  * base class for rendering objects that can be split across lines,
  * columns, or pages
  */
 
 #include "nsSplittableFrame.h"
 #include "nsIContent.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
+#include "nsContainerFrame.h"
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSplittableFrame)
 
 void
 nsSplittableFrame::Init(nsIContent*      aContent,
                         nsIFrame*        aParent,
                         nsIFrame*        aPrevInFlow)
 {
@@ -211,16 +212,90 @@ nsSplittableFrame::GetConsumedHeight() c
   // Reduce the height by the computed height of prev-in-flows.
   for (nsIFrame* prev = GetPrevInFlow(); prev; prev = prev->GetPrevInFlow()) {
     height += prev->GetRect().height;
   }
 
   return height;
 }
 
+nscoord
+nsSplittableFrame::GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState,
+                                              nscoord aConsumedHeight) const
+{
+  nscoord height = aReflowState.ComputedHeight();
+  if (height == NS_INTRINSICSIZE) {
+    return NS_INTRINSICSIZE;
+  }
+
+  if (aConsumedHeight == NS_INTRINSICSIZE) {
+    aConsumedHeight = GetConsumedHeight();
+  }
+
+  height -= aConsumedHeight;
+
+  if (aConsumedHeight != NS_INTRINSICSIZE) {
+    // We just subtracted our top-border padding, since it was included in the
+    // first frame's height. Add it back to get the content height.
+    height += aReflowState.mComputedBorderPadding.top;
+  }
+
+  // We may have stretched the frame beyond its computed height. Oh well.
+  height = std::max(0, height);
+
+  return height;
+}
+
+void
+nsSplittableFrame::ComputeFinalHeight(const nsHTMLReflowState& aReflowState,
+                                      nsReflowStatus*          aStatus,
+                                      nscoord                  aContentHeight,
+                                      const nsMargin&          aBorderPadding,
+                                      nsHTMLReflowMetrics&     aMetrics,
+                                      nscoord                  aConsumed)
+{
+
+  // Figure out how much of the computed height should be
+  // applied to this frame.
+  nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState,
+                                                              aConsumed);
+  NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this)
+                  && computedHeightLeftOver ),
+               "overflow container must not have computedHeightLeftOver");
+
+  aMetrics.height =
+    NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.top,
+                                              computedHeightLeftOver),
+                         aBorderPadding.bottom);
+
+  if (NS_FRAME_IS_NOT_COMPLETE(*aStatus)
+      && aMetrics.height < aReflowState.availableHeight) {
+    // We ran out of height on this page but we're incomplete
+    // Set status to complete except for overflow
+    NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus);
+  }
+
+  if (NS_FRAME_IS_COMPLETE(*aStatus)) {
+    if (computedHeightLeftOver > 0 &&
+        NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight &&
+        aMetrics.height > aReflowState.availableHeight) {
+      // We don't fit and we consumed some of the computed height,
+      // so we should consume all the available height and then
+      // break.  If our bottom border/padding straddles the break
+      // point, then this will increase our height and push the
+      // border/padding to the next page/column.
+      aMetrics.height = std::max(aReflowState.availableHeight,
+                                 aContentHeight);
+      NS_FRAME_SET_INCOMPLETE(*aStatus);
+      if (!GetNextInFlow())
+        *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
+    }
+  }
+}
+
 #ifdef DEBUG
 void
 nsSplittableFrame::DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent)
 {
   nsFrame::DumpBaseRegressionData(aPresContext, out, aIndent);
   if (nullptr != mNextContinuation) {
     IndentBy(out, aIndent);
     fprintf(out, "<next-continuation va=\"%p\"/>\n", (void*)mNextContinuation);
--- a/layout/generic/nsSplittableFrame.h
+++ b/layout/generic/nsSplittableFrame.h
@@ -80,16 +80,49 @@ protected:
    * Determine the height consumed by our previous-in-flows.
    *
    * @note (bz) This makes laying out a splittable frame with N in-flows
    *       O(N^2)! So, use this function with caution and minimize the number
    *       of calls to this method.
    */
   nscoord GetConsumedHeight() const;
 
+  /**
+   * Retrieve the effective computed height of this frame, which is the computed
+   * height, minus the height consumed by any previous in-flows.
+   */
+  nscoord GetEffectiveComputedHeight(const nsHTMLReflowState& aReflowState,
+                                     nscoord aConsumed = NS_INTRINSICSIZE) const;
+
+  /**
+   * Compute the final height of this frame.
+   *
+   * @param aReflowState Data structure passed from parent during reflow.
+   * @param aReflowStatus A pointed to the reflow status for when we're finished
+   *        doing reflow. this will get set appropriately if the height causes
+   *        us to exceed the current available (page) height.
+   * @param aContentHeight The height of content, precomputed outside of this
+   *        function. The final height that is used in aMetrics will be set to
+   *        either this or the available height, whichever is larger, in the
+   *        case where our available height is constrained, and we overflow that
+   *        available height.
+   * @param aBorderPadding The margins representing the border padding for block
+   *        frames. Can be 0.
+   * @param aMetrics Out parameter for final height. Taken as an
+   *        nsHTMLReflowMetrics object so that aMetrics can be passed in
+   *        directly during reflow.
+   * @param aConsumed The height already consumed by our previous-in-flows.
+   */
+  void ComputeFinalHeight(const nsHTMLReflowState& aReflowState,
+                          nsReflowStatus*          aStatus,
+                          nscoord                  aContentHeight,
+                          const nsMargin&          aBorderPadding,
+                          nsHTMLReflowMetrics&     aMetrics,
+                          nscoord                  aConsumed);
+
 #ifdef DEBUG
   virtual void DumpBaseRegressionData(nsPresContext* aPresContext, FILE* out, int32_t aIndent) MOZ_OVERRIDE;
 #endif
 
   nsIFrame*   mPrevContinuation;
   nsIFrame*   mNextContinuation;
 };