Bug 597627 - Adding and removing lines in big textareas is really slow. r=roc a=blocking2.0:final
authorMats Palmgren <matspal@gmail.com>, Boris Zbarsky <bzbarsky@mit.edu>
Fri, 28 Jan 2011 22:08:41 +0100
changeset 61565 f704da8cce12481a5436cafbb692df22691e1b7d
parent 61564 a2f4bc829c88d52e5720109e9b9ecf6954e90e94
child 61566 2a7ac44076692b40386defdc1b2f7cf70019f33c
push idunknown
push userunknown
push dateunknown
reviewersroc, blocking2.0
bugs597627
milestone2.0b11pre
Bug 597627 - Adding and removing lines in big textareas is really slow. r=roc a=blocking2.0:final
layout/generic/nsTextFrameThebes.cpp
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -67,16 +67,17 @@
 #include "nsIRenderingContext.h"
 #include "nsIPresShell.h"
 #include "nsITimer.h"
 #include "nsTArray.h"
 #include "nsIDOMText.h"
 #include "nsIDocument.h"
 #include "nsIDeviceContext.h"
 #include "nsCSSPseudoElements.h"
+#include "nsCSSFrameConstructor.h"
 #include "nsCompatibility.h"
 #include "nsCSSColorUtils.h"
 #include "nsLayoutUtils.h"
 #include "nsDisplayList.h"
 #include "nsFrame.h"
 #include "nsPlaceholderFrame.h"
 #include "nsTextFrameUtils.h"
 #include "nsTextRunTransformations.h"
@@ -6312,16 +6313,39 @@ nsTextFrame::SetLength(PRInt32 aLength, 
   // and ChildIsDirty to handle a range of frames would be worse.
   if (aLineLayout &&
       (end != f->mContentOffset || (f->GetStateBits() & NS_FRAME_IS_DIRTY))) {
     aLineLayout->SetDirtyNextLine();
   }
 
   if (end < f->mContentOffset) {
     // Our frame is shrinking. Give the text to our next in flow.
+    if (aLineLayout &&
+        GetStyleText()->WhiteSpaceIsSignificant() &&
+        HasTerminalNewline() &&
+        GetParent()->GetType() != nsGkAtoms::letterFrame) {
+      // Whatever text we hand to our next-in-flow will end up in a frame all of
+      // its own, since it ends in a forced linebreak.  Might as well just put
+      // it in a separate frame now.  This is important to prevent text run
+      // churn; if we did not do that, then we'd likely end up rebuilding
+      // textruns for all our following continuations.
+      // We skip this optimization when the parent is a first-letter frame
+      // because it doesn't deal well with more than one child frame.
+      nsPresContext* presContext = PresContext();
+      nsIFrame* newFrame;
+      nsresult rv = presContext->PresShell()->FrameConstructor()->
+        CreateContinuingFrame(presContext, this, GetParent(), &newFrame);
+      if (NS_SUCCEEDED(rv)) {
+        nsTextFrame* next = static_cast<nsTextFrame*>(newFrame);
+        nsFrameList temp(next, next);
+        GetParent()->InsertFrames(nsGkAtoms::nextBidi, this, temp);
+        f = next;
+      }
+    }
+
     f->mContentOffset = end;
     if (f->GetTextRun() != mTextRun) {
       ClearTextRun(nsnull);
       f->ClearTextRun(nsnull);
     }
     return;
   }
   // Our frame is growing. Take text from our in-flow(s).
@@ -6330,17 +6354,34 @@ nsTextFrame::SetLength(PRInt32 aLength, 
   // our empty next-in-flow, it will take text from its next-in-flow and
   // dirty that line.
   while (f && f->mContentOffset < end) {
     f->mContentOffset = end;
     if (f->GetTextRun() != mTextRun) {
       ClearTextRun(nsnull);
       f->ClearTextRun(nsnull);
     }
-    f = static_cast<nsTextFrame*>(f->GetNextInFlow());
+    nsTextFrame* next = static_cast<nsTextFrame*>(f->GetNextInFlow());
+    // Note: the "f->GetNextSibling() == next" check below is to restrict
+    // this optimization to the case where they are on the same child list.
+    // Otherwise we might remove the only child of a nsFirstLetterFrame
+    // for example and it can't handle that.  See bug 597627 for details.
+    if (next && next->mContentOffset <= end && f->GetNextSibling() == next) {
+      // |f| is now empty.  We may as well remove it, instead of copying all
+      // the text from |next| into it instead; the latter leads to use
+      // rebuilding textruns for all following continuations.  We have to be
+      // careful here, though, because some RemoveFrame implementations remove
+      // and destroy not only the passed-in frame but also all its following
+      // in-flows (and sometimes all its following continuations in general).
+      // So we remove |f| from the flow first, to make sure that only |f| is
+      // destroyed.
+      nsSplittableFrame::RemoveFromFlow(f);
+      f->GetParent()->RemoveFrame(nsGkAtoms::nextBidi, f);
+    }
+    f = next;
   }
 
 #ifdef DEBUG
   f = this;
   PRInt32 iterations = 0;
   while (f && iterations < 10) {
     f->GetContentLength(); // Assert if negative length
     f = static_cast<nsTextFrame*>(f->GetNextContinuation());