зеркало из https://github.com/mozilla/gecko-dev.git
Bug 403426. We should clear whitespace status when we reset the linebreaker. If a final break opportunity exists we should save it and forward it to the line layout. Relanding with a fix so hopefully we won't crash Tp this time. r=smontagu
This commit is contained in:
Родитель
d68e211e1e
Коммит
b59ab6a038
|
@ -184,8 +184,11 @@ public:
|
||||||
* After this call, this linebreaker can be reused.
|
* After this call, this linebreaker can be reused.
|
||||||
* This must be called at least once between any call to AppendText() and
|
* This must be called at least once between any call to AppendText() and
|
||||||
* destroying the object.
|
* destroying the object.
|
||||||
|
* @param aTrailingBreak this is set to true when there is a break opportunity
|
||||||
|
* at the end of the text. This will normally only be declared true when there
|
||||||
|
* is breakable whitespace at the end.
|
||||||
*/
|
*/
|
||||||
nsresult Reset() { return FlushCurrentWord(); }
|
nsresult Reset(PRBool* aTrailingBreak);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// This is a list of text sources that make up the "current word" (i.e.,
|
// This is a list of text sources that make up the "current word" (i.e.,
|
||||||
|
|
|
@ -391,7 +391,8 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags) {
|
nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags)
|
||||||
|
{
|
||||||
nsresult rv = FlushCurrentWord();
|
nsresult rv = FlushCurrentWord();
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -401,5 +402,18 @@ nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags) {
|
||||||
mBreakHere = PR_TRUE;
|
mBreakHere = PR_TRUE;
|
||||||
}
|
}
|
||||||
mAfterBreakableSpace = isBreakableSpace;
|
mAfterBreakableSpace = isBreakableSpace;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsLineBreaker::Reset(PRBool* aTrailingBreak)
|
||||||
|
{
|
||||||
|
nsresult rv = FlushCurrentWord();
|
||||||
|
if (NS_FAILED(rv))
|
||||||
|
return rv;
|
||||||
|
|
||||||
|
*aTrailingBreak = mBreakHere || mAfterBreakableSpace;
|
||||||
|
mBreakHere = PR_FALSE;
|
||||||
|
mAfterBreakableSpace = PR_FALSE;
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -603,10 +603,16 @@ public:
|
||||||
mDoubleByteText = PR_FALSE;
|
mDoubleByteText = PR_FALSE;
|
||||||
}
|
}
|
||||||
void ResetLineBreaker() {
|
void ResetLineBreaker() {
|
||||||
mLineBreaker.Reset();
|
PRBool trailingBreak;
|
||||||
|
mLineBreaker.Reset(&trailingBreak);
|
||||||
}
|
}
|
||||||
void AccumulateRunInfo(nsTextFrame* aFrame);
|
void AccumulateRunInfo(nsTextFrame* aFrame);
|
||||||
void BuildTextRunForFrames(void* aTextBuffer);
|
/**
|
||||||
|
* @return null to indicate either textrun construction failed or
|
||||||
|
* we constructed just a partial textrun to set up linebreaker and other
|
||||||
|
* state for following textruns.
|
||||||
|
*/
|
||||||
|
gfxTextRun* BuildTextRunForFrames(void* aTextBuffer);
|
||||||
void AssignTextRun(gfxTextRun* aTextRun);
|
void AssignTextRun(gfxTextRun* aTextRun);
|
||||||
nsTextFrame* GetNextBreakBeforeFrame(PRUint32* aIndex);
|
nsTextFrame* GetNextBreakBeforeFrame(PRUint32* aIndex);
|
||||||
void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, PRBool aIsExistingTextRun,
|
void SetupBreakSinksForTextRun(gfxTextRun* aTextRun, PRBool aIsExistingTextRun,
|
||||||
|
@ -691,9 +697,8 @@ private:
|
||||||
gfxContext* mContext;
|
gfxContext* mContext;
|
||||||
nsIFrame* mLineContainer;
|
nsIFrame* mLineContainer;
|
||||||
nsTextFrame* mLastFrame;
|
nsTextFrame* mLastFrame;
|
||||||
// The common ancestor of the current frame and the previous text frame
|
// The common ancestor of the current frame and the previous leaf frame
|
||||||
// on the line, if there's no non-text frame boundaries in between. Otherwise
|
// on the line, or null if there was no previous leaf frame.
|
||||||
// null.
|
|
||||||
nsIFrame* mCommonAncestorWithLastFrame;
|
nsIFrame* mCommonAncestorWithLastFrame;
|
||||||
// mMaxTextLength is an upper bound on the size of the text in all mapped frames
|
// mMaxTextLength is an upper bound on the size of the text in all mapped frames
|
||||||
PRUint32 mMaxTextLength;
|
PRUint32 mMaxTextLength;
|
||||||
|
@ -1028,26 +1033,35 @@ void BuildTextRunsScanner::FlushFrames(PRBool aFlushLineBreaks)
|
||||||
if (mMappedFlows.Length() == 0)
|
if (mMappedFlows.Length() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
gfxTextRun* textRun;
|
||||||
if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
|
if (!mSkipIncompleteTextRuns && mCurrentFramesAllSameTextRun &&
|
||||||
((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
|
((mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_INCOMING_WHITESPACE) != 0) ==
|
||||||
mCurrentRunTrimLeadingWhitespace &&
|
mCurrentRunTrimLeadingWhitespace &&
|
||||||
IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
|
IsTextRunValidForMappedFlows(mCurrentFramesAllSameTextRun)) {
|
||||||
// Optimization: We do not need to (re)build the textrun.
|
// 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 mTrimNextRunLeadingWhitespace appropriately.
|
// updates mTrimNextRunLeadingWhitespace appropriately.
|
||||||
SetupBreakSinksForTextRun(mCurrentFramesAllSameTextRun, PR_TRUE, PR_FALSE);
|
SetupBreakSinksForTextRun(textRun, PR_TRUE, PR_FALSE);
|
||||||
mTrimNextRunLeadingWhitespace =
|
mTrimNextRunLeadingWhitespace =
|
||||||
(mCurrentFramesAllSameTextRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) != 0;
|
(textRun->GetFlags() & nsTextFrameUtils::TEXT_TRAILING_WHITESPACE) != 0;
|
||||||
} else {
|
} else {
|
||||||
nsAutoTArray<PRUint8,BIG_TEXT_NODE_SIZE> buffer;
|
nsAutoTArray<PRUint8,BIG_TEXT_NODE_SIZE> buffer;
|
||||||
if (!buffer.AppendElements(mMaxTextLength*(mDoubleByteText ? 2 : 1)))
|
if (!buffer.AppendElements(mMaxTextLength*(mDoubleByteText ? 2 : 1)))
|
||||||
return;
|
return;
|
||||||
BuildTextRunForFrames(buffer.Elements());
|
textRun = BuildTextRunForFrames(buffer.Elements());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aFlushLineBreaks) {
|
if (aFlushLineBreaks) {
|
||||||
mLineBreaker.Reset();
|
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) {
|
||||||
|
textRun->SetFlagBits(nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK);
|
||||||
|
}
|
||||||
PRUint32 i;
|
PRUint32 i;
|
||||||
for (i = 0; i < mBreakSinks.Length(); ++i) {
|
for (i = 0; i < mBreakSinks.Length(); ++i) {
|
||||||
if (!mBreakSinks[i]->mExistingTextRun || mBreakSinks[i]->mChangedBreaks) {
|
if (!mBreakSinks[i]->mExistingTextRun || mBreakSinks[i]->mChangedBreaks) {
|
||||||
|
@ -1067,7 +1081,7 @@ void BuildTextRunsScanner::AccumulateRunInfo(nsTextFrame* aFrame)
|
||||||
mMaxTextLength += aFrame->GetContentLength();
|
mMaxTextLength += aFrame->GetContentLength();
|
||||||
mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
|
mDoubleByteText |= aFrame->GetContent()->GetText()->Is2b();
|
||||||
mLastFrame = aFrame;
|
mLastFrame = aFrame;
|
||||||
mCommonAncestorWithLastFrame = aFrame;
|
mCommonAncestorWithLastFrame = aFrame->GetParent();
|
||||||
|
|
||||||
MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
|
MappedFlow* mappedFlow = &mMappedFlows[mMappedFlows.Length() - 1];
|
||||||
NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
|
NS_ASSERTION(mappedFlow->mStartFrame == aFrame ||
|
||||||
|
@ -1198,12 +1212,12 @@ void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
|
||||||
PRBool descendInto = PR_TRUE;
|
PRBool descendInto = PR_TRUE;
|
||||||
if (!continueTextRun) {
|
if (!continueTextRun) {
|
||||||
FlushFrames(PR_TRUE);
|
FlushFrames(PR_TRUE);
|
||||||
mCommonAncestorWithLastFrame = nsnull;
|
mCommonAncestorWithLastFrame = aFrame;
|
||||||
|
mTrimNextRunLeadingWhitespace = PR_FALSE;
|
||||||
// XXX do we need this? are there frames we need to descend into that aren't
|
// XXX do we need this? are there frames we need to descend into that aren't
|
||||||
// float-containing-blocks?
|
// float-containing-blocks?
|
||||||
descendInto = !aFrame->IsFloatContainingBlock();
|
descendInto = !aFrame->IsFloatContainingBlock();
|
||||||
mStartOfLine = PR_FALSE;
|
mStartOfLine = PR_FALSE;
|
||||||
mTrimNextRunLeadingWhitespace = PR_FALSE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descendInto) {
|
if (descendInto) {
|
||||||
|
@ -1215,7 +1229,7 @@ void BuildTextRunsScanner::ScanFrame(nsIFrame* aFrame)
|
||||||
|
|
||||||
if (!continueTextRun) {
|
if (!continueTextRun) {
|
||||||
FlushFrames(PR_TRUE);
|
FlushFrames(PR_TRUE);
|
||||||
mCommonAncestorWithLastFrame = nsnull;
|
mCommonAncestorWithLastFrame = aFrame;
|
||||||
mTrimNextRunLeadingWhitespace = PR_FALSE;
|
mTrimNextRunLeadingWhitespace = PR_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1322,7 +1336,7 @@ GetFontMetrics(gfxFontGroup* aFontGroup)
|
||||||
return font->GetMetrics();
|
return font->GetMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
gfxTextRun*
|
||||||
BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||||
{
|
{
|
||||||
gfxSkipCharsBuilder builder;
|
gfxSkipCharsBuilder builder;
|
||||||
|
@ -1421,7 +1435,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||||
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(userData);
|
||||||
return;
|
return nsnull;
|
||||||
}
|
}
|
||||||
PRUint8* bufStart = tempBuf.Elements();
|
PRUint8* bufStart = tempBuf.Elements();
|
||||||
PRUint8* end = nsTextFrameUtils::TransformText(
|
PRUint8* end = nsTextFrameUtils::TransformText(
|
||||||
|
@ -1456,7 +1470,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(userData);
|
||||||
return;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* finalUserData;
|
void* finalUserData;
|
||||||
|
@ -1491,7 +1505,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||||
gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame);
|
gfxFontGroup* fontGroup = GetFontGroupForFrame(firstFrame);
|
||||||
if (!fontGroup) {
|
if (!fontGroup) {
|
||||||
DestroyUserData(userData);
|
DestroyUserData(userData);
|
||||||
return;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
|
if (textFlags & nsTextFrameUtils::TEXT_HAS_TAB) {
|
||||||
|
@ -1598,7 +1612,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||||
}
|
}
|
||||||
if (!textRun) {
|
if (!textRun) {
|
||||||
DestroyUserData(userData);
|
DestroyUserData(userData);
|
||||||
return;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to set these up after we've created the textrun, because
|
// We have to set these up after we've created the textrun, because
|
||||||
|
@ -1615,12 +1629,13 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
||||||
gTextRuns->RemoveFromCache(textRun);
|
gTextRuns->RemoveFromCache(textRun);
|
||||||
delete textRun;
|
delete textRun;
|
||||||
DestroyUserData(userData);
|
DestroyUserData(userData);
|
||||||
return;
|
return nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actually wipe out the textruns associated with the mapped frames and associate
|
// Actually wipe out the textruns associated with the mapped frames and associate
|
||||||
// those frames with this text run.
|
// those frames with this text run.
|
||||||
AssignTextRun(textRun);
|
AssignTextRun(textRun);
|
||||||
|
return textRun;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PRBool
|
static PRBool
|
||||||
|
@ -5508,6 +5523,19 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
||||||
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth);
|
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth);
|
||||||
}
|
}
|
||||||
|
PRBool breakAfter = PR_FALSE;
|
||||||
|
if ((charsFit == length && transformedOffset + transformedLength == mTextRun->GetLength() &&
|
||||||
|
(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TRAILING_BREAK))) {
|
||||||
|
// Note that because we didn't break, we can be sure that (thanks to the
|
||||||
|
// code up above) textMetrics.mAdvanceWidth includes the width of any
|
||||||
|
// trailing whitespace. So we need to subtract trimmableWidth here
|
||||||
|
// because if we did break at this point, that much width would be trimmed.
|
||||||
|
if (textMetrics.mAdvanceWidth - trimmableWidth > availWidth) {
|
||||||
|
breakAfter = PR_TRUE;
|
||||||
|
} else {
|
||||||
|
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length, PR_TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (completedFirstLetter) {
|
if (completedFirstLetter) {
|
||||||
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
||||||
}
|
}
|
||||||
|
@ -5523,6 +5551,8 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
// Ends in \n
|
// Ends in \n
|
||||||
aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
|
aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
|
||||||
lineLayout.SetLineEndsInBR(PR_TRUE);
|
lineLayout.SetLineEndsInBR(PR_TRUE);
|
||||||
|
} else if (breakAfter) {
|
||||||
|
aStatus = NS_INLINE_LINE_BREAK_AFTER(aStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute space and letter counts for justification, if required
|
// Compute space and letter counts for justification, if required
|
||||||
|
|
|
@ -73,7 +73,12 @@ public:
|
||||||
TEXT_TRAILING_WHITESPACE = 0x400000,
|
TEXT_TRAILING_WHITESPACE = 0x400000,
|
||||||
TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
|
TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
|
||||||
TEXT_NO_BREAKS = 0x1000000,
|
TEXT_NO_BREAKS = 0x1000000,
|
||||||
TEXT_IS_TRANSFORMED = 0x2000000
|
TEXT_IS_TRANSFORMED = 0x2000000,
|
||||||
|
// This gets set if there's a break opportunity at the end of the textrun.
|
||||||
|
// We normally don't use this break opportunity because the following text
|
||||||
|
// will have a break opportunity at the start, but it's useful for line
|
||||||
|
// layout to know about it in case the following content is not text
|
||||||
|
TEXT_HAS_TRAILING_BREAK = 0x4000000
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p><span style="white-space:nowrap;"><input type="checkbox">hello1</span>
|
||||||
|
<p>Hello2<br><span style="white-space:nowrap;"><input type="checkbox"></span>
|
||||||
|
<p>Hello3<br><input type="checkbox">
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,9 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<body style="width:0;">
|
||||||
|
<p><span style="white-space:nowrap;"><input type="checkbox">hello1</span>
|
||||||
|
<p>Hello2 <span style="white-space:nowrap;"><input type="checkbox"></span>
|
||||||
|
<p style="white-space:nowrap"><span style="white-space:normal">Hello3
|
||||||
|
</span><span style="white-space:nowrap;"><input type="checkbox"></span>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -473,6 +473,7 @@ random == 403134-1.html 403134-1-ref.html # bug 405377
|
||||||
== 403249-1b.html 403249-1-ref.html
|
== 403249-1b.html 403249-1-ref.html
|
||||||
== 403249-2a.html 403249-2-ref.html
|
== 403249-2a.html 403249-2-ref.html
|
||||||
== 403249-2b.html 403249-2-ref.html
|
== 403249-2b.html 403249-2-ref.html
|
||||||
|
== 403426-1.html 403426-1-ref.html
|
||||||
== 403455-1.html 403455-1-ref.html
|
== 403455-1.html 403455-1-ref.html
|
||||||
== 403505-1.xml 403505-1-ref.xul
|
== 403505-1.xml 403505-1-ref.xul
|
||||||
== 403519-1.html 403519-1-ref.html
|
== 403519-1.html 403519-1-ref.html
|
||||||
|
|
Загрузка…
Ссылка в новой задаче