Bug 430332. Lazily rebuild transformed-textruns, deferring rebuild until after the linebreaker has finished analysis. r=smontagu
authorRobert O'Callahan <robert@ocallahan.org>
Wed, 26 Nov 2008 11:44:05 +1300
changeset 21948 d7a7824d69908ae84c2b76724dad5261908a460d
parent 21906 48da1d4688225df4aaeb0c56744b7f150d65f5df
child 21949 1f5d2c249f869c879d6afcb35a8acb74f2e88c24
push id3754
push userblassey@mozilla.com
push dateWed, 26 Nov 2008 15:09:37 +0000
treeherdermozilla-central@026147c91538 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmontagu
bugs430332
milestone1.9.1b3pre
Bug 430332. Lazily rebuild transformed-textruns, deferring rebuild until after the linebreaker has finished analysis. r=smontagu
layout/generic/nsTextFrameThebes.cpp
layout/generic/nsTextRunTransformations.cpp
layout/generic/nsTextRunTransformations.h
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -709,16 +709,24 @@ public:
       NS_ASSERTION(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED,
                    "Text run should be transformed!");
       nsTransformedTextRun* transformedTextRun =
         static_cast<nsTransformedTextRun*>(mTextRun);
       transformedTextRun->SetCapitalization(aOffset + mOffsetIntoTextRun, aLength,
                                             aCapitalize, mContext);
     }
 
+    void Finish() {
+      if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) {
+        nsTransformedTextRun* transformedTextRun =
+          static_cast<nsTransformedTextRun*>(mTextRun);
+        transformedTextRun->FinishSettingProperties(mContext);
+      }
+    }
+
     gfxTextRun*  mTextRun;
     gfxContext*  mContext;
     PRUint32     mOffsetIntoTextRun;
     PRPackedBool mChangedBreaks;
     PRPackedBool mExistingTextRun;
   };
 
 private:
@@ -1124,16 +1132,17 @@ void BuildTextRunsScanner::FlushFrames(P
       textRun->SetFlagBits(nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK);
     }
     PRUint32 i;
     for (i = 0; i < mBreakSinks.Length(); ++i) {
       if (!mBreakSinks[i]->mExistingTextRun || mBreakSinks[i]->mChangedBreaks) {
         // TODO cause frames associated with the textrun to be reflowed, if they
         // aren't being reflowed already!
       }
+      mBreakSinks[i]->Finish();
     }
     mBreakSinks.Clear();
   }
 
   mCanStopOnThisLine = PR_TRUE;
   ResetRunInfo();
 }
 
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -67,96 +67,40 @@ nsTransformedTextRun::SetCapitalization(
                                         gfxContext* aRefContext)
 {
   if (mCapitalize.IsEmpty()) {
     if (!mCapitalize.AppendElements(GetLength()))
       return;
     memset(mCapitalize.Elements(), 0, GetLength()*sizeof(PRPackedBool));
   }
   memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(PRPackedBool));
-  mFactory->RebuildTextRun(this, aRefContext);
+  mNeedsRebuild = PR_TRUE;
 }
 
 PRBool
 nsTransformedTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
                                              PRPackedBool* aBreakBefore,
                                              gfxContext* aRefContext)
 {
   PRBool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
       aBreakBefore, aRefContext);
