зеркало из https://github.com/mozilla/pjs.git
Not part of the build. Change nsLineBreaker so that breaks at element boundaries are always governed by the white-space property on the element that's the nearest common ancestor of the DOM nodes on each side of the break point --- fixes bug 382289. Also, pass NBSP through to textruns so that whitespace trimming doesn't trim NBSP. Also, count trimmed spaces correctly when updating mClusterCount.
This commit is contained in:
Родитель
2a3bfee38b
Коммит
c71de10d30
|
@ -72,63 +72,50 @@ public:
|
|||
* into AppendText calls.
|
||||
*
|
||||
* The current strategy is that we break the overall text into
|
||||
* whitespace-delimited "words". Then for words that contain a CJK character,
|
||||
* we break within the word using JISx4051 rules.
|
||||
* XXX This approach is not very good and we should replace it with something
|
||||
* better, such as some variant of UAX#14.
|
||||
* whitespace-delimited "words". Then for words that contain a "complex"
|
||||
* character (currently CJK or Thai), we break within the word using complex
|
||||
* rules (JISx4051 or Pango).
|
||||
*/
|
||||
class nsLineBreaker {
|
||||
public:
|
||||
nsLineBreaker();
|
||||
~nsLineBreaker();
|
||||
|
||||
// We need finegrained control of the line breaking behaviour to ensure
|
||||
// that we get tricky CSS semantics right (in particular, the way we currently
|
||||
// interpret and implement them; there's some ambiguity in the spec). The
|
||||
// rules for CSS 'white-space' are slightly different for breaks induced by
|
||||
// whitespace and space induced by nonwhitespace. Breaks induced by
|
||||
// whitespace are always controlled by the
|
||||
// 'white-space' property of the text node containing the
|
||||
// whitespace. Breaks induced by non-whitespace where the break is between
|
||||
// two nodes are controled by the 'white-space' property on the nearest
|
||||
// common ancestor node. Therefore we provide separate control over
|
||||
// a) whether whitespace in this text induces breaks b) whether we can
|
||||
// break between nonwhitespace inside this text and c) whether we can break
|
||||
// between nonwhitespace between the last text and this text.
|
||||
// Normally, break opportunities exist at the end of each run of whitespace
|
||||
// (Unicode ZWSP (U+200B) and ASCII space (U+0020)). Break opportunities can
|
||||
// also exist inside runs of non-whitespace, as determined by nsILineBreaker.
|
||||
// We provide flags to control on a per-chunk basis where breaks are allowed.
|
||||
// At any character boundary, exactly one text chunk governs whether a
|
||||
// break is allowed at that boundary.
|
||||
//
|
||||
// "Whitespace" below means Unicode ZWSP (U+200B) and ASCII space (U+0020). We
|
||||
// operate on text after whitespace processing has been applied, so
|
||||
// We operate on text after whitespace processing has been applied, so
|
||||
// other characters (e.g. tabs and newlines) may have been converted to
|
||||
// spaces.
|
||||
enum {
|
||||
/**
|
||||
* Allow breaks where a non-whitespace character in this block of text
|
||||
* is preceded by a whitespace character.
|
||||
* Allow a break opportunity at the start of this chunk of text.
|
||||
*/
|
||||
BREAK_WHITESPACE_END = 0x01,
|
||||
BREAK_ALLOW_INITIAL = 0x01,
|
||||
/**
|
||||
* Allow breaks between eligible nonwhitespace characters when the break
|
||||
* is in the interior of this block of text.
|
||||
* Allow a break opportunity in the interior of this chunk of text.
|
||||
*/
|
||||
BREAK_NONWHITESPACE_INSIDE = 0x02,
|
||||
/**
|
||||
* Allow break between eligible nonwhitespace characters when the break
|
||||
* is at the beginning of this block of text.
|
||||
*/
|
||||
BREAK_NONWHITESPACE_BEFORE = 0x04
|
||||
BREAK_ALLOW_INSIDE = 0x02,
|
||||
};
|
||||
|
||||
/**
|
||||
* Feed Unicode text into the linebreaker for analysis.
|
||||
* If aLength is zero, then we assume the string is "invisible whitespace"
|
||||
* which can induce breaks.
|
||||
* Append "invisible whitespace". This acts like whitespace, but there is
|
||||
* no actual text associated with it.
|
||||
*/
|
||||
nsresult AppendInvisibleWhitespace();
|
||||
|
||||
/**
|
||||
* Feed Unicode text into the linebreaker for analysis. aLength must be
|
||||
* nonzero.
|
||||
*/
|
||||
nsresult AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
|
||||
PRUint32 aFlags, nsILineBreakSink* aSink);
|
||||
/**
|
||||
* Feed 8-bit text into the linebreaker for analysis.
|
||||
* If aLength is zero, then we assume the string is "invisible whitespace"
|
||||
* which can induce breaks.
|
||||
* Feed 8-bit text into the linebreaker for analysis. aLength must be nonzero.
|
||||
*/
|
||||
nsresult AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
|
||||
PRUint32 aFlags, nsILineBreakSink* aSink);
|
||||
|
@ -143,6 +130,9 @@ public:
|
|||
nsresult Reset() { return FlushCurrentWord(); }
|
||||
|
||||
private:
|
||||
// This is a list of text sources that make up the "current word" (i.e.,
|
||||
// run of text which does not contain any whitespace). All the mLengths
|
||||
// are are nonzero, these cannot overlap.
|
||||
struct TextItem {
|
||||
TextItem(nsILineBreakSink* aSink, PRUint32 aSinkOffset, PRUint32 aLength,
|
||||
PRUint32 aFlags)
|
||||
|
@ -167,9 +157,8 @@ private:
|
|||
nsAutoTArray<TextItem,2> mTextItems;
|
||||
PRPackedBool mCurrentWordContainsCJK;
|
||||
|
||||
// When mCurrentWord is empty, this indicates whether we should allow a break
|
||||
// before the next text if it starts with non-whitespace.
|
||||
PRPackedBool mBreakBeforeNonWhitespace;
|
||||
// True if the previous character was whitespace
|
||||
PRPackedBool mAfterSpace;
|
||||
};
|
||||
|
||||
#endif /*NSLINEBREAKER_H_*/
|
||||
|
|
|
@ -65,7 +65,7 @@ IS_CJK_CHAR(PRUnichar u)
|
|||
|
||||
nsLineBreaker::nsLineBreaker()
|
||||
: mCurrentWordContainsCJK(PR_FALSE),
|
||||
mBreakBeforeNonWhitespace(PR_FALSE)
|
||||
mAfterSpace(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -95,10 +95,10 @@ nsLineBreaker::FlushCurrentWord()
|
|||
TextItem* ti = &mTextItems[i];
|
||||
NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
|
||||
|
||||
if (!(ti->mFlags & BREAK_NONWHITESPACE_BEFORE) && ti->mSinkOffset == 0) {
|
||||
if (!(ti->mFlags & BREAK_ALLOW_INITIAL) && ti->mSinkOffset == 0) {
|
||||
breakState[offset] = PR_FALSE;
|
||||
}
|
||||
if (!(ti->mFlags & BREAK_NONWHITESPACE_INSIDE)) {
|
||||
if (!(ti->mFlags & BREAK_ALLOW_INSIDE)) {
|
||||
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
|
||||
memset(breakState.Elements() + offset + exclude, PR_FALSE, ti->mLength - exclude);
|
||||
}
|
||||
|
@ -122,20 +122,13 @@ nsresult
|
|||
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32 aLength,
|
||||
PRUint32 aFlags, nsILineBreakSink* aSink)
|
||||
{
|
||||
if (aLength == 0) {
|
||||
// Treat as "invisible whitespace"
|
||||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ASSERTION(aLength > 0, "Appending empty text...");
|
||||
|
||||
PRUint32 offset = 0;
|
||||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
|
||||
NS_ASSERTION(!mAfterSpace, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IS_SPACE(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -166,17 +159,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
PRUint32 wordStart = offset;
|
||||
PRBool wordHasCJK = PR_FALSE;
|
||||
|
||||
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
|
||||
for (;;) {
|
||||
PRUnichar ch = aText[offset];
|
||||
PRBool isSpace = IS_SPACE(ch);
|
||||
|
||||
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
|
||||
breakNextIfNonWhitespace = PR_FALSE;
|
||||
breakState[offset] = mAfterSpace && !isSpace &&
|
||||
(aFlags & (start == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
||||
mAfterSpace = isSpace;
|
||||
|
||||
if (isSpace) {
|
||||
if (offset > wordStart && wordHasCJK) {
|
||||
if (aFlags & BREAK_NONWHITESPACE_INSIDE) {
|
||||
if (aFlags & BREAK_ALLOW_INSIDE) {
|
||||
// Save current start-of-word state because GetJISx4051Breaks will
|
||||
// set it to false
|
||||
PRPackedBool currentStart = breakState[wordStart];
|
||||
|
@ -188,9 +181,6 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
wordHasCJK = PR_FALSE;
|
||||
}
|
||||
|
||||
if (aFlags & BREAK_WHITESPACE_END) {
|
||||
breakNextIfNonWhitespace = PR_TRUE;
|
||||
}
|
||||
++offset;
|
||||
if (offset >= aLength)
|
||||
break;
|
||||
|
@ -217,7 +207,6 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
}
|
||||
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -225,20 +214,13 @@ nsresult
|
|||
nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aLength,
|
||||
PRUint32 aFlags, nsILineBreakSink* aSink)
|
||||
{
|
||||
if (aLength == 0) {
|
||||
// Treat as "invisible whitespace"
|
||||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
|
||||
return NS_OK;
|
||||
}
|
||||
NS_ASSERTION(aLength > 0, "Appending empty text...");
|
||||
|
||||
PRUint32 offset = 0;
|
||||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
|
||||
NS_ASSERTION(!mAfterSpace, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IS_SPACE(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -267,18 +249,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
PRUint32 start = offset;
|
||||
PRUint32 wordStart = offset;
|
||||
|
||||
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
|
||||
for (;;) {
|
||||
PRUint8 ch = aText[offset];
|
||||
PRBool isSpace = IS_SPACE(ch);
|
||||
|
||||
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
|
||||
breakNextIfNonWhitespace = PR_FALSE;
|
||||
breakState[offset] = mAfterSpace && !isSpace &&
|
||||
(aFlags & (start == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
||||
mAfterSpace = isSpace;
|
||||
|
||||
if (isSpace) {
|
||||
if (aFlags & BREAK_WHITESPACE_END) {
|
||||
breakNextIfNonWhitespace = PR_TRUE;
|
||||
}
|
||||
// The current word can't have any special (CJK/Thai) characters inside it
|
||||
// because this is 8-bit text, so just ignore it
|
||||
++offset;
|
||||
if (offset >= aLength)
|
||||
break;
|
||||
|
@ -307,6 +288,15 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
}
|
||||
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLineBreaker::AppendInvisibleWhitespace() {
|
||||
// Treat as "invisible whitespace"
|
||||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mAfterSpace = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -851,8 +851,8 @@ public:
|
|||
struct MappedFlow {
|
||||
nsTextFrame* mStartFrame;
|
||||
nsTextFrame* mEndFrame;
|
||||
// When we break between non-whitespace characters, the nearest common
|
||||
// ancestor of the frames containing the characters is the one whose
|
||||
// When we consider breaking between elements, the nearest common
|
||||
// ancestor of the elements containing the characters is the one whose
|
||||
// CSS 'white-space' property governs. So this records the nearest common
|
||||
// ancestor of mStartFrame and the previous text frame, or null if there
|
||||
// was no previous text frame on this line.
|
||||
|
@ -1558,12 +1558,40 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|||
AssignTextRun(textRun);
|
||||
}
|
||||
|
||||
static PRBool
|
||||
HasCompressedLeadingWhitespace(nsTextFrame* aFrame, PRInt32 aContentEndOffset,
|
||||
gfxSkipCharsIterator* aIterator)
|
||||
{
|
||||
if (!aIterator->IsOriginalCharSkipped())
|
||||
return PR_FALSE;
|
||||
|
||||
PRBool result = PR_FALSE;
|
||||
PRInt32 savedOffset = aIterator->GetOriginalOffset();
|
||||
PRInt32 frameContentOffset = aFrame->GetContentOffset();
|
||||
const nsTextFragment* frag = aFrame->GetContent()->GetText();
|
||||
while (frameContentOffset < aContentEndOffset &&
|
||||
aIterator->IsOriginalCharSkipped()) {
|
||||
if (IsSpace(frag, frameContentOffset)) {
|
||||
result = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
++frameContentOffset;
|
||||
}
|
||||
aIterator->SetOriginalOffset(savedOffset);
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
|
||||
PRBool aIsExistingTextRun)
|
||||
{
|
||||
// textruns have uniform language
|
||||
nsIAtom* lang = mMappedFlows[0].mStartFrame->GetStyleVisibility()->mLangGroup;
|
||||
// We keep this pointed at the skip-chars data for the current mappedFlow.
|
||||
// This lets us cheaply check whether the flow has compressed initial
|
||||
// whitespace...
|
||||
gfxSkipCharsIterator iter(aTextRun->GetSkipChars());
|
||||
|
||||
PRUint32 i;
|
||||
for (i = 0; i < mMappedFlows.Length(); ++i) {
|
||||
MappedFlow* mappedFlow = &mMappedFlows[i];
|
||||
|
@ -1578,28 +1606,35 @@ BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
|
|||
: mMappedFlows[i + 1].mTransformedTextOffset)
|
||||
- offset;
|
||||
|
||||
PRUint32 flags = 0;
|
||||
if (!mappedFlow->mAncestorControllingInitialBreak ||
|
||||
mappedFlow->mAncestorControllingInitialBreak->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_NONWHITESPACE_BEFORE;
|
||||
nsTextFrame* startFrame = mappedFlow->mStartFrame;
|
||||
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->mContentEndOffset, &iter)) {
|
||||
mLineBreaker.AppendInvisibleWhitespace();
|
||||
}
|
||||
const nsStyleText* textStyle = mappedFlow->mStartFrame->GetStyleText();
|
||||
if (textStyle->WhiteSpaceCanWrap()) {
|
||||
// If white-space is preserved, then the only break opportunity is at
|
||||
// the end of whitespace runs; otherwise there is a break opportunity before
|
||||
// and after each whitespace character
|
||||
flags |= nsLineBreaker::BREAK_NONWHITESPACE_INSIDE |
|
||||
nsLineBreaker::BREAK_WHITESPACE_END;
|
||||
}
|
||||
// If length is zero, the linebreaker treats the text as invisible whitespace.
|
||||
// Thus runs of entirely-skipped whitespace can still induce breaks.
|
||||
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
||||
length, flags, *breakSink);
|
||||
} else {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetTextUnicode() + offset,
|
||||
length, flags, *breakSink);
|
||||
|
||||
if (length > 0) {
|
||||
PRUint32 flags = 0;
|
||||
if (!mappedFlow->mAncestorControllingInitialBreak ||
|
||||
mappedFlow->mAncestorControllingInitialBreak->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_ALLOW_INITIAL;
|
||||
}
|
||||
const nsStyleText* textStyle = startFrame->GetStyleText();
|
||||
if (textStyle->WhiteSpaceCanWrap()) {
|
||||
// If white-space is preserved, then the only break opportunity is at
|
||||
// the end of whitespace runs; otherwise there is a break opportunity before
|
||||
// and after each whitespace character
|
||||
flags |= nsLineBreaker::BREAK_ALLOW_INSIDE;
|
||||
}
|
||||
|
||||
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
||||
length, flags, *breakSink);
|
||||
} else {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetTextUnicode() + offset,
|
||||
length, flags, *breakSink);
|
||||
}
|
||||
}
|
||||
|
||||
iter.AdvanceOriginal(mappedFlow->mContentEndOffset - mappedFlow->mContentOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1729,17 +1764,17 @@ nsTextFrame::EnsureTextRun(nsIRenderingContext* aRC, nsIFrame* aLineContainer,
|
|||
}
|
||||
|
||||
static PRUint32
|
||||
GetLengthOfTrimmedText(const nsTextFragment* aFrag,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
gfxSkipCharsIterator* aIterator)
|
||||
GetEndOfTrimmedText(const nsTextFragment* aFrag,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
gfxSkipCharsIterator* aIterator)
|
||||
{
|
||||
aIterator->SetSkippedOffset(aEnd);
|
||||
while (aIterator->GetSkippedOffset() > aStart) {
|
||||
aIterator->AdvanceSkipped(-1);
|
||||
if (!IsSpace(aFrag, aIterator->GetOriginalOffset()))
|
||||
return aIterator->GetSkippedOffset() + 1 - aStart;
|
||||
return aIterator->GetSkippedOffset() + 1;
|
||||
}
|
||||
return 0;
|
||||
return aStart;
|
||||
}
|
||||
|
||||
nsTextFrame::TrimmedOffsets
|
||||
|
@ -1877,7 +1912,8 @@ public:
|
|||
nscoord aOffsetFromBlockOriginForTabs)
|
||||
: mTextRun(aTextRun), mFontGroup(nsnull), mTextStyle(aTextStyle), mFrag(aFrag),
|
||||
mLineContainer(aLineContainer),
|
||||
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull), mLength(aLength),
|
||||
mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
|
||||
mTabWidths(nsnull), mLength(aLength),
|
||||
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
|
||||
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
|
||||
mJustificationSpacing(0),
|
||||
|
@ -1898,7 +1934,8 @@ public:
|
|||
mTextStyle(aFrame->GetStyleText()),
|
||||
mFrag(aFrame->GetContent()->GetText()),
|
||||
mLineContainer(nsnull),
|
||||
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull),
|
||||
mFrame(aFrame), mStart(aStart), mTempIterator(aStart),
|
||||
mTabWidths(nsnull),
|
||||
mLength(aFrame->GetContentLength()),
|
||||
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
|
||||
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
|
||||
|
@ -1945,6 +1982,8 @@ public:
|
|||
|
||||
gfxFloat* GetTabWidths(PRUint32 aTransformedStart, PRUint32 aTransformedLength);
|
||||
|
||||
const gfxSkipCharsIterator& GetEndHint() { return mTempIterator; }
|
||||
|
||||
protected:
|
||||
void SetupJustificationSpacing();
|
||||
|
||||
|
@ -1955,6 +1994,7 @@ protected:
|
|||
nsIFrame* mLineContainer;
|
||||
nsTextFrame* mFrame;
|
||||
gfxSkipCharsIterator mStart; // Offset in original and transformed string
|
||||
gfxSkipCharsIterator mTempIterator;
|
||||
nsTArray<gfxFloat>* mTabWidths; // widths for each transformed string character
|
||||
PRInt32 mLength; // DOM string length
|
||||
gfxFloat mWordSpacing; // space for each whitespace char
|
||||
|
@ -4488,10 +4528,10 @@ FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
|
|||
if (aData->skipWhitespace && aCollapseWhitespace) {
|
||||
while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
|
||||
IsSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset())) {
|
||||
aIterator->AdvanceSkipped(1);
|
||||
aIterator->AdvanceOriginal(1);
|
||||
}
|
||||
}
|
||||
return aIterator->GetSkippedOffset();
|
||||
return aIterator->GetSkippedOffset();
|
||||
}
|
||||
|
||||
/* virtual */
|
||||
|
@ -4515,11 +4555,11 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
|
||||
// Pass null for the line container. This will disable tab spacing, but that's
|
||||
// OK since we can't really handle tabs for intrinsic sizing anyway.
|
||||
PropertyProvider provider(mTextRun, GetStyleText(), mContent->GetText(), this,
|
||||
const nsTextFragment* frag = mContent->GetText();
|
||||
PropertyProvider provider(mTextRun, GetStyleText(), frag, this,
|
||||
iter, GetInFlowContentLength(), nsnull, 0);
|
||||
|
||||
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
||||
PRBool canWrap = provider.GetStyleText()->WhiteSpaceCanWrap();
|
||||
PRUint32 start =
|
||||
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
|
||||
&iter, flowEndInTextRun);
|
||||
|
@ -4543,17 +4583,10 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
|
||||
if (collapseWhitespace) {
|
||||
nscoord trailingWhitespaceWidth;
|
||||
PRUint32 lengthAfterTrim;
|
||||
if (canWrap) {
|
||||
lengthAfterTrim = GetLengthOfTrimmedText(provider.GetFragment(),
|
||||
wordStart, i, &iter);
|
||||
} else {
|
||||
lengthAfterTrim = i - wordStart;
|
||||
}
|
||||
if (lengthAfterTrim == 0) {
|
||||
PRUint32 trimStart = GetEndOfTrimmedText(frag, wordStart, i, &iter);
|
||||
if (trimStart == start) {
|
||||
trailingWhitespaceWidth = width;
|
||||
} else {
|
||||
PRUint32 trimStart = wordStart + lengthAfterTrim;
|
||||
trailingWhitespaceWidth =
|
||||
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, i - trimStart, &provider));
|
||||
}
|
||||
|
@ -4613,7 +4646,6 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
iter, GetInFlowContentLength(), nsnull, 0);
|
||||
|
||||
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
||||
PRBool canWrap = provider.GetStyleText()->WhiteSpaceCanWrap();
|
||||
PRUint32 start =
|
||||
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
|
||||
&iter, flowEndInTextRun);
|
||||
|
@ -4623,20 +4655,14 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
if (collapseWhitespace) {
|
||||
// \n line breaks are not honoured, so everything would like to go
|
||||
// onto one line, so just measure it
|
||||
PRUint32 lengthAfterTrim;
|
||||
if (canWrap) {
|
||||
lengthAfterTrim = GetLengthOfTrimmedText(provider.GetFragment(), start,
|
||||
flowEndInTextRun, &iter);
|
||||
} else {
|
||||
lengthAfterTrim = flowEndInTextRun;
|
||||
}
|
||||
aData->currentLine +=
|
||||
NSToCoordCeil(mTextRun->GetAdvanceWidth(start, flowEndInTextRun - start, &provider));
|
||||
|
||||
PRUint32 trimStart = start + lengthAfterTrim;
|
||||
PRUint32 trimStart = GetEndOfTrimmedText(provider.GetFragment(), start,
|
||||
flowEndInTextRun, &iter);
|
||||
nscoord trimWidth =
|
||||
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, flowEndInTextRun - trimStart, &provider));
|
||||
if (lengthAfterTrim == 0) {
|
||||
if (trimStart == start) {
|
||||
// This is *all* trimmable whitespace, so whatever trailingWhitespace
|
||||
// we saw previously is still trailing...
|
||||
aData->trailingWhitespace += trimWidth;
|
||||
|
@ -4876,11 +4902,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
needTightBoundingBox = PR_TRUE;
|
||||
}
|
||||
#endif
|
||||
// The "end" iterator points to the first character after the string mapped
|
||||
// by this frame. Basically, it's original-string offset is offset+charsFit
|
||||
// after we've computed charsFit.
|
||||
gfxSkipCharsIterator end(provider.GetStart());
|
||||
|
||||
PRBool suppressInitialBreak = PR_FALSE;
|
||||
if (!lineLayout.LineIsBreakable()) {
|
||||
suppressInitialBreak = PR_TRUE;
|
||||
|
@ -4928,6 +4949,10 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
||||
&textMetrics, needTightBoundingBox,
|
||||
&usedHyphenation, &transformedLastBreak);
|
||||
// The "end" iterator points to the first character after the string mapped
|
||||
// by this frame. Basically, it's original-string offset is offset+charsFit
|
||||
// after we've computed charsFit.
|
||||
gfxSkipCharsIterator end(provider.GetEndHint());
|
||||
end.SetSkippedOffset(transformedOffset + transformedCharsFit);
|
||||
PRInt32 charsFit = end.GetOriginalOffset() - offset;
|
||||
// That might have taken us beyond our assigned content range (because
|
||||
|
@ -5075,8 +5100,9 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
// but that could change...
|
||||
if (canTrimTrailingWhitespace) {
|
||||
// Count trimmed spaces and add them to the cluster count
|
||||
PRUint32 charIndex = transformedCharsFit;
|
||||
while (charIndex > 0 && mTextRun->GetChar(charIndex - 1) == ' ') {
|
||||
PRUint32 charIndex = transformedOffset + transformedCharsFit;
|
||||
while (charIndex > transformedOffset &&
|
||||
mTextRun->GetChar(charIndex - 1) == ' ') {
|
||||
++textMetrics.mClusterCount;
|
||||
--charIndex;
|
||||
}
|
||||
|
|
|
@ -115,10 +115,7 @@ nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
|
|||
aSkipChars->SkipChar();
|
||||
} else {
|
||||
aSkipChars->KeepChar();
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
} else if (ch == '\t') {
|
||||
if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
|
@ -153,10 +150,6 @@ nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
|
|||
aSkipChars->SkipChar();
|
||||
nowInWhitespace = inWhitespace;
|
||||
} else {
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
aSkipChars->KeepChar();
|
||||
}
|
||||
|
@ -203,10 +196,7 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
|
|||
aSkipChars->SkipChar();
|
||||
} else {
|
||||
aSkipChars->KeepChar();
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
} else if (ch == '\t') {
|
||||
if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
|
@ -224,10 +214,6 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
|
|||
aSkipChars->SkipChar();
|
||||
nowInWhitespace = inWhitespace;
|
||||
} else {
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
aSkipChars->KeepChar();
|
||||
}
|
||||
|
|
|
@ -62,7 +62,8 @@ public:
|
|||
TEXT_IS_SIMPLE_FLOW = 0x100000,
|
||||
TEXT_INCOMING_WHITESPACE = 0x200000,
|
||||
TEXT_TRAILING_WHITESPACE = 0x400000,
|
||||
TEXT_IS_UNCACHED = 0x800000
|
||||
TEXT_COMPRESSED_LEADING_WHITESPACE = 0x800000,
|
||||
TEXT_IS_UNCACHED = 0x1000000
|
||||
};
|
||||
|
||||
static PRBool
|
||||
|
|
Загрузка…
Ссылка в новой задаче