Bug 641341. Speed up CalculateHypotheticalBox for the case of a block containing a bunch of lines but absolutely no in-flows anywhere. r=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 04 Oct 2011 23:47:08 -0400
changeset 78782 0cd9ed297f73e19341910c182b3031ef7272a0b8
parent 78781 308f307cababea77ea12160fc981d9a7e9198ec0
child 78783 109adc21b581e7fa81551bbbc342b3a60391ddfa
child 80515 5b3aeb566a978899d4ca3d296c4e2ab23807bccb
push id506
push userclegnitto@mozilla.com
push dateWed, 09 Nov 2011 02:03:18 +0000
treeherdermozilla-aurora@63587fc7bb93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs641341
milestone10.0a1
Bug 641341. Speed up CalculateHypotheticalBox for the case of a block containing a bunch of lines but absolutely no in-flows anywhere. r=roc
layout/generic/nsHTMLReflowState.cpp
layout/generic/nsHTMLReflowState.h
layout/generic/nsLineBox.cpp
layout/generic/nsLineBox.h
layout/generic/nsPlaceholderFrame.cpp
layout/generic/nsPlaceholderFrame.h
--- a/layout/generic/nsHTMLReflowState.cpp
+++ b/layout/generic/nsHTMLReflowState.cpp
@@ -50,16 +50,18 @@
 #include "nsBlockFrame.h"
 #include "nsLineBox.h"
 #include "nsImageFrame.h"
 #include "nsTableFrame.h"
 #include "nsTableCellFrame.h"
 #include "nsIServiceManager.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsLayoutUtils.h"
+#include "nsPlaceholderFrame.h"
+#include "nsFrameManager.h"
 #include "mozilla/Preferences.h"
 #ifdef IBMBIDI
 #include "nsBidiUtils.h"
 #endif
 
 #ifdef NS_DEBUG
 #undef NOISY_VERTICAL_ALIGN
 #else
