diff --git a/layout/generic/nsTextFrame.cpp b/layout/generic/nsTextFrame.cpp index b0c998825a1..591ac528ef4 100644 --- a/layout/generic/nsTextFrame.cpp +++ b/layout/generic/nsTextFrame.cpp @@ -3359,19 +3359,9 @@ struct SegmentData { PRInt32 ContentLen() {return PRInt32(mContentLen);} }; -#define IS_ODD_NUMBER(n) \ - (0 != ((n) & 0x1)) - -#define IS_EVEN_NUMBER(n) \ - (0 == ((n) & 0x1)) - -#define IS_32BIT_ALIGNED_PTR(p) \ - (0 == (long(p) & 0x3)) - struct TextRun { - PRUnichar mBufSpace[256]; - PRUnichar* mBuf; - PRInt32 mBufSize, mBufLength; + // Total number of characters and the accumulated content length + PRInt32 mTotalNumChars, mTotalContentLen; // Words and whitespace each count as a segment PRInt32 mNumSegments; @@ -3384,22 +3374,14 @@ struct TextRun { TextRun() { - mBuf = mBufSpace; - mBufSize = 256; - mBufLength = mNumSegments = 0; + Reset(); } - ~TextRun() - { - if (mBuf != mBufSpace) { - delete []mBuf; - } - } - void Reset() { - mBufLength = 0; mNumSegments = 0; + mTotalNumChars = 0; + mTotalContentLen = 0; } // Returns PR_TRUE if we're currently buffering text @@ -3408,87 +3390,15 @@ struct TextRun { return mNumSegments > 0; } - void GrowBuffer(PRInt32 aMinBufSize) - { - // Allocate a new buffer - do { - mBufSize += 100; - } while (mBufSize < aMinBufSize); - - PRUnichar* newBufSpace = new PRUnichar[mBufSize]; - memcpy(newBufSpace, mBuf, mBufLength * sizeof(PRUnichar)); - if (mBuf != mBufSpace) { - delete []mBuf; - } - mBuf = newBufSpace; - } - - void AddWhitespace(PRUnichar* aText, - PRInt32 aNumChars, - PRInt32 aContentLen) + void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace) { NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow"); - NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer"); - // See if we have room in the buffer - PRInt32 newBufLength = mBufLength + aNumChars; - if (newBufLength > mBufSize) { - GrowBuffer(newBufLength); - } - - if (1 == aNumChars) { - mBuf[mBufLength] = *aText; - } else { - ::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*)); - } - mBufLength = newBufLength; - - mBreaks[mNumSegments] = mBufLength; - mSegments[mNumSegments].mIsWhitespace = PR_TRUE; - mSegments[mNumSegments].mContentLen = PRUint32(aContentLen); - if (mNumSegments > 0) { - mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen(); - } - mNumSegments++; - } - - void AddWord(PRUnichar* aText, - PRInt32 aNumChars, - PRInt32 aContentLen) - { - NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow"); - NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer"); - - // See if we have room in the buffer - PRInt32 newBufLength = mBufLength + aNumChars; - if (newBufLength > mBufSize) { - GrowBuffer(newBufLength); - } - - if (IS_EVEN_NUMBER(mBufLength)) { - // We have a small amount of text (a single word) and we're at a point - // in our buffer that is 32-bit aligned so copy 32-bit chunks at a time - int* dest = (int*)&mBuf[mBufLength]; - int* src = (int*)aText; - for (int i = aNumChars / 2; i > 0; i--) { - *dest++ = *src++; - } - if (IS_ODD_NUMBER(aNumChars)) { - // Copy the one remaining character - *((PRUnichar*)dest) = *((PRUnichar*)src); - } - - } else { - ::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*)); - } - mBufLength = newBufLength; - - mBreaks[mNumSegments] = mBufLength; - mSegments[mNumSegments].mIsWhitespace = PR_FALSE; - mSegments[mNumSegments].mContentLen = PRUint32(aContentLen); - if (mNumSegments > 0) { - mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen(); - } + mTotalNumChars += aNumChars; + mBreaks[mNumSegments] = mTotalNumChars; + mSegments[mNumSegments].mIsWhitespace = aIsWhitespace; + mTotalContentLen += aContentLen; + mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen); mNumSegments++; } }; @@ -3512,6 +3422,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, nscoord prevMaxWordWidth = 0; PRInt32 lastWordLen = 0; PRInt32 lastWordWidth = 0; + PRUnichar* lastWordPtr = nsnull; PRBool textStartsWithNBSP = PR_FALSE; PRBool endsInWhitespace = PR_FALSE; PRBool endsInNewline = PR_FALSE; @@ -3535,7 +3446,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Get next word/whitespace from the text PRBool isWhitespace; PRInt32 wordLen, contentLen; - PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace); + PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace, + textRun.mNumSegments == 0); if (nsnull == bp) { if (textRun.IsBuffering()) { // Measure the remaining text @@ -3550,6 +3462,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } } lastWordLen = wordLen; + lastWordPtr = bp; aTextData.mInWord = PR_FALSE; // Measure the word/whitespace @@ -3578,7 +3491,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, width = aTs.mSpaceWidth * wordLen; } else if (textRun.IsBuffering()) { - textRun.AddWhitespace(bp, wordLen, contentLen); + // Add a whitespace segment + textRun.AddSegment(wordLen, contentLen, PR_TRUE); continue; } else { @@ -3627,10 +3541,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, if (aTextData.mMeasureText) { if (measureTextRuns && !justDidFirstLetter) { // Add another word to the text run - textRun.AddWord(bp, wordLen, contentLen); + textRun.AddSegment(wordLen, contentLen, PR_FALSE); // See if we should measure the text - if ((textRun.mBufLength >= estimatedNumChars) || + if ((textRun.mTotalNumChars >= estimatedNumChars) || (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) { goto MeasureTextRun; } @@ -3688,7 +3602,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, MeasureTextRun: #ifdef _WIN32 PRInt32 numCharsFit; - aReflowState.rendContext->GetWidth(textRun.mBuf, textRun.mBufLength, + aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), textRun.mTotalNumChars, maxWidth - aTextData.mX, textRun.mBreaks, textRun.mNumSegments, width, numCharsFit); @@ -3700,7 +3614,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Find the index of the last segment that fit PRInt32 lastSegment = textRun.mNumSegments - 1; - if (textRun.mBufLength != numCharsFit) { + if (numCharsFit != textRun.mTotalNumChars) { for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ; NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment"); } @@ -3719,15 +3633,18 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, column += numCharsFit; aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen(); endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace(); + // Since we measure multiple words we don't know what the last word + // width is + lastWordWidth = -1; // If all the text didn't fit, then we're done - if (textRun.mBufLength != numCharsFit) { + if (numCharsFit != textRun.mTotalNumChars) { break; } if (nsnull == bp) { - // No more text so we're all finished. Advance the offset in case we - // just consumed a bunch of discarded characters + // No more text so we're all finished. Advance the offset in case the last + // call to GetNextWord() discarded characters aTextData.mOffset += contentLen; break; } @@ -3735,7 +3652,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Reset the number of text run segments textRun.Reset(); - // Estimate the number of characters we think will fit + // Estimate the remaining number of characters we think will fit estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth; estimatedNumChars += estimatedNumChars / 20; #else @@ -3800,26 +3717,27 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, ListTag(stdout, next); printf("\n"); #endif - PRUnichar* pWordBuf = aTx.GetWordBuffer(); - PRUint32 wordBufLen = aTx.GetWordBufferLength(); + PRUnichar* pWordBuf = lastWordPtr; + PRUint32 wordBufLen = aTx.GetWordBufferLength() - + (lastWordPtr - aTx.GetWordBuffer()); // Look ahead in the text-run and compute the final word // width, taking into account any style changes and stopping // at the first breakable point. - if (!aTextData.mMeasureText) { - // We didn't measure any text so we don't know lastWordWidth. - // We have to compute it now + if (!aTextData.mMeasureText || (lastWordWidth == -1)) { + // We either didn't measure any text or we measured multiple words + // at once so either way we don't know lastWordWidth. We'll have to + // compute it now if (prevOffset == startingOffset) { // There's only one word, so we don't have to measure after all lastWordWidth = aTextData.mX; } else if (aTs.mSmallCaps) { - MeasureSmallCapsText(aReflowState, aTs, aTx.GetWordBuffer(), + MeasureSmallCapsText(aReflowState, aTs, pWordBuf, lastWordLen, &lastWordWidth); } else { - aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), - lastWordLen, lastWordWidth); + aReflowState.rendContext->GetWidth(pWordBuf, lastWordLen, lastWordWidth); if (aTs.mLetterSpacing) { lastWordWidth += aTs.mLetterSpacing * lastWordLen; } diff --git a/layout/generic/nsTextTransformer.cpp b/layout/generic/nsTextTransformer.cpp index 466a4e6a99c..5ddb1e6b98b 100644 --- a/layout/generic/nsTextTransformer.cpp +++ b/layout/generic/nsTextTransformer.cpp @@ -122,7 +122,8 @@ nsTextTransformer::nsTextTransformer(nsILineBreaker* aLineBreaker, mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE), mMode(eNormal), mLineBreaker(aLineBreaker), - mWordBreaker(aWordBreaker) + mWordBreaker(aWordBreaker), + mBufferPos(0) { MOZ_COUNT_CTOR(nsTextTransformer); @@ -196,7 +197,7 @@ nsTextTransformer::ScanNormalWhiteSpace_F() } } - mTransformBuf.mBuffer[0] = ' '; + mTransformBuf.mBuffer[mBufferPos++] = ' '; return offset; } @@ -207,11 +208,14 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; + const unsigned char* cp = (const unsigned char*)frag->Get1b(); + cp += offset; for (; offset < fragLen; offset++) { - PRUnichar ch = frag->CharAt(offset); + PRUnichar ch = *cp++; if (XP_IS_SPACE(ch)) { break; } @@ -234,9 +238,10 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -254,7 +259,7 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, if (CH_NBSP == firstChar) { firstChar = ' '; } - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; if (firstChar > MAX_UNIBYTE) mHasMultibyte = PR_TRUE; // Only evaluate complex breaking logic if there are more characters @@ -264,12 +269,10 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, const PRUnichar* cp = cp0 + offset; PRBool breakBetween = PR_FALSE; if (aForLineBreak) { - mLineBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1, - cp, (fragLen-offset), &breakBetween); + mLineBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween); } else { - mWordBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1, - cp, (fragLen-offset), &breakBetween); + mWordBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween); } if (!breakBetween) { @@ -284,16 +287,20 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, } numChars = (PRInt32) (next - (PRUint32) offset) + 1; - // Grow buffer before copying - nsresult rv = mTransformBuf.GrowTo(numChars); + // Since we know the number of characters we're adding grow the buffer + // now before we start copying + nsresult rv = mTransformBuf.GrowTo(mBufferPos + numChars); if (NS_FAILED(rv)) { - numChars = mTransformBuf.GetBufferLength(); + numChars = mTransformBuf.GetBufferLength() - mBufferPos; } + offset += numChars - 1; + // 1. convert nbsp into space - // 2. check mHasMultibyte flag - // 3. copy buffer - PRUnichar* bp = mTransformBuf.GetBuffer() + 1; + // 2. check for discarded characters + // 3. check mHasMultibyte flag + // 4. copy buffer + PRUnichar* bp = &mTransformBuf.mBuffer[mBufferPos]; const PRUnichar* end = cp + numChars - 1; while (cp < end) { PRUnichar ch = *cp++; @@ -302,15 +309,13 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, } else if (IS_DISCARDED(ch) || (ch == 0x0a) || (ch == 0x0d)) { // Strip discarded characters from the transformed output + numChars--; continue; } if (ch > MAX_UNIBYTE) mHasMultibyte = PR_TRUE; *bp++ = ch; + mBufferPos++; } - - // Recompute offset and numChars in case we stripped something - offset += numChars - 1; - numChars = bp - mTransformBuf.GetBuffer(); } } @@ -325,10 +330,13 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; for (; offset < fragLen; offset++) { + // This function is used for both Unicode and ascii strings so don't + // make any assumptions about what kind of data it is PRUnichar ch = frag->CharAt(offset); if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) { if (IS_DISCARDED(ch)) { @@ -348,9 +356,10 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ' '; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -361,10 +370,13 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; for (; offset < fragLen; offset++) { + // This function is used for both Unicode and ascii strings so don't + // make any assumptions about what kind of data it is PRUnichar ch = frag->CharAt(offset); if ((ch == '\t') || (ch == '\n')) { break; @@ -387,9 +399,10 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -398,10 +411,11 @@ PRInt32 nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen) { const nsTextFragment* frag = mFrag; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); const unsigned char* cp = (const unsigned char*) frag->Get1b(); const unsigned char* end = cp + frag->GetLength(); + PRInt32 prevBufferPos = mBufferPos; cp += mOffset; while (cp < end) { @@ -428,9 +442,10 @@ nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return cp - ((const unsigned char*)frag->Get1b()); } @@ -441,6 +456,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, PRInt32* aWordLenResult, PRInt32* aContentLenResult, PRBool* aIsWhiteSpaceResult, + PRBool aResetTransformBuf, PRBool aForLineBreak) { const nsTextFragment* frag = mFrag; @@ -449,6 +465,14 @@ nsTextTransformer::GetNextWord(PRBool aInWord, PRInt32 wordLen = 0; PRBool isWhitespace = PR_FALSE; PRUnichar* result = nsnull; + PRBool prevBufferPos; + + // See if we should reset the current buffer position back to the + // beginning of the buffer + if (aResetTransformBuf) { + mBufferPos = 0; + } + prevBufferPos = mBufferPos; // Fix word breaking problem w/ PREFORMAT and PREWRAP // for word breaking, we should really go to the normal code @@ -482,7 +506,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, case ePreformatted: if (('\n' == firstChar) || ('\t' == firstChar)) { - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; offset++; wordLen = 1; isWhitespace = PR_TRUE; @@ -498,7 +522,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, case ePreWrap: if (XP_IS_SPACE(firstChar)) { if (('\n' == firstChar) || ('\t' == firstChar)) { - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; offset++; wordLen = 1; } @@ -515,7 +539,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, } break; } - result = mTransformBuf.GetBuffer(); + result = &mTransformBuf.mBuffer[prevBufferPos]; if (!isWhitespace) { switch (mTextTransform) { diff --git a/layout/generic/nsTextTransformer.h b/layout/generic/nsTextTransformer.h index f8a987daeea..dee2b532758 100644 --- a/layout/generic/nsTextTransformer.h +++ b/layout/generic/nsTextTransformer.h @@ -35,7 +35,7 @@ class nsIWordBreaker; #define CH_NBSP 160 #define CH_SHY 173 -#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 100 +#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 256 // A growable text buffer that tries to avoid using malloc by having a // builtin buffer. Ideally used as an automatic variable. @@ -67,6 +67,9 @@ public: *
  • capitalization *
  • lowercasing *
  • uppercasing + *
  • ascii to Unicode + *
  • discarded characters + *
  • conversion of   that is not part of whitespace into a space * * * Note that no transformations are applied that would impact word @@ -85,9 +88,8 @@ public: ~nsTextTransformer(); /** - * Initialize the text transform. This is when the transformation - * occurs. Subsequent calls to GetTransformedTextFor will just - * return the result of the single transformation. + * Initialize the text transform. Use GetNextWord() and GetPrevWord() + * to iterate the text */ nsresult Init(nsIFrame* aFrame, nsIContent* aContent, @@ -97,10 +99,22 @@ public: return mFrag ? mFrag->GetLength() : 0; } + /** + * Iterates the next word in the text fragment. + * + * Returns a pointer to the word, the number of characters in the word, the + * content length of the word, and whether it is whitespace. The content + * length can be greater than the word length if whitespace compression + * occured or if characters were discarded + * + * The default behavior is to reset the transform buffer to the beginning, + * but you can choose to not reste it and buffer across multiple words + */ PRUnichar* GetNextWord(PRBool aInWord, PRInt32* aWordLenResult, PRInt32* aContentLenResult, PRBool* aIsWhitespaceResult, + PRBool aResetTransformBuf = PR_TRUE, PRBool aForLineBreak = PR_TRUE); PRUnichar* GetPrevWord(PRBool aInWord, @@ -169,6 +183,10 @@ protected: // GetPrevWord nsAutoTextBuffer mTransformBuf; + // Our current position within the buffer. Used when iterating the next + // word, because we may be requested to buffer across multiple words + PRInt32 mBufferPos; + #ifdef DEBUG static void SelfTest(nsILineBreaker* aLineBreaker, nsIWordBreaker* aWordBreaker); diff --git a/layout/html/base/src/nsTextFrame.cpp b/layout/html/base/src/nsTextFrame.cpp index b0c998825a1..591ac528ef4 100644 --- a/layout/html/base/src/nsTextFrame.cpp +++ b/layout/html/base/src/nsTextFrame.cpp @@ -3359,19 +3359,9 @@ struct SegmentData { PRInt32 ContentLen() {return PRInt32(mContentLen);} }; -#define IS_ODD_NUMBER(n) \ - (0 != ((n) & 0x1)) - -#define IS_EVEN_NUMBER(n) \ - (0 == ((n) & 0x1)) - -#define IS_32BIT_ALIGNED_PTR(p) \ - (0 == (long(p) & 0x3)) - struct TextRun { - PRUnichar mBufSpace[256]; - PRUnichar* mBuf; - PRInt32 mBufSize, mBufLength; + // Total number of characters and the accumulated content length + PRInt32 mTotalNumChars, mTotalContentLen; // Words and whitespace each count as a segment PRInt32 mNumSegments; @@ -3384,22 +3374,14 @@ struct TextRun { TextRun() { - mBuf = mBufSpace; - mBufSize = 256; - mBufLength = mNumSegments = 0; + Reset(); } - ~TextRun() - { - if (mBuf != mBufSpace) { - delete []mBuf; - } - } - void Reset() { - mBufLength = 0; mNumSegments = 0; + mTotalNumChars = 0; + mTotalContentLen = 0; } // Returns PR_TRUE if we're currently buffering text @@ -3408,87 +3390,15 @@ struct TextRun { return mNumSegments > 0; } - void GrowBuffer(PRInt32 aMinBufSize) - { - // Allocate a new buffer - do { - mBufSize += 100; - } while (mBufSize < aMinBufSize); - - PRUnichar* newBufSpace = new PRUnichar[mBufSize]; - memcpy(newBufSpace, mBuf, mBufLength * sizeof(PRUnichar)); - if (mBuf != mBufSpace) { - delete []mBuf; - } - mBuf = newBufSpace; - } - - void AddWhitespace(PRUnichar* aText, - PRInt32 aNumChars, - PRInt32 aContentLen) + void AddSegment(PRInt32 aNumChars, PRInt32 aContentLen, PRBool aIsWhitespace) { NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow"); - NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer"); - // See if we have room in the buffer - PRInt32 newBufLength = mBufLength + aNumChars; - if (newBufLength > mBufSize) { - GrowBuffer(newBufLength); - } - - if (1 == aNumChars) { - mBuf[mBufLength] = *aText; - } else { - ::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*)); - } - mBufLength = newBufLength; - - mBreaks[mNumSegments] = mBufLength; - mSegments[mNumSegments].mIsWhitespace = PR_TRUE; - mSegments[mNumSegments].mContentLen = PRUint32(aContentLen); - if (mNumSegments > 0) { - mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen(); - } - mNumSegments++; - } - - void AddWord(PRUnichar* aText, - PRInt32 aNumChars, - PRInt32 aContentLen) - { - NS_PRECONDITION(mNumSegments < TEXT_MAX_NUM_SEGMENTS, "segment overflow"); - NS_PRECONDITION(IS_32BIT_ALIGNED_PTR(aText), "unexpected alignment of text pointer"); - - // See if we have room in the buffer - PRInt32 newBufLength = mBufLength + aNumChars; - if (newBufLength > mBufSize) { - GrowBuffer(newBufLength); - } - - if (IS_EVEN_NUMBER(mBufLength)) { - // We have a small amount of text (a single word) and we're at a point - // in our buffer that is 32-bit aligned so copy 32-bit chunks at a time - int* dest = (int*)&mBuf[mBufLength]; - int* src = (int*)aText; - for (int i = aNumChars / 2; i > 0; i--) { - *dest++ = *src++; - } - if (IS_ODD_NUMBER(aNumChars)) { - // Copy the one remaining character - *((PRUnichar*)dest) = *((PRUnichar*)src); - } - - } else { - ::memcpy(&mBuf[mBufLength], aText, aNumChars * sizeof(PRUnichar*)); - } - mBufLength = newBufLength; - - mBreaks[mNumSegments] = mBufLength; - mSegments[mNumSegments].mIsWhitespace = PR_FALSE; - mSegments[mNumSegments].mContentLen = PRUint32(aContentLen); - if (mNumSegments > 0) { - mSegments[mNumSegments].mContentLen += mSegments[mNumSegments - 1].ContentLen(); - } + mTotalNumChars += aNumChars; + mBreaks[mNumSegments] = mTotalNumChars; + mSegments[mNumSegments].mIsWhitespace = aIsWhitespace; + mTotalContentLen += aContentLen; + mSegments[mNumSegments].mContentLen = PRUint32(mTotalContentLen); mNumSegments++; } }; @@ -3512,6 +3422,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, nscoord prevMaxWordWidth = 0; PRInt32 lastWordLen = 0; PRInt32 lastWordWidth = 0; + PRUnichar* lastWordPtr = nsnull; PRBool textStartsWithNBSP = PR_FALSE; PRBool endsInWhitespace = PR_FALSE; PRBool endsInNewline = PR_FALSE; @@ -3535,7 +3446,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Get next word/whitespace from the text PRBool isWhitespace; PRInt32 wordLen, contentLen; - PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace); + PRUnichar* bp = aTx.GetNextWord(aTextData.mInWord, &wordLen, &contentLen, &isWhitespace, + textRun.mNumSegments == 0); if (nsnull == bp) { if (textRun.IsBuffering()) { // Measure the remaining text @@ -3550,6 +3462,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, } } lastWordLen = wordLen; + lastWordPtr = bp; aTextData.mInWord = PR_FALSE; // Measure the word/whitespace @@ -3578,7 +3491,8 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, width = aTs.mSpaceWidth * wordLen; } else if (textRun.IsBuffering()) { - textRun.AddWhitespace(bp, wordLen, contentLen); + // Add a whitespace segment + textRun.AddSegment(wordLen, contentLen, PR_TRUE); continue; } else { @@ -3627,10 +3541,10 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, if (aTextData.mMeasureText) { if (measureTextRuns && !justDidFirstLetter) { // Add another word to the text run - textRun.AddWord(bp, wordLen, contentLen); + textRun.AddSegment(wordLen, contentLen, PR_FALSE); // See if we should measure the text - if ((textRun.mBufLength >= estimatedNumChars) || + if ((textRun.mTotalNumChars >= estimatedNumChars) || (textRun.mNumSegments >= (TEXT_MAX_NUM_SEGMENTS - 1))) { goto MeasureTextRun; } @@ -3688,7 +3602,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, MeasureTextRun: #ifdef _WIN32 PRInt32 numCharsFit; - aReflowState.rendContext->GetWidth(textRun.mBuf, textRun.mBufLength, + aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), textRun.mTotalNumChars, maxWidth - aTextData.mX, textRun.mBreaks, textRun.mNumSegments, width, numCharsFit); @@ -3700,7 +3614,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Find the index of the last segment that fit PRInt32 lastSegment = textRun.mNumSegments - 1; - if (textRun.mBufLength != numCharsFit) { + if (numCharsFit != textRun.mTotalNumChars) { for (lastSegment = 0; textRun.mBreaks[lastSegment] < numCharsFit; lastSegment++) ; NS_ASSERTION(lastSegment < textRun.mNumSegments, "failed to find segment"); } @@ -3719,15 +3633,18 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, column += numCharsFit; aTextData.mOffset += textRun.mSegments[lastSegment].ContentLen(); endsInWhitespace = textRun.mSegments[lastSegment].IsWhitespace(); + // Since we measure multiple words we don't know what the last word + // width is + lastWordWidth = -1; // If all the text didn't fit, then we're done - if (textRun.mBufLength != numCharsFit) { + if (numCharsFit != textRun.mTotalNumChars) { break; } if (nsnull == bp) { - // No more text so we're all finished. Advance the offset in case we - // just consumed a bunch of discarded characters + // No more text so we're all finished. Advance the offset in case the last + // call to GetNextWord() discarded characters aTextData.mOffset += contentLen; break; } @@ -3735,7 +3652,7 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, // Reset the number of text run segments textRun.Reset(); - // Estimate the number of characters we think will fit + // Estimate the remaining number of characters we think will fit estimatedNumChars = (maxWidth - aTextData.mX) / aTs.mAveCharWidth; estimatedNumChars += estimatedNumChars / 20; #else @@ -3800,26 +3717,27 @@ nsTextFrame::MeasureText(nsIPresContext* aPresContext, ListTag(stdout, next); printf("\n"); #endif - PRUnichar* pWordBuf = aTx.GetWordBuffer(); - PRUint32 wordBufLen = aTx.GetWordBufferLength(); + PRUnichar* pWordBuf = lastWordPtr; + PRUint32 wordBufLen = aTx.GetWordBufferLength() - + (lastWordPtr - aTx.GetWordBuffer()); // Look ahead in the text-run and compute the final word // width, taking into account any style changes and stopping // at the first breakable point. - if (!aTextData.mMeasureText) { - // We didn't measure any text so we don't know lastWordWidth. - // We have to compute it now + if (!aTextData.mMeasureText || (lastWordWidth == -1)) { + // We either didn't measure any text or we measured multiple words + // at once so either way we don't know lastWordWidth. We'll have to + // compute it now if (prevOffset == startingOffset) { // There's only one word, so we don't have to measure after all lastWordWidth = aTextData.mX; } else if (aTs.mSmallCaps) { - MeasureSmallCapsText(aReflowState, aTs, aTx.GetWordBuffer(), + MeasureSmallCapsText(aReflowState, aTs, pWordBuf, lastWordLen, &lastWordWidth); } else { - aReflowState.rendContext->GetWidth(aTx.GetWordBuffer(), - lastWordLen, lastWordWidth); + aReflowState.rendContext->GetWidth(pWordBuf, lastWordLen, lastWordWidth); if (aTs.mLetterSpacing) { lastWordWidth += aTs.mLetterSpacing * lastWordLen; } diff --git a/layout/html/base/src/nsTextTransformer.cpp b/layout/html/base/src/nsTextTransformer.cpp index 466a4e6a99c..5ddb1e6b98b 100644 --- a/layout/html/base/src/nsTextTransformer.cpp +++ b/layout/html/base/src/nsTextTransformer.cpp @@ -122,7 +122,8 @@ nsTextTransformer::nsTextTransformer(nsILineBreaker* aLineBreaker, mTextTransform(NS_STYLE_TEXT_TRANSFORM_NONE), mMode(eNormal), mLineBreaker(aLineBreaker), - mWordBreaker(aWordBreaker) + mWordBreaker(aWordBreaker), + mBufferPos(0) { MOZ_COUNT_CTOR(nsTextTransformer); @@ -196,7 +197,7 @@ nsTextTransformer::ScanNormalWhiteSpace_F() } } - mTransformBuf.mBuffer[0] = ' '; + mTransformBuf.mBuffer[mBufferPos++] = ' '; return offset; } @@ -207,11 +208,14 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; + const unsigned char* cp = (const unsigned char*)frag->Get1b(); + cp += offset; for (; offset < fragLen; offset++) { - PRUnichar ch = frag->CharAt(offset); + PRUnichar ch = *cp++; if (XP_IS_SPACE(ch)) { break; } @@ -234,9 +238,10 @@ nsTextTransformer::ScanNormalAsciiText_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -254,7 +259,7 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, if (CH_NBSP == firstChar) { firstChar = ' '; } - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; if (firstChar > MAX_UNIBYTE) mHasMultibyte = PR_TRUE; // Only evaluate complex breaking logic if there are more characters @@ -264,12 +269,10 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, const PRUnichar* cp = cp0 + offset; PRBool breakBetween = PR_FALSE; if (aForLineBreak) { - mLineBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1, - cp, (fragLen-offset), &breakBetween); + mLineBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween); } else { - mWordBreaker->BreakInBetween(mTransformBuf.GetBuffer(), 1, - cp, (fragLen-offset), &breakBetween); + mWordBreaker->BreakInBetween(&firstChar, 1, cp, (fragLen-offset), &breakBetween); } if (!breakBetween) { @@ -284,16 +287,20 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, } numChars = (PRInt32) (next - (PRUint32) offset) + 1; - // Grow buffer before copying - nsresult rv = mTransformBuf.GrowTo(numChars); + // Since we know the number of characters we're adding grow the buffer + // now before we start copying + nsresult rv = mTransformBuf.GrowTo(mBufferPos + numChars); if (NS_FAILED(rv)) { - numChars = mTransformBuf.GetBufferLength(); + numChars = mTransformBuf.GetBufferLength() - mBufferPos; } + offset += numChars - 1; + // 1. convert nbsp into space - // 2. check mHasMultibyte flag - // 3. copy buffer - PRUnichar* bp = mTransformBuf.GetBuffer() + 1; + // 2. check for discarded characters + // 3. check mHasMultibyte flag + // 4. copy buffer + PRUnichar* bp = &mTransformBuf.mBuffer[mBufferPos]; const PRUnichar* end = cp + numChars - 1; while (cp < end) { PRUnichar ch = *cp++; @@ -302,15 +309,13 @@ nsTextTransformer::ScanNormalUnicodeText_F(PRBool aForLineBreak, } else if (IS_DISCARDED(ch) || (ch == 0x0a) || (ch == 0x0d)) { // Strip discarded characters from the transformed output + numChars--; continue; } if (ch > MAX_UNIBYTE) mHasMultibyte = PR_TRUE; *bp++ = ch; + mBufferPos++; } - - // Recompute offset and numChars in case we stripped something - offset += numChars - 1; - numChars = bp - mTransformBuf.GetBuffer(); } } @@ -325,10 +330,13 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; for (; offset < fragLen; offset++) { + // This function is used for both Unicode and ascii strings so don't + // make any assumptions about what kind of data it is PRUnichar ch = frag->CharAt(offset); if (!XP_IS_SPACE(ch) || (ch == '\t') || (ch == '\n')) { if (IS_DISCARDED(ch)) { @@ -348,9 +356,10 @@ nsTextTransformer::ScanPreWrapWhiteSpace_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ' '; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -361,10 +370,13 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen) const nsTextFragment* frag = mFrag; PRInt32 fragLen = frag->GetLength(); PRInt32 offset = mOffset; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); + PRInt32 prevBufferPos = mBufferPos; for (; offset < fragLen; offset++) { + // This function is used for both Unicode and ascii strings so don't + // make any assumptions about what kind of data it is PRUnichar ch = frag->CharAt(offset); if ((ch == '\t') || (ch == '\n')) { break; @@ -387,9 +399,10 @@ nsTextTransformer::ScanPreData_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return offset; } @@ -398,10 +411,11 @@ PRInt32 nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen) { const nsTextFragment* frag = mFrag; - PRUnichar* bp = mTransformBuf.GetBuffer(); + PRUnichar* bp = mTransformBuf.GetBuffer() + mBufferPos; PRUnichar* endbp = mTransformBuf.GetBufferEnd(); const unsigned char* cp = (const unsigned char*) frag->Get1b(); const unsigned char* end = cp + frag->GetLength(); + PRInt32 prevBufferPos = mBufferPos; cp += mOffset; while (cp < end) { @@ -428,9 +442,10 @@ nsTextTransformer::ScanPreAsciiData_F(PRInt32* aWordLen) endbp = mTransformBuf.GetBufferEnd(); } *bp++ = ch; + mBufferPos++; } - *aWordLen = bp - mTransformBuf.GetBuffer(); + *aWordLen = mBufferPos - prevBufferPos; return cp - ((const unsigned char*)frag->Get1b()); } @@ -441,6 +456,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, PRInt32* aWordLenResult, PRInt32* aContentLenResult, PRBool* aIsWhiteSpaceResult, + PRBool aResetTransformBuf, PRBool aForLineBreak) { const nsTextFragment* frag = mFrag; @@ -449,6 +465,14 @@ nsTextTransformer::GetNextWord(PRBool aInWord, PRInt32 wordLen = 0; PRBool isWhitespace = PR_FALSE; PRUnichar* result = nsnull; + PRBool prevBufferPos; + + // See if we should reset the current buffer position back to the + // beginning of the buffer + if (aResetTransformBuf) { + mBufferPos = 0; + } + prevBufferPos = mBufferPos; // Fix word breaking problem w/ PREFORMAT and PREWRAP // for word breaking, we should really go to the normal code @@ -482,7 +506,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, case ePreformatted: if (('\n' == firstChar) || ('\t' == firstChar)) { - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; offset++; wordLen = 1; isWhitespace = PR_TRUE; @@ -498,7 +522,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, case ePreWrap: if (XP_IS_SPACE(firstChar)) { if (('\n' == firstChar) || ('\t' == firstChar)) { - mTransformBuf.mBuffer[0] = firstChar; + mTransformBuf.mBuffer[mBufferPos++] = firstChar; offset++; wordLen = 1; } @@ -515,7 +539,7 @@ nsTextTransformer::GetNextWord(PRBool aInWord, } break; } - result = mTransformBuf.GetBuffer(); + result = &mTransformBuf.mBuffer[prevBufferPos]; if (!isWhitespace) { switch (mTextTransform) { diff --git a/layout/html/base/src/nsTextTransformer.h b/layout/html/base/src/nsTextTransformer.h index f8a987daeea..dee2b532758 100644 --- a/layout/html/base/src/nsTextTransformer.h +++ b/layout/html/base/src/nsTextTransformer.h @@ -35,7 +35,7 @@ class nsIWordBreaker; #define CH_NBSP 160 #define CH_SHY 173 -#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 100 +#define NS_TEXT_TRANSFORMER_AUTO_WORD_BUF_SIZE 256 // A growable text buffer that tries to avoid using malloc by having a // builtin buffer. Ideally used as an automatic variable. @@ -67,6 +67,9 @@ public: *
  • capitalization *
  • lowercasing *
  • uppercasing + *
  • ascii to Unicode + *
  • discarded characters + *
  • conversion of   that is not part of whitespace into a space * * * Note that no transformations are applied that would impact word @@ -85,9 +88,8 @@ public: ~nsTextTransformer(); /** - * Initialize the text transform. This is when the transformation - * occurs. Subsequent calls to GetTransformedTextFor will just - * return the result of the single transformation. + * Initialize the text transform. Use GetNextWord() and GetPrevWord() + * to iterate the text */ nsresult Init(nsIFrame* aFrame, nsIContent* aContent, @@ -97,10 +99,22 @@ public: return mFrag ? mFrag->GetLength() : 0; } + /** + * Iterates the next word in the text fragment. + * + * Returns a pointer to the word, the number of characters in the word, the + * content length of the word, and whether it is whitespace. The content + * length can be greater than the word length if whitespace compression + * occured or if characters were discarded + * + * The default behavior is to reset the transform buffer to the beginning, + * but you can choose to not reste it and buffer across multiple words + */ PRUnichar* GetNextWord(PRBool aInWord, PRInt32* aWordLenResult, PRInt32* aContentLenResult, PRBool* aIsWhitespaceResult, + PRBool aResetTransformBuf = PR_TRUE, PRBool aForLineBreak = PR_TRUE); PRUnichar* GetPrevWord(PRBool aInWord, @@ -169,6 +183,10 @@ protected: // GetPrevWord nsAutoTextBuffer mTransformBuf; + // Our current position within the buffer. Used when iterating the next + // word, because we may be requested to buffer across multiple words + PRInt32 mBufferPos; + #ifdef DEBUG static void SelfTest(nsILineBreaker* aLineBreaker, nsIWordBreaker* aWordBreaker);