Bug 683952. Speed up iterating over the continuations of the frame when scrolling to it. r=roc
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 06 Sep 2011 22:57:46 -0400
changeset 76632 64c328251a24e767893231306b311d81061cc12f
parent 76631 de06684dabc4f450a4e6e2984868e6820fcb6df1
child 76633 fc945dec50bb044858b48c22efd7dbc59f9deabd
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs683952
milestone9.0a1
Bug 683952. Speed up iterating over the continuations of the frame when scrolling to it. r=roc
layout/base/nsPresShell.cpp
layout/generic/nsILineIterator.h
layout/generic/nsLineBox.cpp
layout/generic/nsLineBox.h
layout/tables/nsTableRowGroupFrame.cpp
layout/tables/nsTableRowGroupFrame.h
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -4039,23 +4039,29 @@ PresShell::ScrollToAnchor()
  * Helper (per-continuation) for ScrollContentIntoView.
  *
  * @param aContainerFrame [in] the frame which aRect is relative to
  * @param aFrame [in] Frame whose bounds should be unioned
  * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
  * we should include the top of the line in the added rectangle
  * @param aRect [inout] rect into which its bounds should be unioned
  * @param aHaveRect [inout] whether aRect contains data yet
+ * @param aPrevBlock [inout] the block aLines is a line iterator for
+ * @param aLines [inout] the line iterator we're using
+ * @param aCurLine [inout] the line to start looking from in this iterator
  */
 static void
 AccumulateFrameBounds(nsIFrame* aContainerFrame,
                       nsIFrame* aFrame,
                       PRBool aUseWholeLineHeightForInlines,
                       nsRect& aRect,
-                      PRBool& aHaveRect)
+                      PRBool& aHaveRect,
+                      nsIFrame*& aPrevBlock,
+                      nsAutoLineIterator& aLines,
+                      PRInt32& aCurLine)
 {
   nsRect frameBounds = aFrame->GetRect() +
     aFrame->GetParent()->GetOffsetTo(aContainerFrame);
 
   // If this is an inline frame and either the bounds height is 0 (quirks
   // layout model) or aUseWholeLineHeightForInlines is set, we need to
   // change the top of the bounds to include the whole line.
   if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
@@ -4068,27 +4074,32 @@ AccumulateFrameBounds(nsIFrame* aContain
       prevFrame = f;
       f = prevFrame->GetParent();
     }
 
     if (f != aFrame &&
         f &&
         frameType == nsGkAtoms::blockFrame) {
       // find the line containing aFrame and increase the top of |offset|.
-      nsAutoLineIterator lines = f->GetLineIterator();
-      if (lines) {
-        PRInt32 index = lines->FindLineContaining(prevFrame);
+      if (f != aPrevBlock) {
+        aLines = f->GetLineIterator();
+        aPrevBlock = f;
+        aCurLine = 0;
+      }
+      if (aLines) {
+        PRInt32 index = aLines->FindLineContaining(prevFrame, aCurLine);
         if (index >= 0) {
+          aCurLine = index;
           nsIFrame *trash1;
           PRInt32 trash2;
           nsRect lineBounds;
           PRUint32 trash3;
 
-          if (NS_SUCCEEDED(lines->GetLine(index, &trash1, &trash2,
-                                          lineBounds, &trash3))) {
+          if (NS_SUCCEEDED(aLines->GetLine(index, &trash1, &trash2,
+                                           lineBounds, &trash3))) {
             lineBounds += f->GetOffsetTo(aContainerFrame);
             if (lineBounds.y < frameBounds.y) {
               frameBounds.height = frameBounds.YMost() - lineBounds.y;
               frameBounds.y = lineBounds.y;
             }
           }
         }
       }
@@ -4281,19 +4292,26 @@ PresShell::DoScrollContentIntoView(nsICo
   //         appropriately.
   // frameBounds is relative to container. We're assuming
   // that scrollframes don't split so every continuation of frame will
   // be a descendant of container. (Things would still mostly work
   // even if that assumption was false.)
   nsRect frameBounds;
   PRBool haveRect = PR_FALSE;
   PRBool useWholeLineHeightForInlines = aVPercent != NS_PRESSHELL_SCROLL_ANYWHERE;
+  // Reuse the same line iterator across calls to AccumulateFrameBounds.  We set
+  // it every time we detect a new block (stored in prevBlock).
+  nsIFrame* prevBlock = nsnull;
+  nsAutoLineIterator lines;
+  // The last line we found a continuation on in |lines|.  We assume that later
+  // continuations cannot come on earlier lines.
+  PRInt32 curLine = 0;
   do {
     AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
-                          frameBounds, haveRect);
+                          frameBounds, haveRect, prevBlock, lines, curLine);
   } while ((frame = frame->GetNextContinuation()));
 
   ScrollFrameRectIntoView(container, frameBounds, aVPercent, aHPercent,
                           aFlags);
 }
 
 PRBool
 PresShell::ScrollFrameRectIntoView(nsIFrame*     aFrame,
--- a/layout/generic/nsILineIterator.h
+++ b/layout/generic/nsILineIterator.h
@@ -99,19 +99,22 @@ public:
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags) = 0;
 
   /**
    * Given a frame that's a child of the block, find which line its on
-   * and return that line index. Returns -1 if the frame cannot be found.
+   * and return that line index, as long as it's at least as big as
+   * aStartLine.  Returns -1 if the frame cannot be found on lines
+   * starting with aStartLine.
    */
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame) = 0;
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame,
+                                     PRInt32 aStartLine = 0) = 0;
 
   // Given a line number and an X coordinate, find the frame on the
   // line that is nearest to the X coordinate. The
   // aXIsBeforeFirstFrame and aXIsAfterLastFrame flags are updated
   // appropriately.
   NS_IMETHOD FindFrameAt(PRInt32 aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
--- a/layout/generic/nsLineBox.cpp
+++ b/layout/generic/nsLineBox.cpp
@@ -621,25 +621,26 @@ nsLineIterator::GetLine(PRInt32 aLineNum
       flags |= NS_LINE_FLAG_ENDS_IN_BREAK;
   }
   *aLineFlags = flags;
 
   return NS_OK;
 }
 
 PRInt32
