зеркало из https://github.com/mozilla/gecko-dev.git
Bug 393096. Allow an element containing breakable whitespace to introduce a break opportunity no matter what the context. Also cleans up some trimming stuff and adds comprehensive whitespace breaking and trimming reftests. r+sr=dbaron
This commit is contained in:
Родитель
6bf5ced669
Коммит
e1da1696a2
|
@ -73,9 +73,9 @@ public:
|
|||
* into AppendText calls.
|
||||
*
|
||||
* The current strategy is that we break the overall text into
|
||||
* 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).
|
||||
* whitespace-delimited "words". Then those words are passed to the nsILineBreaker
|
||||
* service for deeper analysis if they contain a "complex" character as described
|
||||
* below.
|
||||
*/
|
||||
class nsLineBreaker {
|
||||
public:
|
||||
|
@ -102,9 +102,9 @@ public:
|
|||
(0xff00 <= u && u <= 0xffef); // Halfwidth and Fullwidth Forms
|
||||
}
|
||||
|
||||
// Normally, break opportunities exist at the end of each run of whitespace
|
||||
// (see IsSpace above). Break opportunities can also exist inside runs of
|
||||
// non-whitespace, as determined by nsILineBreaker. We pass a whitespace-
|
||||
// Break opportunities exist at the end of each run of breakable whitespace
|
||||
// (see IsSpace above). Break opportunities can also exist between pairs of
|
||||
// non-whitespace characters, as determined by nsILineBreaker. We pass a whitespace-
|
||||
// delimited word to nsILineBreaker if it contains at least one character
|
||||
// matching IsComplexChar.
|
||||
// We provide flags to control on a per-chunk basis where breaks are allowed.
|
||||
|
@ -114,22 +114,38 @@ public:
|
|||
// We operate on text after whitespace processing has been applied, so
|
||||
// other characters (e.g. tabs and newlines) may have been converted to
|
||||
// spaces.
|
||||
|
||||
/**
|
||||
* Flags passed with each chunk of text.
|
||||
*/
|
||||
enum {
|
||||
/**
|
||||
* Allow a break opportunity at the start of this chunk of text.
|
||||
/*
|
||||
* Do not introduce a break opportunity at the start of this chunk of text.
|
||||
*/
|
||||
BREAK_ALLOW_INITIAL = 0x01,
|
||||
BREAK_SUPPRESS_INITIAL = 0x01,
|
||||
/**
|
||||
* Allow a break opportunity in the interior of this chunk of text.
|
||||
* Do not introduce a break opportunity in the interior of this chunk of text.
|
||||
* Also, whitespace in this chunk is treated as non-breakable.
|
||||
*/
|
||||
BREAK_ALLOW_INSIDE = 0x02
|
||||
BREAK_SUPPRESS_INSIDE = 0x02,
|
||||
/**
|
||||
* The sink currently is already set up to have no breaks in it;
|
||||
* if no breaks are possible, nsLineBreaker does not need to call
|
||||
* SetBreaks on it. This is useful when handling large quantities of
|
||||
* preformatted text; the textruns will never have any breaks set on them,
|
||||
* and there is no need to ever actually scan the text for breaks, except
|
||||
* at the end of textruns in case context is needed for following breakable
|
||||
* text.
|
||||
*/
|
||||
BREAK_SKIP_SETTING_NO_BREAKS = 0x04
|
||||
};
|
||||
|
||||
/**
|
||||
* Append "invisible whitespace". This acts like whitespace, but there is
|
||||
* no actual text associated with it.
|
||||
* no actual text associated with it. Only the BREAK_SUPPRESS_INSIDE flag
|
||||
* is relevant here.
|
||||
*/
|
||||
nsresult AppendInvisibleWhitespace();
|
||||
nsresult AppendInvisibleWhitespace(PRUint32 aFlags);
|
||||
|
||||
/**
|
||||
* Feed Unicode text into the linebreaker for analysis. aLength must be
|
||||
|
@ -184,8 +200,11 @@ private:
|
|||
nsAutoTArray<TextItem,2> mTextItems;
|
||||
PRPackedBool mCurrentWordContainsComplexChar;
|
||||
|
||||
// True if the previous character was whitespace
|
||||
PRPackedBool mAfterSpace;
|
||||
// True if the previous character was breakable whitespace
|
||||
PRPackedBool mAfterBreakableSpace;
|
||||
// True if a break must be allowed at the current position because
|
||||
// a run of breakable whitespace ends here
|
||||
PRPackedBool mBreakHere;
|
||||
};
|
||||
|
||||
#endif /*NSLINEBREAKER_H_*/
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
|
||||
nsLineBreaker::nsLineBreaker()
|
||||
: mCurrentWordContainsComplexChar(PR_FALSE),
|
||||
mAfterSpace(PR_FALSE)
|
||||
mAfterBreakableSpace(PR_FALSE), mBreakHere(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -72,10 +72,10 @@ nsLineBreaker::FlushCurrentWord()
|
|||
TextItem* ti = &mTextItems[i];
|
||||
NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
|
||||
|
||||
if (!(ti->mFlags & BREAK_ALLOW_INITIAL) && ti->mSinkOffset == 0) {
|
||||
if ((ti->mFlags & BREAK_SUPPRESS_INITIAL) && ti->mSinkOffset == 0) {
|
||||
breakState[offset] = PR_FALSE;
|
||||
}
|
||||
if (!(ti->mFlags & BREAK_ALLOW_INSIDE)) {
|
||||
if (ti->mFlags & BREAK_SUPPRESS_INSIDE) {
|
||||
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
|
||||
memset(breakState.Elements() + offset + exclude, PR_FALSE, ti->mLength - exclude);
|
||||
}
|
||||
|
@ -107,7 +107,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mAfterSpace, "These should not be set");
|
||||
NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IsSpace(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -137,8 +137,14 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
}
|
||||
|
||||
PRUint32 start = offset;
|
||||
if (!aSink && !aFlags) {
|
||||
// Skip to the space before the last word, since we don't need the breaks
|
||||
PRBool noBreaksNeeded = !aSink ||
|
||||
((aFlags & BREAK_SUPPRESS_INITIAL) && (aFlags & BREAK_SUPPRESS_INSIDE) &&
|
||||
!mBreakHere && !mAfterBreakableSpace && (aFlags & BREAK_SKIP_SETTING_NO_BREAKS));
|
||||
if (noBreaksNeeded) {
|
||||
// Skip to the space before the last word, since either the break data
|
||||
// here is not needed, or no breaks are set in the sink and there cannot
|
||||
// be any breaks in this chunk; all we need is the context for the next
|
||||
// chunk (if any)
|
||||
offset = aLength;
|
||||
while (offset > start) {
|
||||
--offset;
|
||||
|
@ -152,16 +158,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
for (;;) {
|
||||
PRUnichar ch = aText[offset];
|
||||
PRBool isSpace = IsSpace(ch);
|
||||
PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||
|
||||
if (aSink) {
|
||||
breakState[offset] = mAfterSpace && !isSpace &&
|
||||
(aFlags & (offset == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
||||
breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
|
||||
}
|
||||
mAfterSpace = isSpace;
|
||||
mBreakHere = PR_FALSE;
|
||||
mAfterBreakableSpace = isBreakableSpace;
|
||||
|
||||
if (isSpace) {
|
||||
if (offset > wordStart && wordHasComplexChar) {
|
||||
if (aSink && (aFlags & BREAK_ALLOW_INSIDE)) {
|
||||
if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
|
||||
// Save current start-of-word state because GetJISx4051Breaks will
|
||||
// set it to false
|
||||
PRPackedBool currentStart = breakState[wordStart];
|
||||
|
@ -198,7 +205,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
}
|
||||
}
|
||||
|
||||
if (aSink) {
|
||||
if (!noBreaksNeeded) {
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
}
|
||||
return NS_OK;
|
||||
|
@ -214,7 +221,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mAfterSpace, "These should not be set");
|
||||
NS_ASSERTION(!mAfterBreakableSpace && !mBreakHere, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IsSpace(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -247,8 +254,14 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
}
|
||||
|
||||
PRUint32 start = offset;
|
||||
if (!aSink && !aFlags) {
|
||||
// Skip to the space before the last word, since we don't need the breaks
|
||||
PRBool noBreaksNeeded = !aSink ||
|
||||
((aFlags & BREAK_SUPPRESS_INITIAL) && (aFlags & BREAK_SUPPRESS_INSIDE) &&
|
||||
!mBreakHere && !mAfterBreakableSpace && (aFlags & BREAK_SKIP_SETTING_NO_BREAKS));
|
||||
if (noBreaksNeeded) {
|
||||
// Skip to the space before the last word, since either the break data
|
||||
// here is not needed, or no breaks are set in the sink and there cannot
|
||||
// be any breaks in this chunk; all we need is the context for the next
|
||||
// chunk (if any)
|
||||
offset = aLength;
|
||||
while (offset > start) {
|
||||
--offset;
|
||||
|
@ -262,16 +275,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
for (;;) {
|
||||
PRUint8 ch = aText[offset];
|
||||
PRBool isSpace = IsSpace(ch);
|
||||
PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||
|
||||
if (aSink) {
|
||||
breakState[offset] = mAfterSpace && !isSpace &&
|
||||
(aFlags & (offset == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
||||
breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
|
||||
}
|
||||
mAfterSpace = isSpace;
|
||||
mBreakHere = PR_FALSE;
|
||||
mAfterBreakableSpace = isBreakableSpace;
|
||||
|
||||
if (isSpace) {
|
||||
if (offset > wordStart && wordHasComplexChar) {
|
||||
if (aSink && (aFlags & BREAK_ALLOW_INSIDE)) {
|
||||
if (aSink && !(aFlags & BREAK_SUPPRESS_INSIDE)) {
|
||||
// Save current start-of-word state because GetJISx4051Breaks will
|
||||
// set it to false
|
||||
PRPackedBool currentStart = breakState[wordStart];
|
||||
|
@ -311,18 +325,22 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
}
|
||||
}
|
||||
|
||||
if (aSink) {
|
||||
if (!noBreaksNeeded) {
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsLineBreaker::AppendInvisibleWhitespace() {
|
||||
// Treat as "invisible whitespace"
|
||||
nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags) {
|
||||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mAfterSpace = PR_TRUE;
|
||||
|
||||
PRBool isBreakableSpace = !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||
if (mAfterBreakableSpace && !isBreakableSpace) {
|
||||
mBreakHere = PR_TRUE;
|
||||
}
|
||||
mAfterBreakableSpace = isBreakableSpace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
|
|||
mPlacedFloats = 0;
|
||||
mTotalPlacedFrames = 0;
|
||||
mTopEdge = 0;
|
||||
mTrimmableWidth = 0;
|
||||
|
||||
// Instead of always pre-initializing the free-lists for frames and
|
||||
// spans, we do it on demand so that situations that only use a few
|
||||
|
@ -1034,7 +1035,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
|||
}
|
||||
|
||||
if (!continuingTextRun) {
|
||||
SetHasTrailingTextFrame(PR_FALSE);
|
||||
if (!pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
|
||||
mTrimmableWidth = 0;
|
||||
}
|
||||
if (!psd->mNoWrap && (!CanPlaceFloatNow() || placedFloat)) {
|
||||
// record soft break opportunity after this content that can't be
|
||||
// part of a text run. This is not a text frame so we know
|
||||
|
@ -1183,7 +1186,7 @@ nsLineLayout::CanPlaceFrame(PerFrameData* pfd,
|
|||
|
||||
// Set outside to PR_TRUE if the result of the reflow leads to the
|
||||
// frame sticking outside of our available area.
|
||||
PRBool outside = pfd->mBounds.XMost() + endMargin > psd->mRightEdge;
|
||||
PRBool outside = pfd->mBounds.XMost() - mTrimmableWidth + endMargin > psd->mRightEdge;
|
||||
if (!outside) {
|
||||
// If it fits, it fits
|
||||
#ifdef NOISY_CAN_PLACE_FRAME
|
||||
|
|
|
@ -143,10 +143,9 @@ protected:
|
|||
#define LL_LASTFLOATWASLETTERFRAME 0x00000080
|
||||
#define LL_CANPLACEFLOAT 0x00000100
|
||||
#define LL_LINEENDSINBR 0x00000200
|
||||
#define LL_HASTRAILINGTEXTFRAME 0x00000400
|
||||
#define LL_NEEDBACKUP 0x00000800
|
||||
#define LL_INFIRSTLINE 0x00002000
|
||||
#define LL_GOTLINEBOX 0x00004000
|
||||
#define LL_NEEDBACKUP 0x00000400
|
||||
#define LL_INFIRSTLINE 0x00000800
|
||||
#define LL_GOTLINEBOX 0x00001000
|
||||
#define LL_LASTFLAG LL_GOTLINEBOX
|
||||
|
||||
PRUint16 mFlags;
|
||||
|
@ -203,17 +202,8 @@ public:
|
|||
return mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the last content placed on the line (not counting inline containers)
|
||||
* was text, and can form a contiguous text flow with the next content to be
|
||||
* placed, and is not just a frame of all-skipped whitespace, this flag is
|
||||
* true.
|
||||
*/
|
||||
PRBool HasTrailingTextFrame() const {
|
||||
return GetFlag(LL_HASTRAILINGTEXTFRAME);
|
||||
}
|
||||
void SetHasTrailingTextFrame(PRBool aHasTrailingTextFrame) {
|
||||
SetFlag(LL_HASTRAILINGTEXTFRAME, aHasTrailingTextFrame);
|
||||
void SetTrimmableWidth(nscoord aTrimmableWidth) {
|
||||
mTrimmableWidth = aTrimmableWidth;
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@ -377,6 +367,9 @@ protected:
|
|||
// Final computed line-height value after VerticalAlignFrames for
|
||||
// the block has been called.
|
||||
nscoord mFinalLineHeight;
|
||||
|
||||
// Amount of trimmable whitespace width for the trailing text frame, if any
|
||||
nscoord mTrimmableWidth;
|
||||
|
||||
// Per-frame data recorded by the line-layout reflow logic. This
|
||||
// state is the state needed to post-process the line after reflow
|
||||
|
|
|
@ -654,6 +654,8 @@ public:
|
|||
if (mTextRun->SetPotentialLineBreaks(aOffset + mOffsetIntoTextRun, aLength,
|
||||
aBreakBefore, mContext)) {
|
||||
mChangedBreaks = PR_TRUE;
|
||||
// Be conservative and assume that some breaks have been set
|
||||
mTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_NO_BREAKS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1627,38 +1629,29 @@ BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
|
|||
: mMappedFlows[i + 1].mTransformedTextOffset)
|
||||
- offset;
|
||||
|
||||
PRUint32 flags = 0;
|
||||
nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
|
||||
if (!initialBreakController) {
|
||||
initialBreakController = mLineContainer;
|
||||
}
|
||||
if (!initialBreakController->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_SUPPRESS_INITIAL;
|
||||
}
|
||||
nsTextFrame* startFrame = mappedFlow->mStartFrame;
|
||||
const nsStyleText* textStyle = startFrame->GetStyleText();
|
||||
if (!textStyle->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
|
||||
}
|
||||
if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
|
||||
flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
|
||||
}
|
||||
|
||||
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->GetContentEnd(), iter)) {
|
||||
mLineBreaker.AppendInvisibleWhitespace();
|
||||
mLineBreaker.AppendInvisibleWhitespace(flags);
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
PRUint32 flags = 0;
|
||||
nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
|
||||
if (!initialBreakController) {
|
||||
initialBreakController = mLineContainer;
|
||||
}
|
||||
if (initialBreakController->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;
|
||||
}
|
||||
|
||||
BreakSink* sink = *breakSink;
|
||||
if (aSuppressSink) {
|
||||
sink = nsnull;
|
||||
} else if (flags) {
|
||||
aTextRun->ClearFlagBits(nsTextFrameUtils::TEXT_NO_BREAKS);
|
||||
} else if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
|
||||
// Don't bother setting breaks on a textrun that can't be broken
|
||||
// and currently has no breaks set...
|
||||
sink = nsnull;
|
||||
}
|
||||
BreakSink* sink = aSuppressSink ? nsnull : (*breakSink).get();
|
||||
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
||||
length, flags, sink);
|
||||
|
@ -1812,8 +1805,7 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
|
|||
offsets.mLength -= whitespaceCount;
|
||||
}
|
||||
|
||||
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE) &&
|
||||
textStyle->WhiteSpaceCanWrap()) {
|
||||
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE)) {
|
||||
PRInt32 whitespaceCount =
|
||||
GetTrimmableWhitespaceCount(aFrag, offsets.GetEnd() - 1,
|
||||
offsets.mLength, -1);
|
||||
|
@ -5216,8 +5208,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
|
||||
"We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
|
||||
#endif
|
||||
PRBool suppressInitialBreak = !lineLayout.LineIsBreakable() ||
|
||||
!lineLayout.HasTrailingTextFrame();
|
||||
|
||||
PRInt32 limitLength = length;
|
||||
PRInt32 forceBreak = lineLayout.GetForcedBreakPosition(mContent);
|
||||
|
@ -5249,13 +5239,12 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
PRBool usedHyphenation;
|
||||
gfxFloat trimmedWidth = 0;
|
||||
gfxFloat availWidth = aReflowState.availableWidth;
|
||||
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() &&
|
||||
textStyle->WhiteSpaceCanWrap();
|
||||
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
||||
PRUint32 transformedCharsFit =
|
||||
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
|
||||
(GetStateBits() & TEXT_START_OF_LINE) != 0,
|
||||
availWidth,
|
||||
&provider, suppressInitialBreak,
|
||||
&provider, !lineLayout.LineIsBreakable(),
|
||||
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
||||
&textMetrics, needTightBoundingBox, ctx,
|
||||
&usedHyphenation, &transformedLastBreak);
|
||||
|
@ -5293,26 +5282,38 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
AddStateBits(TEXT_HYPHEN_BREAK);
|
||||
}
|
||||
|
||||
// If everything fits including trimmed whitespace, then we should add the
|
||||
// trimmed whitespace to our metrics now because it probably won't be trimmed
|
||||
// and we need to position subsequent frames correctly...
|
||||
if (forceBreak < 0 && textMetrics.mAdvanceWidth + trimmedWidth <= availWidth) {
|
||||
textMetrics.mAdvanceWidth += trimmedWidth;
|
||||
if (mTextRun->IsRightToLeft()) {
|
||||
// Space comes before text, so the bounding box is moved to the
|
||||
// right by trimmdWidth
|
||||
textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
|
||||
gfxFloat trimmableWidth = 0;
|
||||
if (canTrimTrailingWhitespace) {
|
||||
// Optimization: if we trimmed trailing whitespace, and we can be sure
|
||||
// this frame will be at the end of the line, then leave it trimmed off.
|
||||
// Otherwise we have to undo the trimming, in case we're not at the end of
|
||||
// the line. (If we actually do end up at the end of the line, we'll have
|
||||
// to trim it off again in TrimTrailingWhiteSpace, and we'd like to avoid
|
||||
// having to re-do it.)
|
||||
if (forceBreak >= 0 || transformedCharsFit < transformedLength) {
|
||||
// We're definitely going to break so our trailing whitespace should
|
||||
// definitely be timmed. Record that we've already done it.
|
||||
AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
|
||||
} else {
|
||||
// We might not be at the end of the line. (Note that even if this frame
|
||||
// ends in breakable whitespace, it might not be at the end of the line
|
||||
// because it might be followed by breakable, but preformatted, whitespace.)
|
||||
// Undo the trimming.
|
||||
textMetrics.mAdvanceWidth += trimmedWidth;
|
||||
trimmableWidth = trimmedWidth;
|
||||
if (mTextRun->IsRightToLeft()) {
|
||||
// Space comes before text, so the bounding box is moved to the
|
||||
// right by trimmdWidth
|
||||
textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
|
||||
}
|
||||
|
||||
// Since everything fit and no break was forced,
|
||||
// record the last break opportunity
|
||||
if (lastBreak >= 0) {
|
||||
lineLayout.NotifyOptionalBreakPosition(mContent, lastBreak,
|
||||
textMetrics.mAdvanceWidth <= aReflowState.availableWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastBreak >= 0) {
|
||||
lineLayout.NotifyOptionalBreakPosition(mContent, lastBreak,
|
||||
textMetrics.mAdvanceWidth <= aReflowState.availableWidth);
|
||||
}
|
||||
} else {
|
||||
// We're definitely going to break and our whitespace will definitely
|
||||
// be trimmed.
|
||||
// Record that whitespace has already been trimmed.
|
||||
AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
|
||||
}
|
||||
PRInt32 contentLength = offset + charsFit - GetContentOffset();
|
||||
|
||||
|
@ -5347,25 +5348,19 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
// Clean up, update state
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
if (charsFit > 0) {
|
||||
lineLayout.SetHasTrailingTextFrame(PR_TRUE);
|
||||
if (charsFit == length) {
|
||||
if (textStyle->WhiteSpaceCanWrap() &&
|
||||
IsTrimmableSpace(frag, offset + charsFit - 1)) {
|
||||
// Record a potential break after final breakable whitespace
|
||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
||||
textMetrics.mAdvanceWidth <= aReflowState.availableWidth);
|
||||
} else if (HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
|
||||
// Record a potential break after final soft hyphen
|
||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
||||
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Don't allow subsequent text frame to break-before. All our text is
|
||||
// being skipped (usually whitespace, could be discarded Unicode control
|
||||
// characters).
|
||||
lineLayout.SetHasTrailingTextFrame(PR_FALSE);
|
||||
// If all our characters are discarded or collapsed, then trimmable width
|
||||
// from the last textframe should be preserved. Otherwise the trimmable width
|
||||
// from this textframe overrides. (Currently in CSS trimmable width can be
|
||||
// at most one space so there's no way for trimmable width from a previous
|
||||
// frame to accumulate with trimmable width from this frame.)
|
||||
if (transformedCharsFit > 0) {
|
||||
lineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth));
|
||||
}
|
||||
if (charsFit > 0 && charsFit == length &&
|
||||
HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
|
||||
// Record a potential break after final soft hyphen
|
||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
||||
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth);
|
||||
}
|
||||
if (completedFirstLetter) {
|
||||
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
||||
|
|
|
@ -2,3 +2,6 @@
|
|||
== soft-hyphens-1a.html soft-hyphens-1-ref.html
|
||||
== soft-hyphens-1b.html soft-hyphens-1-ref.html
|
||||
== soft-hyphens-1c.html soft-hyphens-1-ref.html
|
||||
== white-space-1a.html white-space-1-ref.html
|
||||
== white-space-1b.html white-space-1-ref.html
|
||||
== white-space-2.html white-space-2-ref.html
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Reference -->
|
||||
<style>
|
||||
div { border:1px solid black; }
|
||||
.container { float:left; width:20%; border-color:cyan; }
|
||||
p { width:0; }
|
||||
b { font-weight:normal; background-color:yellow; white-space:pre; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
<p><b>Hello
|
||||
Kitty</b>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Testing all combinations of pre/nowrap/pre-wrap/normal space pairs -->
|
||||
<style>
|
||||
.nowrap { white-space:nowrap; }
|
||||
.pre { white-space:pre; }
|
||||
.prewrap { white-space:-moz-pre-wrap; }
|
||||
div { border:1px solid black; }
|
||||
.container { float:left; width:20%; border-color:cyan; }
|
||||
p { width:0; }
|
||||
b { font-weight:normal; background-color:yellow; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello<span class="pre"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="nowrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="prewrap"> </span>Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello Kitty</b>
|
||||
<p><b>Hello<span class="pre"> </span> Kitty</b>
|
||||
<p><b>Hello<span class="nowrap"> </span> Kitty</b>
|
||||
<p><b>Hello<span class="prewrap"> </span> Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello <span class="pre"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="pre"> </span><span class="pre"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="nowrap"> </span><span class="pre"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="prewrap"> </span><span class="pre"> </span>Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello <span class="nowrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="pre"> </span><span class="nowrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="nowrap"> </span><span class="nowrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="prewrap"> </span><span class="nowrap"> </span>Kitty</b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b>Hello <span class="prewrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="pre"> </span><span class="prewrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="nowrap"> </span><span class="prewrap"> </span>Kitty</b>
|
||||
<p><b>Hello<span class="prewrap"> </span><span class="prewrap"> </span>Kitty</b>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,53 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Adding extra span boundaries -->
|
||||
<style>
|
||||
.nowrap { white-space:nowrap; }
|
||||
.pre { white-space:pre; }
|
||||
.prewrap { white-space:-moz-pre-wrap; }
|
||||
div { border:1px solid black; }
|
||||
.container { float:left; width:20%; border-color:cyan; }
|
||||
p { width:0; }
|
||||
b { font-weight:normal; background-color:yellow; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<p><b><span>Hello<span> <span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="pre"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="nowrap"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="prewrap"> </span><span>Kitty</span></b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b><span>Hello<span> <span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="pre"> </span> <span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="nowrap"> </span> <span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="prewrap"> </span> <span>Kitty</span></b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b><span>Hello<span> <span class="pre"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="pre"> </span><span class="pre"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="nowrap"> </span><span class="pre"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="prewrap"> </span><span class="pre"> </span><span>Kitty</span></b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b><span>Hello<span> <span class="nowrap"> </span>Kitty</b>
|
||||
<p><b><span>Hello<span><span class="pre"> </span><span class="nowrap"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="nowrap"> </span><span class="nowrap"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="prewrap"> </span><span class="nowrap"> </span><span>Kitty</span></b>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><b><span>Hello<span> <span class="prewrap"> </span>Kitty</b>
|
||||
<p><b><span>Hello<span><span class="pre"> </span><span class="prewrap"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="nowrap"> </span><span class="prewrap"> </span><span>Kitty</span></b>
|
||||
<p><b><span>Hello<span><span class="prewrap"> </span><span class="prewrap"> </span><span>Kitty</span></b>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,65 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Reference -->
|
||||
<style>
|
||||
div { border:1px solid black; }
|
||||
.container { float:left; width:20%; border-color:cyan; }
|
||||
p { width:0; }
|
||||
b { font-weight:normal; background-color:yellow; white-space:pre; }
|
||||
.cell { display:table-cell; border:1px solid green; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello
|
||||
Kitty</b></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<!-- Wrapping tests in table-cell to test min-width computation -->
|
||||
<style>
|
||||
div { border:1px solid black; }
|
||||
.container { float:left; width:20%; border-color:cyan; }
|
||||
p { width:0; }
|
||||
b { font-weight:normal; background-color:yellow; }
|
||||
.cell { display:table-cell; border:1px solid green; }
|
||||
.nowrap { white-space:nowrap; }
|
||||
.pre { white-space:pre; }
|
||||
.prewrap { white-space:-moz-pre-wrap; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<p><span class="cell"><b>Hello Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="pre"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="nowrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="prewrap"> </span>Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello <span>Kitty</span></b></span>
|
||||
<p><span class="cell"><b>Hello<span class="pre"> </span> Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="nowrap"> </span> Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="prewrap"> </span> Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello <span class="pre"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="pre"> </span><span class="pre"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="nowrap"> </span><span class="pre"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="prewrap"> </span><span class="pre"> </span>Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello <span class="nowrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="pre"> </span><span class="nowrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="nowrap"> </span><span class="nowrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="prewrap"> </span><span class="nowrap"> </span>Kitty</b></span>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<p><span class="cell"><b>Hello <span class="prewrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="pre"> </span><span class="prewrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="nowrap"> </span><span class="prewrap"> </span>Kitty</b></span>
|
||||
<p><span class="cell"><b>Hello<span class="prewrap"> </span><span class="prewrap"> </span>Kitty</b></span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче