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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
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