diff --git a/gfx/thebes/public/gfxTextRunWordCache.h b/gfx/thebes/public/gfxTextRunWordCache.h index 8a712f693e1..bc5dc9226a9 100644 --- a/gfx/thebes/public/gfxTextRunWordCache.h +++ b/gfx/thebes/public/gfxTextRunWordCache.h @@ -61,7 +61,9 @@ public: * character. This is used for the context detection necessary for * bidi.numeral implementation. */ - TEXT_INCOMING_ARABICCHAR = 0x40000000 + TEXT_INCOMING_ARABICCHAR = 0x40000000, + + TEXT_UNUSED_FLAGS = 0x80000000 }; /** diff --git a/gfx/thebes/src/gfxFont.cpp b/gfx/thebes/src/gfxFont.cpp index b5ec902a92d..77ddfad369a 100644 --- a/gfx/thebes/src/gfxFont.cpp +++ b/gfx/thebes/src/gfxFont.cpp @@ -1394,6 +1394,10 @@ gfxTextRun::~gfxTextRun() { #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS AccountStorageForTextRun(this, -1); +#endif +#ifdef DEBUG + // Make it easy to detect a dead text run + mFlags = 0xFFFFFFFF; #endif NS_RELEASE(mFontGroup); MOZ_COUNT_DTOR(gfxTextRun); diff --git a/layout/generic/crashtests/430332-1.html b/layout/generic/crashtests/430332-1.html new file mode 100644 index 00000000000..c9ba0022991 --- /dev/null +++ b/layout/generic/crashtests/430332-1.html @@ -0,0 +1,17 @@ + + + +ab cd +ab cd +ab cd +ab cd +ab cd +ab cd +ab cd +ab cdef + + + diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index fb3522ad2a9..afd96401081 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -615,6 +615,12 @@ public: mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) { ResetRunInfo(); } + ~BuildTextRunsScanner() { + NS_ASSERTION(mBreakSinks.IsEmpty(), "Should have been cleared"); + NS_ASSERTION(mTextRunsToDelete.IsEmpty(), "Should have been cleared"); + NS_ASSERTION(mLineBreakBeforeFrames.IsEmpty(), "Should have been cleared"); + NS_ASSERTION(mMappedFlows.IsEmpty(), "Should have been cleared"); + } void SetAtStartOfLine() { mStartOfLine = PR_TRUE; @@ -641,6 +647,7 @@ public: void ScanFrame(nsIFrame* aFrame); PRBool IsTextRunValidForMappedFlows(gfxTextRun* aTextRun); void FlushFrames(PRBool aFlushLineBreaks, PRBool aSuppressTrailingBreak); + void FlushLineBreaks(gfxTextRun* aTrailingTextRun); void ResetRunInfo() { mLastFrame = nsnull; mMappedFlows.Clear(); @@ -648,10 +655,6 @@ public: mMaxTextLength = 0; mDoubleByteText = PR_FALSE; } - void ResetLineBreaker() { - PRBool trailingBreak; - mLineBreaker.Reset(&trailingBreak); - } void AccumulateRunInfo(nsTextFrame* aFrame); /** * @return null to indicate either textrun construction failed or @@ -727,6 +730,18 @@ public: aCapitalize, mContext); } + void Finish() { + NS_ASSERTION(!(mTextRun->GetFlags() & + (gfxTextRunWordCache::TEXT_UNUSED_FLAGS | + nsTextFrameUtils::TEXT_UNUSED_FLAG)), + "Flag set that should never be set! (memory safety error?)"); + if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_IS_TRANSFORMED) { + nsTransformedTextRun* transformedTextRun = + static_cast(mTextRun); + transformedTextRun->FinishSettingProperties(mContext); + } + } + gfxTextRun* mTextRun; gfxContext* mContext; PRUint32 mOffsetIntoTextRun; @@ -738,6 +753,7 @@ private: nsAutoTArray mMappedFlows; nsAutoTArray mLineBreakBeforeFrames; nsAutoTArray,10> mBreakSinks; + nsAutoTArray mTextRunsToDelete; nsLineBreaker mLineBreaker; gfxTextRun* mCurrentFramesAllSameTextRun; gfxContext* mContext; @@ -1075,12 +1091,15 @@ BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame, if (seenStartLine) { ++linesAfterStartLine; if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS && scanner.CanStopOnThisLine()) { - // Don't flush; we may be in the middle of a textrun that we can't - // end here. That's OK, we just won't build it. + // Don't flush frames; we may be in the middle of a textrun + // that we can't end here. That's OK, we just won't build it. // Note that we must already have finished the textrun for aForFrame, // because we've seen the end of a textrun in a line after the line // containing aForFrame. - scanner.ResetLineBreaker(); + scanner.FlushLineBreaks(nsnull); + // This flushes out mMappedFlows and mLineBreakBeforeFrames, which + // silences assertions in the scanner destructor. + scanner.ResetRunInfo(); return; } } @@ -1132,56 +1151,72 @@ void BuildTextRunsScanner::FlushFrames(PRBool aFlushLineBreaks, PRBool aSuppress if (mMappedFlows.Length() == 0) return; - gfxTextRun* textRun; - if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun && - ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) == - ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) && - ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunWordCache::TEXT_INCOMING_ARABICCHAR) != 0) == - ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) && - IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) { - // Optimization: We do not need to (re)build the textrun. - textRun = mCurrentFramesAllSameTextRun; + gfxTextRun* textRun = nsnull; + if (!mMappedFlows.IsEmpty()) { + if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun && + ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) == + ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) && + ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunWordCache::TEXT_INCOMING_ARABICCHAR) != 0) == + ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) && + IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) { + // Optimization: We do not need to (re)build the textrun. + textRun = mCurrentFramesAllSameTextRun; - // Feed this run's text into the linebreaker to provide context. This also - // updates mNextRunContextInfo appropriately. - SetupBreakSinksForTextRun(textRun, PR_TRUE, PR_FALSE); - mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE; - if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) { - mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE; + // Feed this run's text into the linebreaker to provide context. This also + // updates mNextRunContextInfo appropriately. + SetupBreakSinksForTextRun(textRun, PR_TRUE, PR_FALSE); + mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE; + if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) { + mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE; + } + if (textRun->GetFlags() & gfxTextRunWordCache::TEXT_TRAILING_ARABICCHAR) { + mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR; + } + } else { + nsAutoTArray buffer; + if (!buffer.AppendElements(mMaxTextLength*(mDoubleByteText ? 2 : 1))) + return; + textRun = BuildTextRunForFrames(buffer.Elements()); } - if (textRun->GetFlags() & gfxTextRunWordCache::TEXT_TRAILING_ARABICCHAR) { - mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR; - } - } else { - nsAutoTArray buffer; - if (!buffer.AppendElements(mMaxTextLength*(mDoubleByteText ? 2 : 1))) - return; - textRun = BuildTextRunForFrames(buffer.Elements()); } if (aFlushLineBreaks) { - PRBool trailingLineBreak; - nsresult rv = mLineBreaker.Reset(&trailingLineBreak); - // textRun may be null for various reasons, including because we constructed - // a partial textrun just to get the linebreaker and other state set up - // to build the next textrun. - if (NS_SUCCEEDED(rv) && trailingLineBreak && textRun && !aSuppressTrailingBreak) { - 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.Clear(); + FlushLineBreaks(aSuppressTrailingBreak ? nsnull : textRun); } mCanStopOnThisLine = PR_TRUE; ResetRunInfo(); } +void BuildTextRunsScanner::FlushLineBreaks(gfxTextRun* aTrailingTextRun) +{ + PRBool trailingLineBreak; + nsresult rv = mLineBreaker.Reset(&trailingLineBreak); + // textRun may be null for various reasons, including because we constructed + // a partial textrun just to get the linebreaker and other state set up + // to build the next textrun. + if (NS_SUCCEEDED(rv) && trailingLineBreak && aTrailingTextRun) { + aTrailingTextRun->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(); + + for (i = 0; i < mTextRunsToDelete.Length(); ++i) { + gfxTextRun* deleteTextRun = mTextRunsToDelete[i]; + gTextRuns->RemoveFromCache(deleteTextRun); + delete deleteTextRun; + } + mTextRunsToDelete.Clear(); +} + void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame) { NS_ASSERTION(mMaxTextLength <= mMaxTextLength + aFrame->GetContentLength(), "integer overflow"); @@ -1493,15 +1528,18 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) TextRunMappedFlow dummyMappedFlow; TextRunUserData* userData; + TextRunUserData* userDataToDestroy; // If the situation is particularly simple (and common) we don't need to // allocate userData. if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame && mMappedFlows[0].mStartFrame->GetContentOffset() == 0) { userData = &dummyData; + userDataToDestroy = nsnull; dummyData.mMappedFlows = &dummyMappedFlow; } else { userData = static_cast (nsMemory::Alloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow))); + userDataToDestroy = userData; userData->mMappedFlows = reinterpret_cast(userData + 1); } userData->mMappedFlowCount = mMappedFlows.Length(); @@ -1578,7 +1616,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) // then expand. nsAutoTArray tempBuf; if (!tempBuf.AppendElements(contentLength)) { - DestroyUserData(userData); + DestroyUserData(userDataToDestroy); return nsnull; } PRUint8* bufStart = tempBuf.Elements(); @@ -1606,7 +1644,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) // Check for out-of-memory in gfxSkipCharsBuilder if (!builder.IsOK()) { - DestroyUserData(userData); + DestroyUserData(userDataToDestroy); return nsnull; } @@ -1641,7 +1679,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame; gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame); if (!fontGroup) { - DestroyUserData(userData); + DestroyUserData(userDataToDestroy); return nsnull; } @@ -1751,7 +1789,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) } } if (!textRun) { - DestroyUserData(userData); + DestroyUserData(userDataToDestroy); return nsnull; } @@ -1764,11 +1802,16 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer) if (mSkipIncompleteTextRuns) { mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr, transformedLength, mDoubleByteText); - - // Nuke the textrun - gTextRuns->RemoveFromCache(textRun); - delete textRun; - DestroyUserData(userData); + // Arrange for this textrun to be deleted the next time the linebreaker + // is flushed out + mTextRunsToDelete.AppendElement(textRun); + // Since we're doing to destroy the user data now, avoid a dangling + // pointer. Strictly speaking we don't need to do this since it should + // not be used (since this textrun will not be used and will be + // itself deleted soon), but it's always better to not have dangling + // pointers around. + textRun->SetUserData(nsnull); + DestroyUserData(userDataToDestroy); return nsnull; } diff --git a/layout/generic/nsTextFrameUtils.h b/layout/generic/nsTextFrameUtils.h index 92b8bb7b5e6..92456815087 100644 --- a/layout/generic/nsTextFrameUtils.h +++ b/layout/generic/nsTextFrameUtils.h @@ -66,6 +66,7 @@ public: // the original text has at least one soft hyphen character TEXT_HAS_SHY = 0x020000, TEXT_WAS_TRANSFORMED = 0x040000, + TEXT_UNUSED_FLAG = 0x080000, // The following flags are set by nsTextFrame diff --git a/layout/generic/nsTextRunTransformations.cpp b/layout/generic/nsTextRunTransformations.cpp index 34f6fb5b9f0..53023e4cc6e 100644 --- a/layout/generic/nsTextRunTransformations.cpp +++ b/layout/generic/nsTextRunTransformations.cpp @@ -72,7 +72,7 @@ nsTransformedTextRun::SetCapitalization(PRUint32 aStart, PRUint32 aLength, memset(mCapitalize.Elements(), 0, GetLength()*sizeof(PRPackedBool)); } memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(PRPackedBool)); - mFactory->RebuildTextRun(this, aRefContext); + mNeedsRebuild = PR_TRUE; } PRBool @@ -82,76 +82,20 @@ nsTransformedTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength, { PRBool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength, aBreakBefore, aRefContext); - mFactory->RebuildTextRun(this, aRefContext); + if (changed) { + mNeedsRebuild = PR_TRUE; + } return changed; } -PRBool -nsTransformedTextRun::SetLineBreaks(PRUint32 aStart, PRUint32 aLength, - PRBool aLineBreakBefore, PRBool aLineBreakAfter, - gfxFloat* aAdvanceWidthDelta, - gfxContext* aRefContext) -{ - nsTArray 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 (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; -} - 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* @@ -271,7 +215,7 @@ GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags, { gfxTextRunFactory::Parameters params = { aRefContext, nsnull, nsnull, - nsnull, nsnull, aTextRun->GetAppUnitsPerDevUnit() + nsnull, 0, aTextRun->GetAppUnitsPerDevUnit() }; *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT; return params; @@ -313,17 +257,9 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, PRBool runIsLowercase = PR_FALSE; nsAutoTArray styleArray; nsAutoTArray canBreakBeforeArray; - nsAutoTArray 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 @@ -346,9 +282,7 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, nsAutoPtr 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); @@ -372,10 +306,6 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, runStart = i; styleArray.Clear(); canBreakBeforeArray.Clear(); - lineBreakBeforeArray.Clear(); - if (nextLineBreak > 0 && aTextRun->mLineBreaks[nextLineBreak - 1] == i) { - lineBreakBeforeArray.AppendElement(0); - } } if (i < length) { @@ -384,8 +314,6 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); } } - NS_ASSERTION(nextLineBreak == aTextRun->mLineBreaks.Length(), - "lost track of line breaks somehow"); } void @@ -404,8 +332,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, nsAutoTArray charsToMergeArray; nsAutoTArray styleArray; nsAutoTArray canBreakBeforeArray; - nsAutoTArray lineBreakBeforeArray; - PRUint32 nextLineBreak = 0; PRUint32 extraCharsCount = 0; PRUint32 i; @@ -415,11 +341,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, 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; @@ -461,13 +382,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, 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 = @@ -477,9 +391,7 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun, nsAutoPtr 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(), diff --git a/layout/generic/nsTextRunTransformations.h b/layout/generic/nsTextRunTransformations.h index 86c09f79608..cd9baa21ab5 100644 --- a/layout/generic/nsTextRunTransformations.h +++ b/layout/generic/nsTextRunTransformations.h @@ -113,22 +113,30 @@ public: } } - 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 mLineBreaks; nsTArray > mStyles; nsTArray mCapitalize; PRPackedBool mOwnsFactory; + PRPackedBool mNeedsRebuild; private: nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams, @@ -138,15 +146,12 @@ private: 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]); - } } };