diff --git a/mozilla/layout/generic/nsFrame.cpp b/mozilla/layout/generic/nsFrame.cpp index 3f1502ccd98..56a35d144a9 100644 --- a/mozilla/layout/generic/nsFrame.cpp +++ b/mozilla/layout/generic/nsFrame.cpp @@ -3210,17 +3210,6 @@ nsFrame::Reflow(nsPresContext* aPresContext, return NS_OK; } -NS_IMETHODIMP -nsFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable) -{ - aDeltaWidth = 0; - aLastCharIsJustifiable = PR_FALSE; - return NS_OK; -} - NS_IMETHODIMP nsFrame::CharacterDataChanged(nsPresContext* aPresContext, nsIContent* aChild, diff --git a/mozilla/layout/generic/nsFrame.h b/mozilla/layout/generic/nsFrame.h index 1e5c41faeb3..ec51d38f9e7 100644 --- a/mozilla/layout/generic/nsFrame.h +++ b/mozilla/layout/generic/nsFrame.h @@ -329,10 +329,6 @@ public: const nsHTMLReflowState* aReflowState, nsDidReflowStatus aStatus); virtual PRBool CanContinueTextRun() const; - NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable); // Selection Methods // XXX Doc me... (in nsIFrame.h puhleeze) diff --git a/mozilla/layout/generic/nsIFrame.h b/mozilla/layout/generic/nsIFrame.h index 4100ca9e08e..28b4307f818 100644 --- a/mozilla/layout/generic/nsIFrame.h +++ b/mozilla/layout/generic/nsIFrame.h @@ -103,11 +103,11 @@ struct nsMargin; typedef class nsIFrame nsIBox; -// IID for the nsIFrame interface -// 95f75c0a-de85-437a-a195-0304df3f62ce -#define NS_IFRAME_IID \ -{ 0x95f75c0a, 0xde85, 0x437a, \ - { 0xa1, 0x95, 0x03, 0x04, 0xdf, 0x3f, 0x62, 0xce } } +// IID for the nsIFrame interface +// 04a7dee5-3435-47dc-bd42-a36c0f66a42c + #define NS_IFRAME_IID \ +{ 0x04a7dee5, 0x3435, 0x47dc, \ + { 0xbd, 0x42, 0xa3, 0x6c, 0x0f, 0x66, 0xa4, 0x2c } } /** * Indication of how the frame can be split. This is used when doing runaround @@ -1430,13 +1430,6 @@ public: */ virtual PRBool CanContinueTextRun() const = 0; - // Justification helper method that is used to remove trailing - // whitespace before justification. - NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable) = 0; - /** * Append the rendered text to the passed-in string. * The appended text will often not contain all the whitespace from source, diff --git a/mozilla/layout/generic/nsLineLayout.cpp b/mozilla/layout/generic/nsLineLayout.cpp index 7c31efd4721..3b5a68e36b4 100644 --- a/mozilla/layout/generic/nsLineLayout.cpp +++ b/mozilla/layout/generic/nsLineLayout.cpp @@ -2283,27 +2283,30 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, *aDeltaWidth = 0; return PR_TRUE; } - else if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME)) { - nscoord deltaWidth = 0; - PRBool lastCharIsJustifiable = PR_FALSE; - pfd->mFrame->TrimTrailingWhiteSpace(mPresContext, - *mBlockReflowState->rendContext, - deltaWidth, - lastCharIsJustifiable); + else if (pfd->GetFlag(PFD_ISTEXTFRAME)) { + // Call TrimTrailingWhiteSpace even on empty textframes because they + // might have a soft hyphen which should now appear, changing the frame's + // width + nsTextFrame::TrimOutput trimOutput = static_cast(pfd->mFrame)-> + TrimTrailingWhiteSpace(mBlockReflowState->rendContext); #ifdef NOISY_TRIM nsFrame::ListTag(stdout, (psd == mRootSpan ? mBlockReflowState->frame : psd->mFrame->mFrame)); printf(": trim of "); nsFrame::ListTag(stdout, pfd->mFrame); - printf(" returned %d\n", deltaWidth); + printf(" returned %d\n", trimOutput.mDeltaWidth); #endif - if (lastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) { + if (trimOutput.mLastCharIsJustifiable && pfd->mJustificationNumSpaces > 0) { pfd->mJustificationNumSpaces--; } + + if (trimOutput.mChanged) { + pfd->SetFlag(PFD_RECOMPUTEOVERFLOW, PR_TRUE); + } - if (deltaWidth) { - pfd->mBounds.width -= deltaWidth; + if (trimOutput.mDeltaWidth) { + pfd->mBounds.width -= trimOutput.mDeltaWidth; // See if the text frame has already been placed in its parent if (psd != mRootSpan) { @@ -2313,7 +2316,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, } // Adjust containing span's right edge - psd->mX -= deltaWidth; + psd->mX -= trimOutput.mDeltaWidth; // Slide any frames that follow the text frame over by the // right amount. The only thing that can follow the text @@ -2321,7 +2324,7 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, // sensible (keeping the combined area honest). while (pfd->mNext) { pfd = pfd->mNext; - pfd->mBounds.x -= deltaWidth; + pfd->mBounds.x -= trimOutput.mDeltaWidth; if (psd != mRootSpan) { // When the child span is not a direct child of the block // we need to update the child spans frame rectangle @@ -2329,14 +2332,16 @@ nsLineLayout::TrimTrailingWhiteSpaceIn(PerSpanData* psd, // that are direct children of the block will be updated // later, however, because the VerticalAlignFrames method // will be run after this method. - SlideSpanFrameRect(pfd->mFrame, deltaWidth); + SlideSpanFrameRect(pfd->mFrame, trimOutput.mDeltaWidth); } } } - // Pass up to caller so they can shrink their span - *aDeltaWidth = deltaWidth; - return PR_TRUE; + if (pfd->GetFlag(PFD_ISNONEMPTYTEXTFRAME) || trimOutput.mChanged) { + // Pass up to caller so they can shrink their span + *aDeltaWidth = trimOutput.mDeltaWidth; + return PR_TRUE; + } } pfd = pfd->mPrev; } diff --git a/mozilla/layout/generic/nsTextFrame.h b/mozilla/layout/generic/nsTextFrame.h index 53e1af28e62..8c90d8211d8 100644 --- a/mozilla/layout/generic/nsTextFrame.h +++ b/mozilla/layout/generic/nsTextFrame.h @@ -208,10 +208,21 @@ public: const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus); virtual PRBool CanContinueTextRun() const; - NS_IMETHOD TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable); + // Method that is called for a text frame that is logically + // adjacent to the end of the line (i.e. followed only by empty text frames, + // placeholders or inlines containing such). + struct TrimOutput { + // true if we trimmed some space or changed metrics in some other way. + // In this case, we should call RecomputeOverflowRect on this frame. + PRPackedBool mChanged; + // true if the last character is not justifiable so should be subtracted + // from the count of justifiable characters in the frame, since the last + // character in a line is not justifiable. + PRPackedBool mLastCharIsJustifiable; + // an amount to *subtract* from the frame's width (zero if !mChanged) + nscoord mDeltaWidth; + }; + TrimOutput TrimTrailingWhiteSpace(nsIRenderingContext* aRC); virtual nsresult GetRenderedText(nsAString* aString = nsnull, gfxSkipChars* aSkipChars = nsnull, gfxSkipCharsIterator* aSkipIter = nsnull, diff --git a/mozilla/layout/generic/nsTextFrameThebes.cpp b/mozilla/layout/generic/nsTextFrameThebes.cpp index e208f841b51..a31911ff9e2 100644 --- a/mozilla/layout/generic/nsTextFrameThebes.cpp +++ b/mozilla/layout/generic/nsTextFrameThebes.cpp @@ -2017,6 +2017,11 @@ public: * Count the number of justifiable characters in the given DOM range */ PRUint32 ComputeJustifiableCharacters(PRInt32 aOffset, PRInt32 aLength); + /** + * Find the start and end of the justifiable characters. Does not depend on the + * position of aStart or aEnd, although it's most efficient if they are near the + * start and end of the text frame. + */ void FindJustificationRange(gfxSkipCharsIterator* aStart, gfxSkipCharsIterator* aEnd); @@ -4883,7 +4888,6 @@ nsTextFrame::AddInlineMinWidthForFlow(nsIRenderingContext *aRenderingContext, aData->atStartOfLine = PR_FALSE; if (collapseWhitespace) { - nscoord trailingWhitespaceWidth; PRUint32 trimStart = GetEndOfTrimmedText(frag, wordStart, i, &iter); if (trimStart == start) { // This is *all* trimmable whitespace, so whatever trailingWhitespace @@ -5513,39 +5517,39 @@ nsTextFrame::CanContinueTextRun() const return PR_TRUE; } -NS_IMETHODIMP -nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, - nsIRenderingContext& aRC, - nscoord& aDeltaWidth, - PRBool& aLastCharIsJustifiable) +nsTextFrame::TrimOutput +nsTextFrame::TrimTrailingWhiteSpace(nsIRenderingContext* aRC) { - aLastCharIsJustifiable = PR_FALSE; - aDeltaWidth = 0; + TrimOutput result; + result.mChanged = PR_FALSE; + result.mLastCharIsJustifiable = PR_FALSE; + result.mDeltaWidth = 0; AddStateBits(TEXT_END_OF_LINE); PRInt32 contentLength = GetContentLength(); if (!contentLength) - return NS_OK; + return result; gfxContext* ctx = static_cast - (aRC.GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)); + (aRC->GetNativeGraphicData(nsIRenderingContext::NATIVE_THEBES_CONTEXT)); gfxSkipCharsIterator start = EnsureTextRun(ctx); - if (!mTextRun) - return NS_ERROR_FAILURE; + NS_ENSURE_TRUE(mTextRun, result); + PRUint32 trimmedStart = start.GetSkippedOffset(); const nsTextFragment* frag = mContent->GetText(); TrimmedOffsets trimmed = GetTrimmedOffsets(frag, PR_TRUE); - gfxSkipCharsIterator iter = start; + gfxSkipCharsIterator trimmedEndIter = start; const nsStyleText* textStyle = GetStyleText(); gfxFloat delta = 0; - PRUint32 trimmedEnd = iter.ConvertOriginalToSkipped(trimmed.GetEnd()); + PRUint32 trimmedEnd = trimmedEndIter.ConvertOriginalToSkipped(trimmed.GetEnd()); if (GetStateBits() & TEXT_TRIMMED_TRAILING_WHITESPACE) { - aLastCharIsJustifiable = PR_TRUE; + // We pre-trimmed this frame, so the last character is justifiable + result.mLastCharIsJustifiable = PR_TRUE; } else if (trimmed.GetEnd() < GetContentEnd()) { - gfxSkipCharsIterator end = iter; + gfxSkipCharsIterator end = trimmedEndIter; PRUint32 endOffset = end.ConvertOriginalToSkipped(GetContentOffset() + contentLength); if (trimmedEnd < endOffset) { // We can't be dealing with tabs here ... they wouldn't be trimmed. So it's @@ -5556,23 +5560,36 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, // non-compressed whitespace being skipped at end of line -> justifiable // XXX should we actually *count* justifiable characters that should be // removed from the overall count? I think so... - aLastCharIsJustifiable = PR_TRUE; + result.mLastCharIsJustifiable = PR_TRUE; + result.mChanged = PR_TRUE; } } - if (!aLastCharIsJustifiable && + if (trimmed.GetEnd() == GetContentEnd() && + HasSoftHyphenBefore(frag, mTextRun, trimmed.mStart, trimmedEndIter)) { + // This is a soft hyphen break. + // Fix up metrics to include hyphen + result.mChanged = PR_TRUE; + gfxTextRunCache::AutoTextRun hyphenTextRun(GetHyphenTextRun(mTextRun, ctx, this)); + if (hyphenTextRun.get()) { + delta = -hyphenTextRun->GetAdvanceWidth(0, hyphenTextRun->GetLength(), nsnull); + } + AddStateBits(TEXT_HYPHEN_BREAK); + } + + if (!result.mLastCharIsJustifiable && NS_STYLE_TEXT_ALIGN_JUSTIFY == textStyle->mTextAlign) { // Check if any character in the last cluster is justifiable PropertyProvider provider(mTextRun, textStyle, frag, this, start, contentLength, nsnull, 0); PRBool isCJK = IsChineseJapaneseLangGroup(this); - gfxSkipCharsIterator justificationStart(iter), justificationEnd(iter); + gfxSkipCharsIterator justificationStart(start), justificationEnd(trimmedEndIter); provider.FindJustificationRange(&justificationStart, &justificationEnd); PRInt32 i; for (i = justificationEnd.GetOriginalOffset(); i < trimmed.GetEnd(); ++i) { if (IsJustifiableCharacter(frag, i, isCJK)) { - aLastCharIsJustifiable = PR_TRUE; + result.mLastCharIsJustifiable = PR_TRUE; } } } @@ -5581,31 +5598,36 @@ nsTextFrame::TrimTrailingWhiteSpace(nsPresContext* aPresContext, mTextRun->SetLineBreaks(trimmedStart, trimmedEnd - trimmedStart, (GetStateBits() & TEXT_START_OF_LINE) != 0, PR_TRUE, &advanceDelta, ctx); + if (advanceDelta != 0) { + result.mChanged = PR_TRUE; + } // aDeltaWidth is *subtracted* from our width. // If advanceDelta is positive then setting the line break made us longer, // so aDeltaWidth could go negative. - aDeltaWidth = NSToCoordFloor(delta - advanceDelta); - // XXX if aDeltaWidth goes negative, that means this frame might not actually fit - // anymore!!! We need higher level line layout to recover somehow. This can - // really only happen when we have glyphs with special shapes at the end of - // lines, I think. Breaking inside a kerning pair won't do it because that - // would mean we broke inside this textrun, and BreakAndMeasureText should - // make sure the resulting shaped substring fits. Maybe if we passed a - // maxTextLength? But that only happens at direction changes (so we - // we wouldn't kern across the boundary) or for first-letter (which always fits - // because it starts the line!). - if (aDeltaWidth < 0) { - NS_WARNING("Negative deltawidth, something odd is happening"); - } - - // XXX what about adjusting bounding metrics? + result.mDeltaWidth = NSToCoordFloor(delta - advanceDelta); + // If aDeltaWidth goes negative, that means this frame might not actually fit + // anymore!!! We need higher level line layout to recover somehow. + // If it's because the frame has a soft hyphen that is now being displayed, + // this should actually be OK, because our reflow recorded the break + // opportunity that allowed the soft hyphen to be used, and we wouldn't + // have recorded the opportunity unless the hyphen fit (or was the first + // opportunity on the line). + // Otherwise this can/ really only happen when we have glyphs with special + // shapes at the end of lines, I think. Breaking inside a kerning pair won't + // do it because that would mean we broke inside this textrun, and + // BreakAndMeasureText should make sure the resulting shaped substring fits. + // Maybe if we passed a maxTextLength? But that only happens at direction + // changes (so we wouldn't kern across the boundary) or for first-letter + // (which always fits because it starts the line!). + NS_WARN_IF_FALSE(result.mDeltaWidth >= 0 || (GetStateBits() & TEXT_HYPHEN_BREAK), + "Negative deltawidth in a non-hyphen case, something odd is happening"); #ifdef NOISY_TRIM ListTag(stdout); - printf(": trim => %d\n", aDeltaWidth); + printf(": trim => %d\n", result.mDeltaWidth); #endif - return NS_OK; + return result; } nsRect diff --git a/mozilla/layout/reftests/reftest.list b/mozilla/layout/reftests/reftest.list index e479280c289..78687716026 100644 --- a/mozilla/layout/reftests/reftest.list +++ b/mozilla/layout/reftests/reftest.list @@ -48,6 +48,9 @@ include first-line/reftest.list # svg/ include svg/reftest.list +# text/ +include text/reftest.list + # text-decoration/ include text-decoration/reftest.list diff --git a/mozilla/layout/reftests/text/soft-hyphens-1-ref.html b/mozilla/layout/reftests/text/soft-hyphens-1-ref.html index b405be7cfb1..22856622cd7 100644 --- a/mozilla/layout/reftests/text/soft-hyphens-1-ref.html +++ b/mozilla/layout/reftests/text/soft-hyphens-1-ref.html @@ -2,11 +2,11 @@
-

Hy-
phen. -

Hy-
phen. -

Hy-
phen. -

Hy-
phen. -

Hy-
phen. +

Hy‐
phen. +

Hy‐
phen. +

Hy‐
phen. +

Hy‐
phen. +

Hy‐
phen.