Bug 430332. Defer nsTransformingTextRun::RebuildTextRun until the linebreaker has completely set up the break and capitalization data on the transformed text run. r=smontagu

This commit is contained in:
Robert O'Callahan 2009-01-09 13:23:28 +13:00
Родитель 72a5cdcb42
Коммит f570471138
7 изменённых файлов: 149 добавлений и 165 удалений

Просмотреть файл

@ -61,7 +61,9 @@ public:
* character. This is used for the context detection necessary for * character. This is used for the context detection necessary for
* bidi.numeral implementation. * bidi.numeral implementation.
*/ */
TEXT_INCOMING_ARABICCHAR = 0x40000000 TEXT_INCOMING_ARABICCHAR = 0x40000000,
TEXT_UNUSED_FLAGS = 0x80000000
}; };
/** /**

Просмотреть файл

@ -1394,6 +1394,10 @@ gfxTextRun::~gfxTextRun()
{ {
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
AccountStorageForTextRun(this, -1); AccountStorageForTextRun(this, -1);
#endif
#ifdef DEBUG
// Make it easy to detect a dead text run
mFlags = 0xFFFFFFFF;
#endif #endif
NS_RELEASE(mFontGroup); NS_RELEASE(mFontGroup);
MOZ_COUNT_DTOR(gfxTextRun); MOZ_COUNT_DTOR(gfxTextRun);

Просмотреть файл

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html>
<body style="font-family:monospace; width:6ch;">
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd
<b>ab</b> cd<span id="s">ef</span>
<script>
document.body.clientWidth;
document.getElementById("s").style.fontSize = "200%";
</script>
</body>
</html>

Просмотреть файл

@ -615,6 +615,12 @@ public:
mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) { mCurrentRunContextInfo(nsTextFrameUtils::INCOMING_NONE) {
ResetRunInfo(); 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() { void SetAtStartOfLine() {
mStartOfLine = PR_TRUE; mStartOfLine = PR_TRUE;
@ -641,6 +647,7 @@ public:
void ScanFrame(nsIFrame* aFrame); void ScanFrame(nsIFrame* aFrame);
PRBool IsTextRunValidForMappedFlows(gfxTextRun* aTextRun); PRBool IsTextRunValidForMappedFlows(gfxTextRun* aTextRun);
void FlushFrames(PRBool aFlushLineBreaks, PRBool aSuppressTrailingBreak); void FlushFrames(PRBool aFlushLineBreaks, PRBool aSuppressTrailingBreak);
void FlushLineBreaks(gfxTextRun* aTrailingTextRun);
void ResetRunInfo() { void ResetRunInfo() {
mLastFrame = nsnull; mLastFrame = nsnull;
mMappedFlows.Clear(); mMappedFlows.Clear();
@ -648,10 +655,6 @@ public:
mMaxTextLength = 0; mMaxTextLength = 0;
mDoubleByteText = PR_FALSE; mDoubleByteText = PR_FALSE;
} }
void ResetLineBreaker() {
PRBool trailingBreak;
mLineBreaker.Reset(&trailingBreak);
}
void AccumulateRunInfo(nsTextFrame* aFrame); void AccumulateRunInfo(nsTextFrame* aFrame);
/** /**
* @return null to indicate either textrun construction failed or * @return null to indicate either textrun construction failed or
@ -727,6 +730,18 @@ public:
aCapitalize, mContext); 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<nsTransformedTextRun*>(mTextRun);
transformedTextRun->FinishSettingProperties(mContext);
}
}
gfxTextRun* mTextRun; gfxTextRun* mTextRun;
gfxContext* mContext; gfxContext* mContext;
PRUint32 mOffsetIntoTextRun; PRUint32 mOffsetIntoTextRun;
@ -738,6 +753,7 @@ private:
nsAutoTArray<MappedFlow,10> mMappedFlows; nsAutoTArray<MappedFlow,10> mMappedFlows;
nsAutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames; nsAutoTArray<nsTextFrame*,50> mLineBreakBeforeFrames;
nsAutoTArray<nsAutoPtr<BreakSink>,10> mBreakSinks; nsAutoTArray<nsAutoPtr<BreakSink>,10> mBreakSinks;
nsAutoTArray<gfxTextRun*,5> mTextRunsToDelete;
nsLineBreaker mLineBreaker; nsLineBreaker mLineBreaker;
gfxTextRun* mCurrentFramesAllSameTextRun; gfxTextRun* mCurrentFramesAllSameTextRun;
gfxContext* mContext; gfxContext* mContext;
@ -1075,12 +1091,15 @@ BuildTextRuns(gfxContext* aContext, nsTextFrame* aForFrame,
if (seenStartLine) { if (seenStartLine) {
++linesAfterStartLine; ++linesAfterStartLine;
if (linesAfterStartLine >= NUM_LINES_TO_BUILD_TEXT_RUNS && scanner.CanStopOnThisLine()) { 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 // Don't flush frames; we may be in the middle of a textrun
// end here. That's OK, we just won't build it. // 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, // 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 // because we've seen the end of a textrun in a line after the line
// containing aForFrame. // containing aForFrame.
scanner.ResetLineBreaker(); scanner.FlushLineBreaks(nsnull);
// This flushes out mMappedFlows and mLineBreakBeforeFrames, which
// silences assertions in the scanner destructor.
scanner.ResetRunInfo();
return; return;
} }
} }
@ -1132,56 +1151,72 @@ void BuildTextRunsScanner::FlushFrames(PRBool aFlushLineBreaks, PRBool aSuppress
if (mMappedFlows.Length() == 0) if (mMappedFlows.Length() == 0)
return; return;
gfxTextRun* textRun; gfxTextRun* textRun = nsnull;
if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun && if (!mMappedFlows.IsEmpty()) {
((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) == if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) && ((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunWordCache::TEXT_INCOMING_ARABICCHAR) != 0) == ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_WHITESPACE) != 0) &&
((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) && ((mCurrentFramesAllSameTextRun->GetFlags() & gfxTextRunWordCache::TEXT_INCOMING_ARABICCHAR) != 0) ==
IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) { ((mCurrentRunContextInfo & nsTextFrameUtils::INCOMING_ARABICCHAR) != 0) &&
// Optimization: We do not need to (re)build the textrun. IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
textRun = 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 // Feed this run's text into the linebreaker to provide context. This also
// updates mNextRunContextInfo appropriately. // updates mNextRunContextInfo appropriately.
SetupBreakSinksForTextRun(textRun, PR_TRUE, PR_FALSE); SetupBreakSinksForTextRun(textRun, PR_TRUE, PR_FALSE);
mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE; mNextRunContextInfo = nsTextFrameUtils::INCOMING_NONE;
if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) { if (textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) {
mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE; mNextRunContextInfo |= nsTextFrameUtils::INCOMING_WHITESPACE;
}
if (textRun->GetFlags() & gfxTextRunWordCache::TEXT_TRAILING_ARABICCHAR) {
mNextRunContextInfo |= nsTextFrameUtils::INCOMING_ARABICCHAR;
}
} else {
nsAutoTArray<PRUint8,BIG_TEXT_NODE_SIZE> 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<PRUint8,BIG_TEXT_NODE_SIZE> buffer;
if (!buffer.AppendElements(mMaxTextLength*(mDoubleByteText ? 2 : 1)))
return;
textRun = BuildTextRunForFrames(buffer.Elements());
} }
if (aFlushLineBreaks) { if (aFlushLineBreaks) {
PRBool trailingLineBreak; FlushLineBreaks(aSuppressTrailingBreak ? nsnull : textRun);
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();
} }
mCanStopOnThisLine = PR_TRUE; mCanStopOnThisLine = PR_TRUE;
ResetRunInfo(); 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) void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
{ {
NS_ASSERTION(mMaxTextLength <= mMaxTextLength + aFrame->GetContentLength(), "integer overflow"); NS_ASSERTION(mMaxTextLength <= mMaxTextLength + aFrame->GetContentLength(), "integer overflow");
@ -1493,15 +1528,18 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
TextRunMappedFlow dummyMappedFlow; TextRunMappedFlow dummyMappedFlow;
TextRunUserData* userData; TextRunUserData* userData;
TextRunUserData* userDataToDestroy;
// If the situation is particularly simple (and common) we don't need to // If the situation is particularly simple (and common) we don't need to
// allocate userData. // allocate userData.
if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame && if (mMappedFlows.Length() == 1 && !mMappedFlows[0].mEndFrame &&
mMappedFlows[0].mStartFrame->GetContentOffset() == 0) { mMappedFlows[0].mStartFrame->GetContentOffset() == 0) {
userData = &dummyData; userData = &dummyData;
userDataToDestroy = nsnull;
dummyData.mMappedFlows = &dummyMappedFlow; dummyData.mMappedFlows = &dummyMappedFlow;
} else { } else {
userData = static_cast<TextRunUserData*> userData = static_cast<TextRunUserData*>
(nsMemory::Alloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow))); (nsMemory::Alloc(sizeof(TextRunUserData) + mMappedFlows.Length()*sizeof(TextRunMappedFlow)));
userDataToDestroy = userData;
userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1); userData->mMappedFlows = reinterpret_cast<TextRunMappedFlow*>(userData + 1);
} }
userData->mMappedFlowCount = mMappedFlows.Length(); userData->mMappedFlowCount = mMappedFlows.Length();
@ -1578,7 +1616,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
// then expand. // then expand.
nsAutoTArray<PRUint8,BIG_TEXT_NODE_SIZE> tempBuf; nsAutoTArray<PRUint8,BIG_TEXT_NODE_SIZE> tempBuf;
if (!tempBuf.AppendElements(contentLength)) { if (!tempBuf.AppendElements(contentLength)) {
DestroyUserData(userData); DestroyUserData(userDataToDestroy);
return nsnull; return nsnull;
} }
PRUint8* bufStart = tempBuf.Elements(); PRUint8* bufStart = tempBuf.Elements();
@ -1606,7 +1644,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
// Check for out-of-memory in gfxSkipCharsBuilder // Check for out-of-memory in gfxSkipCharsBuilder
if (!builder.IsOK()) { if (!builder.IsOK()) {
DestroyUserData(userData); DestroyUserData(userDataToDestroy);
return nsnull; return nsnull;
} }
@ -1641,7 +1679,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame; nsTextFrame* firstFrame = mMappedFlows[0].mStartFrame;
gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame); gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame);
if (!fontGroup) { if (!fontGroup) {
DestroyUserData(userData); DestroyUserData(userDataToDestroy);
return nsnull; return nsnull;
} }
@ -1751,7 +1789,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
} }
} }
if (!textRun) { if (!textRun) {
DestroyUserData(userData); DestroyUserData(userDataToDestroy);
return nsnull; return nsnull;
} }
@ -1764,11 +1802,16 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
if (mSkipIncompleteTextRuns) { if (mSkipIncompleteTextRuns) {
mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr, mSkipIncompleteTextRuns = !TextContainsLineBreakerWhiteSpace(textPtr,
transformedLength, mDoubleByteText); transformedLength, mDoubleByteText);
// Arrange for this textrun to be deleted the next time the linebreaker
// Nuke the textrun // is flushed out
gTextRuns->RemoveFromCache(textRun); mTextRunsToDelete.AppendElement(textRun);
delete textRun; // Since we're doing to destroy the user data now, avoid a dangling
DestroyUserData(userData); // 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; return nsnull;
} }

Просмотреть файл

@ -66,6 +66,7 @@ public:
// the original text has at least one soft hyphen character // the original text has at least one soft hyphen character
TEXT_HAS_SHY = 0x020000, TEXT_HAS_SHY = 0x020000,
TEXT_WAS_TRANSFORMED = 0x040000, TEXT_WAS_TRANSFORMED = 0x040000,
TEXT_UNUSED_FLAG = 0x080000,
// The following flags are set by nsTextFrame // The following flags are set by nsTextFrame

Просмотреть файл

@ -72,7 +72,7 @@ nsTransformedTextRun::SetCapitalization(PRUint32 aStart, PRUint32 aLength,
memset(mCapitalize.Elements(), 0, GetLength()*sizeof(PRPackedBool)); memset(mCapitalize.Elements(), 0, GetLength()*sizeof(PRPackedBool));
} }
memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(PRPackedBool)); memcpy(mCapitalize.Elements() + aStart, aCapitalization, aLength*sizeof(PRPackedBool));
mFactory->RebuildTextRun(this, aRefContext); mNeedsRebuild = PR_TRUE;
} }
PRBool PRBool
@ -82,76 +82,20 @@ nsTransformedTextRun::SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
{ {
PRBool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength, PRBool changed = gfxTextRun::SetPotentialLineBreaks(aStart, aLength,
aBreakBefore, aRefContext); aBreakBefore, aRefContext);
mFactory->RebuildTextRun(this, aRefContext); if (changed) {
mNeedsRebuild = PR_TRUE;
}
return changed; 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 (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* gfxTextRun*
nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength, nsTransformingTextRunFactory::MakeTextRun(const PRUnichar* aString, PRUint32 aLength,
const gfxTextRunFactory::Parameters* aParams, const gfxTextRunFactory::Parameters* aParams,
gfxFontGroup* aFontGroup, PRUint32 aFlags, gfxFontGroup* aFontGroup, PRUint32 aFlags,
nsStyleContext** aStyles, PRBool aOwnsFactory) nsStyleContext** aStyles, PRBool aOwnsFactory)
{ {
nsTransformedTextRun* textRun = return nsTransformedTextRun::Create(aParams, this, aFontGroup,
nsTransformedTextRun::Create(aParams, this, aFontGroup, aString, aLength, aFlags, aStyles, aOwnsFactory);
aString, aLength, aFlags, aStyles, aOwnsFactory);
if (!textRun)
return nsnull;
RebuildTextRun(textRun, aParams->mContext);
return textRun;
} }
gfxTextRun* gfxTextRun*
@ -271,7 +215,7 @@ GetParametersForInner(nsTransformedTextRun* aTextRun, PRUint32* aFlags,
{ {
gfxTextRunFactory::Parameters params = gfxTextRunFactory::Parameters params =
{ aRefContext, nsnull, nsnull, { aRefContext, nsnull, nsnull,
nsnull, nsnull, aTextRun->GetAppUnitsPerDevUnit() nsnull, 0, aTextRun->GetAppUnitsPerDevUnit()
}; };
*aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT; *aFlags = aTextRun->GetFlags() & ~gfxFontGroup::TEXT_IS_PERSISTENT;
return params; return params;
@ -313,17 +257,9 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
PRBool runIsLowercase = PR_FALSE; PRBool runIsLowercase = PR_FALSE;
nsAutoTArray<nsStyleContext*,50> styleArray; nsAutoTArray<nsStyleContext*,50> styleArray;
nsAutoTArray<PRPackedBool,50> canBreakBeforeArray; nsAutoTArray<PRPackedBool,50> canBreakBeforeArray;
nsAutoTArray<PRUint32,10> lineBreakBeforeArray;
PRUint32 nextLineBreak = 0;
PRUint32 i; PRUint32 i;
for (i = 0; i <= length; ++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; PRBool isLowercase = PR_FALSE;
if (i < length) { if (i < length) {
// Characters that aren't the start of a cluster are ignored here. They // Characters that aren't the start of a cluster are ignored here. They
@ -346,9 +282,7 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
nsAutoPtr<gfxTextRun> transformedChild; nsAutoPtr<gfxTextRun> transformedChild;
gfxTextRunCache::AutoTextRun cachedChild; gfxTextRunCache::AutoTextRun cachedChild;
gfxTextRun* child; gfxTextRun* child;
// Setup actual line break data for child (which may affect shaping)
innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
if (runIsLowercase) { if (runIsLowercase) {
transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart, transformedChild = uppercaseFactory.MakeTextRun(str + runStart, i - runStart,
&innerParams, smallFont, flags, styleArray.Elements(), PR_FALSE); &innerParams, smallFont, flags, styleArray.Elements(), PR_FALSE);
@ -372,10 +306,6 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
runStart = i; runStart = i;
styleArray.Clear(); styleArray.Clear();
canBreakBeforeArray.Clear(); canBreakBeforeArray.Clear();
lineBreakBeforeArray.Clear();
if (nextLineBreak > 0 && aTextRun->mLineBreaks[nextLineBreak - 1] == i) {
lineBreakBeforeArray.AppendElement(0);
}
} }
if (i < length) { if (i < length) {
@ -384,8 +314,6 @@ nsFontVariantTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i)); canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(i));
} }
} }
NS_ASSERTION(nextLineBreak == aTextRun->mLineBreaks.Length(),
"lost track of line breaks somehow");
} }
void void
@ -404,8 +332,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
nsAutoTArray<PRPackedBool,50> charsToMergeArray; nsAutoTArray<PRPackedBool,50> charsToMergeArray;
nsAutoTArray<nsStyleContext*,50> styleArray; nsAutoTArray<nsStyleContext*,50> styleArray;
nsAutoTArray<PRPackedBool,50> canBreakBeforeArray; nsAutoTArray<PRPackedBool,50> canBreakBeforeArray;
nsAutoTArray<PRUint32,10> lineBreakBeforeArray;
PRUint32 nextLineBreak = 0;
PRUint32 extraCharsCount = 0; PRUint32 extraCharsCount = 0;
PRUint32 i; PRUint32 i;
@ -415,11 +341,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
charsToMergeArray.AppendElement(PR_FALSE); charsToMergeArray.AppendElement(PR_FALSE);
styleArray.AppendElement(styles[i]); styleArray.AppendElement(styles[i]);
canBreakBeforeArray.AppendElement(aTextRun->CanBreakLineBefore(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 PRUint8 style = mAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE
: styles[i]->GetStyleText()->mTextTransform; : styles[i]->GetStyleText()->mTextTransform;
@ -461,13 +382,6 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
canBreakBeforeArray.AppendElement(PR_FALSE); 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; PRUint32 flags;
gfxTextRunFactory::Parameters innerParams = gfxTextRunFactory::Parameters innerParams =
@ -477,9 +391,7 @@ nsCaseTransformTextRunFactory::RebuildTextRun(nsTransformedTextRun* aTextRun,
nsAutoPtr<gfxTextRun> transformedChild; nsAutoPtr<gfxTextRun> transformedChild;
gfxTextRunCache::AutoTextRun cachedChild; gfxTextRunCache::AutoTextRun cachedChild;
gfxTextRun* child; gfxTextRun* child;
// Setup actual line break data for child (which may affect shaping)
innerParams.mInitialBreaks = lineBreakBeforeArray.Elements();
innerParams.mInitialBreakCount = lineBreakBeforeArray.Length();
if (mInnerTransformingTextRunFactory) { if (mInnerTransformingTextRunFactory) {
transformedChild = mInnerTransformingTextRunFactory->MakeTextRun( transformedChild = mInnerTransformingTextRunFactory->MakeTextRun(
convertedString.BeginReading(), convertedString.Length(), convertedString.BeginReading(), convertedString.Length(),

Просмотреть файл

@ -113,22 +113,30 @@ public:
} }
} }
virtual void SetCapitalization(PRUint32 aStart, PRUint32 aLength, void SetCapitalization(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aCapitalization, PRPackedBool* aCapitalization,
gfxContext* aRefContext); gfxContext* aRefContext);
virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength, virtual PRBool SetPotentialLineBreaks(PRUint32 aStart, PRUint32 aLength,
PRPackedBool* aBreakBefore, PRPackedBool* aBreakBefore,
gfxContext* aRefContext); gfxContext* aRefContext);
virtual PRBool SetLineBreaks(PRUint32 aStart, PRUint32 aLength, /**
PRBool aLineBreakBefore, PRBool aLineBreakAfter, * Called after SetCapitalization and SetPotentialLineBreaks
gfxFloat* aAdvanceWidthDelta, * are done and before we request any data from the textrun. Also always
gfxContext* aRefContext); * called after a Create.
*/
void FinishSettingProperties(gfxContext* aRefContext)
{
if (mNeedsRebuild) {
mNeedsRebuild = PR_FALSE;
mFactory->RebuildTextRun(this, aRefContext);
}
}
nsTransformingTextRunFactory *mFactory; nsTransformingTextRunFactory *mFactory;
nsTArray<PRUint32> mLineBreaks;
nsTArray<nsRefPtr<nsStyleContext> > mStyles; nsTArray<nsRefPtr<nsStyleContext> > mStyles;
nsTArray<PRPackedBool> mCapitalize; nsTArray<PRPackedBool> mCapitalize;
PRPackedBool mOwnsFactory; PRPackedBool mOwnsFactory;
PRPackedBool mNeedsRebuild;
private: private:
nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams, nsTransformedTextRun(const gfxTextRunFactory::Parameters* aParams,
@ -138,15 +146,12 @@ private:
const PRUint32 aFlags, nsStyleContext** aStyles, const PRUint32 aFlags, nsStyleContext** aStyles,
PRBool aOwnsFactory) PRBool aOwnsFactory)
: gfxTextRun(aParams, aString, aLength, aFontGroup, aFlags, sizeof(nsTransformedTextRun)), : gfxTextRun(aParams, aString, aLength, aFontGroup, aFlags, sizeof(nsTransformedTextRun)),
mFactory(aFactory), mOwnsFactory(aOwnsFactory) mFactory(aFactory), mOwnsFactory(aOwnsFactory), mNeedsRebuild(PR_TRUE)
{ {
PRUint32 i; PRUint32 i;
for (i = 0; i < aLength; ++i) { for (i = 0; i < aLength; ++i) {
mStyles.AppendElement(aStyles[i]); mStyles.AppendElement(aStyles[i]);
} }
for (i = 0; i < aParams->mInitialBreakCount; ++i) {
mLineBreaks.AppendElement(aParams->mInitialBreaks[i]);
}
} }
}; };