-  mFactory->RebuildTextRun(this, aRefContext);
-  return changed;
-}
-
-PRBool
-nsTransformedTextRun::SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
-                                    PRBool aLineBreakBefore, PRBool aLineBreakAfter,
-                                    gfxFloat* aAdvanceWidthDelta,
-                                    gfxContext* aRefContext)
-{
-  nsTArray<PRUint32> newBreaks;
-  PRUint32 i;
-  PRBool changed = PR_FALSE;
-  for (i = 0; i < mLineBreaks.Length(); ++i) {
-    PRUint32 pos = mLineBreaks[i];
-    if (pos >= aStart)
-      break;
-    newBreaks.AppendElement(pos);
-  }
-  if (aLineBreakBefore != (i < mLineBreaks.Length() &&
-                           mLineBreaks[i] == aStart)) {
-    changed = PR_TRUE;
-  }
-  if (aLineBreakBefore) {
-    nsTextFrameUtils::AppendLineBreakOffset(&newBreaks, aStart);
+  if (changed) {
+    mNeedsRebuild = PR_TRUE;
   }
-  if (aLineBreakAfter != (i + 1 < mLineBreaks.Length() &&
-                          mLineBreaks[i + 1] == aStart + aLength)) {
-    changed = PR_TRUE;
-  }
-  if (aLineBreakAfter) {
-    nsTextFrameUtils::AppendLineBreakOffset(&newBreaks, aStart + aLength);
-  }
-  for (; i < mLineBreaks.Length(); ++i) {
-    if (mLineBreaks[i] > aStart + aLength)
-      break;
-    changed = PR_TRUE;
-  }
-  if (!changed) {
-    if (aAdvanceWidthDelta) {
-      *aAdvanceWidthDelta = 0;
-    }
-    return PR_FALSE;
-  }
-
-  newBreaks.AppendElements(mLineBreaks.Elements() + i, mLineBreaks.Length() - i);
-  mLineBreaks.SwapElements(newBreaks);
-
-  gfxFloat currentAdvance = GetAdvanceWidth(aStart, aLength, nsnull);
-  mFactory->RebuildTextRun(this, aRefContext);
-  if (aAdvanceWidthDelta) {
-    *aAdvanceWidthDelta = GetAdvanceWidth(aStart, aLength, nsnull) - currentAdvance;
-  }
-  return PR_TRUE;
+  return changed;
 }
 
 gfxTextRun*
 nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
                                           nsStyleContext** aStyles, PRBool aOwnsFactory)
 {
-  nsTransformedTextRun* textRun =
-    nsTransformedTextRun::Create(aParams, this, aFontGroup,
-                                 aString, aLength, aFlags, aStyles, aOwnsFactory);
-  if (!textRun)
-    return nsnull;
-
-  RebuildTextRun(textRun, aParams->mContext);
-  return textRun;
+  return nsTransformedTextRun::Create(aParams, this, aFontGroup,
+                                      aString, aLength, aFlags, aStyles, aOwnsFactory);
 }
 
 gfxTextRun*
 nsTransformingTextRunFactory::MakeTextRun(const PRUint8* aString, PRUint32 aLength,
                                           const gfxTextRunFactory::Parameters* aParams,
                                           gfxFontGroup* aFontGroup, PRUint32 aFlags,
                                           nsStyleContext** aStyles, PRBool aOwnsFactory)
 {
@@ -266,17 +210,17 @@ MergeCharactersInTextRun(gfxTextRun* aDe
 }
 
 static gfxTextRunFactory::Parameters
 GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags,
     gfxContext* aRefContext)
 {
   gfxTextRunFactory::Parameters params =
     { aRefContext, nsnull, nsnull,
-      nsnull, nsnull, aTextRun->GetAppUnitsPerDevUnit()
+      nsnull, 0, aTextRun->GetAppUnitsPerDevUnit()
     };
   *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
   return params;
 }
 
 void
 nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
     gfxContext* aRefContext)
@@ -308,27 +252,19 @@ nsFontVariantTextRunFactory::RebuildText
   nsCaseTransformTextRunFactory uppercaseFactory(nsnull, PR_TRUE);
 
   aTextRun->ResetGlyphRuns();
 
   PRUint32 runStart = 0;
   PRBool runIsLowercase = PR_FALSE;
   nsAutoTArray<nsStyleContext*,50> styleArray;
   nsAutoTArray<PRPackedBool,50> canBreakBeforeArray;
-  nsAutoTArray<PRUint32,10> lineBreakBeforeArray;
 
