зеркало из https://github.com/mozilla/pjs.git
Not Part Of The Build. Update nsTextFrameThebes whitespace handling; allow line breaking only at the end of a run of whitespace.
This commit is contained in:
Родитель
0b1d94265f
Коммит
8a715b72c5
|
@ -95,11 +95,17 @@ public:
|
|||
// a) whether whitespace in this text induces breaks b) whether we can
|
||||
// break between nonwhitespace inside this text and c) whether we can break
|
||||
// between nonwhitespace between the last text and this text.
|
||||
//
|
||||
// "Whitespace" below means Unicode ZWSP (U+200B) and ASCII space (U+0020). We
|
||||
// operate on text after whitespace processing has been applied, so
|
||||
// other characters (e.g. tabs and newlines) may have been converted to
|
||||
// spaces.
|
||||
enum {
|
||||
/**
|
||||
* Allow breaks before and after whitespace in this block of text
|
||||
* Allow breaks where a non-whitespace character in this block of text
|
||||
* is preceded by a whitespace character.
|
||||
*/
|
||||
BREAK_WHITESPACE = 0x01,
|
||||
BREAK_WHITESPACE_END = 0x01,
|
||||
/**
|
||||
* Allow breaks between eligible nonwhitespace characters when the break
|
||||
* is in the interior of this block of text.
|
||||
|
@ -162,8 +168,8 @@ private:
|
|||
PRPackedBool mCurrentWordContainsCJK;
|
||||
|
||||
// When mCurrentWord is empty, this indicates whether we should allow a break
|
||||
// before the next text.
|
||||
PRPackedBool mBreakBeforeNextWord;
|
||||
// before the next text if it starts with non-whitespace.
|
||||
PRPackedBool mBreakBeforeNonWhitespace;
|
||||
};
|
||||
|
||||
#endif /*NSLINEBREAKER_H_*/
|
||||
|
|
|
@ -40,21 +40,18 @@
|
|||
#include "nsContentUtils.h"
|
||||
#include "nsILineBreaker.h"
|
||||
|
||||
// We only operate on post-transformation text, so we don't see tabs
|
||||
#define UNICODE_ZWSP 0x200b
|
||||
|
||||
static inline int
|
||||
IS_SPACE(PRUnichar u)
|
||||
{
|
||||
NS_ASSERTION(u != 0x0009, "Tabs should have been eliminated");
|
||||
// \r characters are just ignored
|
||||
return u == 0x0020 || u == 0x000a || u == 0x200b;
|
||||
return u == 0x0020 || u == UNICODE_ZWSP;
|
||||
}
|
||||
|
||||
static inline int
|
||||
IS_SPACE(PRUint8 u)
|
||||
{
|
||||
NS_ASSERTION(u != 0x0009, "Tabs should have been eliminated");
|
||||
// \r characters are just ignored
|
||||
return u == 0x0020 || u == 0x000a;
|
||||
return u == 0x0020;
|
||||
}
|
||||
|
||||
static inline int
|
||||
|
@ -67,7 +64,8 @@ IS_CJK_CHAR(PRUnichar u)
|
|||
}
|
||||
|
||||
nsLineBreaker::nsLineBreaker()
|
||||
: mCurrentWordContainsCJK(PR_FALSE), mBreakBeforeNextWord(PR_FALSE)
|
||||
: mCurrentWordContainsCJK(PR_FALSE),
|
||||
mBreakBeforeNonWhitespace(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -129,7 +127,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mBreakBeforeNextWord |= (aFlags & BREAK_WHITESPACE) != 0;
|
||||
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -137,7 +135,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mBreakBeforeNextWord, "This should not be set");
|
||||
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IS_SPACE(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -168,13 +166,13 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
PRUint32 wordStart = offset;
|
||||
PRBool wordHasCJK = PR_FALSE;
|
||||
|
||||
PRBool breakNext = mBreakBeforeNextWord;
|
||||
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
|
||||
for (;;) {
|
||||
PRUnichar ch = aText[offset];
|
||||
PRBool isSpace = IS_SPACE(ch);
|
||||
|
||||
breakState[offset] = breakNext;
|
||||
breakNext = PR_FALSE;
|
||||
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
|
||||
breakNextIfNonWhitespace = PR_FALSE;
|
||||
|
||||
if (isSpace) {
|
||||
if (offset > wordStart && wordHasCJK) {
|
||||
|
@ -190,10 +188,8 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
wordHasCJK = PR_FALSE;
|
||||
}
|
||||
|
||||
if (aFlags & BREAK_WHITESPACE) {
|
||||
// Allow break either side of the whitespace
|
||||
breakState[offset] = PR_TRUE;
|
||||
breakNext = PR_TRUE;
|
||||
if (aFlags & BREAK_WHITESPACE_END) {
|
||||
breakNextIfNonWhitespace = PR_TRUE;
|
||||
}
|
||||
++offset;
|
||||
if (offset >= aLength)
|
||||
|
@ -221,7 +217,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUnichar* aText, PRUint32
|
|||
}
|
||||
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
mBreakBeforeNextWord = breakNext;
|
||||
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -234,7 +230,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
nsresult rv = FlushCurrentWord();
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
mBreakBeforeNextWord |= (aFlags & BREAK_WHITESPACE) != 0;
|
||||
mBreakBeforeNonWhitespace = (aFlags & BREAK_WHITESPACE_END) != 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -242,7 +238,7 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
|
||||
// Continue the current word
|
||||
if (mCurrentWord.Length() > 0) {
|
||||
NS_ASSERTION(!mBreakBeforeNextWord, "This should not be set");
|
||||
NS_ASSERTION(!mBreakBeforeNonWhitespace, "These should not be set");
|
||||
|
||||
while (offset < aLength && !IS_SPACE(aText[offset])) {
|
||||
mCurrentWord.AppendElement(aText[offset]);
|
||||
|
@ -271,19 +267,17 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
PRUint32 start = offset;
|
||||
PRUint32 wordStart = offset;
|
||||
|
||||
PRBool breakNext = mBreakBeforeNextWord;
|
||||
PRBool breakNextIfNonWhitespace = mBreakBeforeNonWhitespace;
|
||||
for (;;) {
|
||||
PRUint8 ch = aText[offset];
|
||||
PRBool isSpace = IS_SPACE(ch);
|
||||
|
||||
breakState[offset] = breakNext;
|
||||
breakNext = PR_FALSE;
|
||||
breakState[offset] = breakNextIfNonWhitespace && !isSpace;
|
||||
breakNextIfNonWhitespace = PR_FALSE;
|
||||
|
||||
if (isSpace) {
|
||||
if (aFlags & BREAK_WHITESPACE) {
|
||||
// Allow break either side of the whitespace
|
||||
breakState[offset] = PR_TRUE;
|
||||
breakNext = PR_TRUE;
|
||||
if (aFlags & BREAK_WHITESPACE_END) {
|
||||
breakNextIfNonWhitespace = PR_TRUE;
|
||||
}
|
||||
++offset;
|
||||
if (offset >= aLength)
|
||||
|
@ -313,6 +307,6 @@ nsLineBreaker::AppendText(nsIAtom* aLangGroup, const PRUint8* aText, PRUint32 aL
|
|||
}
|
||||
|
||||
aSink->SetBreaks(start, offset - start, breakState.Elements() + start);
|
||||
mBreakBeforeNextWord = breakNext;
|
||||
mBreakBeforeNonWhitespace = breakNextIfNonWhitespace;
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -175,12 +175,6 @@
|
|||
* We go to considerable effort to make sure things work even if in-flow
|
||||
* siblings have different style contexts (i.e., first-letter and first-line).
|
||||
*
|
||||
* Tabs in preformatted text act as if they expand to 1-8 spaces, so that the
|
||||
* number of clusters on the line up to the end of the tab is a multiple of 8.
|
||||
* This is implemented by transforming tabs to spaces before handing off text
|
||||
* to the text run, and then configuring the spacing after the tab to be the
|
||||
* width of 0-7 spaces.
|
||||
*
|
||||
* Our convention is that unsigned integer character offsets are offsets into
|
||||
* the transformed string. Signed integer character offsets are offsets into
|
||||
* the DOM string.
|
||||
|
@ -188,6 +182,8 @@
|
|||
* XXX currently we don't handle hyphenated breaks between text frames where the
|
||||
* hyphen occurs at the end of the first text frame, e.g.
|
||||
* <b>Kit­</b>ty
|
||||
*
|
||||
* Preformatted text basically matches
|
||||
*/
|
||||
|
||||
class nsTextFrame;
|
||||
|
@ -527,8 +523,6 @@ public:
|
|||
|
||||
gfxTextRun* GetTextRun() { return mTextRun; }
|
||||
void SetTextRun(gfxTextRun* aTextRun) { mTextRun = aTextRun; }
|
||||
|
||||
PRInt32 GetColumn() { return mColumn; }
|
||||
|
||||
// Get the DOM content range mapped by this frame after excluding
|
||||
// whitespace subject to start-of-line and end-of-line trimming.
|
||||
|
@ -546,7 +540,6 @@ protected:
|
|||
nsIFrame* mNextContinuation;
|
||||
PRInt32 mContentOffset;
|
||||
PRInt32 mContentLength;
|
||||
PRInt32 mColumn;
|
||||
nscoord mAscent;
|
||||
gfxTextRun* mTextRun;
|
||||
|
||||
|
@ -1558,11 +1551,15 @@ BuildTextRunsScanner::SetupBreakSinksForTextRun(gfxTextRun* aTextRun,
|
|||
mappedFlow->mAncestorControllingInitialBreak->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_NONWHITESPACE_BEFORE;
|
||||
}
|
||||
if (mappedFlow->mStartFrame->GetStyleText()->WhiteSpaceCanWrap()) {
|
||||
flags |= nsLineBreaker::BREAK_WHITESPACE | nsLineBreaker::BREAK_NONWHITESPACE_INSIDE;
|
||||
const nsStyleText* textStyle = mappedFlow->mStartFrame->GetStyleText();
|
||||
if (textStyle->WhiteSpaceCanWrap()) {
|
||||
// If white-space is preserved, then the only break opportunity is at
|
||||
// the end of whitespace runs; otherwise there is a break opportunity before
|
||||
// and after each whitespace character
|
||||
flags |= nsLineBreaker::BREAK_NONWHITESPACE_INSIDE |
|
||||
nsLineBreaker::BREAK_WHITESPACE_END;
|
||||
}
|
||||
// If length is zero and BREAK_WHITESPACE is active, this will notify
|
||||
// the linebreaker to insert a break opportunity before the next character.
|
||||
// If length is zero, the linebreaker treats the text as invisible whitespace.
|
||||
// Thus runs of entirely-skipped whitespace can still induce breaks.
|
||||
if (aTextRun->GetFlags() & gfxFontGroup::TEXT_IS_8BIT) {
|
||||
mLineBreaker.AppendText(lang, aTextRun->GetText8Bit() + offset,
|
||||
|
@ -1841,13 +1838,18 @@ public:
|
|||
*/
|
||||
PropertyProvider(gfxTextRun* aTextRun, const nsStyleText* aTextStyle,
|
||||
const nsTextFragment* aFrag, nsTextFrame* aFrame,
|
||||
const gfxSkipCharsIterator& aStart, PRInt32 aLength)
|
||||
const gfxSkipCharsIterator& aStart, PRInt32 aLength,
|
||||
nsIFrame* aLineContainer,
|
||||
nscoord aOffsetFromBlockOriginForTabs)
|
||||
: mTextRun(aTextRun), mFontGroup(nsnull), mTextStyle(aTextStyle), mFrag(aFrag),
|
||||
mFrame(aFrame), mStart(aStart), mLength(aLength),
|
||||
mLineContainer(aLineContainer),
|
||||
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull), mLength(aLength),
|
||||
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
|
||||
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
|
||||
mJustificationSpacing(0),
|
||||
mHyphenWidth(-1)
|
||||
mHyphenWidth(-1),
|
||||
mOffsetFromBlockOriginForTabs(aOffsetFromBlockOriginForTabs),
|
||||
mReflowing(PR_TRUE)
|
||||
{
|
||||
NS_ASSERTION(mStart.IsInitialized(), "Start not initialized?");
|
||||
}
|
||||
|
@ -1858,13 +1860,18 @@ public:
|
|||
* *must* be called before this!!!
|
||||
*/
|
||||
PropertyProvider(nsTextFrame* aFrame, const gfxSkipCharsIterator& aStart)
|
||||
: mTextRun(aFrame->GetTextRun()), mFontGroup(nsnull), mTextStyle(aFrame->GetStyleText()),
|
||||
: mTextRun(aFrame->GetTextRun()), mFontGroup(nsnull),
|
||||
mTextStyle(aFrame->GetStyleText()),
|
||||
mFrag(aFrame->GetContent()->GetText()),
|
||||
mFrame(aFrame), mStart(aStart), mLength(aFrame->GetContentLength()),
|
||||
mLineContainer(nsnull),
|
||||
mFrame(aFrame), mStart(aStart), mTabWidths(nsnull),
|
||||
mLength(aFrame->GetContentLength()),
|
||||
mWordSpacing(StyleToCoord(mTextStyle->mWordSpacing)),
|
||||
mLetterSpacing(StyleToCoord(mTextStyle->mLetterSpacing)),
|
||||
mJustificationSpacing(0),
|
||||
mHyphenWidth(-1)
|
||||
mHyphenWidth(-1),
|
||||
mOffsetFromBlockOriginForTabs(0),
|
||||
mReflowing(PR_FALSE)
|
||||
{
|
||||
NS_ASSERTION(mTextRun, "Textrun not initialized!");
|
||||
}
|
||||
|
@ -1877,17 +1884,15 @@ public:
|
|||
virtual void GetHyphenationBreaks(PRUint32 aStart, PRUint32 aLength,
|
||||
PRPackedBool* aBreakBefore);
|
||||
|
||||
void GetSpacingInternal(PRUint32 aStart, PRUint32 aLength, Spacing* aSpacing,
|
||||
PRBool aIgnoreTabs);
|
||||
|
||||
/**
|
||||
* Count the number of justifiable characters in the given DOM range
|
||||
*/
|
||||
PRUint32 ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength);
|
||||
void FindEndOfJustificationRange(gfxSkipCharsIterator* aIter);
|
||||
|
||||
/**
|
||||
* Count the number of spaces inserted for tabs in the given transformed substring
|
||||
*/
|
||||
PRUint32 GetTabExpansionCount(PRUint32 aOffset, PRUint32 aLength);
|
||||
|
||||
const nsStyleText* GetStyleText() { return mTextStyle; }
|
||||
nsTextFrame* GetFrame() { return mFrame; }
|
||||
// This may not be equal to the frame offset/length in because we may have
|
||||
|
@ -1904,25 +1909,26 @@ public:
|
|||
return mFontGroup;
|
||||
}
|
||||
|
||||
gfxFloat* GetTabWidths(PRUint32 aTransformedStart, PRUint32 aTransformedLength);
|
||||
|
||||
protected:
|
||||
void SetupJustificationSpacing();
|
||||
|
||||
// Offsets in transformed string coordinates
|
||||
PRUint8* ComputeTabSpaceCount(PRUint32 aOffset, PRUint32 aLength);
|
||||
|
||||
|
||||
gfxTextRun* mTextRun;
|
||||
gfxFontGroup* mFontGroup;
|
||||
const nsStyleText* mTextStyle;
|
||||
const nsTextFragment* mFrag;
|
||||
nsIFrame* mLineContainer;
|
||||
nsTextFrame* mFrame;
|
||||
gfxSkipCharsIterator mStart; // Offset in original and transformed string
|
||||
nsTArray<PRUint8> mTabSpaceCounts; // counts for transformed string characters
|
||||
PRUint32 mCurrentColumn;
|
||||
nsTArray<gfxFloat>* mTabWidths; // widths for each transformed string character
|
||||
PRInt32 mLength; // DOM string length
|
||||
gfxFloat mWordSpacing; // space for each whitespace char
|
||||
gfxFloat mLetterSpacing; // space for each letter
|
||||
gfxFloat mJustificationSpacing;
|
||||
gfxFloat mHyphenWidth;
|
||||
gfxFloat mOffsetFromBlockOriginForTabs;
|
||||
PRPackedBool mReflowing;
|
||||
};
|
||||
|
||||
PRUint32
|
||||
|
@ -1944,42 +1950,6 @@ PropertyProvider::ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength)
|
|||
return justifiableChars;
|
||||
}
|
||||
|
||||
PRUint8*
|
||||
PropertyProvider::ComputeTabSpaceCount(PRUint32 aOffset, PRUint32 aLength)
|
||||
{
|
||||
PRUint32 tabsEnd = mStart.GetSkippedOffset() + mTabSpaceCounts.Length();
|
||||
// We incrementally compute the tab space counts because it could be
|
||||
// inefficient to run ahead and compute space counts for tabs we don't need.
|
||||
// mTabSpaceCounts is an array of space counts whose first element is
|
||||
// the space count for the character at startOffset
|
||||
if (aOffset + aLength > tabsEnd) {
|
||||
PRUint32 column = mTabSpaceCounts.Length() ? mCurrentColumn : mFrame->GetColumn();
|
||||
PRInt32 count = aOffset + aLength - tabsEnd;
|
||||
nsSkipCharsRunIterator
|
||||
run(mStart, nsSkipCharsRunIterator::LENGTH_UNSKIPPED_ONLY, count);
|
||||
run.SetSkippedOffset(tabsEnd);
|
||||
while (run.NextRun()) {
|
||||
PRInt32 i;
|
||||
for (i = 0; i < run.GetRunLength(); ++i) {
|
||||
if (mFrag->CharAt(i + run.GetOriginalOffset()) == '\t') {
|
||||
PRInt32 spaces = 8 - column%8;
|
||||
column += spaces;
|
||||
// The tab itself counts as a space
|
||||
mTabSpaceCounts.AppendElement(spaces - 1);
|
||||
} else {
|
||||
if (mTextRun->IsClusterStart(i + run.GetSkippedOffset())) {
|
||||
++column;
|
||||
}
|
||||
mTabSpaceCounts.AppendElement(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
mCurrentColumn = column;
|
||||
}
|
||||
|
||||
return mTabSpaceCounts.Elements() + aOffset - mStart.GetSkippedOffset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the offset of the first character of the cluster containing aPos
|
||||
*/
|
||||
|
@ -2018,6 +1988,14 @@ static void FindClusterEnd(gfxTextRun* aTextRun, PRInt32 aOriginalEnd,
|
|||
void
|
||||
PropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength,
|
||||
Spacing* aSpacing)
|
||||
{
|
||||
GetSpacingInternal(aStart, aLength, aSpacing,
|
||||
(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) == 0);
|
||||
}
|
||||
|
||||
void
|
||||
PropertyProvider::GetSpacingInternal(PRUint32 aStart, PRUint32 aLength,
|
||||
Spacing* aSpacing, PRBool aIgnoreTabs)
|
||||
{
|
||||
NS_PRECONDITION(IsInBounds(mStart, mLength, aStart, aLength), "Range out of bounds");
|
||||
|
||||
|
@ -2059,16 +2037,12 @@ PropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength,
|
|||
}
|
||||
|
||||
// Now add tab spacing, if there is any
|
||||
if (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) {
|
||||
// ComputeTabSpaceCount() will tell us where spaces need to be inserted
|
||||
// for tabs, and how many.
|
||||
// ComputeTabSpaceCount takes transformed string offsets.
|
||||
PRUint8* tabSpaceList = ComputeTabSpaceCount(aStart, aLength);
|
||||
gfxFloat spaceWidth = mLetterSpacing + mWordSpacing +
|
||||
mTextRun->GetFontGroup()->GetFontAt(0)->GetMetrics().spaceWidth*mTextRun->GetAppUnitsPerDevUnit();
|
||||
for (index = 0; index < aLength; ++index) {
|
||||
PRInt32 tabSpaces = tabSpaceList[index];
|
||||
aSpacing[index].mAfter += spaceWidth*tabSpaces;
|
||||
if (!aIgnoreTabs) {
|
||||
gfxFloat* tabs = GetTabWidths(aStart, aLength);
|
||||
if (tabs) {
|
||||
for (index = 0; index < aLength; ++index) {
|
||||
aSpacing[index].mAfter += tabs[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2105,19 +2079,91 @@ PropertyProvider::GetSpacing(PRUint32 aStart, PRUint32 aLength,
|
|||
}
|
||||
}
|
||||
|
||||
PRUint32
|
||||
PropertyProvider::GetTabExpansionCount(PRUint32 aStart, PRUint32 aLength)
|
||||
static void TabWidthDestructor(void* aObject, nsIAtom* aProp, void* aValue,
|
||||
void* aData)
|
||||
{
|
||||
if (!(mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB))
|
||||
return 0;
|
||||
delete NS_STATIC_CAST(nsTArray<gfxFloat>*, aValue);
|
||||
}
|
||||
|
||||
PRUint8* spaces = ComputeTabSpaceCount(aStart, aLength);
|
||||
PRUint32 i;
|
||||
PRUint32 sum = 0;
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
sum += spaces[i];
|
||||
gfxFloat*
|
||||
PropertyProvider::GetTabWidths(PRUint32 aStart, PRUint32 aLength)
|
||||
{
|
||||
if (!mTabWidths) {
|
||||
if (!mReflowing) {
|
||||
mTabWidths = NS_STATIC_CAST(nsTArray<gfxFloat>*,
|
||||
mFrame->GetProperty(nsGkAtoms::tabWidthProperty));
|
||||
if (!mTabWidths) {
|
||||
NS_WARNING("We need precomputed tab widths, but they're not here...");
|
||||
return nsnull;
|
||||
}
|
||||
} else {
|
||||
nsAutoPtr<nsTArray<gfxFloat> > tabs(new nsTArray<gfxFloat>());
|
||||
if (!tabs)
|
||||
return nsnull;
|
||||
nsresult rv = mFrame->SetProperty(nsGkAtoms::tabWidthProperty, tabs,
|
||||
TabWidthDestructor, nsnull);
|
||||
if (NS_FAILED(rv))
|
||||
return nsnull;
|
||||
mTabWidths = tabs.forget();
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
|
||||
PRUint32 startOffset = mStart.GetSkippedOffset();
|
||||
PRUint32 tabsEnd = startOffset + mTabWidths->Length();
|
||||
if (tabsEnd < aStart + aLength) {
|
||||
if (!mReflowing) {
|
||||
NS_WARNING("We need precomputed tab widths, but we don't have enough...");
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
if (!mTabWidths->AppendElements(aStart + aLength - tabsEnd))
|
||||
return nsnull;
|
||||
|
||||
PRUint32 i;
|
||||
if (!mLineContainer) {
|
||||
NS_WARNING("Tabs encountered in a situation where we don't support tabbing");
|
||||
for (i = tabsEnd; i < aStart + aLength; ++i) {
|
||||
(*mTabWidths)[i - startOffset] = 0;
|
||||
}
|
||||
} else {
|
||||
gfxFloat tabWidth = NS_round(8*mTextRun->GetAppUnitsPerDevUnit()*
|
||||
GetFontMetrics(GetFontGroupForFrame(mLineContainer)).spaceWidth);
|
||||
|
||||
for (i = tabsEnd; i < aStart + aLength; ++i) {
|
||||
Spacing spacing;
|
||||
GetSpacingInternal(i, 1, &spacing, PR_TRUE);
|
||||
mOffsetFromBlockOriginForTabs += spacing.mBefore;
|
||||
|
||||
if (mTextRun->GetChar(i) != '\t') {
|
||||
(*mTabWidths)[i - startOffset] = 0;
|
||||
if (mTextRun->IsClusterStart(i)) {
|
||||
PRUint32 clusterEnd = i + 1;
|
||||
while (clusterEnd < mTextRun->GetLength() &&
|
||||
!mTextRun->IsClusterStart(clusterEnd)) {
|
||||
++clusterEnd;
|
||||
}
|
||||
mOffsetFromBlockOriginForTabs +=
|
||||
mTextRun->GetAdvanceWidth(i, clusterEnd - i, nsnull);
|
||||
}
|
||||
} else {
|
||||
// Advance mOffsetFromBlockOriginForTabs to the next multiple of tabWidth
|
||||
// Ensure that if it's just epsilon less than a multiple of tabWidth, we still
|
||||
// advance by tabWidth.
|
||||
static const double EPSILON = 0.000001;
|
||||
double nextTab = NS_ceil(mOffsetFromBlockOriginForTabs/tabWidth)*tabWidth;
|
||||
if (nextTab < mOffsetFromBlockOriginForTabs + EPSILON) {
|
||||
nextTab += tabWidth;
|
||||
}
|
||||
(*mTabWidths)[i - startOffset] = nextTab - mOffsetFromBlockOriginForTabs;
|
||||
mOffsetFromBlockOriginForTabs = nextTab;
|
||||
}
|
||||
|
||||
mOffsetFromBlockOriginForTabs += spacing.mAfter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mTabWidths->Elements() + aStart - startOffset;
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
|
@ -4132,10 +4178,10 @@ nsTextFrame::GetPointFromOffset(nsPresContext* aPresContext,
|
|||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
|
||||
PRBool inHint,
|
||||
PRInt32* outFrameContentOffset,
|
||||
nsIFrame **outChildFrame)
|
||||
nsTextFrame::GetChildFrameContainingOffset(PRInt32 aContentOffset,
|
||||
PRBool aHint,
|
||||
PRInt32* aOutOffset,
|
||||
nsIFrame**aOutFrame)
|
||||
{
|
||||
DEBUG_VERIFY_NOT_DIRTY(mState);
|
||||
#if 0 //XXXrbs disable due to bug 310227
|
||||
|
@ -4143,39 +4189,40 @@ nsTextFrame::GetChildFrameContainingOffset(PRInt32 inContentOffset,
|
|||
return NS_ERROR_UNEXPECTED;
|
||||
#endif
|
||||
|
||||
if (nsnull == outChildFrame)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
PRInt32 contentOffset = inContentOffset;
|
||||
|
||||
if (contentOffset != -1) //-1 signified the end of the current content
|
||||
contentOffset = inContentOffset - mContentOffset;
|
||||
NS_ASSERTION(aOutOffset && aOutFrame, "Bad out parameters");
|
||||
NS_ASSERTION(aContentOffset >= 0, "Negative content offset, existing code was very broken!");
|
||||
|
||||
if ((contentOffset > mContentLength) || ((contentOffset == mContentLength) && inHint) )
|
||||
{
|
||||
//this is not the frame we are looking for.
|
||||
nsIFrame* nextContinuation = GetNextContinuation();
|
||||
if (nextContinuation)
|
||||
{
|
||||
return nextContinuation->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
|
||||
nsTextFrame* f = this;
|
||||
if (aContentOffset >= mContentOffset) {
|
||||
while (PR_TRUE) {
|
||||
nsTextFrame* next = NS_STATIC_CAST(nsTextFrame*, f->GetNextContinuation());
|
||||
if (!next || aContentOffset < next->GetContentOffset())
|
||||
break;
|
||||
if (aContentOffset == next->GetContentOffset()) {
|
||||
if (aHint) {
|
||||
f = next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f = next;
|
||||
}
|
||||
else {
|
||||
if (contentOffset != mContentLength) //that condition was only for when there is a choice
|
||||
return NS_ERROR_FAILURE;
|
||||
} else {
|
||||
while (PR_TRUE) {
|
||||
nsTextFrame* prev = NS_STATIC_CAST(nsTextFrame*, f->GetPrevContinuation());
|
||||
if (!prev || aContentOffset > f->GetContentOffset())
|
||||
break;
|
||||
if (aContentOffset == f->GetContentOffset()) {
|
||||
if (!aHint) {
|
||||
f = prev;
|
||||
}
|
||||
break;
|
||||
}
|
||||
f = prev;
|
||||
}
|
||||
}
|
||||
|
||||
if (inContentOffset < mContentOffset) //could happen with floats!
|
||||
{
|
||||
*outChildFrame = GetPrevInFlow();
|
||||
if (*outChildFrame)
|
||||
return (*outChildFrame)->GetChildFrameContainingOffset(inContentOffset, inHint,
|
||||
outFrameContentOffset,outChildFrame);
|
||||
else
|
||||
return NS_OK; //this can't be the right thing to do?
|
||||
}
|
||||
|
||||
*outFrameContentOffset = contentOffset;
|
||||
*outChildFrame = this;
|
||||
*aOutOffset = aContentOffset - f->GetContentOffset();
|
||||
*aOutFrame = f;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4432,8 +4479,10 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
if (!mTextRun)
|
||||
return;
|
||||
|
||||
// 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,
|
||||
iter, GetInFlowContentLength());
|
||||
iter, GetInFlowContentLength(), nsnull, 0);
|
||||
|
||||
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
||||
PRUint32 start =
|
||||
|
@ -4518,8 +4567,10 @@ nsTextFrame::AddInlinePrefWidthForFlow(nsIRenderingContext *aRenderingContext,
|
|||
if (!mTextRun)
|
||||
return;
|
||||
|
||||
// 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,
|
||||
iter, GetInFlowContentLength());
|
||||
iter, GetInFlowContentLength(), nsnull, 0);
|
||||
|
||||
PRBool collapseWhitespace = !provider.GetStyleText()->WhiteSpaceIsSignificant();
|
||||
PRUint32 start =
|
||||
|
@ -4707,9 +4758,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
AddStateBits(TEXT_START_OF_LINE);
|
||||
}
|
||||
|
||||
PRInt32 column = lineLayout.GetColumn();
|
||||
mColumn = column;
|
||||
|
||||
// Layout dependent styles are a problem because we need to reconstruct
|
||||
// the gfxTextRun based on our layout.
|
||||
PRBool layoutDependentTextRun =
|
||||
|
@ -4748,6 +4796,7 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
newLineOffset = FindChar(frag, offset, length, '\n');
|
||||
if (newLineOffset >= 0) {
|
||||
length = newLineOffset + 1 - offset;
|
||||
newLineOffset -= mContentOffset;
|
||||
}
|
||||
} else {
|
||||
if (atStartOfLine) {
|
||||
|
@ -4770,7 +4819,10 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
iter.SetOriginalOffset(offset);
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length);
|
||||
nscoord xOffsetForTabs = (mTextRun->GetFlags() & nsTextFrameUtils::TEXT_HAS_TAB) ?
|
||||
lineLayout.GetCurrentFrameXDistanceFromBlock() : -1;
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, length,
|
||||
lineContainer, xOffsetForTabs);
|
||||
|
||||
PRUint32 transformedOffset = provider.GetStart().GetSkippedOffset();
|
||||
|
||||
|
@ -4822,17 +4874,22 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
}
|
||||
PRUint32 transformedLastBreak = 0;
|
||||
PRBool usedHyphenation;
|
||||
gfxFloat trimmedWidth = 0;
|
||||
gfxFloat availWidth = aReflowState.availableWidth;
|
||||
PRBool canTrimTrailingWhitespace = !textStyle->WhiteSpaceIsSignificant();
|
||||
PRUint32 transformedCharsFit =
|
||||
mTextRun->BreakAndMeasureText(transformedOffset, transformedLength,
|
||||
(GetStateBits() & TEXT_START_OF_LINE) != 0,
|
||||
aReflowState.availableWidth,
|
||||
availWidth,
|
||||
&provider, suppressInitialBreak,
|
||||
canTrimTrailingWhitespace ? &trimmedWidth : nsnull,
|
||||
&textMetrics, needTightBoundingBox,
|
||||
&usedHyphenation, &transformedLastBreak);
|
||||
end.SetSkippedOffset(transformedOffset + transformedCharsFit);
|
||||
PRInt32 charsFit = end.GetOriginalOffset() - offset;
|
||||
// That might have taken us beyond our assigned content range, so get back
|
||||
// in.
|
||||
// That might have taken us beyond our assigned content range (because
|
||||
// we might have advanced over some skipped chars that extend outside
|
||||
// this frame), so get back in.
|
||||
PRInt32 lastBreak = -1;
|
||||
if (charsFit >= limitLength) {
|
||||
charsFit = limitLength;
|
||||
|
@ -4856,41 +4913,27 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
AddStateBits(TEXT_HYPHEN_BREAK);
|
||||
}
|
||||
|
||||
// If it's only whitespace that didn't fit, then we will include
|
||||
// the whitespace in this frame, but we will eagerly trim all trailing
|
||||
// whitespace from our width calculations. Basically we do
|
||||
// TrimTrailingWhitespace early so that line layout sees that we fit on
|
||||
// the line.
|
||||
if (charsFit < length) {
|
||||
PRInt32 whitespaceCount =
|
||||
GetWhitespaceCount(frag, offset + charsFit, length - charsFit, 1);
|
||||
if (whitespaceCount > 0) {
|
||||
charsFit += whitespaceCount;
|
||||
|
||||
// Now trim all trailing whitespace from our width, including possibly
|
||||
// even whitespace that fitted (which could only happen with
|
||||
// white-space:pre-wrap, because when whitespace collapsing is in effect
|
||||
// there can only be one whitespace character rendered at the end of
|
||||
// the frame)
|
||||
AddStateBits(TEXT_TRIMMED_TRAILING_WHITESPACE);
|
||||
PRUint32 currentEnd = end.GetSkippedOffset();
|
||||
PRUint32 trimmedEnd = transformedOffset +
|
||||
GetLengthOfTrimmedText(frag, transformedOffset, currentEnd, &end);
|
||||
textMetrics.mAdvanceWidth -=
|
||||
mTextRun->GetAdvanceWidth(trimmedEnd,
|
||||
currentEnd - trimmedEnd, &provider);
|
||||
// XXX We don't adjust the bounding box in textMetrics. But we should! Do
|
||||
// we need to remeasure the text? Maybe the metrics returned by the textrun
|
||||
// should have a way of saying "no glyphs outside their font-boxes" so
|
||||
// we know we don't need to adjust the bounding box here and elsewhere...
|
||||
end.SetOriginalOffset(offset + charsFit);
|
||||
// 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 (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));
|
||||
}
|
||||
} else {
|
||||
|
||||
// All text fit. Record the last potential break, if there is one.
|
||||
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);
|
||||
}
|
||||
mContentLength = offset + charsFit - mContentOffset;
|
||||
|
||||
|
@ -4941,11 +4984,6 @@ nsTextFrame::Reflow(nsPresContext* aPresContext,
|
|||
// Clean up, update state
|
||||
/////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Update lineLayout state
|
||||
column += textMetrics.mClusterCount +
|
||||
provider.GetTabExpansionCount(provider.GetStart().GetSkippedOffset(),
|
||||
GetSkippedDistance(provider.GetStart(), end));
|
||||
lineLayout.SetColumn(column);
|
||||
lineLayout.SetUnderstandsWhiteSpace(PR_TRUE);
|
||||
PRBool endsInWhitespace = PR_FALSE;
|
||||
if (charsFit > 0) {
|
||||
|
@ -5050,7 +5088,10 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
|||
gfxSkipCharsIterator end = iter;
|
||||
PRUint32 endOffset = end.ConvertOriginalToSkipped(mContentOffset + mContentLength);
|
||||
if (trimmedEnd < endOffset) {
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, mContentLength);
|
||||
// We can't be dealing with tabs here ... they wouldn't be trimmed. So it's
|
||||
// OK to pass null for the line container.
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, mContentLength,
|
||||
nsnull, 0);
|
||||
delta = mTextRun->GetAdvanceWidth(trimmedEnd, endOffset - trimmedEnd, &provider);
|
||||
// non-compressed whitespace being skipped at end of line -> justifiable
|
||||
// XXX should we actually *count* justifiable characters that should be
|
||||
|
@ -5062,7 +5103,8 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext,
|
|||
if (!aLastCharIsJustifiable &&
|
||||
NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign) {
|
||||
// Check if any character in the last cluster is justifiable
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, mContentLength);
|
||||
PropertyProvider provider(mTextRun, textStyle, frag, this, iter, mContentLength,
|
||||
nsnull, 0);
|
||||
PRBool isCJK = IsChineseJapaneseLangGroup(this);
|
||||
gfxSkipCharsIterator justificationEnd(iter);
|
||||
provider.FindEndOfJustificationRange(&justificationEnd);
|
||||
|
|
|
@ -107,21 +107,19 @@ nsTextFrameUtils::TransformText(const PRUnichar* aText, PRUint32 aLength,
|
|||
PRUnichar* outputStart = aOutput;
|
||||
|
||||
if (!aCompressWhitespace) {
|
||||
// Convert tabs and formfeeds to spaces and skip discardables.
|
||||
// Skip discardables.
|
||||
PRUint32 i;
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
PRUnichar ch = *aText++;
|
||||
if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB|TEXT_WAS_TRANSFORMED;
|
||||
aSkipChars->KeepChar();
|
||||
*aOutput++ = ' ';
|
||||
} else if (IsDiscardable(ch, &flags)) {
|
||||
if (IsDiscardable(ch, &flags)) {
|
||||
aSkipChars->SkipChar();
|
||||
} else {
|
||||
aSkipChars->KeepChar();
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
} else if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
}
|
||||
|
@ -197,21 +195,19 @@ nsTextFrameUtils::TransformText(const PRUint8* aText, PRUint32 aLength,
|
|||
PRUint8* outputStart = aOutput;
|
||||
|
||||
if (!aCompressWhitespace) {
|
||||
// Convert tabs to spaces and skip discardables.
|
||||
// Skip discardables.
|
||||
PRUint32 i;
|
||||
for (i = 0; i < aLength; ++i) {
|
||||
PRUint8 ch = *aText++;
|
||||
if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB|TEXT_WAS_TRANSFORMED;
|
||||
aSkipChars->KeepChar();
|
||||
*aOutput++ = ' ';
|
||||
} else if (IsDiscardable(ch, &flags)) {
|
||||
if (IsDiscardable(ch, &flags)) {
|
||||
aSkipChars->SkipChar();
|
||||
} else {
|
||||
aSkipChars->KeepChar();
|
||||
if (ch == CH_NBSP) {
|
||||
ch = ' ';
|
||||
flags |= TEXT_WAS_TRANSFORMED;
|
||||
} else if (ch == '\t') {
|
||||
flags |= TEXT_HAS_TAB;
|
||||
}
|
||||
*aOutput++ = ch;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче