зеркало из 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.
|
* into AppendText calls.
|
||||||
*
|
*
|
||||||
* The current strategy is that we break the overall text into
|
* The current strategy is that we break the overall text into
|
||||||
* whitespace-delimited "words". Then for words that contain a "complex"
|
* whitespace-delimited "words". Then those words are passed to the nsILineBreaker
|
||||||
* character (currently CJK or Thai), we break within the word using complex
|
* service for deeper analysis if they contain a "complex" character as described
|
||||||
* rules (JISx4051 or Pango).
|
* below.
|
||||||
*/
|
*/
|
||||||
class nsLineBreaker {
|
class nsLineBreaker {
|
||||||
public:
|
public:
|
||||||
|
@ -102,9 +102,9 @@ public:
|
||||||
(0xff00 <= u && u <= 0xffef); // Halfwidth and Fullwidth Forms
|
(0xff00 <= u && u <= 0xffef); // Halfwidth and Fullwidth Forms
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normally, break opportunities exist at the end of each run of whitespace
|
// Break opportunities exist at the end of each run of breakable whitespace
|
||||||
// (see IsSpace above). Break opportunities can also exist inside runs of
|
// (see IsSpace above). Break opportunities can also exist between pairs of
|
||||||
// non-whitespace, as determined by nsILineBreaker. We pass a whitespace-
|
// non-whitespace characters, as determined by nsILineBreaker. We pass a whitespace-
|
||||||
// delimited word to nsILineBreaker if it contains at least one character
|
// delimited word to nsILineBreaker if it contains at least one character
|
||||||
// matching IsComplexChar.
|
// matching IsComplexChar.
|
||||||
// We provide flags to control on a per-chunk basis where breaks are allowed.
|
// 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
|
// We operate on text after whitespace processing has been applied, so
|
||||||
// other characters (e.g. tabs and newlines) may have been converted to
|
// other characters (e.g. tabs and newlines) may have been converted to
|
||||||
// spaces.
|
// spaces.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flags passed with each chunk of text.
|
||||||
|
*/
|
||||||
enum {
|
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
|
* 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
|
* Feed Unicode text into the linebreaker for analysis. aLength must be
|
||||||
|
@ -184,8 +200,11 @@ private:
|
||||||
nsAutoTArray<TextItem,2> mTextItems;
|
nsAutoTArray<TextItem,2> mTextItems;
|
||||||
PRPackedBool mCurrentWordContainsComplexChar;
|
PRPackedBool mCurrentWordContainsComplexChar;
|
||||||
|
|
||||||
// True if the previous character was whitespace
|
// True if the previous character was breakable whitespace
|
||||||
PRPackedBool mAfterSpace;
|
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_*/
|
#endif /*NSLINEBREAKER_H_*/
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
|
|
||||||
nsLineBreaker::nsLineBreaker()
|
nsLineBreaker::nsLineBreaker()
|
||||||
: mCurrentWordContainsComplexChar(PR_FALSE),
|
: mCurrentWordContainsComplexChar(PR_FALSE),
|
||||||
mAfterSpace(PR_FALSE)
|
mAfterBreakableSpace(PR_FALSE), mBreakHere(PR_FALSE)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,10 +72,10 @@ nsLineBreaker::FlushCurrentWord()
|
||||||
TextItem* ti = &mTextItems[i];
|
TextItem* ti = &mTextItems[i];
|
||||||
NS_ASSERTION(ti->mLength > 0, "Zero length word contribution?");
|
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;
|
breakState[offset] = PR_FALSE;
|
||||||
}
|
}
|
||||||
if (!(ti->mFlags & BREAK_ALLOW_INSIDE)) {
|
if (ti->mFlags & BREAK_SUPPRESS_INSIDE) {
|
||||||
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
|
PRUint32 exclude = ti->mSinkOffset == 0 ? 1 : 0;
|
||||||
memset(breakState.Elements() + offset + exclude, PR_FALSE, ti->mLength - exclude);
|
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
|
// Continue the current word
|
||||||
if (mCurrentWord.Length() > 0) {
|
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])) {
|
while (offset < aLength && !IsSpace(aText[offset])) {
|
||||||
mCurrentWord.AppendElement(aText[offset]);
|
mCurrentWord.AppendElement(aText[offset]);
|
||||||
|
@ -137,8 +137,14 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
||||||
}
|
}
|
||||||
|
|
||||||
PRUint32 start = offset;
|
PRUint32 start = offset;
|
||||||
if (!aSink && !aFlags) {
|
PRBool noBreaksNeeded = !aSink ||
|
||||||
// Skip to the space before the last word, since we don't need the breaks
|
((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;
|
offset = aLength;
|
||||||
while (offset > start) {
|
while (offset > start) {
|
||||||
--offset;
|
--offset;
|
||||||
|
@ -152,16 +158,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
||||||
for (;;) {
|
for (;;) {
|
||||||
PRUnichar ch = aText[offset];
|
PRUnichar ch = aText[offset];
|
||||||
PRBool isSpace = IsSpace(ch);
|
PRBool isSpace = IsSpace(ch);
|
||||||
|
PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||||
|
|
||||||
if (aSink) {
|
if (aSink) {
|
||||||
breakState[offset] = mAfterSpace && !isSpace &&
|
breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
|
||||||
(aFlags & (offset == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
|
||||||
}
|
}
|
||||||
mAfterSpace = isSpace;
|
mBreakHere = PR_FALSE;
|
||||||
|
mAfterBreakableSpace = isBreakableSpace;
|
||||||
|
|
||||||
if (isSpace) {
|
if (isSpace) {
|
||||||
if (offset > wordStart && wordHasComplexChar) {
|
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
|
// Save current start-of-word state because GetJISx4051Breaks will
|
||||||
// set it to false
|
// set it to false
|
||||||
PRPackedBool currentStart = breakState[wordStart];
|
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);
|
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
@ -214,7 +221,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
||||||
|
|
||||||
// Continue the current word
|
// Continue the current word
|
||||||
if (mCurrentWord.Length() > 0) {
|
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])) {
|
while (offset < aLength && !IsSpace(aText[offset])) {
|
||||||
mCurrentWord.AppendElement(aText[offset]);
|
mCurrentWord.AppendElement(aText[offset]);
|
||||||
|
@ -247,8 +254,14 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
||||||
}
|
}
|
||||||
|
|
||||||
PRUint32 start = offset;
|
PRUint32 start = offset;
|
||||||
if (!aSink && !aFlags) {
|
PRBool noBreaksNeeded = !aSink ||
|
||||||
// Skip to the space before the last word, since we don't need the breaks
|
((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;
|
offset = aLength;
|
||||||
while (offset > start) {
|
while (offset > start) {
|
||||||
--offset;
|
--offset;
|
||||||
|
@ -262,16 +275,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
||||||
for (;;) {
|
for (;;) {
|
||||||
PRUint8 ch = aText[offset];
|
PRUint8 ch = aText[offset];
|
||||||
PRBool isSpace = IsSpace(ch);
|
PRBool isSpace = IsSpace(ch);
|
||||||
|
PRBool isBreakableSpace = isSpace && !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||||
|
|
||||||
if (aSink) {
|
if (aSink) {
|
||||||
breakState[offset] = mAfterSpace && !isSpace &&
|
breakState[offset] = mBreakHere || (mAfterBreakableSpace && !isBreakableSpace);
|
||||||
(aFlags & (offset == 0 ? BREAK_ALLOW_INITIAL : BREAK_ALLOW_INSIDE));
|
|
||||||
}
|
}
|
||||||
mAfterSpace = isSpace;
|
mBreakHere = PR_FALSE;
|
||||||
|
mAfterBreakableSpace = isBreakableSpace;
|
||||||
|
|
||||||
if (isSpace) {
|
if (isSpace) {
|
||||||
if (offset > wordStart && wordHasComplexChar) {
|
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
|
// Save current start-of-word state because GetJISx4051Breaks will
|
||||||
// set it to false
|
// set it to false
|
||||||
PRPackedBool currentStart = breakState[wordStart];
|
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);
|
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||||
}
|
}
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
nsLineBreaker::AppendInvisibleWhitespace() {
|
nsLineBreaker::AppendInvisibleWhitespace(PRUint32 aFlags) {
|
||||||
// Treat as "invisible whitespace"
|
|
||||||
nsresult rv = FlushCurrentWord();
|
nsresult rv = FlushCurrentWord();
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
return rv;
|
return rv;
|
||||||
mAfterSpace = PR_TRUE;
|
|
||||||
|
PRBool isBreakableSpace = !(aFlags & BREAK_SUPPRESS_INSIDE);
|
||||||
|
if (mAfterBreakableSpace && !isBreakableSpace) {
|
||||||
|
mBreakHere = PR_TRUE;
|
||||||
|
}
|
||||||
|
mAfterBreakableSpace = isBreakableSpace;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,6 +118,7 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext,
|
||||||
mPlacedFloats = 0;
|
mPlacedFloats = 0;
|
||||||
mTotalPlacedFrames = 0;
|
mTotalPlacedFrames = 0;
|
||||||
mTopEdge = 0;
|
mTopEdge = 0;
|
||||||
|
mTrimmableWidth = 0;
|
||||||
|
|
||||||
// Instead of always pre-initializing the free-lists for frames and
|
// 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
|
// spans, we do it on demand so that situations that only use a few
|
||||||
|
@ -1034,7 +1035,9 @@ nsLineLayout::ReflowFrame(nsIFrame* aFrame,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!continuingTextRun) {
|
if (!continuingTextRun) {
|
||||||
SetHasTrailingTextFrame(PR_FALSE);
|
if (!pfd->GetFlag(PFD_SKIPWHENTRIMMINGWHITESPACE)) {
|
||||||
|
mTrimmableWidth = 0;
|
||||||
|
}
|
||||||
if (!psd->mNoWrap && (!CanPlaceFloatNow() || placedFloat)) {
|
if (!psd->mNoWrap && (!CanPlaceFloatNow() || placedFloat)) {
|
||||||
// record soft break opportunity after this content that can't be
|
// 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
|
// 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
|
// Set outside to PR_TRUE if the result of the reflow leads to the
|
||||||
// frame sticking outside of our available area.
|
// 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 (!outside) {
|
||||||
// If it fits, it fits
|
// If it fits, it fits
|
||||||
#ifdef NOISY_CAN_PLACE_FRAME
|
#ifdef NOISY_CAN_PLACE_FRAME
|
||||||
|
|
|
@ -143,10 +143,9 @@ protected:
|
||||||
#define LL_LASTFLOATWASLETTERFRAME 0x00000080
|
#define LL_LASTFLOATWASLETTERFRAME 0x00000080
|
||||||
#define LL_CANPLACEFLOAT 0x00000100
|
#define LL_CANPLACEFLOAT 0x00000100
|
||||||
#define LL_LINEENDSINBR 0x00000200
|
#define LL_LINEENDSINBR 0x00000200
|
||||||
#define LL_HASTRAILINGTEXTFRAME 0x00000400
|
#define LL_NEEDBACKUP 0x00000400
|
||||||
#define LL_NEEDBACKUP 0x00000800
|
#define LL_INFIRSTLINE 0x00000800
|
||||||
#define LL_INFIRSTLINE 0x00002000
|
#define LL_GOTLINEBOX 0x00001000
|
||||||
#define LL_GOTLINEBOX 0x00004000
|
|
||||||
#define LL_LASTFLAG LL_GOTLINEBOX
|
#define LL_LASTFLAG LL_GOTLINEBOX
|
||||||
|
|
||||||
PRUint16 mFlags;
|
PRUint16 mFlags;
|
||||||
|
@ -203,17 +202,8 @@ public:
|
||||||
return mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus);
|
return mBlockRS->AddFloat(*this, aFrame, PR_FALSE, aReflowStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void SetTrimmableWidth(nscoord aTrimmableWidth) {
|
||||||
* If the last content placed on the line (not counting inline containers)
|
mTrimmableWidth = aTrimmableWidth;
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
@ -378,6 +368,9 @@ protected:
|
||||||
// the block has been called.
|
// the block has been called.
|
||||||
nscoord mFinalLineHeight;
|
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
|
// Per-frame data recorded by the line-layout reflow logic. This
|
||||||
// state is the state needed to post-process the line after reflow
|
// state is the state needed to post-process the line after reflow
|
||||||
// has completed (vertical alignment, horizontal alignment,
|
// has completed (vertical alignment, horizontal alignment,
|
||||||
|
|
|
@ -654,6 +654,8 @@ public:
|
||||||
if (mTextRun->SetPotentialLineBreaks(aOffset + mOffsetIntoTextRun, aLength,
|
if (mTextRun->SetPotentialLineBreaks(aOffset + mOffsetIntoTextRun, aLength,
|
||||||
aBreakBefore, mContext)) {
|
aBreakBefore, mContext)) {
|
||||||
mChangedBreaks = PR_TRUE;
|
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)
|
: mMappedFlows[i + 1].mTransformedTextOffset)
|
||||||
- offset;
|
- offset;
|
||||||
|
|
||||||
nsTextFrame* startFrame = mappedFlow->mStartFrame;
|
|
||||||
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->GetContentEnd(), iter)) {
|
|
||||||
mLineBreaker.AppendInvisibleWhitespace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (length > 0) {
|
|
||||||
PRUint32 flags = 0;
|
PRUint32 flags = 0;
|
||||||
nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
|
nsIFrame* initialBreakController = mappedFlow->mAncestorControllingInitialBreak;
|
||||||
if (!initialBreakController) {
|
if (!initialBreakController) {
|
||||||
initialBreakController = mLineContainer;
|
initialBreakController = mLineContainer;
|
||||||
}
|
}
|
||||||
if (initialBreakController->GetStyleText()->WhiteSpaceCanWrap()) {
|
if (!initialBreakController->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||||
flags |= nsLineBreaker::BREAK_ALLOW_INITIAL;
|
flags |= nsLineBreaker::BREAK_SUPPRESS_INITIAL;
|
||||||
}
|
}
|
||||||
|
nsTextFrame* startFrame = mappedFlow->mStartFrame;
|
||||||
const nsStyleText* textStyle = startFrame->GetStyleText();
|
const nsStyleText* textStyle = startFrame->GetStyleText();
|
||||||
if (textStyle->WhiteSpaceCanWrap()) {
|
if (!textStyle->WhiteSpaceCanWrap()) {
|
||||||
// If white-space is preserved, then the only break opportunity is at
|
flags |= nsLineBreaker::BREAK_SUPPRESS_INSIDE;
|
||||||
// the end of whitespace runs; otherwise there is a break opportunity before
|
}
|
||||||
// and after each whitespace character
|
if (aTextRun->GetFlags() & nsTextFrameUtils::TEXT_NO_BREAKS) {
|
||||||
flags |= nsLineBreaker::BREAK_ALLOW_INSIDE;
|
flags |= nsLineBreaker::BREAK_SKIP_SETTING_NO_BREAKS;
|
||||||
}
|
}
|
||||||
|
|
||||||
BreakSink* sink = *breakSink;
|
if (HasCompressedLeadingWhitespace(startFrame, mappedFlow->GetContentEnd(), iter)) {
|
||||||
if (aSuppressSink) {
|
mLineBreaker.AppendInvisibleWhitespace(flags);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (length > 0) {
|
||||||
|
BreakSink* sink = aSuppressSink ? nsnull : (*breakSink).get();
|
||||||
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||||
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
||||||
length, flags, sink);
|
length, flags, sink);
|
||||||
|
@ -1812,8 +1805,7 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag,
|
||||||
offsets.mLength -= whitespaceCount;
|
offsets.mLength -= whitespaceCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE) &&
|
if (aTrimAfter && (GetStateBits() & TEXT_END_OF_LINE)) {
|
||||||
textStyle->WhiteSpaceCanWrap()) {
|
|
||||||
PRInt32 whitespaceCount =
|
PRInt32 whitespaceCount =
|
||||||
GetTrimmableWhitespaceCount(aFrag, offsets.GetEnd() - 1,
|
GetTrimmableWhitespaceCount(aFrag, offsets.GetEnd() - 1,
|
||||||
offsets.mLength, -1);
|
offsets.mLength, -1);
|
||||||
|
@ -5216,8 +5208,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
|
NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
|
||||||
"We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
|
"We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
|
||||||
#endif
|
#endif
|
||||||
PRBool suppressInitialBreak = !lineLayout.LineIsBreakable() ||
|
|
||||||
!lineLayout.HasTrailingTextFrame();
|
|
||||||
|
|
||||||
PRInt32 limitLength = length;
|
PRInt32 limitLength = length;
|
||||||
PRInt32 forceBreak = lineLayout.GetForcedBreakPosition(mContent);
|
PRInt32 forceBreak = lineLayout.GetForcedBreakPosition(mContent);
|
||||||
|
@ -5249,13 +5239,12 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
PRBool usedHyphenation;
|
PRBool usedHyphenation;
|
||||||
gfxFloat trimmedWidth = 0;
|
gfxFloat trimmedWidth = 0;
|
||||||
gfxFloat availWidth = aReflowState.availableWidth;
|
gfxFloat availWidth = aReflowState.availableWidth;
|
||||||
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant() &&
|
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
||||||
textStyle->WhiteSpaceCanWrap();
|
|
||||||
PRUint32 transformedCharsFit =
|
PRUint32 transformedCharsFit =
|
||||||
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
|
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
|
||||||
(GetStateBits() & TEXT_START_OF_LINE) != 0,
|
(GetStateBits() & TEXT_START_OF_LINE) != 0,
|
||||||
availWidth,
|
availWidth,
|
||||||
&provider, suppressInitialBreak,
|
&provider, !lineLayout.LineIsBreakable(),
|
||||||
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
||||||
&textMetrics, needTightBoundingBox, ctx,
|
&textMetrics, needTightBoundingBox, ctx,
|
||||||
&usedHyphenation, &transformedLastBreak);
|
&usedHyphenation, &transformedLastBreak);
|
||||||
|
@ -5293,26 +5282,38 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
AddStateBits(TEXT_HYPHEN_BREAK);
|
AddStateBits(TEXT_HYPHEN_BREAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If everything fits including trimmed whitespace, then we should add the
|
gfxFloat trimmableWidth = 0;
|
||||||
// trimmed whitespace to our metrics now because it probably won't be trimmed
|
if (canTrimTrailingWhitespace) {
|
||||||
// and we need to position subsequent frames correctly...
|
// Optimization: if we trimmed trailing whitespace, and we can be sure
|
||||||
if (forceBreak < 0 && textMetrics.mAdvanceWidth + trimmedWidth <= availWidth) {
|
// 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;
|
textMetrics.mAdvanceWidth += trimmedWidth;
|
||||||
|
trimmableWidth = trimmedWidth;
|
||||||
if (mTextRun->IsRightToLeft()) {
|
if (mTextRun->IsRightToLeft()) {
|
||||||
// Space comes before text, so the bounding box is moved to the
|
// Space comes before text, so the bounding box is moved to the
|
||||||
// right by trimmdWidth
|
// right by trimmdWidth
|
||||||
textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
|
textMetrics.mBoundingBox.MoveBy(gfxPoint(trimmedWidth, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since everything fit and no break was forced,
|
||||||
|
// record the last break opportunity
|
||||||
if (lastBreak >= 0) {
|
if (lastBreak >= 0) {
|
||||||
lineLayout.NotifyOptionalBreakPosition(mContent, lastBreak,
|
lineLayout.NotifyOptionalBreakPosition(mContent, lastBreak,
|
||||||
textMetrics.mAdvanceWidth <= aReflowState.availableWidth);
|
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();
|
PRInt32 contentLength = offset + charsFit - GetContentOffset();
|
||||||
|
|
||||||
|
@ -5347,26 +5348,20 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
||||||
// Clean up, update state
|
// Clean up, update state
|
||||||
/////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
if (charsFit > 0) {
|
// If all our characters are discarded or collapsed, then trimmable width
|
||||||
lineLayout.SetHasTrailingTextFrame(PR_TRUE);
|
// from the last textframe should be preserved. Otherwise the trimmable width
|
||||||
if (charsFit == length) {
|
// from this textframe overrides. (Currently in CSS trimmable width can be
|
||||||
if (textStyle->WhiteSpaceCanWrap() &&
|
// at most one space so there's no way for trimmable width from a previous
|
||||||
IsTrimmableSpace(frag, offset + charsFit - 1)) {
|
// frame to accumulate with trimmable width from this frame.)
|
||||||
// Record a potential break after final breakable whitespace
|
if (transformedCharsFit > 0) {
|
||||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
lineLayout.SetTrimmableWidth(NSToCoordFloor(trimmableWidth));
|
||||||
textMetrics.mAdvanceWidth <= aReflowState.availableWidth);
|
}
|
||||||
} else if (HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
|
if (charsFit > 0 && charsFit == length &&
|
||||||
|
HasSoftHyphenBefore(frag, mTextRun, offset, end)) {
|
||||||
// Record a potential break after final soft hyphen
|
// Record a potential break after final soft hyphen
|
||||||
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
lineLayout.NotifyOptionalBreakPosition(mContent, offset + length,
|
||||||
textMetrics.mAdvanceWidth + provider.GetHyphenWidth() <= availWidth);
|
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 (completedFirstLetter) {
|
if (completedFirstLetter) {
|
||||||
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
lineLayout.SetFirstLetterStyleOK(PR_FALSE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,6 @@
|
||||||
== soft-hyphens-1a.html soft-hyphens-1-ref.html
|
== soft-hyphens-1a.html soft-hyphens-1-ref.html
|
||||||
== soft-hyphens-1b.html soft-hyphens-1-ref.html
|
== soft-hyphens-1b.html soft-hyphens-1-ref.html
|
||||||
== soft-hyphens-1c.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>
|
Загрузка…
Ссылка в новой задаче