-  PRUint32 nextLineBreak = 0;
   PRUint32 i;
   for (i = 0; i <= length; ++i) {
-    if (nextLineBreak < aTextRun->mLineBreaks.Length() &&
-        aTextRun->mLineBreaks[nextLineBreak] == i) {
-      lineBreakBeforeArray.AppendElement(i - runStart);
-      ++nextLineBreak;
-    }
-
     PRBool isLowercase = PR_FALSE;
     if (i < length) {
       // Characters that aren't the start of a cluster are ignored here. They
       // get added to whatever lowercase/non-lowercase run we're in.
       if (!inner->IsClusterStart(i)) {
         isLowercase = runIsLowercase;
       } else {
         if (styles[i]->GetStyleFont()->mFont.variant == NS_STYLE_FONT_VARIANT_SMALL_CAPS) {
@@ -341,19 +277,17 @@ nsFontVariantTextRunFactory::RebuildText
         }
       }
     }
 
     if ((i == length || runIsLowercase != isLowercase) && runStart < i) {
       nsAutoPtr<gfxTextRun> transformedChild;
       gfxTextRunCache::AutoTextRun cachedChild;
       gfxTextRun* child;
-      // Setup actual line break data for child (which may affect shaping)
-      innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
-      innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
+
       if (runIsLowercase) {
         transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
             &innerParams, smallFont, flags, styleArray.Elements(), PR_FALSE);
         child = transformedChild;
       } else {
         cachedChild =
           gfxTextRunCache::MakeTextRun(str + runStart, i - runStart, fontGroup,
               &innerParams, flags);
@@ -367,30 +301,24 @@ nsFontVariantTextRunFactory::RebuildText
                    "lost some break-before values?");
       child->SetPotentialLineBreaks(0, canBreakBeforeArray.Length(),
           canBreakBeforeArray.Elements(), aRefContext);
       aTextRun->CopyGlyphDataFrom(child, 0, child->GetLength(), runStart, PR_FALSE);
 
       runStart = i;
       styleArray.Clear();
       canBreakBeforeArray.Clear();
-      lineBreakBeforeArray.Clear();
-      if (nextLineBreak > 0 && aTextRun->mLineBreaks[nextLineBreak - 1] == i) {
-        lineBreakBeforeArray.AppendElement(0);
-      }
     }
 
     if (i < length) {
       runIsLowercase = isLowercase;
       styleArray.AppendElement(styles[i]);
       canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
     }
   }