-nsLineIterator::FindLineContaining(nsIFrame* aFrame)
+nsLineIterator::FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine)
 {
-  nsLineBox* line = mLines[0];
-  PRInt32 lineNumber = 0;
+  NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
+  PRInt32 lineNumber = aStartLine;
   while (lineNumber != mNumLines) {
+    nsLineBox* line = mLines[lineNumber];
     if (line->Contains(aFrame)) {
       return lineNumber;
     }
-    line = mLines[++lineNumber];
+    ++lineNumber;
   }
   return -1;
 }
 
 #ifdef IBMBIDI
 NS_IMETHODIMP
 nsLineIterator::CheckLineOrder(PRInt32                  aLine,
                                PRBool                   *aIsReordered,
--- a/layout/generic/nsLineBox.h
+++ b/layout/generic/nsLineBox.h
@@ -1576,17 +1576,17 @@ public:
 
   virtual PRInt32 GetNumLines();
   virtual PRBool GetDirection();
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags);
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame);
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine = 0);
   NS_IMETHOD FindFrameAt(PRInt32 aLineNumber,
                          nscoord aX,
                          nsIFrame** aFrameFound,
                          PRBool* aXIsBeforeFirstFrame,
                          PRBool* aXIsAfterLastFrame);
 
   NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber);
 #ifdef IBMBIDI
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -1701,24 +1701,26 @@ nsTableRowGroupFrame::GetLine(PRInt32   
       return NS_OK;
     }
   }
   NS_ERROR("cellmap is lying");
   return NS_ERROR_FAILURE;
 }
   
 PRInt32
-nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame)
+nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine)
 {
   NS_ENSURE_ARG_POINTER(aFrame);
   
   nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
   NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
 
-  return rowFrame->GetRowIndex() - GetStartRowIndex();
+  PRInt32 rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
+
+  return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
 }
 
 #ifdef IBMBIDI
 NS_IMETHODIMP
 nsTableRowGroupFrame::CheckLineOrder(PRInt32                  aLine,
                                      PRBool                   *aIsReordered,
                                      nsIFrame                 **aFirstVisual,
                                      nsIFrame                 **aLastVisual)
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -253,20 +253,22 @@ public:
   NS_IMETHOD GetLine(PRInt32 aLineNumber,
                      nsIFrame** aFirstFrameOnLine,
                      PRInt32* aNumFramesOnLine,
                      nsRect& aLineBounds,
                      PRUint32* aLineFlags);
   
   /** Given a frame that's a child of the rowgroup, find which line its on.
     * @param aFrame       - frame, should be a row
+    * @param aStartLine   - minimal index to return
     * @return               row index relative to the row group if this a row
-    *                       frame. -1 if the frame cannot be found.
+    *                       frame and the index is at least aStartLine.
+    *                       -1 if the frame cannot be found.
     */
-  virtual PRInt32 FindLineContaining(nsIFrame* aFrame);
+  virtual PRInt32 FindLineContaining(nsIFrame* aFrame, PRInt32 aStartLine = 0);
 
   /** Find the orginating cell frame on a row that is the nearest to the
     * coordinate X.
     * @param aLineNumber          - the index of the row relative to the row group
     * @param aX                   - X coordinate in twips relative to the
     *                               origin of the row group
     * @param aFrameFound          - pointer to the cellframe
     * @param aXIsBeforeFirstFrame - the point is before the first originating