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 id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, blocking2.0
bugs597627
milestone2.0b11pre
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 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());