-  NS_ASSERTION(nextLineBreak == aTextRun->mLineBreaks.Length(),
-               "lost track of line breaks somehow");
 }
 
 void
 nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
     gfxContext* aRefContext)
 {
   nsICaseConversion* converter = nsContentUtils::GetCaseConv();
   if (!converter)
@@ -399,32 +327,25 @@ nsCaseTransformTextRunFactory::RebuildTe
   PRUint32 length = aTextRun->GetLength();
   const PRUnichar* str = aTextRun->GetTextUnicode();
   nsRefPtr<nsStyleContext>* styles = aTextRun->mStyles.Elements();
 
   nsAutoString convertedString;
   nsAutoTArray<PRPackedBool,50> charsToMergeArray;
   nsAutoTArray<nsStyleContext*,50> styleArray;
   nsAutoTArray<PRPackedBool,50> canBreakBeforeArray;
-  nsAutoTArray<PRUint32,10> lineBreakBeforeArray;
-  PRUint32 nextLineBreak = 0;
   PRUint32 extraCharsCount = 0;
 
   PRUint32 i;
   for (i = 0; i < length; ++i) {
     PRUnichar ch = str[i];
 
     charsToMergeArray.AppendElement(PR_FALSE);
     styleArray.AppendElement(styles[i]);
     canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
-    if (nextLineBreak < aTextRun->mLineBreaks.Length() &&
-        aTextRun->mLineBreaks[nextLineBreak] == i) {
-      lineBreakBeforeArray.AppendElement(i + extraCharsCount);
-      ++nextLineBreak;
-    }
 
     PRUint8 style = mAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE
       : styles[i]->GetStyleText()->mTextTransform;
     PRBool extraChar = PR_FALSE;
 
     switch (style) {
     case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
       converter->ToLower(ch, &ch);
@@ -456,35 +377,26 @@ nsCaseTransformTextRunFactory::RebuildTe
     convertedString.Append(ch);
     if (extraChar) {
       ++extraCharsCount;
       charsToMergeArray.AppendElement(PR_TRUE);
       styleArray.AppendElement(styles[i]);
       canBreakBeforeArray.AppendElement(PR_FALSE);
     }
   }
-  if (nextLineBreak < aTextRun->mLineBreaks.Length() &&
-      aTextRun->mLineBreaks[nextLineBreak] == length) {
-    lineBreakBeforeArray.AppendElement(length + extraCharsCount);
-    ++nextLineBreak;
-  }
-  NS_ASSERTION(nextLineBreak == aTextRun->mLineBreaks.Length(),
-               "lost track of line breaks somehow");
 
   PRUint32 flags;
   gfxTextRunFactory::Parameters innerParams =
       GetParametersForInner(aTextRun, &flags, aRefContext);
   gfxFontGroup* fontGroup = aTextRun->GetFontGroup();
 
   nsAutoPtr<gfxTextRun> transformedChild;
   gfxTextRunCache::AutoTextRun cachedChild;
   gfxTextRun* child;
-  // Setup actual line break data for child (which may affect shaping)
-  innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
-  innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
+
   if (mInnerTransformingTextRunFactory) {
     transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
         convertedString.BeginReading(), convertedString.Length(),
         &innerParams, fontGroup, flags, styleArray.Elements(), PR_FALSE);
     child = transformedChild.get();
   } else {
     cachedChild = gfxTextRunCache::MakeTextRun(
         convertedString.BeginReading(), convertedString.Length(), fontGroup,
--- a/layout/generic/nsTextRunTransformations.h
+++ b/layout/generic/nsTextRunTransformations.h
@@ -108,46 +108,51 @@ public:
                                       PRBool aOwnsFactory);
 
   ~nsTransformedTextRun() {
     if (mOwnsFactory) {
       delete mFactory;
     }
   }
   
-  virtual void SetCapitalization(PRUint32 aStart, PRUint32 aLength,
-                                 PRPackedBool* aCapitalization,
-                                 gfxContext* aRefContext);
+  void SetCapitalization(PRUint32 aStart, PRUint32 aLength,
+                         PRPackedBool* aCapitalization,
+                         gfxContext* aRefContext);
   virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
                                         PRPackedBool* aBreakBefore,
                                         gfxContext* aRefContext);
-  virtual PRBool SetLineBreaks(PRUint32 aStart, PRUint32 aLength,
-                               PRBool aLineBreakBefore, PRBool aLineBreakAfter,
-                               gfxFloat* aAdvanceWidthDelta,
-                               gfxContext* aRefContext);
+  /**
+   * Called after SetCapitalization and SetPotentialLineBreaks
+   * are done and before we request any data from the textrun. Also always
+   * called after a Create.
+   */
+  void FinishSettingProperties(gfxContext* aRefContext)
+  {
+    if (mNeedsRebuild) {
+      mNeedsRebuild = PR_FALSE;
+      mFactory->RebuildTextRun(this, aRefContext);
+    }
+  }
 
   nsTransformingTextRunFactory       *mFactory;
-  nsTArray<PRUint32>                  mLineBreaks;
   nsTArray<nsRefPtr<nsStyleContext> > mStyles;
   nsTArray<PRPackedBool>              mCapitalize;
   PRPackedBool                        mOwnsFactory;
+  PRPackedBool                        mNeedsRebuild;
 
 private:
   nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
                        nsTransformingTextRunFactory* aFactory,
                        gfxFontGroup* aFontGroup,
                        const PRUnichar* aString, PRUint32 aLength,
                        const PRUint32 aFlags, nsStyleContext** aStyles,
                        PRBool aOwnsFactory)
     : gfxTextRun(aParams, aString, aLength, aFontGroup, aFlags, sizeof(nsTransformedTextRun)),
-      mFactory(aFactory), mOwnsFactory(aOwnsFactory)
+      mFactory(aFactory), mOwnsFactory(aOwnsFactory), mNeedsRebuild(PR_TRUE)
   {
     PRUint32 i;
     for (i = 0; i < aLength; ++i) {
       mStyles.AppendElement(aStyles[i]);
     }
-    for (i = 0; i < aParams->mInitialBreakCount; ++i) {
-      mLineBreaks.AppendElement(aParams->mInitialBreaks[i]);
-    }
   }  
 };
 
 #endif /*NSTEXTRUNTRANSFORMATIONS_H_*/