@@ -842,17 +844,17 @@ static bool AreAllEarlierInFlowFramesEmp
 
 // Calculate the hypothetical box that the element would have if it were in
 // the flow. The values returned are relative to the padding edge of the
 // absolute containing block
 // aContainingBlock is the placeholder's containing block (XXX rename it?)
 // cbrs->frame is the actual containing block
 void
 nsHTMLReflowState::CalculateHypotheticalBox(nsPresContext*    aPresContext,
-                                            nsIFrame*         aPlaceholderFrame,
+                                            nsPlaceholderFrame* aPlaceholderFrame,
                                             nsIFrame*         aContainingBlock,
                                             nscoord           aBlockLeftContentEdge,
                                             nscoord           aBlockContentWidth,
                                             const nsHTMLReflowState* cbrs,
                                             nsHypotheticalBox& aHypotheticalBox,
                                             nsIAtom*          aFrameType)
 {
   NS_ASSERTION(mStyleDisplay->mOriginalDisplay != NS_STYLE_DISPLAY_NONE,
@@ -925,66 +927,58 @@ nsHTMLReflowState::CalculateHypothetical
   // First, determine the hypothetical box's mTop.  We want to check the
   // content insertion frame of aContainingBlock for block-ness, but make
   // sure to compute all coordinates in the coordinate system of
   // aContainingBlock.
   nsBlockFrame* blockFrame =
     nsLayoutUtils::GetAsBlock(aContainingBlock->GetContentInsertionFrame());
   if (blockFrame) {
     nscoord blockYOffset = blockFrame->GetOffsetTo(aContainingBlock).y;
-    bool isValid;
-    nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid);
-    if (!isValid) {
+    const nsLineBox* lineBox = aPlaceholderFrame->GetCachedLineBox();
+    if (!lineBox) {
       // Give up.  We're probably dealing with somebody using
       // position:absolute inside native-anonymous content anyway.
       aHypotheticalBox.mTop = placeholderOffset.y;
     } else {
-      NS_ASSERTION(iter.GetContainer() == blockFrame,
-                   "Found placeholder in wrong block!");
-      nsBlockFrame::line_iterator lineBox = iter.GetLine();
-
       // How we determine the hypothetical box depends on whether the element
       // would have been inline-level or block-level
       if (mStyleDisplay->IsOriginalDisplayInlineOutside()) {
         // Use the top of the inline box which the placeholder lives in
         // as the hypothetical box's top.
         aHypotheticalBox.mTop = lineBox->mBounds.y + blockYOffset;
       } else {
         // The element would have been block-level which means it would
         // be below the line containing the placeholder frame, unless
         // all the frames before it are empty.  In that case, it would
         // have been just before this line.
         // XXXbz the line box is not fully reflowed yet if our
         // containing block is relatively positioned...
-        if (lineBox != iter.End()) {
+        bool allEmpty = true;
+        if (!lineBox->IsValidCachedIsEmpty() || !lineBox->CachedIsEmpty()) {
           nsIFrame * firstFrame = lineBox->mFirstChild;
           bool found = false;
-          bool allEmpty = true;
           while (firstFrame) { // See bug 223064
             allEmpty = AreAllEarlierInFlowFramesEmpty(firstFrame,
               aPlaceholderFrame, &found);
             if (found || !allEmpty)
               break;
             firstFrame = firstFrame->GetNextSibling();
           }
           NS_ASSERTION(firstFrame, "Couldn't find placeholder!");
+        }
 
-          if (allEmpty) {
-            // The top of the hypothetical box is the top of the line
-            // containing the placeholder, since there is nothing in the
-            // line before our placeholder except empty frames.
-            aHypotheticalBox.mTop = lineBox->mBounds.y + blockYOffset;
-          } else {
-            // The top of the hypothetical box is just below the line
-            // containing the placeholder.
-            aHypotheticalBox.mTop = lineBox->mBounds.YMost() + blockYOffset;
-          }
+        if (allEmpty) {
+          // The top of the hypothetical box is the top of the line
+          // containing the placeholder, since there is nothing in the
+          // line before our placeholder except empty frames.
+          aHypotheticalBox.mTop = lineBox->mBounds.y + blockYOffset;
         } else {
-          // Just use the placeholder's y-offset wrt the containing block
-          aHypotheticalBox.mTop = placeholderOffset.y;
+          // The top of the hypothetical box is just below the line
+          // containing the placeholder.
+          aHypotheticalBox.mTop = lineBox->mBounds.YMost() + blockYOffset;
         }
       }
     }
   } else {
     // The containing block is not a block, so it's probably something
     // like a XUL box, etc.
     // Just use the placeholder's y-offset
     aHypotheticalBox.mTop = placeholderOffset.y;
@@ -1100,19 +1094,20 @@ nsHTMLReflowState::InitAbsoluteConstrain
                   "containing block height must be constrained");
 
   NS_ASSERTION(aFrameType != nsGkAtoms::tableFrame,
                "InitAbsoluteConstraints should not be called on table frames");
   NS_ASSERTION(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
                "Why are we here?");
 
   // Get the placeholder frame
-  nsIFrame*     placeholderFrame;
+  nsPlaceholderFrame*     placeholderFrame;
 
-  placeholderFrame = aPresContext->PresShell()->GetPlaceholderFrameFor(frame);
+  placeholderFrame =
+    aPresContext->PresShell()->FrameManager()->GetPlaceholderFrameFor(frame);
   NS_ASSERTION(nsnull != placeholderFrame, "no placeholder frame");
 
   // If both 'left' and 'right' are 'auto' or both 'top' and 'bottom' are
   // 'auto', then compute the hypothetical box of where the element would
   // have been if it had been in the flow
   nsHypotheticalBox hypotheticalBox;
   if (((eStyleUnit_Auto == mStylePosition->mOffset.GetLeftUnit()) &&
        (eStyleUnit_Auto == mStylePosition->mOffset.GetRightUnit())) ||
--- a/layout/generic/nsHTMLReflowState.h
+++ b/layout/generic/nsHTMLReflowState.h
@@ -44,16 +44,17 @@
 #include "nsStyleCoord.h"
 #include "nsIFrame.h"
 
 class nsPresContext;
 class nsRenderingContext;
 class nsFloatManager;
 class nsLineLayout;
 class nsIPercentHeightObserver;
+class nsPlaceholderFrame;
 
 struct nsStyleDisplay;
 struct nsStyleVisibility;
 struct nsStylePosition;
 struct nsStyleBorder;
 struct nsStyleMargin;
 struct nsStylePadding;
 struct nsStyleText;
@@ -506,17 +507,17 @@ protected:
   // it is a containing block) for the specified frame.  Also returns
   // the left edge and width of the containing block's content area.
   // These are returned in the coordinate space of the containing block.
   nsIFrame* GetHypotheticalBoxContainer(nsIFrame* aFrame,
                                         nscoord& aCBLeftEdge,
                                         nscoord& aCBWidth);
 
   void CalculateHypotheticalBox(nsPresContext*    aPresContext,
-                                nsIFrame*         aPlaceholderFrame,
+                                nsPlaceholderFrame* aPlaceholderFrame,
                                 nsIFrame*         aContainingBlock,
                                 nscoord           aBlockLeftContentEdge,
                                 nscoord           aBlockContentWidth,
                                 const nsHTMLReflowState* cbrs,
                                 nsHypotheticalBox& aHypotheticalBox,
                                 nsIAtom*          aFrameType);
 
   void InitAbsoluteConstraints(nsPresContext* aPresContext,
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -284,17 +284,17 @@ nsLineBox::IsEmpty() const
   }
   if (HasBullet()) {
     return PR_FALSE;
   }
   return PR_TRUE;
 }
 
 bool
-nsLineBox::CachedIsEmpty()
+nsLineBox::CachedIsEmpty() const
 {
   if (mFlags.mDirty) {
     return IsEmpty();
   }
   
   if (mFlags.mEmptyCacheValid) {
     return mFlags.mEmptyCacheState;
   }
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -481,24 +481,24 @@ public:
   }
 
   // whether the line box is "logically" empty (just like nsIFrame::IsEmpty)
   bool IsEmpty() const;
 
   // Call this only while in Reflow() for the block the line belongs
   // to, only between reflowing the line (or sliding it, if we skip
   // reflowing it) and the end of reflowing the block.
-  bool CachedIsEmpty();
+  bool CachedIsEmpty() const;
 
   void InvalidateCachedIsEmpty() {
     mFlags.mEmptyCacheValid = PR_FALSE;
   }
 
   // For debugging purposes
-  bool IsValidCachedIsEmpty() {
+  bool IsValidCachedIsEmpty() const {
     return mFlags.mEmptyCacheValid;
   }
 
 #ifdef DEBUG
   static PRInt32 GetCtorCount();
 #endif
 
   nsIFrame* mFirstChild;
@@ -509,18 +509,18 @@ public:
     PRUint32 mDirty : 1;
     PRUint32 mPreviousMarginDirty : 1;
     PRUint32 mHasClearance : 1;
     PRUint32 mBlock : 1;
     PRUint32 mImpactedByFloat : 1;
     PRUint32 mLineWrapped: 1;
     PRUint32 mInvalidateTextRuns : 1;
     PRUint32 mResizeReflowOptimizationDisabled: 1;  // default 0 = means that the opt potentially applies to this line. 1 = never skip reflowing this line for a resize reflow
-    PRUint32 mEmptyCacheValid: 1;
-    PRUint32 mEmptyCacheState: 1;
+    mutable PRUint32 mEmptyCacheValid: 1;
+    mutable PRUint32 mEmptyCacheState: 1;
     // mHasBullet indicates that this is an inline line whose block's
     // bullet is adjacent to this line and non-empty.
     PRUint32 mHasBullet : 1;
     // Indicates that this line *may* have a placeholder for a float
     // that was pushed to a later column or page.
     PRUint32 mHadFloatPushed : 1;
     PRUint32 mBreakType : 4;
 
--- a/layout/generic/nsPlaceholderFrame.cpp
+++ b/layout/generic/nsPlaceholderFrame.cpp
@@ -140,16 +140,25 @@ nsPlaceholderFrame::Reflow(nsPresContext
                            const nsHTMLReflowState& aReflowState,
                            nsReflowStatus&          aStatus)
 {
   DO_GLOBAL_REFLOW_COUNT("nsPlaceholderFrame");
   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
   aDesiredSize.width = 0;
   aDesiredSize.height = 0;
 
+  // Cache our line box.
+  mCachedLineBox = nsnull;
+  if (aReflowState.mLineLayout) {
+    nsLineList::iterator* line = aReflowState.mLineLayout->GetLine();
+    if (line) {
+      mCachedLineBox = line->get();
+    }
+  }
+
   aStatus = NS_FRAME_COMPLETE;
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return NS_OK;
 }
 
 void
 nsPlaceholderFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
--- a/layout/generic/nsPlaceholderFrame.h
+++ b/layout/generic/nsPlaceholderFrame.h
@@ -64,16 +64,18 @@
  */
 
 #ifndef nsPlaceholderFrame_h___
 #define nsPlaceholderFrame_h___
 
 #include "nsFrame.h"
 #include "nsGkAtoms.h"
 
+class nsLineBox;
+
 nsIFrame* NS_NewPlaceholderFrame(nsIPresShell* aPresShell,
                                  nsStyleContext* aContext,
                                  nsFrameState aTypeBit);
 
 // Frame state bits that are used to keep track of what this is a
 // placeholder for.
 #define PLACEHOLDER_FOR_FLOAT    NS_FRAME_STATE_BIT(20)
 #define PLACEHOLDER_FOR_ABSPOS   NS_FRAME_STATE_BIT(21)
@@ -195,13 +197,22 @@ public:
     NS_PRECONDITION(aFrame->GetType() == nsGkAtoms::placeholderFrame,
                     "Must have placeholder frame as input");
     nsIFrame* outOfFlow =
       static_cast<nsPlaceholderFrame*>(aFrame)->GetOutOfFlowFrame();
     NS_ASSERTION(outOfFlow, "Null out-of-flow for placeholder?");
     return outOfFlow;
   }
 
+  // GetCachedLineBox is only OK to call if you're sure this
+  // placeholder has has Reflow() called since any changes to the
+  // frame tree that could have affected which line box the
+  // placeholder is in.
+  const nsLineBox* GetCachedLineBox() const {
+    return mCachedLineBox;
+  }
+
 protected:
   nsIFrame* mOutOfFlowFrame;
+  nsLineBox* mCachedLineBox;
 };
 
 #endif /* nsPlaceholderFrame_h___ */