|
|
|
@ -143,13 +143,17 @@
|
|
|
|
|
// This bit is set on frames that trimmed trailing whitespace characters when
|
|
|
|
|
// calculating their width during reflow.
|
|
|
|
|
#define TEXT_TRIMMED_TRAILING_WHITESPACE 0x01000000
|
|
|
|
|
// This bit is set on frames that have justification enabled. We record
|
|
|
|
|
// this in a state bit because we don't always have the containing block
|
|
|
|
|
// easily available to check text-align on.
|
|
|
|
|
#define TEXT_JUSTIFICATION_ENABLED 0x02000000
|
|
|
|
|
// Set this bit if the textframe has overflow area for IME/spellcheck underline.
|
|
|
|
|
#define TEXT_SELECTION_UNDERLINE_OVERFLOWED 0x04000000
|
|
|
|
|
|
|
|
|
|
#define TEXT_REFLOW_FLAGS \
|
|
|
|
|
(TEXT_FIRST_LETTER|TEXT_START_OF_LINE|TEXT_END_OF_LINE|TEXT_HYPHEN_BREAK| \
|
|
|
|
|
TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_HAS_NONCOLLAPSED_CHARACTERS| \
|
|
|
|
|
TEXT_SELECTION_UNDERLINE_OVERFLOWED)
|
|
|
|
|
TEXT_TRIMMED_TRAILING_WHITESPACE|TEXT_JUSTIFICATION_ENABLED| \
|
|
|
|
|
TEXT_HAS_NONCOLLAPSED_CHARACTERS|TEXT_SELECTION_UNDERLINE_OVERFLOWED)
|
|
|
|
|
|
|
|
|
|
// Cache bits for IsEmpty().
|
|
|
|
|
// Set this bit if the textframe is known to be only collapsible whitespace.
|
|
|
|
@ -158,7 +162,6 @@
|
|
|
|
|
#define TEXT_ISNOT_ONLY_WHITESPACE 0x10000000
|
|
|
|
|
|
|
|
|
|
#define TEXT_WHITESPACE_FLAGS 0x18000000
|
|
|
|
|
|
|
|
|
|
// This bit is set while the frame is registered as a blinking frame.
|
|
|
|
|
#define TEXT_BLINK_ON 0x80000000
|
|
|
|
|
|
|
|
|
@ -474,47 +477,49 @@ static PRBool IsSpaceCombiningSequenceTail(const nsTextFragment* aFrag, PRUint32
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check whether aPos is a space for CSS 'word-spacing' purposes
|
|
|
|
|
static PRBool IsCSSWordSpacingSpace(const nsTextFragment* aFrag, PRUint32 aPos)
|
|
|
|
|
static PRBool IsCSSWordSpacingSpace(const nsTextFragment* aFrag,
|
|
|
|
|
PRUint32 aPos)
|
|
|
|
|
{
|
|
|
|
|
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
|
|
|
|
|
|
|
|
|
|
PRUnichar ch = aFrag->CharAt(aPos);
|
|
|
|
|
if (ch == ' ' || ch == CH_CJKSP)
|
|
|
|
|
return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
|
|
|
|
|
return ch == '\t' || ch == '\n' || ch == '\f';
|
|
|
|
|
return ch == '\t' || ch == '\f' || ch == '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check whether the string aChars/aLength starts with a space that's
|
|
|
|
|
// trimmable according to CSS 'white-space'.
|
|
|
|
|
// Check whether the string aChars/aLength starts with space that's
|
|
|
|
|
// trimmable according to CSS 'white-space:normal/nowrap'.
|
|
|
|
|
static PRBool IsTrimmableSpace(const PRUnichar* aChars, PRUint32 aLength)
|
|
|
|
|
{
|
|
|
|
|
NS_ASSERTION(aLength > 0, "No text for IsSpace!");
|
|
|
|
|
|
|
|
|
|
PRUnichar ch = *aChars;
|
|
|
|
|
if (ch == ' ')
|
|
|
|
|
return !nsTextFrameUtils::IsSpaceCombiningSequenceTail(aChars + 1, aLength - 1);
|
|
|
|
|
return ch == '\t' || ch == '\n' || ch == '\f';
|
|
|
|
|
return ch == '\t' || ch == '\f' || ch == '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check whether the character aCh is trimmable according to CSS 'white-space'
|
|
|
|
|
// Check whether the character aCh is trimmable according to CSS
|
|
|
|
|
// 'white-space:normal/nowrap'
|
|
|
|
|
static PRBool IsTrimmableSpace(char aCh)
|
|
|
|
|
{
|
|
|
|
|
return aCh == ' ' || aCh == '\t' || aCh == '\n' || aCh == '\f';
|
|
|
|
|
return aCh == ' ' || aCh == '\t' || aCh == '\f' || aCh == '\n';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRBool IsTrimmableSpace(const nsTextFragment* aFrag, PRUint32 aPos)
|
|
|
|
|
static PRBool IsTrimmableSpace(const nsTextFragment* aFrag, PRUint32 aPos,
|
|
|
|
|
const nsStyleText* aStyleText)
|
|
|
|
|
{
|
|
|
|
|
NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
|
|
|
|
|
PRUnichar ch = aFrag->CharAt(aPos);
|
|
|
|
|
if (ch == ' ')
|
|
|
|
|
return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
|
|
|
|
|
return ch == '\t' || ch == '\n' || ch == '\f';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRBool IsTrimmableSpace(const nsTextFragment* aFrag,
|
|
|
|
|
const nsStyleText* aText, PRUint32 aPos)
|
|
|
|
|
{
|
|
|
|
|
if (aText->WhiteSpaceIsSignificant())
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
return IsTrimmableSpace(aFrag, aPos);
|
|
|
|
|
switch (aFrag->CharAt(aPos)) {
|
|
|
|
|
case ' ': return !aStyleText->WhiteSpaceIsSignificant() &&
|
|
|
|
|
!IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
|
|
|
|
|
case '\n': return !aStyleText->NewlineIsSignificant();
|
|
|
|
|
case '\t':
|
|
|
|
|
case '\f': return !aStyleText->WhiteSpaceIsSignificant();
|
|
|
|
|
default: return PR_FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRBool IsSelectionSpace(const nsTextFragment* aFrag, PRUint32 aPos)
|
|
|
|
@ -526,13 +531,15 @@ static PRBool IsSelectionSpace(const nsTextFragment* aFrag, PRUint32 aPos)
|
|
|
|
|
return ch == '\t' || ch == '\n' || ch == '\f';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Count the amount of trimmable whitespace in a text fragment. The first
|
|
|
|
|
// Count the amount of trimmable whitespace (as per CSS
|
|
|
|
|
// 'white-space:normal/nowrap') in a text fragment. The first
|
|
|
|
|
// character is at offset aStartOffset; the maximum number of characters
|
|
|
|
|
// to check is aLength. aDirection is -1 or 1 depending on whether we should
|
|
|
|
|
// progress backwards or forwards.
|
|
|
|
|
static PRUint32
|
|
|
|
|
GetTrimmableWhitespaceCount(const nsTextFragment* aFrag, PRInt32 aStartOffset,
|
|
|
|
|
PRInt32 aLength, PRInt32 aDirection)
|
|
|
|
|
GetTrimmableWhitespaceCount(const nsTextFragment* aFrag,
|
|
|
|
|
PRInt32 aStartOffset, PRInt32 aLength,
|
|
|
|
|
PRInt32 aDirection)
|
|
|
|
|
{
|
|
|
|
|
PRInt32 count = 0;
|
|
|
|
|
if (aFrag->Is2b()) {
|
|
|
|
@ -555,6 +562,22 @@ GetTrimmableWhitespaceCount(const nsTextFragment* aFrag, PRInt32 aStartOffset,
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
|
IsAllWhitespace(const nsTextFragment* aFrag, PRBool aAllowNewline)
|
|
|
|
|
{
|
|
|
|
|
if (aFrag->Is2b())
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
PRInt32 len = aFrag->GetLength();
|
|
|
|
|
const char* str = aFrag->Get1b();
|
|
|
|
|
for (PRInt32 i = 0; i < len; ++i) {
|
|
|
|
|
char ch = str[i];
|
|
|
|
|
if (ch == ' ' || ch == '\t' || (ch == '\n' && aAllowNewline))
|
|
|
|
|
continue;
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
}
|
|
|
|
|
return PR_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This class accumulates state as we scan a paragraph of text. It detects
|
|
|
|
|
* textrun boundaries (changes from text to non-text, hard
|
|
|
|
@ -1167,7 +1190,7 @@ BuildTextRunsScanner::ContinueTextRunAcrossFrames(nsTextFrame* aFrame1, nsTextFr
|
|
|
|
|
// even if it has newlines in it, so typically we won't see trailing newlines
|
|
|
|
|
// until after reflow has broken up the frame into one (or more) frames per
|
|
|
|
|
// line. That's OK though.
|
|
|
|
|
if (textStyle1->WhiteSpaceIsSignificant() && HasTerminalNewline(aFrame1))
|
|
|
|
|
if (textStyle1->NewlineIsSignificant() && HasTerminalNewline(aFrame1))
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
|
|
if (aFrame1->GetContent() == aFrame2->GetContent() &&
|
|
|
|
@ -1386,6 +1409,21 @@ GetFirstFontMetrics(gfxFontGroup* aFontGroup)
|
|
|
|
|
return font->GetMetrics();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
|
|
|
|
|
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE == 1);
|
|
|
|
|
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NOWRAP == 2);
|
|
|
|
|
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_WRAP == 3);
|
|
|
|
|
PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_LINE == 4);
|
|
|
|
|
|
|
|
|
|
static const nsTextFrameUtils::CompressionMode CSSWhitespaceToCompressionMode[] =
|
|
|
|
|
{
|
|
|
|
|
nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // normal
|
|
|
|
|
nsTextFrameUtils::COMPRESS_NONE, // pre
|
|
|
|
|
nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // nowrap
|
|
|
|
|
nsTextFrameUtils::COMPRESS_NONE, // pre-wrap
|
|
|
|
|
nsTextFrameUtils::COMPRESS_WHITESPACE // pre-line
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
gfxTextRun*
|
|
|
|
|
BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
{
|
|
|
|
@ -1425,6 +1463,8 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
|
|
|
|
|
PRUint32 nextBreakIndex = 0;
|
|
|
|
|
nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
|
|
|
|
|
PRBool enabledJustification = mLineContainer &&
|
|
|
|
|
mLineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY;
|
|
|
|
|
|
|
|
|
|
PRUint32 i;
|
|
|
|
|
const nsStyleText* textStyle = nsnull;
|
|
|
|
@ -1442,8 +1482,9 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
}
|
|
|
|
|
textFlags |= GetSpacingFlags(textStyle->mLetterSpacing);
|
|
|
|
|
textFlags |= GetSpacingFlags(textStyle->mWordSpacing);
|
|
|
|
|
PRBool compressWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
|
|
|
|
if (NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign && compressWhitespace) {
|
|
|
|
|
nsTextFrameUtils::CompressionMode compression =
|
|
|
|
|
CSSWhitespaceToCompressionMode[textStyle->mWhiteSpace];
|
|
|
|
|
if (enabledJustification && !textStyle->WhiteSpaceIsSignificant()) {
|
|
|
|
|
textFlags |= gfxTextRunFactory::TEXT_ENABLE_SPACING;
|
|
|
|
|
}
|
|
|
|
|
fontStyle = f->GetStyleFont();
|
|
|
|
@ -1476,7 +1517,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
PRUnichar* bufStart = static_cast<PRUnichar*>(aTextBuffer);
|
|
|
|
|
PRUnichar* bufEnd = nsTextFrameUtils::TransformText(
|
|
|
|
|
frag->Get2b() + contentStart, contentLength, bufStart,
|
|
|
|
|
compressWhitespace, &mTrimNextRunLeadingWhitespace, &builder, &analysisFlags);
|
|
|
|
|
compression, &mTrimNextRunLeadingWhitespace, &builder, &analysisFlags);
|
|
|
|
|
aTextBuffer = bufEnd;
|
|
|
|
|
} else {
|
|
|
|
|
if (mDoubleByteText) {
|
|
|
|
@ -1490,7 +1531,7 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
PRUint8* bufStart = tempBuf.Elements();
|
|
|
|
|
PRUint8* end = nsTextFrameUtils::TransformText(
|
|
|
|
|
reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
|
|
|
|
|
bufStart, compressWhitespace, &mTrimNextRunLeadingWhitespace,
|
|
|
|
|
bufStart, compression, &mTrimNextRunLeadingWhitespace,
|
|
|
|
|
&builder, &analysisFlags);
|
|
|
|
|
aTextBuffer = ExpandBuffer(static_cast<PRUnichar*>(aTextBuffer),
|
|
|
|
|
tempBuf.Elements(), end - tempBuf.Elements());
|
|
|
|
@ -1499,15 +1540,10 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
PRUint8* end = nsTextFrameUtils::TransformText(
|
|
|
|
|
reinterpret_cast<const PRUint8*>(frag->Get1b()) + contentStart, contentLength,
|
|
|
|
|
bufStart,
|
|
|
|
|
compressWhitespace, &mTrimNextRunLeadingWhitespace, &builder, &analysisFlags);
|
|
|
|
|
compression, &mTrimNextRunLeadingWhitespace, &builder, &analysisFlags);
|
|
|
|
|
aTextBuffer = end;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// In CSS 2.1, we do not compress a space that is preceded by a non-compressible
|
|
|
|
|
// space.
|
|
|
|
|
if (!compressWhitespace) {
|
|
|
|
|
mTrimNextRunLeadingWhitespace = PR_FALSE;
|
|
|
|
|
}
|
|
|
|
|
textFlags |= analysisFlags;
|
|
|
|
|
|
|
|
|
|
currentTransformedTextOffset =
|
|
|
|
@ -1689,7 +1725,8 @@ BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRBool
|
|
|
|
|
HasCompressedLeadingWhitespace(nsTextFrame* aFrame, PRInt32 aContentEndOffset,
|
|
|
|
|
HasCompressedLeadingWhitespace(nsTextFrame* aFrame, const nsStyleText* aStyleText,
|
|
|
|
|
PRInt32 aContentEndOffset,
|
|
|
|
|
const gfxSkipCharsIterator& aIterator)
|
|
|
|
|
{
|
|
|
|
|
if (!aIterator.IsOriginalCharSkipped())
|
|
|
|
@ -1699,7 +1736,7 @@ HasCompressedLeadingWhitespace(nsTextFrame* aFrame, PRInt32 aContentEndOffset,
|
|
|
|
|
PRInt32 frameContentOffset = aFrame->GetContentOffset();
|
|
|
|
|
const nsTextFragment* frag = aFrame->GetContent()->GetText();
|
|
|
|
|
while (frameContentOffset < aContentEndOffset && iter.IsOriginalCharSkipped()) {
|
|
|
|
|
if (IsTrimmableSpace(frag, frameContentOffset))
|
|
|
|
|
if (IsTrimmableSpace(frag, frameContentOffset, aStyleText))
|
|
|
|
|
return PR_TRUE;
|
|
|
|
|
++frameContentOffset;
|
|
|
|
|
iter.AdvanceOriginal(1);
|
|
|
|
@ -1753,7 +1790,8 @@ BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
|
|
|
|
|
flags |= nsLineBreaker::BREAK_NEED_CAPITALIZATION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->GetContentEnd(), iter)) {
|
|
|
|
|
if (HasCompressedLeadingWhitespace(startFrame, textStyle,
|
|
|
|
|
mappedFlow->GetContentEnd(), iter)) {
|
|
|
|
|
mLineBreaker.AppendInvisibleWhitespace(flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1880,14 +1918,14 @@ nsTextFrame::EnsureTextRun(gfxContext* aReferenceContext, nsIFrame* aLineContain
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static PRUint32
|
|
|
|
|
GetEndOfTrimmedText(const nsTextFragment* aFrag,
|
|
|
|
|
GetEndOfTrimmedText(const nsTextFragment* aFrag, const nsStyleText* aStyleText,
|
|
|
|
|
PRUint32 aStart, PRUint32 aEnd,
|
|
|
|
|
gfxSkipCharsIterator* aIterator)
|
|
|
|
|
{
|
|
|
|
|
aIterator->SetSkippedOffset(aEnd);
|
|
|
|
|
while (aIterator->GetSkippedOffset() > aStart) {
|
|
|
|
|
aIterator->AdvanceSkipped(-1);
|
|
|
|
|
if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset()))
|
|
|
|
|
if (!IsTrimmableSpace(aFrag, aIterator->GetOriginalOffset(), aStyleText))
|
|
|
|
|
return aIterator->GetSkippedOffset() + 1;
|
|
|
|
|
}
|
|
|
|
|
return aStart;
|
|
|
|
@ -1898,23 +1936,35 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
|
|
|
|
|
PRBool aTrimAfter)
|
|
|
|
|
{
|
|
|
|
|
NS_ASSERTION(mTextRun, "Need textrun here");
|
|
|
|
|
// This should not be used during reflow. We need our TEXT_REFLOW_FLAGS
|
|
|
|
|
// to be set correctly.
|
|
|
|
|
NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW),
|
|
|
|
|
"Can only call this on frames that have been reflowed");
|
|
|
|
|
NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW),
|
|
|
|
|
"Can only call this on frames that are not being reflowed");
|
|
|
|
|
|
|
|
|
|
TrimmedOffsets offsets = { GetContentOffset(), GetContentLength() };
|
|
|
|
|
const nsStyleText* textStyle = GetStyleText();
|
|
|
|
|
// Note that pre-line newlines should still allow us to trim spaces
|
|
|
|
|
// for display
|
|
|
|
|
if (textStyle->WhiteSpaceIsSignificant())
|
|
|
|
|
return offsets;
|
|
|
|
|
|
|
|
|
|
if (GetStateBits() & TEXT_START_OF_LINE) {
|
|
|
|
|
PRInt32 whitespaceCount =
|
|
|
|
|
GetTrimmableWhitespaceCount(aFrag, offsets.mStart, offsets.mLength, 1);
|
|
|
|
|
GetTrimmableWhitespaceCount(aFrag,
|
|
|
|
|
offsets.mStart, offsets.mLength, 1);
|
|
|
|
|
offsets.mStart += whitespaceCount;
|
|
|
|
|
offsets.mLength -= whitespaceCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE)) {
|
|
|
|
|
// This treats a trailing 'pre-line' newline as trimmable. That's fine,
|
|
|
|
|
// it's actually what we want since we want whitespace before it to
|
|
|
|
|
// be trimmed.
|
|
|
|
|
PRInt32 whitespaceCount =
|
|
|
|
|
GetTrimmableWhitespaceCount(aFrag, offsets.GetEnd() - 1,
|
|
|
|
|
offsets.mLength, -1);
|
|
|
|
|
GetTrimmableWhitespaceCount(aFrag,
|
|
|
|
|
offsets.GetEnd() - 1, offsets.mLength, -1);
|
|
|
|
|
offsets.mLength -= whitespaceCount;
|
|
|
|
|
}
|
|
|
|
|
return offsets;
|
|
|
|
@ -2507,8 +2557,7 @@ PropertyProvider::SetupJustificationSpacing()
|
|
|
|
|
{
|
|
|
|
|
NS_PRECONDITION(mLength != PR_INT32_MAX, "Can't call this with undefined length");
|
|
|
|
|
|
|
|
|
|
if (NS_STYLE_TEXT_ALIGN_JUSTIFY != mTextStyle->mTextAlign ||
|
|
|
|
|
mTextStyle->WhiteSpaceIsSignificant())
|
|
|
|
|
if (!(mFrame->GetStateBits() & TEXT_JUSTIFICATION_ENABLED))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
gfxSkipCharsIterator start(mStart), end(mStart);
|
|
|
|
@ -4810,7 +4859,7 @@ IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter, gfxTextRun* aTextRu
|
|
|
|
|
PRUint32 index = aIter.GetSkippedOffset();
|
|
|
|
|
if (!aTextRun->IsClusterStart(index))
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
return !(aFrame->GetStyleText()->WhiteSpaceIsSignificant() &&
|
|
|
|
|
return !(aFrame->GetStyleText()->NewlineIsSignificant() &&
|
|
|
|
|
aTextRun->GetChar(index) == '\n');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -5150,13 +5199,13 @@ FindFirstLetterRange(const nsTextFragment* aFrag,
|
|
|
|
|
static PRUint32
|
|
|
|
|
FindStartAfterSkippingWhitespace(PropertyProvider* aProvider,
|
|
|
|
|
nsIFrame::InlineIntrinsicWidthData* aData,
|
|
|
|
|
PRBool aCollapseWhitespace,
|
|
|
|
|
const nsStyleText* aTextStyle,
|
|
|
|
|
gfxSkipCharsIterator* aIterator,
|
|
|
|
|
PRUint32 aFlowEndInTextRun)
|
|
|
|
|
{
|
|
|
|
|
if (aData->skipWhitespace && aCollapseWhitespace) {
|
|
|
|
|
if (aData->skipWhitespace) {
|
|
|
|
|
while (aIterator->GetSkippedOffset() < aFlowEndInTextRun &&
|
|
|
|
|
IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset())) {
|
|
|
|
|
IsTrimmableSpace(aProvider->GetFragment(), aIterator->GetOriginalOffset(), aTextStyle)) {
|
|
|
|
|
aIterator->AdvanceOriginal(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -5185,14 +5234,15 @@ 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.
|
|
|
|
|
const nsStyleText* textStyle = GetStyleText();
|
|
|
|
|
const nsTextFragment* frag = mContent->GetText();
|
|
|
|
|
PropertyProvider provider(mTextRun, GetStyleText(), frag, this,
|
|
|
|
|
PropertyProvider provider(mTextRun, textStyle, frag, this,
|
|
|
|
|
iter, PR_INT32_MAX, nsnull, 0);
|
|
|
|
|
|
|
|
|
|
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
|
|
|
|
PRBool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
|
|
|
|
PRBool preformatNewlines = textStyle->NewlineIsSignificant();
|
|
|
|
|
PRUint32 start =
|
|
|
|
|
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
|
|
|
|
|
&iter, flowEndInTextRun);
|
|
|
|
|
FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
|
|
|
|
|
if (start >= flowEndInTextRun)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
@ -5203,7 +5253,7 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|
|
|
|
// XXXldb Shouldn't we be including the newline as part of the
|
|
|
|
|
// segment that it ends rather than part of the segment that it
|
|
|
|
|
// starts?
|
|
|
|
|
preformattedNewline = !collapseWhitespace && mTextRun->GetChar(i) == '\n';
|
|
|
|
|
preformattedNewline = preformatNewlines && mTextRun->GetChar(i) == '\n';
|
|
|
|
|
if (!mTextRun->CanBreakLineBefore(i) && !preformattedNewline) {
|
|
|
|
|
// we can't break here (and it's not the end of the flow)
|
|
|
|
|
continue;
|
|
|
|
@ -5217,7 +5267,7 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|
|
|
|
aData->atStartOfLine = PR_FALSE;
|
|
|
|
|
|
|
|
|
|
if (collapseWhitespace) {
|
|
|
|
|
PRUint32 trimStart = GetEndOfTrimmedText(frag, wordStart, i, &iter);
|
|
|
|
|
PRUint32 trimStart = GetEndOfTrimmedText(frag, textStyle, wordStart, i, &iter);
|
|
|
|
|
if (trimStart == start) {
|
|
|
|
|
// This is *all* trimmable whitespace, so whatever trailingWhitespace
|
|
|
|
|
// we saw previously is still trailing...
|
|
|
|
@ -5246,8 +5296,9 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|
|
|
|
|
|
|
|
|
// Check if we have collapsible whitespace at the end
|
|
|
|
|
aData->skipWhitespace =
|
|
|
|
|
IsTrimmableSpace(provider.GetFragment(), provider.GetStyleText(),
|
|
|
|
|
iter.ConvertSkippedToOriginal(flowEndInTextRun - 1));
|
|
|
|
|
IsTrimmableSpace(provider.GetFragment(),
|
|
|
|
|
iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
|
|
|
|
|
textStyle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX Need to do something here to avoid incremental reflow bugs due to
|
|
|
|
@ -5287,56 +5338,69 @@ nsTextFrame::AddInlinePrefWidthForFlow(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 nsStyleText* textStyle = GetStyleText();
|
|
|
|
|
const nsTextFragment* frag = mContent->GetText();
|
|
|
|
|
PropertyProvider provider(mTextRun, textStyle, frag, this,
|
|
|
|
|
iter, PR_INT32_MAX, nsnull, 0);
|
|
|
|
|
|
|
|
|
|
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
|
|
|
|
PRBool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
|
|
|
|
PRBool preformatNewlines = textStyle->NewlineIsSignificant();
|
|
|
|
|
PRUint32 start =
|
|
|
|
|
FindStartAfterSkippingWhitespace(&provider, aData, collapseWhitespace,
|
|
|
|
|
&iter, flowEndInTextRun);
|
|
|
|
|
FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
|
|
|
|
|
if (start >= flowEndInTextRun)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (collapseWhitespace) {
|
|
|
|
|
// \n line breaks are not honoured, so everything would like to go
|
|
|
|
|
// onto one line, so just measure it
|
|
|
|
|
nscoord width =
|
|
|
|
|
NSToCoordCeil(mTextRun->GetAdvanceWidth(start, flowEndInTextRun - start, &provider));
|
|
|
|
|
aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width);
|
|
|
|
|
|
|
|
|
|
PRUint32 trimStart = GetEndOfTrimmedText(provider.GetFragment(), start,
|
|
|
|
|
flowEndInTextRun, &iter);
|
|
|
|
|
if (trimStart == start) {
|
|
|
|
|
// This is *all* trimmable whitespace, so whatever trailingWhitespace
|
|
|
|
|
// we saw previously is still trailing...
|
|
|
|
|
aData->trailingWhitespace += width;
|
|
|
|
|
} else {
|
|
|
|
|
// Some non-whitespace so the old trailingWhitespace is no longer trailing
|
|
|
|
|
aData->trailingWhitespace =
|
|
|
|
|
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, flowEndInTextRun - trimStart, &provider));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// We respect line breaks, so measure off each line (or part of line).
|
|
|
|
|
aData->trailingWhitespace = 0;
|
|
|
|
|
PRUint32 i;
|
|
|
|
|
PRUint32 startRun = start;
|
|
|
|
|
for (i = start; i <= flowEndInTextRun; ++i) {
|
|
|
|
|
if (i < flowEndInTextRun && mTextRun->GetChar(i) != '\n')
|
|
|
|
|
// XXX Should we consider hyphenation here?
|
|
|
|
|
// If newlines aren't hard breaks, there are no hard breaks so just
|
|
|
|
|
// make i skip to the end
|
|
|
|
|
for (PRUint32 i = preformatNewlines ? start : flowEndInTextRun, lineStart = start;
|
|
|
|
|
i <= flowEndInTextRun; ++i) {
|
|
|
|
|
PRBool preformattedNewline = PR_FALSE;
|
|
|
|
|
if (i < flowEndInTextRun) {
|
|
|
|
|
// XXXldb Shouldn't we be including the newline as part of the
|
|
|
|
|
// segment that it ends rather than part of the segment that it
|
|
|
|
|
// starts?
|
|
|
|
|
NS_ASSERTION(preformatNewlines, "We can't be here unless newlines are hard breaks");
|
|
|
|
|
preformattedNewline = mTextRun->GetChar(i) == '\n';
|
|
|
|
|
if (!preformattedNewline) {
|
|
|
|
|
// we needn't break here (and it's not the end of the flow)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
aData->currentLine +=
|
|
|
|
|
NSToCoordCeil(mTextRun->GetAdvanceWidth(startRun, i - startRun, &provider));
|
|
|
|
|
if (i < flowEndInTextRun) {
|
|
|
|
|
aData->ForceBreak(aRenderingContext);
|
|
|
|
|
startRun = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (i > lineStart) {
|
|
|
|
|
nscoord width =
|
|
|
|
|
NSToCoordCeil(mTextRun->GetAdvanceWidth(lineStart, i - lineStart, &provider));
|
|
|
|
|
aData->currentLine = NSCoordSaturatingAdd(aData->currentLine, width);
|
|
|
|
|
|
|
|
|
|
if (collapseWhitespace) {
|
|
|
|
|
PRUint32 trimStart = GetEndOfTrimmedText(frag, textStyle, lineStart, i, &iter);
|
|
|
|
|
if (trimStart == start) {
|
|
|
|
|
// This is *all* trimmable whitespace, so whatever trailingWhitespace
|
|
|
|
|
// we saw previously is still trailing...
|
|
|
|
|
aData->trailingWhitespace += width;
|
|
|
|
|
} else {
|
|
|
|
|
// Some non-whitespace so the old trailingWhitespace is no longer trailing
|
|
|
|
|
aData->trailingWhitespace =
|
|
|
|
|
NSToCoordCeil(mTextRun->GetAdvanceWidth(trimStart, i - trimStart, &provider));
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
aData->trailingWhitespace = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (preformattedNewline) {
|
|
|
|
|
aData->ForceBreak(aRenderingContext);
|
|
|
|
|
lineStart = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if we have collapsible whitespace at the end
|
|
|
|
|
aData->skipWhitespace =
|
|
|
|
|
IsTrimmableSpace(provider.GetFragment(), provider.GetStyleText(),
|
|
|
|
|
iter.ConvertSkippedToOriginal(flowEndInTextRun - 1));
|
|
|
|
|
IsTrimmableSpace(provider.GetFragment(),
|
|
|
|
|
iter.ConvertSkippedToOriginal(flowEndInTextRun - 1),
|
|
|
|
|
textStyle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// XXX Need to do something here to avoid incremental reflow bugs due to
|
|
|
|
@ -5575,18 +5639,20 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|
|
|
|
|
|
|
|
|
// Restrict preformatted text to the nearest newline
|
|
|
|
|
PRInt32 newLineOffset = -1; // this will be -1 or a content offset
|
|
|
|
|
if (textStyle->WhiteSpaceIsSignificant()) {
|
|
|
|
|
if (textStyle->NewlineIsSignificant()) {
|
|
|
|
|
newLineOffset = FindChar(frag, offset, length, '\n');
|
|
|
|
|
if (newLineOffset >= 0) {
|
|
|
|
|
length = newLineOffset + 1 - offset;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (atStartOfLine) {
|
|
|
|
|
// Skip leading whitespace
|
|
|
|
|
PRInt32 whitespaceCount = GetTrimmableWhitespaceCount(frag, offset, length, 1);
|
|
|
|
|
offset += whitespaceCount;
|
|
|
|
|
length -= whitespaceCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (atStartOfLine && !textStyle->WhiteSpaceIsSignificant()) {
|
|
|
|
|
// Skip leading whitespace. Make sure we don't skip a 'pre-line'
|
|
|
|
|
// newline if there is one.
|
|
|
|
|
PRInt32 skipLength = newLineOffset >= 0 ? length - 1 : length;
|
|
|
|
|
PRInt32 whitespaceCount =
|
|
|
|
|
GetTrimmableWhitespaceCount(frag, offset, skipLength, 1);
|
|
|
|
|
offset += whitespaceCount;
|
|
|
|
|
length -= whitespaceCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRBool completedFirstLetter = PR_FALSE;
|
|
|
|
@ -5605,7 +5671,20 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|
|
|
|
EnsureTextRun(ctx, lineContainer, lineLayout.GetLine(), &flowEndInTextRun);
|
|
|
|
|
|
|
|
|
|
if (mTextRun) {
|
|
|
|
|
completedFirstLetter = FindFirstLetterRange(frag, mTextRun, offset, iter, &length);
|
|
|
|
|
PRInt32 firstLetterLength = length;
|
|
|
|
|
completedFirstLetter =
|
|
|
|
|
FindFirstLetterRange(frag, mTextRun, offset, iter, &firstLetterLength);
|
|
|
|
|
if (newLineOffset >= 0) {
|
|
|
|
|
// Don't allow a preformatted newline to be part of a first-letter.
|
|
|
|
|
firstLetterLength = PR_MIN(firstLetterLength, length - 1);
|
|
|
|
|
if (length == 1) {
|
|
|
|
|
// There is no text to be consumed by the first-letter before the
|
|
|
|
|
// preformatted newline. Note that the first letter is therefore
|
|
|
|
|
// complete (FindFirstLetterRange will have returned false).
|
|
|
|
|
completedFirstLetter = PR_TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
length = firstLetterLength;
|
|
|
|
|
if (length) {
|
|
|
|
|
AddStateBits(TEXT_FIRST_LETTER);
|
|
|
|
|
}
|
|
|
|
@ -5901,9 +5980,9 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compute space and letter counts for justification, if required
|
|
|
|
|
if (NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign &&
|
|
|
|
|
!textStyle->WhiteSpaceIsSignificant()) {
|
|
|
|
|
// This will include a space for trailing whitespace, if any is present.
|
|
|
|
|
if (!textStyle->WhiteSpaceIsSignificant() &&
|
|
|
|
|
lineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
|
|
|
|
|
AddStateBits(TEXT_JUSTIFICATION_ENABLED); // This will include a space for trailing whitespace, if any is present.
|
|
|
|
|
// This is corrected for in nsLineLayout::TrimWhiteSpaceIn.
|
|
|
|
|
PRInt32 numJustifiableCharacters =
|
|
|
|
|
provider.ComputeJustifiableCharacters(offset, charsFit);
|
|
|
|
@ -5995,7 +6074,7 @@ nsTextFrame::TrimTrailingWhiteSpace(nsIRenderingContext* aRC)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!result.mLastCharIsJustifiable &&
|
|
|
|
|
NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign) {
|
|
|
|
|
(GetStateBits() & TEXT_JUSTIFICATION_ENABLED)) {
|
|
|
|
|
// Check if any character in the last cluster is justifiable
|
|
|
|
|
PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength,
|
|
|
|
|
nsnull, 0);
|
|
|
|
@ -6074,8 +6153,8 @@ nsTextFrame::RecomputeOverflowRect()
|
|
|
|
|
static PRUnichar TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
|
|
|
|
|
PRUint32 aSkippedOffset, PRUnichar aChar)
|
|
|
|
|
{
|
|
|
|
|
if (aChar == '\n' || aChar == '\r') {
|
|
|
|
|
return aStyle->WhiteSpaceIsSignificant() ? aChar : ' ';
|
|
|
|
|
if (aChar == '\n') {
|
|
|
|
|
return aStyle->NewlineIsSignificant() ? aChar : ' ';
|
|
|
|
|
}
|
|
|
|
|
switch (aStyle->mTextTransform) {
|
|
|
|
|
case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
|
|
|
|
@ -6215,7 +6294,9 @@ nsTextFrame::IsEmpty()
|
|
|
|
|
"Invalid state");
|
|
|
|
|
|
|
|
|
|
// XXXldb Should this check compatibility mode as well???
|
|
|
|
|
if (GetStyleText()->WhiteSpaceIsSignificant()) {
|
|
|
|
|
const nsStyleText* textStyle = GetStyleText();
|
|
|
|
|
if (textStyle->WhiteSpaceIsSignificant()) {
|
|
|
|
|
// XXX shouldn't we return true if the length is zero?
|
|
|
|
|
return PR_FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -6227,7 +6308,8 @@ nsTextFrame::IsEmpty()
|
|
|
|
|
return PR_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PRBool isEmpty = mContent->TextIsOnlyWhitespace();
|
|
|
|
|
PRBool isEmpty = IsAllWhitespace(mContent->GetText(),
|
|
|
|
|
textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_LINE);
|
|
|
|
|
mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
|
|
|
|
|
return isEmpty;
|
|
|
|
|
}
|
|
|
|
|